Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

webgl-obj-loader

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webgl-obj-loader - npm Package Compare versions

Comparing version 1.0.0 to 1.0.2

.prettierignore

2

dist/webgl-obj-loader.min.js

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("OBJ",[],t):"object"==typeof exports?exports.OBJ=t():e.OBJ=t()}(this,function(){return function(e){function t(a){if(r[a])return r[a].exports;var i=r[a]={i:a,l:!1,exports:{}};return e[a].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var r={};return t.m=e,t.c=r,t.d=function(exports,e,r){t.o(exports,e)||Object.defineProperty(exports,e,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/",t(t.s=3)}([function(e,exports,t){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e){switch(e){case"BYTE":case"UNSIGNED_BYTE":return 1;case"SHORT":case"UNSIGNED_SHORT":return 2;case"FLOAT":return 4}}Object.defineProperty(exports,"__esModule",{value:!0});var i=exports.Layout=function e(){r(this,e);for(var t=arguments.length,a=Array(t),i=0;i<t;i++)a[i]=arguments[i];this.attributes=a;var s=0,l=0,o=!0,u=!1,f=void 0;try{for(var c,p=a[Symbol.iterator]();!(o=(c=p.next()).done);o=!0){var h=c.value;if(this[h.key])throw new n(h);s%h.sizeOfType!=0&&(s+=h.sizeOfType-s%h.sizeOfType),this[h.key]={attribute:h,size:h.size,type:h.type,normalized:h.normalized,offset:s},s+=h.sizeInBytes,l=Math.max(l,h.sizeOfType)}}catch(e){u=!0,f=e}finally{try{!o&&p.return&&p.return()}finally{if(u)throw f}}s%l!=0&&(s+=l-s%l),this.stride=s;var v=!0,m=!1,y=void 0;try{for(var d,M=a[Symbol.iterator]();!(v=(d=M.next()).done);v=!0){this[d.value.key].stride=this.stride}}catch(e){m=!0,y=e}finally{try{!v&&M.return&&M.return()}finally{if(m)throw y}}},n=function e(t){r(this,e),this.message="found duplicate attribute: "+t.key},s=function e(t,i,n){arguments.length>3&&void 0!==arguments[3]&&arguments[3];r(this,e),this.key=t,this.size=i,this.type=n,this.normalized=!1,this.sizeOfType=a(n),this.sizeInBytes=this.sizeOfType*i};i.POSITION=new s("position",3,"FLOAT"),i.NORMAL=new s("normal",3,"FLOAT"),i.TANGENT=new s("tangent",3,"FLOAT"),i.BITANGENT=new s("bitangent",3,"FLOAT"),i.UV=new s("uv",2,"FLOAT"),i.MATERIAL_INDEX=new s("materialIndex",1,"SHORT"),i.MATERIAL_ENABLED=new s("materialEnabled",1,"UNSIGNED_SHORT"),i.AMBIENT=new s("ambient",3,"FLOAT"),i.DIFFUSE=new s("diffuse",3,"FLOAT"),i.SPECULAR=new s("specular",3,"FLOAT"),i.SPECULAR_EXPONENT=new s("specularExponent",3,"FLOAT"),i.EMISSIVE=new s("emissive",3,"FLOAT"),i.TRANSMISSION_FILTER=new s("transmissionFilter",3,"FLOAT"),i.DISSOLVE=new s("dissolve",1,"FLOAT"),i.ILLUMINATION=new s("illumination",1,"UNSIGNED_SHORT"),i.REFRACTION_INDEX=new s("refractionIndex",1,"FLOAT"),i.SHARPNESS=new s("sharpness",1,"FLOAT"),i.MAP_DIFFUSE=new s("mapDiffuse",1,"SHORT"),i.MAP_AMBIENT=new s("mapAmbient",1,"SHORT"),i.MAP_SPECULAR=new s("mapSpecular",1,"SHORT"),i.MAP_SPECULAR_EXPONENT=new s("mapSpecularExponent",1,"SHORT"),i.MAP_DISSOLVE=new s("mapDissolve",1,"SHORT"),i.ANTI_ALIASING=new s("antiAliasing",1,"UNSIGNED_SHORT"),i.MAP_BUMP=new s("mapBump",1,"SHORT"),i.MAP_DISPLACEMENT=new s("mapDisplacement",1,"SHORT"),i.MAP_DECAL=new s("mapDecal",1,"SHORT"),i.MAP_EMISSIVE=new s("mapEmissive",1,"SHORT")},function(e,exports,t){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,r=Array(e.length);t<e.length;t++)r[t]=e[t];return r}return Array.from(e)}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var i=function(){function e(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(t,r,a){return r&&e(t.prototype,r),a&&e(t,a),t}}(),n=t(0),s=function(){function e(t,i){a(this,e),i=i||{},i.materials=i.materials||[];var n=this;n.has_materials=!!i.materials,n.materials={},n.vertices=[],n.vertexNormals=[],n.textures=[],n.indices=[],this.name="";var s=[],l=[],o=[],u={},f=[],c={},p=-1;u.verts=[],u.norms=[],u.textures=[],u.hashindices={},u.indices=[],u.materialIndices=[],u.index=0;for(var h=/^v\s/,v=/^vn\s/,m=/^vt\s/,y=/^f\s/,d=/\s+/,M=/^usemtl/,b=t.split("\n"),I=0;I<b.length;I++){var _=b[I].trim();if(_&&!_.startsWith("#")){var x=_.split(d);if(x.shift(),h.test(_))s.push.apply(s,r(x));else if(v.test(_))l.push.apply(l,r(x));else if(m.test(_))o.push.apply(o,r(x));else if(M.test(_)){var k=x[0];k in c||(f.push(k),c[k]=f.length-1),p=c[k]}else if(y.test(_))for(var A=!1,w=0,F=x.length;w<F;w++){3!==w||A||(w=2,A=!0);var E=x[0]+","+p,S=x[w]+","+p;if(S in u.hashindices)u.indices.push(u.hashindices[S]);else{var T=x[w].split("/"),O=T.length-1;u.verts.push(+s[3*(T[0]-1)+0]),u.verts.push(+s[3*(T[0]-1)+1]),u.verts.push(+s[3*(T[0]-1)+2]),o.length&&(u.textures.push(+o[2*(T[1]-1)+0]),u.textures.push(+o[2*(T[1]-1)+1])),u.norms.push(+l[3*(T[O]-1)+0]),u.norms.push(+l[3*(T[O]-1)+1]),u.norms.push(+l[3*(T[O]-1)+2]),u.materialIndices.push(p),u.hashindices[S]=u.index,u.indices.push(u.index),u.index+=1}3===w&&A&&u.indices.push(u.hashindices[E])}}}n.vertices=u.verts,n.vertexNormals=u.norms,n.textures=u.textures,n.vertexMaterialIndices=u.materialIndices,n.indices=u.indices,n.materialNames=f,n.materialIndices=c,n.materialsByIndex={}}return i(e,[{key:"makeBufferData",value:function(e){var t=this.vertices.length/3,r=new ArrayBuffer(e.stride*t);r.numItems=t;for(var a=new DataView(r),i=0,s=0;i<t;i++){s=i*e.stride;var l=!0,o=!1,u=void 0;try{for(var f,c=e.attributes[Symbol.iterator]();!(l=(f=c.next()).done);l=!0){var p=f.value,h=s+e[p.key].offset;switch(p.key){case n.Layout.POSITION.key:a.setFloat32(h,this.vertices[3*i],!0),a.setFloat32(h+4,this.vertices[3*i+1],!0),a.setFloat32(h+8,this.vertices[3*i+2],!0);break;case n.Layout.UV.key:a.setFloat32(h,this.textures[2*i],!0),a.setFloat32(h+4,this.vertices[2*i+1],!0);break;case n.Layout.NORMAL.key:a.setFloat32(h,this.vertexNormals[3*i],!0),a.setFloat32(h+4,this.vertexNormals[3*i+1],!0),a.setFloat32(h+8,this.vertexNormals[3*i+2],!0);break;case n.Layout.MATERIAL_INDEX.key:a.setInt16(h,this.vertexMaterialIndices[i],!0);break;case n.Layout.AMBIENT.key:var v=this.vertexMaterialIndices[i],m=this.materialsByIndex[v];if(!m)break;a.setFloat32(h,m.ambient[0],!0),a.setFloat32(h+4,m.ambient[1],!0),a.setFloat32(h+8,m.ambient[2],!0);break;case n.Layout.DIFFUSE.key:var y=this.vertexMaterialIndices[i],d=this.materialsByIndex[y];if(!d)break;a.setFloat32(h,d.diffuse[0],!0),a.setFloat32(h+4,d.diffuse[1],!0),a.setFloat32(h+8,d.diffuse[2],!0);break;case n.Layout.SPECULAR.key:var M=this.vertexMaterialIndices[i],b=this.materialsByIndex[M];if(!b)break;a.setFloat32(h,b.specular[0],!0),a.setFloat32(h+4,b.specular[1],!0),a.setFloat32(h+8,b.specular[2],!0);break;case n.Layout.SPECULAR_EXPONENT.key:var I=this.vertexMaterialIndices[i],_=this.materialsByIndex[I];if(!_)break;a.setFloat32(h,_.specularExponent,!0);break;case n.Layout.EMISSIVE.key:var x=this.vertexMaterialIndices[i],k=this.materialsByIndex[x];if(!k)break;a.setFloat32(h,k.emissive[0],!0),a.setFloat32(h+4,k.emissive[1],!0),a.setFloat32(h+8,k.emissive[2],!0);break;case n.Layout.TRANSMISSION_FILTER.key:var A=this.vertexMaterialIndices[i],w=this.materialsByIndex[A];if(!w)break;a.setFloat32(h,w.transmissionFilter[0],!0),a.setFloat32(h+4,w.transmissionFilter[1],!0),a.setFloat32(h+8,w.transmissionFilter[2],!0);break;case n.Layout.DISSOLVE.key:var F=this.vertexMaterialIndices[i],E=this.materialsByIndex[F];if(!E)break;a.setFloat32(h,E.dissolve,!0);break;case n.Layout.ILLUMINATION.key:var S=this.vertexMaterialIndices[i],T=this.materialsByIndex[S];if(!T)break;a.setInt16(h,T.illumination,!0);break;case n.Layout.REFRACTION_INDEX.key:var O=this.vertexMaterialIndices[i],L=this.materialsByIndex[O];if(!L)break;a.setFloat32(h,L.refractionIndex,!0);break;case n.Layout.SHARPNESS.key:var B=this.vertexMaterialIndices[i],N=this.materialsByIndex[B];if(!N)break;a.setFloat32(h,N.sharpness,!0);break;case n.Layout.ANTI_ALIASING.key:var R=this.vertexMaterialIndices[i],P=this.materialsByIndex[R];if(!P)break;a.setInt16(h,P.antiAliasing,!0)}}}catch(e){o=!0,u=e}finally{try{!l&&c.return&&c.return()}finally{if(o)throw u}}}return r}},{key:"makeIndexBufferData",value:function(){var e=new Uint16Array(this.indices);return e.numItems=this.indices.length,e}},{key:"addMaterialLibrary",value:function(e){for(var t in e.materials)if(t in this.materialIndices){var r=e.materials[t],a=this.materialIndices[r.name];this.materialsByIndex[a]=r}}}]),e}();exports.default=s},function(e,exports,t){"use strict";function r(e){return Array.isArray(e)?e:Array.from(e)}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var i=function(){function e(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(t,r,a){return r&&e(t.prototype,r),a&&e(t,a),t}}(),n=exports.Material=function e(t){a(this,e),this.name=t,this.ambient=[0,0,0],this.diffuse=[0,0,0],this.specular=[0,0,0],this.emissive=[0,0,0],this.transmissionFilter=[0,0,0],this.dissolve=0,this.specularExponent=0,this.transparency=0,this.illumination=0,this.refractionIndex=1,this.sharpness=0,this.mapDiffuse=null,this.mapAmbient=null,this.mapSpecular=null,this.mapSpecularExponent=null,this.mapDissolve=null,this.antiAliasing=!1,this.mapBump=null,this.mapDisplacement=null,this.mapDecal=null,this.mapEmissive=null,this.mapReflections=[]};exports.MaterialLibrary=function(){function e(t){a(this,e),this.data=t,this.currentMaterial=null,this.materials={},this.parse()}return i(e,[{key:"parse_newmtl",value:function(e){var t=e[0];this.currentMaterial=new n(t),this.materials[t]=this.currentMaterial}},{key:"parseColor",value:function(e){if("spectral"!=e[0]&&"xyz"!=e[0]){if(3==e.length)return e.map(parseFloat);var t=parseFloat(e[0]);return[t,t,t]}}},{key:"parse_Ka",value:function(e){this.currentMaterial.ambient=this.parseColor(e)}},{key:"parse_Kd",value:function(e){this.currentMaterial.diffuse=this.parseColor(e)}},{key:"parse_Ks",value:function(e){this.currentMaterial.specular=this.parseColor(e)}},{key:"parse_Ke",value:function(e){this.currentMaterial.emissive=this.parseColor(e)}},{key:"parse_Tf",value:function(e){this.currentMaterial.transmissionFilter=this.parseColor(e)}},{key:"parse_d",value:function(e){this.currentMaterial.dissolve=parseFloat(e.pop())}},{key:"parse_illum",value:function(e){this.currentMaterial.illumination=parseInt(e[0])}},{key:"parse_Ni",value:function(e){this.currentMaterial.refractionIndex=parseFloat(e[0])}},{key:"parse_Ns",value:function(e){this.currentMaterial.specularExponent=parseInt(e[0])}},{key:"parse_sharpness",value:function(e){this.currentMaterial.sharpness=parseInt(e[0])}},{key:"parse_cc",value:function(e,t){t.colorCorrection="on"==e[0]}},{key:"parse_blendu",value:function(e,t){t.horizontalBlending="on"==e[0]}},{key:"parse_blendv",value:function(e,t){t.verticalBlending="on"==e[0]}},{key:"parse_boost",value:function(e,t){t.boostMipMapSharpness=parseFloat(e[0])}},{key:"parse_mm",value:function(e,t){t.modifyTextureMap.brightness=parseFloat(e[0]),t.modifyTextureMap.contrast=parseFloat(e[1])}},{key:"parse_ost",value:function(e,t,r){for(;e.length<3;)e.push(r);t.u=parseFloat(e[0]),t.v=parseFloat(e[1]),t.w=parseFloat(e[2])}},{key:"parse_o",value:function(e,t){this.parse_ost(e,t.offset,0)}},{key:"parse_s",value:function(e,t){this.parse_ost(e,t.scale,1)}},{key:"parse_t",value:function(e,t){this.parse_ost(e,t.turbulence,0)}},{key:"parse_texres",value:function(e,t){t.textureResolution=parseFloat(e[0])}},{key:"parse_clamp",value:function(e,t){t.clamp="on"==e[0]}},{key:"parse_bm",value:function(e,t){t.bumpMultiplier=parseFloat(e[0])}},{key:"parse_imfchan",value:function(e,t){t.imfChan=e[0]}},{key:"parse_type",value:function(e,t){t.reflectionType=e[0]}},{key:"parseOptions",value:function(e){var t={colorCorrection:!1,horizontalBlending:!0,verticalBlending:!0,boostMipMapSharpness:0,modifyTextureMap:{brightness:0,contrast:1},offset:{u:0,v:0,w:0},scale:{u:1,v:1,w:1},turbulence:{u:0,v:0,w:0},clamp:!1,textureResolution:null,bumpMultiplier:1,imfChan:null},r=void 0,a=void 0,i={};for(e.reverse();e.length;){var n=e.pop();n.startsWith("-")?(r=n.substr(1),i[r]=[]):i[r].push(n)}for(r in i)if(i.hasOwnProperty(r)){a=i[r];var s=this["parse_"+r];s&&s.bind(this)(a,t)}return t}},{key:"parseMap",value:function(e){var t=void 0,a=void 0;if(e[0].startsWith("-"))t=e.pop(),a=e;else{var i=r(e);t=i[0],a=i.slice(1)}return a=this.parseOptions(a),a.filename=t,a}},{key:"parse_map_Ka",value:function(e){this.currentMaterial.mapAmbient=this.parseMap(e)}},{key:"parse_map_Kd",value:function(e){this.currentMaterial.mapDiffuse=this.parseMap(e)}},{key:"parse_map_Ks",value:function(e){this.currentMaterial.mapSpecular=this.parseMap(e)}},{key:"parse_map_Ke",value:function(e){this.currentMaterial.mapEmissive=this.parseMap(e)}},{key:"parse_map_Ns",value:function(e){this.currentMaterial.mapSpecularExponent=this.parseMap(e)}},{key:"parse_map_d",value:function(e){this.currentMaterial.mapDissolve=this.parseMap(e)}},{key:"parse_map_aat",value:function(e){this.currentMaterial.antiAliasing="on"==e[0]}},{key:"parse_map_bump",value:function(e){this.currentMaterial.mapBump=this.parseMap(e)}},{key:"parse_bump",value:function(e){this.parse_map_bump(e)}},{key:"parse_disp",value:function(e){this.currentMaterial.mapDisplacement=this.parseMap(e)}},{key:"parse_decal",value:function(e){this.currentMaterial.mapDecal=this.parseMap(e)}},{key:"parse_refl",value:function(e){this.currentMaterial.mapReflections.push(this.parseMap(e))}},{key:"parse",value:function(){var e=this.data.split(/\r?\n/),t=!0,a=!1,i=void 0;try{for(var n,s=e[Symbol.iterator]();!(t=(n=s.next()).done);t=!0){var l=n.value;if((l=l.trim())&&!l.startsWith("#")){var o=l.split(/\s/),u=void 0,f=o,c=r(f);u=c[0],o=c.slice(1);var p=this["parse_"+u];p&&p.bind(this)(o)}}}catch(e){a=!0,i=e}finally{try{!t&&s.return&&s.return()}finally{if(a)throw i}}delete this.data,this.currentMaterial=null}}]),e}()},function(e,exports,t){e.exports=t(4)},function(e,exports,t){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.deleteMeshBuffers=exports.initMeshBuffers=exports.downloadMeshes=exports.downloadModels=exports.Layout=exports.MaterialLibrary=exports.Material=exports.Mesh=void 0;var r=t(1),a=function(e){return e&&e.__esModule?e:{default:e}}(r),i=t(2),n=t(0),s=t(5);exports.Mesh=a.default,exports.Material=i.Material,exports.MaterialLibrary=i.MaterialLibrary,exports.Layout=n.Layout,exports.downloadModels=s.downloadModels,exports.downloadMeshes=s.downloadMeshes,exports.initMeshBuffers=s.initMeshBuffers,exports.deleteMeshBuffers=s.deleteMeshBuffers},function(e,exports,t){"use strict";function r(e,t){var r=["mapDiffuse","mapAmbient","mapSpecular","mapDissolve","mapBump","mapDisplacement","mapDecal","mapEmissive"];t.endsWith("/")||(t+="/");var a=[];for(var i in e.materials)if(e.materials.hasOwnProperty(i)){i=e.materials[i];var n=!0,s=!1,l=void 0;try{for(var o,u=r[Symbol.iterator]();!(n=(o=u.next()).done);n=!0){var f=o.value;(function(e){var r=i[e];if(!r)return"continue";var n=t+r.filename;a.push(fetch(n).then(function(e){if(!e.ok)throw new Error;return e.blob()}).then(function(e){var t=new Image;t.src=URL.createObjectURL(e),r.texture=t}).catch(function(){}))})(f)}}catch(e){s=!0,l=e}finally{try{!n&&u.return&&u.return()}finally{if(s)throw l}}}return Promise.all(a)}function a(e){var t=[],a=!0,i=!1,n=void 0;try{for(var s,o=e[Symbol.iterator]();!(a=(s=o.next()).done);a=!0){var c=s.value;!function(e){var a=[];if(!e.obj)throw new Error('"obj" attribute of model object not set. The .obj file is required to be set in order to use downloadModels()');var i=e.name;if(!i){var n=e.obj.split("/");i=n[n.length-1].replace(".obj","")}if(a.push(Promise.resolve(i)),a.push(fetch(e.obj).then(function(e){return e.text()}).then(function(e){return new u.default(e)})),e.mtl){var s=e.mtl;"boolean"==typeof s&&(s=e.obj.replace(/\.obj$/,".mtl")),a.push(fetch(s).then(function(e){return e.text()}).then(function(t){var a=new f.MaterialLibrary(t);if(!1!==e.downloadMtlTextures){var i=e.mtlTextureRoot;return i||(i=s.substr(0,s.lastIndexOf("/"))),Promise.all([Promise.resolve(a),r(a,i)])}return Promise.all(Promise.resolve(a))}).then(function(e){return e[0]}))}t.push(Promise.all(a))}(c)}}catch(e){i=!0,n=e}finally{try{!a&&o.return&&o.return()}finally{if(i)throw n}}return Promise.all(t).then(function(e){var t={},r=!0,a=!1,i=void 0;try{for(var n,s=e[Symbol.iterator]();!(r=(n=s.next()).done);r=!0){var o=n.value,u=l(o,3),f=u[0],c=u[1],p=u[2];c.name=f,p&&c.addMaterialLibrary(p),t[f]=c}}catch(e){a=!0,i=e}finally{try{!r&&s.return&&s.return()}finally{if(a)throw i}}return t})}function i(e,t,r){void 0===r&&(r={});var a=[];for(var i in e){(function(t){if(!e.hasOwnProperty(t))return"continue";var r=e[t];a.push(fetch(r).then(function(e){return e.text()}).then(function(e){return[t,new u.default(e)]}))})(i)}Promise.all(a).then(function(e){var a=!0,i=!1,n=void 0;try{for(var s,o=e[Symbol.iterator]();!(a=(s=o.next()).done);a=!0){var u=s.value,f=l(u,2),c=f[0],p=f[1];r[c]=p}}catch(e){i=!0,n=e}finally{try{!a&&o.return&&o.return()}finally{if(i)throw n}}return t(r)})}function n(e,t){t.normalBuffer=c(e,e.ARRAY_BUFFER,t.vertexNormals,3),t.textureBuffer=c(e,e.ARRAY_BUFFER,t.textures,2),t.vertexBuffer=c(e,e.ARRAY_BUFFER,t.vertices,3),t.indexBuffer=c(e,e.ELEMENT_ARRAY_BUFFER,t.indices,1)}function s(e,t){e.deleteBuffer(t.normalBuffer),e.deleteBuffer(t.textureBuffer),e.deleteBuffer(t.vertexBuffer),e.deleteBuffer(t.indexBuffer)}Object.defineProperty(exports,"__esModule",{value:!0});var l=function(){function e(e,t){var r=[],a=!0,i=!1,n=void 0;try{for(var s,l=e[Symbol.iterator]();!(a=(s=l.next()).done)&&(r.push(s.value),!t||r.length!==t);a=!0);}catch(e){i=!0,n=e}finally{try{!a&&l.return&&l.return()}finally{if(i)throw n}}return r}return function(t,r){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,r);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();exports.downloadModels=a,exports.downloadMeshes=i,exports.initMeshBuffers=n,exports.deleteMeshBuffers=s;var o=t(1),u=function(e){return e&&e.__esModule?e:{default:e}}(o),f=t(2),c=(t(0),function(e,t,r,a){var i=e.createBuffer(),n=t===e.ARRAY_BUFFER?Float32Array:Uint16Array;return e.bindBuffer(t,i),e.bufferData(t,new n(r),e.STATIC_DRAW),i.itemSize=a,i.numItems=r.length/a,i})}])});
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("OBJ",[],t):"object"==typeof exports?exports.OBJ=t():e.OBJ=t()}(this,function(){return function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,t),n.l=!0,n.exports}var r={};return t.m=e,t.c=r,t.d=function(exports,e,r){t.o(exports,e)||Object.defineProperty(exports,e,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/",t(t.s=3)}([function(e,exports,t){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e){switch(e){case"BYTE":case"UNSIGNED_BYTE":return 1;case"SHORT":case"UNSIGNED_SHORT":return 2;case"FLOAT":return 4}}Object.defineProperty(exports,"__esModule",{value:!0});var n=exports.Layout=function e(){r(this,e);for(var t=arguments.length,a=Array(t),n=0;n<t;n++)a[n]=arguments[n];this.attributes=a;var s=0,l=0,o=!0,u=!1,c=void 0;try{for(var f,h=a[Symbol.iterator]();!(o=(f=h.next()).done);o=!0){var p=f.value;if(this[p.key])throw new i(p);s%p.sizeOfType!=0&&(s+=p.sizeOfType-s%p.sizeOfType),this[p.key]={attribute:p,size:p.size,type:p.type,normalized:p.normalized,offset:s},s+=p.sizeInBytes,l=Math.max(l,p.sizeOfType)}}catch(e){u=!0,c=e}finally{try{!o&&h.return&&h.return()}finally{if(u)throw c}}s%l!=0&&(s+=l-s%l),this.stride=s;var v=!0,d=!1,y=void 0;try{for(var m,M=a[Symbol.iterator]();!(v=(m=M.next()).done);v=!0){this[m.value.key].stride=this.stride}}catch(e){d=!0,y=e}finally{try{!v&&M.return&&M.return()}finally{if(d)throw y}}},i=function e(t){r(this,e),this.message="found duplicate attribute: "+t.key},s=function e(t,n,i){arguments.length>3&&void 0!==arguments[3]&&arguments[3];r(this,e),this.key=t,this.size=n,this.type=i,this.normalized=!1,this.sizeOfType=a(i),this.sizeInBytes=this.sizeOfType*n};n.POSITION=new s("position",3,"FLOAT"),n.NORMAL=new s("normal",3,"FLOAT"),n.TANGENT=new s("tangent",3,"FLOAT"),n.BITANGENT=new s("bitangent",3,"FLOAT"),n.UV=new s("uv",2,"FLOAT"),n.MATERIAL_INDEX=new s("materialIndex",1,"SHORT"),n.MATERIAL_ENABLED=new s("materialEnabled",1,"UNSIGNED_SHORT"),n.AMBIENT=new s("ambient",3,"FLOAT"),n.DIFFUSE=new s("diffuse",3,"FLOAT"),n.SPECULAR=new s("specular",3,"FLOAT"),n.SPECULAR_EXPONENT=new s("specularExponent",3,"FLOAT"),n.EMISSIVE=new s("emissive",3,"FLOAT"),n.TRANSMISSION_FILTER=new s("transmissionFilter",3,"FLOAT"),n.DISSOLVE=new s("dissolve",1,"FLOAT"),n.ILLUMINATION=new s("illumination",1,"UNSIGNED_SHORT"),n.REFRACTION_INDEX=new s("refractionIndex",1,"FLOAT"),n.SHARPNESS=new s("sharpness",1,"FLOAT"),n.MAP_DIFFUSE=new s("mapDiffuse",1,"SHORT"),n.MAP_AMBIENT=new s("mapAmbient",1,"SHORT"),n.MAP_SPECULAR=new s("mapSpecular",1,"SHORT"),n.MAP_SPECULAR_EXPONENT=new s("mapSpecularExponent",1,"SHORT"),n.MAP_DISSOLVE=new s("mapDissolve",1,"SHORT"),n.ANTI_ALIASING=new s("antiAliasing",1,"UNSIGNED_SHORT"),n.MAP_BUMP=new s("mapBump",1,"SHORT"),n.MAP_DISPLACEMENT=new s("mapDisplacement",1,"SHORT"),n.MAP_DECAL=new s("mapDecal",1,"SHORT"),n.MAP_EMISSIVE=new s("mapEmissive",1,"SHORT")},function(e,exports,t){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,r=Array(e.length);t<e.length;t++)r[t]=e[t];return r}return Array.from(e)}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var n=function(){function e(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(t,r,a){return r&&e(t.prototype,r),a&&e(t,a),t}}(),i=t(0),s=function(){function e(t,n){a(this,e),n=n||{},n.materials=n.materials||{},n.enableWTextureCoord=!!n.enableWTextureCoord,n.indicesPerMaterial=!!n.indicesPerMaterial;var i=this;i.vertices=[],i.vertexNormals=[],i.textures=[],i.indices=[],i.textureStride=n.enableWTextureCoord?3:2,this.name="";var s=[],l=[],o=[],u={},c=[],f={},h=-1,p=0;u.verts=[],u.norms=[],u.textures=[],u.hashindices={},u.indices=[[]],u.materialIndices=[],u.index=0;for(var v=/^v\s/,d=/^vn\s/,y=/^vt\s/,m=/^f\s/,M=/\s+/,b=/^usemtl/,x=t.split("\n"),I=0;I<x.length;I++){var A=x[I].trim();if(A&&!A.startsWith("#")){var _=A.split(M);if(_.shift(),v.test(A))s.push.apply(s,r(_));else if(d.test(A))l.push.apply(l,r(_));else if(y.test(A)){var k=_;_.length>2&&!n.enableWTextureCoord?k=_.slice(0,1):2===_.length&&n.enableWTextureCoord&&k.push(0),o.push.apply(o,r(k))}else if(b.test(A)){var T=_[0];T in f||(c.push(T),f[T]=c.length-1,n.indicesPerMaterial&&f[T]>0&&u.indices.push([])),h=f[T],n.indicesPerMaterial&&(p=h)}else if(m.test(A))for(var w=!1,F=0,S=_.length;F<S;F++){3!==F||w||(F=2,w=!0);var E=_[0]+","+h,g=_[F]+","+h;if(g in u.hashindices)u.indices[p].push(u.hashindices[g]);else{var O=_[F].split("/"),B=O.length-1;if(u.verts.push(+s[3*(O[0]-1)+0]),u.verts.push(+s[3*(O[0]-1)+1]),u.verts.push(+s[3*(O[0]-1)+2]),o.length){var L=n.enableWTextureCoord?3:2;u.textures.push(+o[(O[1]-1)*L+0]),u.textures.push(+o[(O[1]-1)*L+1]),n.enableWTextureCoord&&u.textures.push(+o[(O[1]-1)*L+2])}u.norms.push(+l[3*(O[B]-1)+0]),u.norms.push(+l[3*(O[B]-1)+1]),u.norms.push(+l[3*(O[B]-1)+2]),u.materialIndices.push(h),u.hashindices[g]=u.index,u.indices[p].push(u.hashindices[g]),u.index+=1}3===F&&w&&u.indices[p].push(u.hashindices[E])}}}i.vertices=u.verts,i.vertexNormals=u.norms,i.textures=u.textures,i.vertexMaterialIndices=u.materialIndices,i.indices=n.indicesPerMaterial?u.indices:u.indices[p],i.materialNames=c,i.materialIndices=f,i.materialsByIndex={},n.calcTangentsAndBitangents&&this.calculateTangentsAndBitangents()}return n(e,[{key:"calculateTangentsAndBitangents",value:function(){var e={};e.tangents=[].concat(r(new Array(this.vertices.length))).map(function(e){return 0}),e.bitangents=[].concat(r(new Array(this.vertices.length))).map(function(e){return 0});var t=void 0;t=Array.isArray(this.indices[0])?[].concat.apply([],this.indices):this.indices;for(var a=this.vertices,n=this.vertexNormals,i=this.textures,s=0;s<t.length;s+=3){var l=t[s+0],o=t[s+1],u=t[s+2],c=a[3*l+0],f=a[3*l+1],h=a[3*l+2],p=i[2*l+0],v=i[2*l+1],d=a[3*o+0],y=a[3*o+1],m=a[3*o+2],M=i[2*o+0],b=i[2*o+1],x=a[3*u+0],I=a[3*u+1],A=a[3*u+2],_=i[2*u+0],k=i[2*u+1],T=d-c,w=y-f,F=m-h,S=x-c,E=I-f,g=A-h,O=M-p,B=b-v,L=_-p,N=k-v,R=O*N-B*L,P=1/(Math.abs(R<1e-4)?1:R),D=(T*N-S*B)*P,C=(w*N-E*B)*P,U=(F*N-g*B)*P,j=(S*O-T*L)*P,z=(E*O-w*L)*P,H=(g*O-F*L)*P,W=n[3*l+0],G=n[3*l+1],V=n[3*l+2],K=n[3*o+0],q=n[3*o+1],X=n[3*o+2],Y=n[3*u+0],J=n[3*u+1],Q=n[3*u+2],Z=D*W+C*G+U*V,ee=D*K+C*q+U*X,te=D*Y+C*J+U*Q,re=D-W*Z,ae=C-G*Z,ne=U-V*Z,ie=D-K*ee,se=C-q*ee,le=U-X*ee,oe=D-Y*te,ue=C-J*te,ce=U-Q*te,fe=Math.sqrt(re*re+ae*ae+ne*ne),he=Math.sqrt(ie*ie+se*se+le*le),pe=Math.sqrt(oe*oe+ue*ue+ce*ce),ve=j*W+z*G+H*V,de=j*K+z*q+H*X,ye=j*Y+z*J+H*Q,me=j-W*ve,Me=z-G*ve,be=H-V*ve,xe=j-K*de,Ie=z-q*de,Ae=H-X*de,_e=j-Y*ye,ke=z-J*ye,Te=H-Q*ye,we=Math.sqrt(me*me+Me*Me+be*be),Fe=Math.sqrt(xe*xe+Ie*Ie+Ae*Ae),Se=Math.sqrt(_e*_e+ke*ke+Te*Te);e.tangents[3*l+0]+=re/fe,e.tangents[3*l+1]+=ae/fe,e.tangents[3*l+2]+=ne/fe,e.tangents[3*o+0]+=ie/he,e.tangents[3*o+1]+=se/he,e.tangents[3*o+2]+=le/he,e.tangents[3*u+0]+=oe/pe,e.tangents[3*u+1]+=ue/pe,e.tangents[3*u+2]+=ce/pe,e.bitangents[3*l+0]+=me/we,e.bitangents[3*l+1]+=Me/we,e.bitangents[3*l+2]+=be/we,e.bitangents[3*o+0]+=xe/Fe,e.bitangents[3*o+1]+=Ie/Fe,e.bitangents[3*o+2]+=Ae/Fe,e.bitangents[3*u+0]+=_e/Se,e.bitangents[3*u+1]+=ke/Se,e.bitangents[3*u+2]+=Te/Se}this.tangents=e.tangents,this.bitangents=e.bitangents}},{key:"makeBufferData",value:function(e){var t=this.vertices.length/3,r=new ArrayBuffer(e.stride*t);r.numItems=t;for(var a=new DataView(r),n=0,s=0;n<t;n++){s=n*e.stride;var l=!0,o=!1,u=void 0;try{for(var c,f=e.attributes[Symbol.iterator]();!(l=(c=f.next()).done);l=!0){var h=c.value,p=s+e[h.key].offset;switch(h.key){case i.Layout.POSITION.key:a.setFloat32(p,this.vertices[3*n],!0),a.setFloat32(p+4,this.vertices[3*n+1],!0),a.setFloat32(p+8,this.vertices[3*n+2],!0);break;case i.Layout.UV.key:a.setFloat32(p,this.textures[2*n],!0),a.setFloat32(p+4,this.vertices[2*n+1],!0);break;case i.Layout.NORMAL.key:a.setFloat32(p,this.vertexNormals[3*n],!0),a.setFloat32(p+4,this.vertexNormals[3*n+1],!0),a.setFloat32(p+8,this.vertexNormals[3*n+2],!0);break;case i.Layout.MATERIAL_INDEX.key:a.setInt16(p,this.vertexMaterialIndices[n],!0);break;case i.Layout.AMBIENT.key:var v=this.vertexMaterialIndices[n],d=this.materialsByIndex[v];if(!d)break;a.setFloat32(p,d.ambient[0],!0),a.setFloat32(p+4,d.ambient[1],!0),a.setFloat32(p+8,d.ambient[2],!0);break;case i.Layout.DIFFUSE.key:var y=this.vertexMaterialIndices[n],m=this.materialsByIndex[y];if(!m)break;a.setFloat32(p,m.diffuse[0],!0),a.setFloat32(p+4,m.diffuse[1],!0),a.setFloat32(p+8,m.diffuse[2],!0);break;case i.Layout.SPECULAR.key:var M=this.vertexMaterialIndices[n],b=this.materialsByIndex[M];if(!b)break;a.setFloat32(p,b.specular[0],!0),a.setFloat32(p+4,b.specular[1],!0),a.setFloat32(p+8,b.specular[2],!0);break;case i.Layout.SPECULAR_EXPONENT.key:var x=this.vertexMaterialIndices[n],I=this.materialsByIndex[x];if(!I)break;a.setFloat32(p,I.specularExponent,!0);break;case i.Layout.EMISSIVE.key:var A=this.vertexMaterialIndices[n],_=this.materialsByIndex[A];if(!_)break;a.setFloat32(p,_.emissive[0],!0),a.setFloat32(p+4,_.emissive[1],!0),a.setFloat32(p+8,_.emissive[2],!0);break;case i.Layout.TRANSMISSION_FILTER.key:var k=this.vertexMaterialIndices[n],T=this.materialsByIndex[k];if(!T)break;a.setFloat32(p,T.transmissionFilter[0],!0),a.setFloat32(p+4,T.transmissionFilter[1],!0),a.setFloat32(p+8,T.transmissionFilter[2],!0);break;case i.Layout.DISSOLVE.key:var w=this.vertexMaterialIndices[n],F=this.materialsByIndex[w];if(!F)break;a.setFloat32(p,F.dissolve,!0);break;case i.Layout.ILLUMINATION.key:var S=this.vertexMaterialIndices[n],E=this.materialsByIndex[S];if(!E)break;a.setInt16(p,E.illumination,!0);break;case i.Layout.REFRACTION_INDEX.key:var g=this.vertexMaterialIndices[n],O=this.materialsByIndex[g];if(!O)break;a.setFloat32(p,O.refractionIndex,!0);break;case i.Layout.SHARPNESS.key:var B=this.vertexMaterialIndices[n],L=this.materialsByIndex[B];if(!L)break;a.setFloat32(p,L.sharpness,!0);break;case i.Layout.ANTI_ALIASING.key:var N=this.vertexMaterialIndices[n],R=this.materialsByIndex[N];if(!R)break;a.setInt16(p,R.antiAliasing,!0)}}}catch(e){o=!0,u=e}finally{try{!l&&f.return&&f.return()}finally{if(o)throw u}}}return r}},{key:"makeIndexBufferData",value:function(){var e=new Uint16Array(this.indices);return e.numItems=this.indices.length,e}},{key:"addMaterialLibrary",value:function(e){for(var t in e.materials)if(t in this.materialIndices){var r=e.materials[t],a=this.materialIndices[r.name];this.materialsByIndex[a]=r}}}]),e}();exports.default=s},function(e,exports,t){"use strict";function r(e){return Array.isArray(e)?e:Array.from(e)}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var n=function(){function e(e,t){for(var r=0;r<t.length;r++){var a=t[r];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(t,r,a){return r&&e(t.prototype,r),a&&e(t,a),t}}(),i=exports.Material=function e(t){a(this,e),this.name=t,this.ambient=[0,0,0],this.diffuse=[0,0,0],this.specular=[0,0,0],this.emissive=[0,0,0],this.transmissionFilter=[0,0,0],this.dissolve=0,this.specularExponent=0,this.transparency=0,this.illumination=0,this.refractionIndex=1,this.sharpness=0,this.mapDiffuse=null,this.mapAmbient=null,this.mapSpecular=null,this.mapSpecularExponent=null,this.mapDissolve=null,this.antiAliasing=!1,this.mapBump=null,this.mapDisplacement=null,this.mapDecal=null,this.mapEmissive=null,this.mapReflections=[]};exports.MaterialLibrary=function(){function e(t){a(this,e),this.data=t,this.currentMaterial=null,this.materials={},this.parse()}return n(e,[{key:"parse_newmtl",value:function(e){var t=e[0];this.currentMaterial=new i(t),this.materials[t]=this.currentMaterial}},{key:"parseColor",value:function(e){if("spectral"!=e[0]&&"xyz"!=e[0]){if(3==e.length)return e.map(parseFloat);var t=parseFloat(e[0]);return[t,t,t]}}},{key:"parse_Ka",value:function(e){this.currentMaterial.ambient=this.parseColor(e)}},{key:"parse_Kd",value:function(e){this.currentMaterial.diffuse=this.parseColor(e)}},{key:"parse_Ks",value:function(e){this.currentMaterial.specular=this.parseColor(e)}},{key:"parse_Ke",value:function(e){this.currentMaterial.emissive=this.parseColor(e)}},{key:"parse_Tf",value:function(e){this.currentMaterial.transmissionFilter=this.parseColor(e)}},{key:"parse_d",value:function(e){this.currentMaterial.dissolve=parseFloat(e.pop())}},{key:"parse_illum",value:function(e){this.currentMaterial.illumination=parseInt(e[0])}},{key:"parse_Ni",value:function(e){this.currentMaterial.refractionIndex=parseFloat(e[0])}},{key:"parse_Ns",value:function(e){this.currentMaterial.specularExponent=parseInt(e[0])}},{key:"parse_sharpness",value:function(e){this.currentMaterial.sharpness=parseInt(e[0])}},{key:"parse_cc",value:function(e,t){t.colorCorrection="on"==e[0]}},{key:"parse_blendu",value:function(e,t){t.horizontalBlending="on"==e[0]}},{key:"parse_blendv",value:function(e,t){t.verticalBlending="on"==e[0]}},{key:"parse_boost",value:function(e,t){t.boostMipMapSharpness=parseFloat(e[0])}},{key:"parse_mm",value:function(e,t){t.modifyTextureMap.brightness=parseFloat(e[0]),t.modifyTextureMap.contrast=parseFloat(e[1])}},{key:"parse_ost",value:function(e,t,r){for(;e.length<3;)e.push(r);t.u=parseFloat(e[0]),t.v=parseFloat(e[1]),t.w=parseFloat(e[2])}},{key:"parse_o",value:function(e,t){this.parse_ost(e,t.offset,0)}},{key:"parse_s",value:function(e,t){this.parse_ost(e,t.scale,1)}},{key:"parse_t",value:function(e,t){this.parse_ost(e,t.turbulence,0)}},{key:"parse_texres",value:function(e,t){t.textureResolution=parseFloat(e[0])}},{key:"parse_clamp",value:function(e,t){t.clamp="on"==e[0]}},{key:"parse_bm",value:function(e,t){t.bumpMultiplier=parseFloat(e[0])}},{key:"parse_imfchan",value:function(e,t){t.imfChan=e[0]}},{key:"parse_type",value:function(e,t){t.reflectionType=e[0]}},{key:"parseOptions",value:function(e){var t={colorCorrection:!1,horizontalBlending:!0,verticalBlending:!0,boostMipMapSharpness:0,modifyTextureMap:{brightness:0,contrast:1},offset:{u:0,v:0,w:0},scale:{u:1,v:1,w:1},turbulence:{u:0,v:0,w:0},clamp:!1,textureResolution:null,bumpMultiplier:1,imfChan:null},r=void 0,a=void 0,n={};for(e.reverse();e.length;){var i=e.pop();i.startsWith("-")?(r=i.substr(1),n[r]=[]):n[r].push(i)}for(r in n)if(n.hasOwnProperty(r)){a=n[r];var s=this["parse_"+r];s&&s.bind(this)(a,t)}return t}},{key:"parseMap",value:function(e){var t=void 0,a=void 0;if(e[0].startsWith("-"))t=e.pop(),a=e;else{var n=r(e);t=n[0],a=n.slice(1)}return a=this.parseOptions(a),a.filename=t,a}},{key:"parse_map_Ka",value:function(e){this.currentMaterial.mapAmbient=this.parseMap(e)}},{key:"parse_map_Kd",value:function(e){this.currentMaterial.mapDiffuse=this.parseMap(e)}},{key:"parse_map_Ks",value:function(e){this.currentMaterial.mapSpecular=this.parseMap(e)}},{key:"parse_map_Ke",value:function(e){this.currentMaterial.mapEmissive=this.parseMap(e)}},{key:"parse_map_Ns",value:function(e){this.currentMaterial.mapSpecularExponent=this.parseMap(e)}},{key:"parse_map_d",value:function(e){this.currentMaterial.mapDissolve=this.parseMap(e)}},{key:"parse_map_aat",value:function(e){this.currentMaterial.antiAliasing="on"==e[0]}},{key:"parse_map_bump",value:function(e){this.currentMaterial.mapBump=this.parseMap(e)}},{key:"parse_bump",value:function(e){this.parse_map_bump(e)}},{key:"parse_disp",value:function(e){this.currentMaterial.mapDisplacement=this.parseMap(e)}},{key:"parse_decal",value:function(e){this.currentMaterial.mapDecal=this.parseMap(e)}},{key:"parse_refl",value:function(e){this.currentMaterial.mapReflections.push(this.parseMap(e))}},{key:"parse",value:function(){var e=this.data.split(/\r?\n/),t=!0,a=!1,n=void 0;try{for(var i,s=e[Symbol.iterator]();!(t=(i=s.next()).done);t=!0){var l=i.value;if((l=l.trim())&&!l.startsWith("#")){var o=l.split(/\s/),u=void 0,c=o,f=r(c);u=f[0],o=f.slice(1);var h=this["parse_"+u];h&&h.bind(this)(o)}}}catch(e){a=!0,n=e}finally{try{!t&&s.return&&s.return()}finally{if(a)throw n}}delete this.data,this.currentMaterial=null}}]),e}()},function(e,exports,t){e.exports=t(4)},function(e,exports,t){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.deleteMeshBuffers=exports.initMeshBuffers=exports.downloadMeshes=exports.downloadModels=exports.Layout=exports.MaterialLibrary=exports.Material=exports.Mesh=void 0;var r=t(1),a=function(e){return e&&e.__esModule?e:{default:e}}(r),n=t(2),i=t(0),s=t(5);exports.Mesh=a.default,exports.Material=n.Material,exports.MaterialLibrary=n.MaterialLibrary,exports.Layout=i.Layout,exports.downloadModels=s.downloadModels,exports.downloadMeshes=s.downloadMeshes,exports.initMeshBuffers=s.initMeshBuffers,exports.deleteMeshBuffers=s.deleteMeshBuffers},function(e,exports,t){"use strict";function r(e,t){var r=["mapDiffuse","mapAmbient","mapSpecular","mapDissolve","mapBump","mapDisplacement","mapDecal","mapEmissive"];t.endsWith("/")||(t+="/");var a=[];for(var n in e.materials)if(e.materials.hasOwnProperty(n)){n=e.materials[n];var i=!0,s=!1,l=void 0;try{for(var o,u=r[Symbol.iterator]();!(i=(o=u.next()).done);i=!0){var c=o.value;(function(e){var r=n[e];if(!r)return"continue";var i=t+r.filename;a.push(fetch(i).then(function(e){if(!e.ok)throw new Error;return e.blob()}).then(function(e){var t=new Image;return t.src=URL.createObjectURL(e),r.texture=t,new Promise(function(e){return t.onload=function(){return Promise.resolve()}})}).catch(function(){}))})(c)}}catch(e){s=!0,l=e}finally{try{!i&&u.return&&u.return()}finally{if(s)throw l}}}return Promise.all(a)}function a(e){var t=[],a=!0,n=!1,i=void 0;try{for(var s,o=e[Symbol.iterator]();!(a=(s=o.next()).done);a=!0){var f=s.value;!function(e){var a=[];if(!e.obj)throw new Error('"obj" attribute of model object not set. The .obj file is required to be set in order to use downloadModels()');var n={};n.indicesPerMaterial=!!e.indicesPerMaterial,n.calcTangentsAndBitangents=!!e.calcTangentsAndBitangents;var i=e.name;if(!i){var s=e.obj.split("/");i=s[s.length-1].replace(".obj","")}if(a.push(Promise.resolve(i)),a.push(fetch(e.obj).then(function(e){return e.text()}).then(function(e){return new u.default(e,n)})),e.mtl){var l=e.mtl;"boolean"==typeof l&&(l=e.obj.replace(/\.obj$/,".mtl")),a.push(fetch(l).then(function(e){return e.text()}).then(function(t){var a=new c.MaterialLibrary(t);if(!1!==e.downloadMtlTextures){var n=e.mtlTextureRoot;return n||(n=l.substr(0,l.lastIndexOf("/"))),Promise.all([Promise.resolve(a),r(a,n)])}return Promise.all(Promise.resolve(a))}).then(function(e){return e[0]}))}t.push(Promise.all(a))}(f)}}catch(e){n=!0,i=e}finally{try{!a&&o.return&&o.return()}finally{if(n)throw i}}return Promise.all(t).then(function(e){var t={},r=!0,a=!1,n=void 0;try{for(var i,s=e[Symbol.iterator]();!(r=(i=s.next()).done);r=!0){var o=i.value,u=l(o,3),c=u[0],f=u[1],h=u[2];f.name=c,h&&f.addMaterialLibrary(h),t[c]=f}}catch(e){a=!0,n=e}finally{try{!r&&s.return&&s.return()}finally{if(a)throw n}}return t})}function n(e,t,r){void 0===r&&(r={});var a=[];for(var n in e){(function(t){if(!e.hasOwnProperty(t))return"continue";var r=e[t];a.push(fetch(r).then(function(e){return e.text()}).then(function(e){return[t,new u.default(e)]}))})(n)}Promise.all(a).then(function(e){var a=!0,n=!1,i=void 0;try{for(var s,o=e[Symbol.iterator]();!(a=(s=o.next()).done);a=!0){var u=s.value,c=l(u,2),f=c[0],h=c[1];r[f]=h}}catch(e){n=!0,i=e}finally{try{!a&&o.return&&o.return()}finally{if(n)throw i}}return t(r)})}function i(e,t){t.normalBuffer=f(e,e.ARRAY_BUFFER,t.vertexNormals,3),t.textureBuffer=f(e,e.ARRAY_BUFFER,t.textures,t.textureStride),t.vertexBuffer=f(e,e.ARRAY_BUFFER,t.vertices,3),t.indexBuffer=f(e,e.ELEMENT_ARRAY_BUFFER,t.indices,1)}function s(e,t){e.deleteBuffer(t.normalBuffer),e.deleteBuffer(t.textureBuffer),e.deleteBuffer(t.vertexBuffer),e.deleteBuffer(t.indexBuffer)}Object.defineProperty(exports,"__esModule",{value:!0});var l=function(){function e(e,t){var r=[],a=!0,n=!1,i=void 0;try{for(var s,l=e[Symbol.iterator]();!(a=(s=l.next()).done)&&(r.push(s.value),!t||r.length!==t);a=!0);}catch(e){n=!0,i=e}finally{try{!a&&l.return&&l.return()}finally{if(n)throw i}}return r}return function(t,r){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,r);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();exports.downloadModels=a,exports.downloadMeshes=n,exports.initMeshBuffers=i,exports.deleteMeshBuffers=s;var o=t(1),u=function(e){return e&&e.__esModule?e:{default:e}}(o),c=t(2),f=(t(0),function(e,t,r,a){var n=e.createBuffer(),i=t===e.ARRAY_BUFFER?Float32Array:Uint16Array;return e.bindBuffer(t,n),e.bufferData(t,new i(r),e.STATIC_DRAW),n.itemSize=a,n.numItems=r.length/a,n})}])});
{
"name": "webgl-obj-loader",
"version": "1.0.0",
"description": "A simple OBJ model loader to help facilitate the learning of WebGL",
"main": "dist/webgl-obj-loader.min.js",
"scripts": {
"build": "webpack --config webpack.dev.js",
"watch": "npm run build -- -w",
"release": "webpack --config webpack.dev.js && webpack --config webpack.prod.js",
"test": "mocha --require babel-register",
"coverage": "nyc npm test",
"coverage:report": "nyc report -r html"
},
"repository": {
"type": "git",
"url": "https://github.com/frenchtoast747/webgl-obj-loader.git"
},
"keywords": [
"obj",
"mesh",
"load",
"webgl",
"parse",
"vertex",
"index",
"normal",
"texture"
],
"author": "Aaron Boman",
"license": "MIT",
"bugs": {
"url": "https://github.com/frenchtoast747/webgl-obj-loader/issues"
},
"homepage": "https://github.com/frenchtoast747/webgl-obj-loader",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-3": "^6.24.1",
"chai": "^4.1.2",
"eslint": "^4.10.0",
"eslint-config-google": "^0.9.1",
"mocha": "^4.0.1",
"nyc": "^11.2.1",
"webpack": "^3.8.1",
"webpack-merge": "^4.1.1"
}
"name": "webgl-obj-loader",
"version": "1.0.2",
"description": "A simple OBJ model loader to help facilitate the learning of WebGL",
"main": "dist/webgl-obj-loader.min.js",
"scripts": {
"build": "webpack --config webpack.dev.js",
"watch": "npm run build -- -w",
"release": "npm run prettify && webpack --config webpack.dev.js && webpack --config webpack.prod.js",
"test": "mocha --require babel-register",
"coverage": "nyc npm test",
"coverage:report": "nyc report -r html",
"prettify": "prettier --write src/**/*.js",
"publish": "npm publish"
},
"prettier": {
"printWidth": 120,
"tabWidth": 4
},
"repository": {
"type": "git",
"url": "https://github.com/frenchtoast747/webgl-obj-loader.git"
},
"keywords": ["obj", "mtl", "mesh", "load", "webgl", "parse", "vertex", "index", "normal", "texture"],
"author": "Aaron Boman",
"license": "MIT",
"bugs": {
"url": "https://github.com/frenchtoast747/webgl-obj-loader/issues"
},
"homepage": "https://github.com/frenchtoast747/webgl-obj-loader",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-3": "^6.24.1",
"chai": "^4.1.2",
"eslint": "^4.10.0",
"eslint-config-google": "^0.9.1",
"mocha": "^4.0.1",
"nyc": "^11.2.1",
"prettier": "^1.11.1",
"webpack": "^3.8.1",
"webpack-merge": "^4.1.1"
}
}

@@ -159,3 +159,3 @@ # webgl-obj-loader

**textureBuffer** |contains the model&#39;s Texture Coordinates
textureBuffer.itemSize |set to 2 items
textureBuffer.itemSize |set to 2 items (or 3 if W texture coord is enabled)
textureBuffer.numItems |the number of texture coordinates

@@ -239,2 +239,5 @@ **vertexBuffer** |contains the model&#39;s Vertex Position Coordinates (does not include w)

## Webpack Support
Thanks to [mentos1386](https://github.com/mentos1386) for the [webpack-obj-loader](https://github.com/mentos1386/webpack-obj-loader)!
## Demo

@@ -245,13 +248,24 @@ http://frenchtoast747.github.com/webgl-obj-loader/

## ChangeLog
**1.0.2**
* Add Support for separating mesh indices by materials.
* Add calculation for tangents and bitangents
* Add runtime OBJ library version.
**1.0.1**
* Add support for 3D texture coordinates. By default the third texture
coordinate, w, is truncated. Support can be enabled by passing
`enableWTextureCoord: true` in the options parameter of the Mesh
class.
**1.0.0**
* Modularized all of the source files into ES6 modules.
* * The Mesh, MaterialLibrary, and Material classes are now
* The Mesh, MaterialLibrary, and Material classes are now
actual ES6 classes.
* Added tests for each of the classes
* * Found a bug in the Mesh class. Vertex normals would not appear
* Found a bug in the Mesh class. Vertex normals would not appear
if the face declaration used the shorthand variant; e.g. `f 1/1`
* Provided Initial MTL file parsing support.
* * Still requires Documentation. For now, have a look at the tests in the
* Still requires Documentation. For now, have a look at the tests in the
test directory for examples of use.
* * Use the new downloadModels() function in order to download the OBJ meshes
* Use the new downloadModels() function in order to download the OBJ meshes
complete with their MTL files attached. If the MTL files reference images,

@@ -258,0 +272,0 @@ by default, those images will be downloaded and attached.

@@ -1,7 +0,7 @@

import Mesh from './mesh';
import {Material, MaterialLibrary} from './material';
import {Layout} from './layout';
import {downloadModels, downloadMeshes,
initMeshBuffers, deleteMeshBuffers} from './utils';
import Mesh from "./mesh";
import { Material, MaterialLibrary } from "./material";
import { Layout } from "./layout";
import { downloadModels, downloadMeshes, initMeshBuffers, deleteMeshBuffers } from "./utils";
version = "1.0.2";

@@ -16,8 +16,7 @@ /**

Layout,
downloadModels,
downloadMeshes,
initMeshBuffers,
deleteMeshBuffers
deleteMeshBuffers,
version
};

@@ -51,15 +51,13 @@ /**

offset += attribute.sizeOfType - offset % attribute.sizeOfType;
console.warn('Layout requires padding before ' + attribute.key + ' attribute');
console.warn("Layout requires padding before " + attribute.key + " attribute");
}
this[attribute.key] = {
'attribute': attribute,
'size': attribute.size,
'type': attribute.type,
'normalized': attribute.normalized,
'offset': offset,
attribute: attribute,
size: attribute.size,
type: attribute.type,
normalized: attribute.normalized,
offset: offset
};
offset += attribute.sizeInBytes;
maxStrideMultiple = Math.max(
maxStrideMultiple,
attribute.sizeOfType);
maxStrideMultiple = Math.max(maxStrideMultiple, attribute.sizeOfType);
}

@@ -73,3 +71,3 @@ // Add padding to the end to satisfy WebGL's requirement that all

offset += maxStrideMultiple - offset % maxStrideMultiple;
console.warn('Layout requires padding at the back');
console.warn("Layout requires padding at the back");
}

@@ -83,3 +81,2 @@ this.stride = offset;

/**

@@ -97,3 +94,3 @@ * An exception for when two or more of the same attributes are found in the

constructor(attribute) {
this.message = 'found duplicate attribute: ' + attribute.key;
this.message = "found duplicate attribute: " + attribute.key;
}

@@ -131,3 +128,3 @@ }

*/
constructor(key, size, type, normalized=false) {
constructor(key, size, type, normalized = false) {
this.key = key;

@@ -150,9 +147,9 @@ this.size = size;

switch (type) {
case 'BYTE':
case 'UNSIGNED_BYTE':
case "BYTE":
case "UNSIGNED_BYTE":
return 1;
case 'SHORT':
case 'UNSIGNED_SHORT':
case "SHORT":
case "UNSIGNED_SHORT":
return 2;
case 'FLOAT':
case "FLOAT":
return 4;

@@ -168,3 +165,3 @@ }

*/
Layout.POSITION = new Attribute('position', 3, 'FLOAT');
Layout.POSITION = new Attribute("position", 3, "FLOAT");

@@ -176,3 +173,3 @@ /**

*/
Layout.NORMAL = new Attribute('normal', 3, 'FLOAT');
Layout.NORMAL = new Attribute("normal", 3, "FLOAT");

@@ -188,3 +185,3 @@ /**

*/
Layout.TANGENT = new Attribute('tangent', 3, 'FLOAT');
Layout.TANGENT = new Attribute("tangent", 3, "FLOAT");

@@ -199,3 +196,3 @@ /**

*/
Layout.BITANGENT = new Attribute('bitangent', 3, 'FLOAT');
Layout.BITANGENT = new Attribute("bitangent", 3, "FLOAT");

@@ -207,3 +204,3 @@ /**

*/
Layout.UV = new Attribute('uv', 2, 'FLOAT');
Layout.UV = new Attribute("uv", 2, "FLOAT");

@@ -244,23 +241,23 @@ // Material attributes

*/
Layout.MATERIAL_INDEX = new Attribute('materialIndex', 1, 'SHORT');
Layout.MATERIAL_ENABLED = new Attribute('materialEnabled', 1, 'UNSIGNED_SHORT');
Layout.AMBIENT = new Attribute('ambient', 3, 'FLOAT');
Layout.DIFFUSE = new Attribute('diffuse', 3, 'FLOAT');
Layout.SPECULAR = new Attribute('specular', 3, 'FLOAT');
Layout.SPECULAR_EXPONENT = new Attribute('specularExponent', 3, 'FLOAT');
Layout.EMISSIVE = new Attribute('emissive', 3, 'FLOAT');
Layout.TRANSMISSION_FILTER = new Attribute('transmissionFilter', 3, 'FLOAT');
Layout.DISSOLVE = new Attribute('dissolve', 1, 'FLOAT');
Layout.ILLUMINATION = new Attribute('illumination', 1, 'UNSIGNED_SHORT');
Layout.REFRACTION_INDEX = new Attribute('refractionIndex', 1, 'FLOAT');
Layout.SHARPNESS = new Attribute('sharpness', 1, 'FLOAT');
Layout.MAP_DIFFUSE = new Attribute('mapDiffuse', 1, 'SHORT');
Layout.MAP_AMBIENT = new Attribute('mapAmbient', 1, 'SHORT');
Layout.MAP_SPECULAR = new Attribute('mapSpecular', 1, 'SHORT');
Layout.MAP_SPECULAR_EXPONENT = new Attribute('mapSpecularExponent', 1, 'SHORT');
Layout.MAP_DISSOLVE = new Attribute('mapDissolve', 1, 'SHORT');
Layout.ANTI_ALIASING = new Attribute('antiAliasing', 1, 'UNSIGNED_SHORT');
Layout.MAP_BUMP = new Attribute('mapBump', 1, 'SHORT');
Layout.MAP_DISPLACEMENT = new Attribute('mapDisplacement', 1, 'SHORT');
Layout.MAP_DECAL = new Attribute('mapDecal', 1, 'SHORT');
Layout.MAP_EMISSIVE = new Attribute('mapEmissive', 1, 'SHORT');
Layout.MATERIAL_INDEX = new Attribute("materialIndex", 1, "SHORT");
Layout.MATERIAL_ENABLED = new Attribute("materialEnabled", 1, "UNSIGNED_SHORT");
Layout.AMBIENT = new Attribute("ambient", 3, "FLOAT");
Layout.DIFFUSE = new Attribute("diffuse", 3, "FLOAT");
Layout.SPECULAR = new Attribute("specular", 3, "FLOAT");
Layout.SPECULAR_EXPONENT = new Attribute("specularExponent", 3, "FLOAT");
Layout.EMISSIVE = new Attribute("emissive", 3, "FLOAT");
Layout.TRANSMISSION_FILTER = new Attribute("transmissionFilter", 3, "FLOAT");
Layout.DISSOLVE = new Attribute("dissolve", 1, "FLOAT");
Layout.ILLUMINATION = new Attribute("illumination", 1, "UNSIGNED_SHORT");
Layout.REFRACTION_INDEX = new Attribute("refractionIndex", 1, "FLOAT");
Layout.SHARPNESS = new Attribute("sharpness", 1, "FLOAT");
Layout.MAP_DIFFUSE = new Attribute("mapDiffuse", 1, "SHORT");
Layout.MAP_AMBIENT = new Attribute("mapAmbient", 1, "SHORT");
Layout.MAP_SPECULAR = new Attribute("mapSpecular", 1, "SHORT");
Layout.MAP_SPECULAR_EXPONENT = new Attribute("mapSpecularExponent", 1, "SHORT");
Layout.MAP_DISSOLVE = new Attribute("mapDissolve", 1, "SHORT");
Layout.ANTI_ALIASING = new Attribute("antiAliasing", 1, "UNSIGNED_SHORT");
Layout.MAP_BUMP = new Attribute("mapBump", 1, "SHORT");
Layout.MAP_DISPLACEMENT = new Attribute("mapDisplacement", 1, "SHORT");
Layout.MAP_DECAL = new Attribute("mapDecal", 1, "SHORT");
Layout.MAP_EMISSIVE = new Attribute("mapEmissive", 1, "SHORT");

@@ -5,58 +5,58 @@ /**

export class Material {
/**
* Constructor
* @param {String} name the unique name of the material
*/
constructor(name) {
// the unique material ID.
this.name = name;
// The values for the following attibutes
// are an array of R, G, B normalized values.
// Ka - Ambient Reflectivity
this.ambient = [0, 0, 0];
// Kd - Defuse Reflectivity
this.diffuse = [0, 0, 0];
// Ks
this.specular = [0, 0, 0];
// Ke
this.emissive = [0, 0, 0];
// Tf
this.transmissionFilter = [0, 0, 0];
// d
this.dissolve = 0;
// valid range is between 0 and 1000
this.specularExponent = 0;
// either d or Tr; valid values are normalized
this.transparency = 0;
// illum - the enum of the illumination model to use
this.illumination = 0;
// Ni - Set to "normal" (air).
this.refractionIndex = 1;
// sharpness
this.sharpness = 0;
// map_Kd
this.mapDiffuse = null;
// map_Ka
this.mapAmbient = null;
// map_Ks
this.mapSpecular = null;
// map_Ns
this.mapSpecularExponent = null;
// map_d
this.mapDissolve = null;
// map_aat
this.antiAliasing = false;
// map_bump or bump
this.mapBump = null;
// disp
this.mapDisplacement = null;
// decal
this.mapDecal = null;
// map_Ke
this.mapEmissive = null;
// refl - when the reflection type is a cube, there will be multiple refl
// statements for each side of the cube. If it's a spherical
// reflection, there should only ever be one.
this.mapReflections = [];
}
/**
* Constructor
* @param {String} name the unique name of the material
*/
constructor(name) {
// the unique material ID.
this.name = name;
// The values for the following attibutes
// are an array of R, G, B normalized values.
// Ka - Ambient Reflectivity
this.ambient = [0, 0, 0];
// Kd - Defuse Reflectivity
this.diffuse = [0, 0, 0];
// Ks
this.specular = [0, 0, 0];
// Ke
this.emissive = [0, 0, 0];
// Tf
this.transmissionFilter = [0, 0, 0];
// d
this.dissolve = 0;
// valid range is between 0 and 1000
this.specularExponent = 0;
// either d or Tr; valid values are normalized
this.transparency = 0;
// illum - the enum of the illumination model to use
this.illumination = 0;
// Ni - Set to "normal" (air).
this.refractionIndex = 1;
// sharpness
this.sharpness = 0;
// map_Kd
this.mapDiffuse = null;
// map_Ka
this.mapAmbient = null;
// map_Ks
this.mapSpecular = null;
// map_Ns
this.mapSpecularExponent = null;
// map_d
this.mapDissolve = null;
// map_aat
this.antiAliasing = false;
// map_bump or bump
this.mapBump = null;
// disp
this.mapDisplacement = null;
// decal
this.mapDecal = null;
// map_Ke
this.mapEmissive = null;
// refl - when the reflection type is a cube, there will be multiple refl
// statements for each side of the cube. If it's a spherical
// reflection, there should only ever be one.
this.mapReflections = [];
}
}

@@ -69,662 +69,660 @@

export class MaterialLibrary {
/**
* Constructs the Material Parser
* @param {String} mtlData the MTL file contents
*/
constructor(mtlData) {
this.data = mtlData;
this.currentMaterial = null;
this.materials = {};
/**
* Constructs the Material Parser
* @param {String} mtlData the MTL file contents
*/
constructor(mtlData) {
this.data = mtlData;
this.currentMaterial = null;
this.materials = {};
this.parse();
}
this.parse();
}
/* eslint-disable camelcase */
/* the function names here disobey camelCase conventions
/* eslint-disable camelcase */
/* the function names here disobey camelCase conventions
to make parsing/routing easier. see the parse function
documentation for more information. */
/**
* Creates a new Material object and adds to the registry.
* @param {string[]} tokens the tokens associated with the directive
*/
parse_newmtl(tokens) {
let name = tokens[0];
// console.info('Parsing new Material:', name);
/**
* Creates a new Material object and adds to the registry.
* @param {string[]} tokens the tokens associated with the directive
*/
parse_newmtl(tokens) {
let name = tokens[0];
// console.info('Parsing new Material:', name);
this.currentMaterial = new Material(name);
this.materials[name] = this.currentMaterial;
}
this.currentMaterial = new Material(name);
this.materials[name] = this.currentMaterial;
}
/**
* See the documenation for parse_Ka below for a better understanding.
*
* Given a list of possible color tokens, returns an array of R, G, and B
* color values.
*
* @param {string[]} tokens the tokens associated with the directive
* @return {*} a 3 element array containing the R, G, and B values
* of the color.
*/
parseColor(tokens) {
if (tokens[0] == 'spectral') {
console.error(
'The MTL parser does not support spectral curve files. You will ' +
'need to convert the MTL colors to either RGB or CIEXYZ.'
);
return;
/**
* See the documenation for parse_Ka below for a better understanding.
*
* Given a list of possible color tokens, returns an array of R, G, and B
* color values.
*
* @param {string[]} tokens the tokens associated with the directive
* @return {*} a 3 element array containing the R, G, and B values
* of the color.
*/
parseColor(tokens) {
if (tokens[0] == "spectral") {
console.error(
"The MTL parser does not support spectral curve files. You will " +
"need to convert the MTL colors to either RGB or CIEXYZ."
);
return;
}
if (tokens[0] == "xyz") {
console.warn("TODO: convert XYZ to RGB");
return;
}
// from my understanding of the spec, RGB values at this point
// will either be 3 floats or exactly 1 float, so that's the check
// that i'm going to perform here
if (tokens.length == 3) {
return tokens.map(parseFloat);
}
// Since tokens at this point has a length of 3, we're going to assume
// it's exactly 1, skipping the check for 2.
let value = parseFloat(tokens[0]);
// in this case, all values are equivalent
return [value, value, value];
}
if (tokens[0] == 'xyz') {
console.warn('TODO: convert XYZ to RGB');
return;
/**
* Parse the ambient reflectivity
*
* A Ka directive can take one of three forms:
* - Ka r g b
* - Ka spectral file.rfl
* - Ka xyz x y z
* These three forms are mutually exclusive in that only one
* declaration can exist per material. It is considered a syntax
* error otherwise.
*
* The "Ka" form specifies the ambient reflectivity using RGB values.
* The "g" and "b" values are optional. If only the "r" value is
* specified, then the "g" and "b" values are assigned the value of
* "r". Values are normally in the range 0.0 to 1.0. Values outside
* of this range increase or decrease the reflectivity accordingly.
*
* The "Ka spectral" form specifies the ambient reflectivity using a
* spectral curve. "file.rfl" is the name of the ".rfl" file containing
* the curve data. "factor" is an optional argument which is a multiplier
* for the values in the .rfl file and defaults to 1.0 if not specified.
*
* The "Ka xyz" form specifies the ambient reflectivity using CIEXYZ values.
* "x y z" are the values of the CIEXYZ color space. The "y" and "z" arguments
* are optional and take on the value of the "x" component if only "x" is
* specified. The "x y z" values are normally in the range of 0.0 to 1.0 and
* increase or decrease ambient reflectivity accordingly outside of that
* range.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ka(tokens) {
this.currentMaterial.ambient = this.parseColor(tokens);
}
// from my understanding of the spec, RGB values at this point
// will either be 3 floats or exactly 1 float, so that's the check
// that i'm going to perform here
if (tokens.length == 3) {
return tokens.map(parseFloat);
/**
* Diffuse Reflectivity
*
* Similar to the Ka directive. Simply replace "Ka" with "Kd" and the rules
* are the same
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Kd(tokens) {
this.currentMaterial.diffuse = this.parseColor(tokens);
}
// Since tokens at this point has a length of 3, we're going to assume
// it's exactly 1, skipping the check for 2.
let value = parseFloat(tokens[0]);
// in this case, all values are equivalent
return [value, value, value];
}
/**
* Spectral Reflectivity
*
* Similar to the Ka directive. Simply replace "Ks" with "Kd" and the rules
* are the same
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ks(tokens) {
this.currentMaterial.specular = this.parseColor(tokens);
}
/**
* Parse the ambient reflectivity
*
* A Ka directive can take one of three forms:
* - Ka r g b
* - Ka spectral file.rfl
* - Ka xyz x y z
* These three forms are mutually exclusive in that only one
* declaration can exist per material. It is considered a syntax
* error otherwise.
*
* The "Ka" form specifies the ambient reflectivity using RGB values.
* The "g" and "b" values are optional. If only the "r" value is
* specified, then the "g" and "b" values are assigned the value of
* "r". Values are normally in the range 0.0 to 1.0. Values outside
* of this range increase or decrease the reflectivity accordingly.
*
* The "Ka spectral" form specifies the ambient reflectivity using a
* spectral curve. "file.rfl" is the name of the ".rfl" file containing
* the curve data. "factor" is an optional argument which is a multiplier
* for the values in the .rfl file and defaults to 1.0 if not specified.
*
* The "Ka xyz" form specifies the ambient reflectivity using CIEXYZ values.
* "x y z" are the values of the CIEXYZ color space. The "y" and "z" arguments
* are optional and take on the value of the "x" component if only "x" is
* specified. The "x y z" values are normally in the range of 0.0 to 1.0 and
* increase or decrease ambient reflectivity accordingly outside of that
* range.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ka(tokens) {
this.currentMaterial.ambient = this.parseColor(tokens);
}
/**
* Emissive
*
* The amount and color of light emitted by the object.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ke(tokens) {
this.currentMaterial.emissive = this.parseColor(tokens);
}
/**
* Diffuse Reflectivity
*
* Similar to the Ka directive. Simply replace "Ka" with "Kd" and the rules
* are the same
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Kd(tokens) {
this.currentMaterial.diffuse = this.parseColor(tokens);
}
/**
* Transmission Filter
*
* Any light passing through the object is filtered by the transmission
* filter, which only allows specific colors to pass through. For example, Tf
* 0 1 0 allows all of the green to pass through and filters out all of the
* red and blue.
*
* Similar to the Ka directive. Simply replace "Ks" with "Tf" and the rules
* are the same
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Tf(tokens) {
this.currentMaterial.transmissionFilter = this.parseColor(tokens);
}
/**
* Spectral Reflectivity
*
* Similar to the Ka directive. Simply replace "Ks" with "Kd" and the rules
* are the same
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ks(tokens) {
this.currentMaterial.specular = this.parseColor(tokens);
}
/**
* Specifies the dissolve for the current material.
*
* Statement: d [-halo] `factor`
*
* Example: "d 0.5"
*
* The factor is the amount this material dissolves into the background. A
* factor of 1.0 is fully opaque. This is the default when a new material is
* created. A factor of 0.0 is fully dissolved (completely transparent).
*
* Unlike a real transparent material, the dissolve does not depend upon
* material thickness nor does it have any spectral character. Dissolve works
* on all illumination models.
*
* The dissolve statement allows for an optional "-halo" flag which indicates
* that a dissolve is dependent on the surface orientation relative to the
* viewer. For example, a sphere with the following dissolve, "d -halo 0.0",
* will be fully dissolved at its center and will appear gradually more opaque
* toward its edge.
*
* "factor" is the minimum amount of dissolve applied to the material. The
* amount of dissolve will vary between 1.0 (fully opaque) and the specified
* "factor". The formula is:
*
* dissolve = 1.0 - (N*v)(1.0-factor)
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_d(tokens) {
// this ignores the -halo option as I can't find any documentation on what
// it's supposed to be.
this.currentMaterial.dissolve = parseFloat(tokens.pop());
}
/**
* Emissive
*
* The amount and color of light emitted by the object.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ke(tokens) {
this.currentMaterial.emissive = this.parseColor(tokens);
}
/**
* The "illum" statement specifies the illumination model to use in the
* material. Illumination models are mathematical equations that represent
* various material lighting and shading effects.
*
* The illumination number can be a number from 0 to 10. The following are
* the list of illumination enumerations and their summaries:
* 0. Color on and Ambient off
* 1. Color on and Ambient on
* 2. Highlight on
* 3. Reflection on and Ray trace on
* 4. Transparency: Glass on, Reflection: Ray trace on
* 5. Reflection: Fresnel on and Ray trace on
* 6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
* 7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
* 8. Reflection on and Ray trace off
* 9. Transparency: Glass on, Reflection: Ray trace off
* 10. Casts shadows onto invisible surfaces
*
* Example: "illum 2" to specify the "Highlight on" model
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_illum(tokens) {
this.currentMaterial.illumination = parseInt(tokens[0]);
}
/**
* Transmission Filter
*
* Any light passing through the object is filtered by the transmission
* filter, which only allows specific colors to pass through. For example, Tf
* 0 1 0 allows all of the green to pass through and filters out all of the
* red and blue.
*
* Similar to the Ka directive. Simply replace "Ks" with "Tf" and the rules
* are the same
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Tf(tokens) {
this.currentMaterial.transmissionFilter = this.parseColor(tokens);
}
/**
* Optical Density (AKA Index of Refraction)
*
* Statement: Ni `index`
*
* Example: Ni 1.0
*
* Specifies the optical density for the surface. `index` is the value
* for the optical density. The values can range from 0.001 to 10. A value of
* 1.0 means that light does not bend as it passes through an object.
* Increasing the optical_density increases the amount of bending. Glass has
* an index of refraction of about 1.5. Values of less than 1.0 produce
* bizarre results and are not recommended
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ni(tokens) {
this.currentMaterial.refractionIndex = parseFloat(tokens[0]);
}
/**
* Specifies the dissolve for the current material.
*
* Statement: d [-halo] `factor`
*
* Example: "d 0.5"
*
* The factor is the amount this material dissolves into the background. A
* factor of 1.0 is fully opaque. This is the default when a new material is
* created. A factor of 0.0 is fully dissolved (completely transparent).
*
* Unlike a real transparent material, the dissolve does not depend upon
* material thickness nor does it have any spectral character. Dissolve works
* on all illumination models.
*
* The dissolve statement allows for an optional "-halo" flag which indicates
* that a dissolve is dependent on the surface orientation relative to the
* viewer. For example, a sphere with the following dissolve, "d -halo 0.0",
* will be fully dissolved at its center and will appear gradually more opaque
* toward its edge.
*
* "factor" is the minimum amount of dissolve applied to the material. The
* amount of dissolve will vary between 1.0 (fully opaque) and the specified
* "factor". The formula is:
*
* dissolve = 1.0 - (N*v)(1.0-factor)
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_d(tokens) {
// this ignores the -halo option as I can't find any documentation on what
// it's supposed to be.
this.currentMaterial.dissolve = parseFloat(tokens.pop());
}
/**
* Specifies the specular exponent for the current material. This defines the
* focus of the specular highlight.
*
* Statement: Ns `exponent`
*
* Example: "Ns 250"
*
* `exponent` is the value for the specular exponent. A high exponent results
* in a tight, concentrated highlight. Ns Values normally range from 0 to
* 1000.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ns(tokens) {
this.currentMaterial.specularExponent = parseInt(tokens[0]);
}
/**
* The "illum" statement specifies the illumination model to use in the
* material. Illumination models are mathematical equations that represent
* various material lighting and shading effects.
*
* The illumination number can be a number from 0 to 10. The following are
* the list of illumination enumerations and their summaries:
* 0. Color on and Ambient off
* 1. Color on and Ambient on
* 2. Highlight on
* 3. Reflection on and Ray trace on
* 4. Transparency: Glass on, Reflection: Ray trace on
* 5. Reflection: Fresnel on and Ray trace on
* 6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
* 7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
* 8. Reflection on and Ray trace off
* 9. Transparency: Glass on, Reflection: Ray trace off
* 10. Casts shadows onto invisible surfaces
*
* Example: "illum 2" to specify the "Highlight on" model
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_illum(tokens) {
this.currentMaterial.illumination = parseInt(tokens[0]);
}
/**
* Specifies the sharpness of the reflections from the local reflection map.
*
* Statement: sharpness `value`
*
* Example: "sharpness 100"
*
* If a material does not have a local reflection map defined in its material
* defintions, sharpness will apply to the global reflection map defined in
* PreView.
*
* `value` can be a number from 0 to 1000. The default is 60. A high value
* results in a clear reflection of objects in the reflection map.
*
* Tip: sharpness values greater than 100 introduce aliasing effects in
* flat surfaces that are viewed at a sharp angle.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_sharpness(tokens) {
this.currentMaterial.sharpness = parseInt(tokens[0]);
}
/**
* Optical Density (AKA Index of Refraction)
*
* Statement: Ni `index`
*
* Example: Ni 1.0
*
* Specifies the optical density for the surface. `index` is the value
* for the optical density. The values can range from 0.001 to 10. A value of
* 1.0 means that light does not bend as it passes through an object.
* Increasing the optical_density increases the amount of bending. Glass has
* an index of refraction of about 1.5. Values of less than 1.0 produce
* bizarre results and are not recommended
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ni(tokens) {
this.currentMaterial.refractionIndex = parseFloat(tokens[0]);
}
/**
* Parses the -cc flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -cc flag
* @param {Object} options the Object of all image options
*/
parse_cc(values, options) {
options.colorCorrection = values[0] == "on";
}
/**
* Specifies the specular exponent for the current material. This defines the
* focus of the specular highlight.
*
* Statement: Ns `exponent`
*
* Example: "Ns 250"
*
* `exponent` is the value for the specular exponent. A high exponent results
* in a tight, concentrated highlight. Ns Values normally range from 0 to
* 1000.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_Ns(tokens) {
this.currentMaterial.specularExponent = parseInt(tokens[0]);
}
/**
* Parses the -blendu flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -blendu flag
* @param {Object} options the Object of all image options
*/
parse_blendu(values, options) {
options.horizontalBlending = values[0] == "on";
}
/**
* Specifies the sharpness of the reflections from the local reflection map.
*
* Statement: sharpness `value`
*
* Example: "sharpness 100"
*
* If a material does not have a local reflection map defined in its material
* defintions, sharpness will apply to the global reflection map defined in
* PreView.
*
* `value` can be a number from 0 to 1000. The default is 60. A high value
* results in a clear reflection of objects in the reflection map.
*
* Tip: sharpness values greater than 100 introduce aliasing effects in
* flat surfaces that are viewed at a sharp angle.
*
* @param {string[]} tokens the tokens associated with the directive
*/
parse_sharpness(tokens) {
this.currentMaterial.sharpness = parseInt(tokens[0]);
}
/**
* Parses the -blendv flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -blendv flag
* @param {Object} options the Object of all image options
*/
parse_blendv(values, options) {
options.verticalBlending = values[0] == "on";
}
/**
* Parses the -cc flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -cc flag
* @param {Object} options the Object of all image options
*/
parse_cc(values, options) {
options.colorCorrection = values[0] == 'on';
}
/**
* Parses the -boost flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -boost flag
* @param {Object} options the Object of all image options
*/
parse_boost(values, options) {
options.boostMipMapSharpness = parseFloat(values[0]);
}
/**
* Parses the -blendu flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -blendu flag
* @param {Object} options the Object of all image options
*/
parse_blendu(values, options) {
options.horizontalBlending = values[0] == 'on';
}
/**
* Parses the -mm flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -mm flag
* @param {Object} options the Object of all image options
*/
parse_mm(values, options) {
options.modifyTextureMap.brightness = parseFloat(values[0]);
options.modifyTextureMap.contrast = parseFloat(values[1]);
}
/**
* Parses the -blendv flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -blendv flag
* @param {Object} options the Object of all image options
*/
parse_blendv(values, options) {
options.verticalBlending = values[0] == 'on';
}
/**
* Parses and sets the -o, -s, and -t u, v, and w values
*
* @param {string[]} values the values passed to the -o, -s, -t flag
* @param {Object} option the Object of either the -o, -s, -t option
* @param {Integer} defaultValue the Object of all image options
*/
parse_ost(values, option, defaultValue) {
while (values.length < 3) {
values.push(defaultValue);
}
/**
* Parses the -boost flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -boost flag
* @param {Object} options the Object of all image options
*/
parse_boost(values, options) {
options.boostMipMapSharpness = parseFloat(values[0]);
}
option.u = parseFloat(values[0]);
option.v = parseFloat(values[1]);
option.w = parseFloat(values[2]);
}
/**
* Parses the -mm flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -mm flag
* @param {Object} options the Object of all image options
*/
parse_mm(values, options) {
options.modifyTextureMap.brightness = parseFloat(values[0]);
options.modifyTextureMap.contrast = parseFloat(values[1]);
}
/**
* Parses the -o flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -o flag
* @param {Object} options the Object of all image options
*/
parse_o(values, options) {
this.parse_ost(values, options.offset, 0);
}
/**
* Parses and sets the -o, -s, and -t u, v, and w values
*
* @param {string[]} values the values passed to the -o, -s, -t flag
* @param {Object} option the Object of either the -o, -s, -t option
* @param {Integer} defaultValue the Object of all image options
*/
parse_ost(values, option, defaultValue) {
while (values.length < 3) {
values.push(defaultValue);
/**
* Parses the -s flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -s flag
* @param {Object} options the Object of all image options
*/
parse_s(values, options) {
this.parse_ost(values, options.scale, 1);
}
option.u = parseFloat(values[0]);
option.v = parseFloat(values[1]);
option.w = parseFloat(values[2]);
}
/**
* Parses the -t flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -t flag
* @param {Object} options the Object of all image options
*/
parse_t(values, options) {
this.parse_ost(values, options.turbulence, 0);
}
/**
* Parses the -o flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -o flag
* @param {Object} options the Object of all image options
*/
parse_o(values, options) {
this.parse_ost(values, options.offset, 0);
}
/**
* Parses the -texres flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -texres flag
* @param {Object} options the Object of all image options
*/
parse_texres(values, options) {
options.textureResolution = parseFloat(values[0]);
}
/**
* Parses the -s flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -s flag
* @param {Object} options the Object of all image options
*/
parse_s(values, options) {
this.parse_ost(values, options.scale, 1);
}
/**
* Parses the -clamp flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -clamp flag
* @param {Object} options the Object of all image options
*/
parse_clamp(values, options) {
options.clamp = values[0] == "on";
}
/**
* Parses the -t flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -t flag
* @param {Object} options the Object of all image options
*/
parse_t(values, options) {
this.parse_ost(values, options.turbulence, 0);
}
/**
* Parses the -bm flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -bm flag
* @param {Object} options the Object of all image options
*/
parse_bm(values, options) {
options.bumpMultiplier = parseFloat(values[0]);
}
/**
* Parses the -texres flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -texres flag
* @param {Object} options the Object of all image options
*/
parse_texres(values, options) {
options.textureResolution = parseFloat(values[0]);
}
/**
* Parses the -imfchan flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -imfchan flag
* @param {Object} options the Object of all image options
*/
parse_imfchan(values, options) {
options.imfChan = values[0];
}
/**
* Parses the -clamp flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -clamp flag
* @param {Object} options the Object of all image options
*/
parse_clamp(values, options) {
options.clamp = values[0] == 'on';
}
/**
* This only exists for relection maps and denotes the type of reflection.
*
* @param {string[]} values the values passed to the -type flag
* @param {Object} options the Object of all image options
*/
parse_type(values, options) {
options.reflectionType = values[0];
}
/**
* Parses the -bm flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -bm flag
* @param {Object} options the Object of all image options
*/
parse_bm(values, options) {
options.bumpMultiplier = parseFloat(values[0]);
}
/**
* Parses the texture's options and returns an options object with the info
*
* @param {string[]} tokens all of the option tokens to pass to the texture
* @return {Object} a complete object of objects to apply to the texture
*/
parseOptions(tokens) {
let options = {
colorCorrection: false,
horizontalBlending: true,
verticalBlending: true,
boostMipMapSharpness: 0,
modifyTextureMap: {
brightness: 0,
contrast: 1
},
offset: { u: 0, v: 0, w: 0 },
scale: { u: 1, v: 1, w: 1 },
turbulence: { u: 0, v: 0, w: 0 },
clamp: false,
textureResolution: null,
bumpMultiplier: 1,
imfChan: null
};
/**
* Parses the -imfchan flag and updates the options object with the values.
*
* @param {string[]} values the values passed to the -imfchan flag
* @param {Object} options the Object of all image options
*/
parse_imfchan(values, options) {
options.imfChan = values[0];
}
let option;
let values;
let optionsToValues = {};
/**
* This only exists for relection maps and denotes the type of reflection.
*
* @param {string[]} values the values passed to the -type flag
* @param {Object} options the Object of all image options
*/
parse_type(values, options) {
options.reflectionType = values[0];
}
tokens.reverse();
/**
* Parses the texture's options and returns an options object with the info
*
* @param {string[]} tokens all of the option tokens to pass to the texture
* @return {Object} a complete object of objects to apply to the texture
*/
parseOptions(tokens) {
let options = {
colorCorrection: false,
horizontalBlending: true,
verticalBlending: true,
boostMipMapSharpness: 0,
modifyTextureMap: {
brightness: 0,
contrast: 1,
},
offset: {u: 0, v: 0, w: 0},
scale: {u: 1, v: 1, w: 1},
turbulence: {u: 0, v: 0, w: 0},
clamp: false,
textureResolution: null,
bumpMultiplier: 1,
imfChan: null,
};
while (tokens.length) {
const token = tokens.pop();
let option;
let values;
let optionsToValues = {};
if (token.startsWith("-")) {
option = token.substr(1);
optionsToValues[option] = [];
} else {
optionsToValues[option].push(token);
}
}
tokens.reverse();
for (option in optionsToValues) {
if (!optionsToValues.hasOwnProperty(option)) {
continue;
}
values = optionsToValues[option];
let optionMethod = this[`parse_${option}`];
if (optionMethod) {
optionMethod.bind(this)(values, options);
}
}
while (tokens.length) {
const token = tokens.pop();
return options;
}
if (token.startsWith('-')) {
option = token.substr(1);
optionsToValues[option] = [];
} else {
optionsToValues[option].push(token);
}
/**
* Parses the given texture map line.
*
* @param {string[]} tokens all of the tokens representing the texture
* @return {Object} a complete object of objects to apply to the texture
*/
parseMap(tokens) {
// according to wikipedia:
// (https://en.wikipedia.org/wiki/Wavefront_.obj_file#Vendor_specific_alterations)
// there is at least one vendor that places the filename before the options
// rather than after (which is to spec). All options start with a '-'
// so if the first token doesn't start with a '-', we're going to assume
// it's the name of the map file.
let filename;
let options;
if (!tokens[0].startsWith("-")) {
[filename, ...options] = tokens;
} else {
filename = tokens.pop();
options = tokens;
}
options = this.parseOptions(options);
options["filename"] = filename;
return options;
}
for (option in optionsToValues) {
if (!optionsToValues.hasOwnProperty(option)){
continue;
}
values = optionsToValues[option];
let optionMethod = this[`parse_${option}`];
if (optionMethod) {
optionMethod.bind(this)(values, options);
}
/**
* Parses the ambient map.
*
* @param {string[]} tokens list of tokens for the map_Ka direcive
*/
parse_map_Ka(tokens) {
this.currentMaterial.mapAmbient = this.parseMap(tokens);
}
return options;
}
/**
* Parses the diffuse map.
*
* @param {string[]} tokens list of tokens for the map_Kd direcive
*/
parse_map_Kd(tokens) {
this.currentMaterial.mapDiffuse = this.parseMap(tokens);
}
/**
* Parses the given texture map line.
*
* @param {string[]} tokens all of the tokens representing the texture
* @return {Object} a complete object of objects to apply to the texture
*/
parseMap(tokens) {
// according to wikipedia:
// (https://en.wikipedia.org/wiki/Wavefront_.obj_file#Vendor_specific_alterations)
// there is at least one vendor that places the filename before the options
// rather than after (which is to spec). All options start with a '-'
// so if the first token doesn't start with a '-', we're going to assume
// it's the name of the map file.
let filename;
let options;
if (!tokens[0].startsWith('-')) {
[filename, ...options] = tokens;
} else {
filename = tokens.pop();
options = tokens;
/**
* Parses the specular map.
*
* @param {string[]} tokens list of tokens for the map_Ks direcive
*/
parse_map_Ks(tokens) {
this.currentMaterial.mapSpecular = this.parseMap(tokens);
}
options = this.parseOptions(options);
options['filename'] = filename;
return options;
}
/**
* Parses the emissive map.
*
* @param {string[]} tokens list of tokens for the map_Ke direcive
*/
parse_map_Ke(tokens) {
this.currentMaterial.mapEmissive = this.parseMap(tokens);
}
/**
* Parses the ambient map.
*
* @param {string[]} tokens list of tokens for the map_Ka direcive
*/
parse_map_Ka(tokens) {
this.currentMaterial.mapAmbient = this.parseMap(tokens);
}
/**
* Parses the specular exponent map.
*
* @param {string[]} tokens list of tokens for the map_Ns direcive
*/
parse_map_Ns(tokens) {
this.currentMaterial.mapSpecularExponent = this.parseMap(tokens);
}
/**
* Parses the diffuse map.
*
* @param {string[]} tokens list of tokens for the map_Kd direcive
*/
parse_map_Kd(tokens) {
this.currentMaterial.mapDiffuse = this.parseMap(tokens);
}
/**
* Parses the dissolve map.
*
* @param {string[]} tokens list of tokens for the map_d direcive
*/
parse_map_d(tokens) {
this.currentMaterial.mapDissolve = this.parseMap(tokens);
}
/**
* Parses the specular map.
*
* @param {string[]} tokens list of tokens for the map_Ks direcive
*/
parse_map_Ks(tokens) {
this.currentMaterial.mapSpecular = this.parseMap(tokens);
}
/**
* Parses the anti-aliasing option.
*
* @param {string[]} tokens list of tokens for the map_aat direcive
*/
parse_map_aat(tokens) {
this.currentMaterial.antiAliasing = tokens[0] == "on";
}
/**
* Parses the emissive map.
*
* @param {string[]} tokens list of tokens for the map_Ke direcive
*/
parse_map_Ke(tokens) {
this.currentMaterial.mapEmissive = this.parseMap(tokens);
}
/**
* Parses the bump map.
*
* @param {string[]} tokens list of tokens for the map_bump direcive
*/
parse_map_bump(tokens) {
this.currentMaterial.mapBump = this.parseMap(tokens);
}
/**
* Parses the specular exponent map.
*
* @param {string[]} tokens list of tokens for the map_Ns direcive
*/
parse_map_Ns(tokens) {
this.currentMaterial.mapSpecularExponent = this.parseMap(tokens);
}
/**
* Parses the bump map.
*
* @param {string[]} tokens list of tokens for the bump direcive
*/
parse_bump(tokens) {
this.parse_map_bump(tokens);
}
/**
* Parses the dissolve map.
*
* @param {string[]} tokens list of tokens for the map_d direcive
*/
parse_map_d(tokens) {
this.currentMaterial.mapDissolve = this.parseMap(tokens);
}
/**
* Parses the disp map.
*
* @param {string[]} tokens list of tokens for the disp direcive
*/
parse_disp(tokens) {
this.currentMaterial.mapDisplacement = this.parseMap(tokens);
}
/**
* Parses the anti-aliasing option.
*
* @param {string[]} tokens list of tokens for the map_aat direcive
*/
parse_map_aat(tokens) {
this.currentMaterial.antiAliasing = tokens[0] == 'on';
}
/**
* Parses the decal map.
*
* @param {string[]} tokens list of tokens for the map_decal direcive
*/
parse_decal(tokens) {
this.currentMaterial.mapDecal = this.parseMap(tokens);
}
/**
* Parses the bump map.
*
* @param {string[]} tokens list of tokens for the map_bump direcive
*/
parse_map_bump(tokens) {
this.currentMaterial.mapBump = this.parseMap(tokens);
}
/**
* Parses the refl map.
*
* @param {string[]} tokens list of tokens for the refl direcive
*/
parse_refl(tokens) {
this.currentMaterial.mapReflections.push(this.parseMap(tokens));
}
/**
* Parses the bump map.
*
* @param {string[]} tokens list of tokens for the bump direcive
*/
parse_bump(tokens) {
this.parse_map_bump(tokens);
}
/**
* Parses the MTL file.
*
* Iterates line by line parsing each MTL directive.
*
* This function expects the first token in the line
* to be a valid MTL directive. That token is then used
* to try and run a method on this class. parse_[directive]
* E.g., the `newmtl` directive would try to call the method
* parse_newmtl. Each parsing function takes in the remaining
* list of tokens and updates the currentMaterial class with
* the attributes provided.
*/
parse() {
let lines = this.data.split(/\r?\n/);
for (let line of lines) {
line = line.trim();
if (!line || line.startsWith("#")) {
continue;
}
/**
* Parses the disp map.
*
* @param {string[]} tokens list of tokens for the disp direcive
*/
parse_disp(tokens) {
this.currentMaterial.mapDisplacement = this.parseMap(tokens);
}
let tokens = line.split(/\s/);
let directive;
[directive, ...tokens] = tokens;
/**
* Parses the decal map.
*
* @param {string[]} tokens list of tokens for the map_decal direcive
*/
parse_decal(tokens) {
this.currentMaterial.mapDecal = this.parseMap(tokens);
}
let parseMethod = this[`parse_${directive}`];
/**
* Parses the refl map.
*
* @param {string[]} tokens list of tokens for the refl direcive
*/
parse_refl(tokens) {
this.currentMaterial.mapReflections.push(this.parseMap(tokens));
}
if (!parseMethod) {
console.warn(`Don't know how to parse the directive: "${directive}"`);
continue;
}
/**
* Parses the MTL file.
*
* Iterates line by line parsing each MTL directive.
*
* This function expects the first token in the line
* to be a valid MTL directive. That token is then used
* to try and run a method on this class. parse_[directive]
* E.g., the `newmtl` directive would try to call the method
* parse_newmtl. Each parsing function takes in the remaining
* list of tokens and updates the currentMaterial class with
* the attributes provided.
*/
parse() {
let lines = this.data.split(/\r?\n/);
for (let line of lines) {
line = line.trim();
if (!line || line.startsWith('#')) {
continue;
}
// console.log(`Parsing "${directive}" with tokens: ${tokens}`);
parseMethod.bind(this)(tokens);
}
let tokens = line.split(/\s/);
let directive;
[directive, ...tokens] = tokens;
let parseMethod = this[`parse_${directive}`];
if (!parseMethod) {
console.warn(`Don't know how to parse the directive: "${directive}"`);
continue;
}
// console.log(`Parsing "${directive}" with tokens: ${tokens}`);
parseMethod.bind(this)(tokens);
// some cleanup. These don't need to be exposed as public data.
delete this.data;
this.currentMaterial = null;
}
// some cleanup. These don't need to be exposed as public data.
delete this.data;
this.currentMaterial = null;
}
/* eslint-enable camelcase*/
};
/* eslint-enable camelcase*/
}

@@ -1,2 +0,2 @@

import {Layout} from "./layout"
import { Layout } from "./layout";

@@ -8,5 +8,2 @@ /**

* OBJ.initMeshBuffers for an example of how to use the newly created Mesh
*
* - Options
* - materials
*/

@@ -16,15 +13,26 @@ export default class Mesh {

* Create a Mesh
* @param {String} objectData a string representation of an OBJ file with
* newlines preserved.
* @param {Object} options a JS object containing valid options. See class
* documentation for options.
* @param {String} objectData - a string representation of an OBJ file with
* newlines preserved.
* @param {Object} options - a JS object containing valid options. See class
* documentation for options.
* @param {bool} options.enableWTextureCoord - Texture coordinates can have
* an optional "w" coordinate after the u and v coordinates. This extra
* value can be used in order to perform fancy transformations on the
* textures themselves. Default is to truncate to only the u an v
* coordinates. Passing true will provide a default value of 0 in the
* event that any or all texture coordinates don't provide a w value.
* Always use the textureStride attribute in order to determine the
* stride length of the texture coordinates when rendering the element
* array.
* @param {bool} options.calcTangentsAndBitangents - Calculate the tangents
* and bitangents when loading of the OBJ is completed. This adds two new
* attributes to the Mesh instance: `tangents` and `bitangents`.
*/
constructor(objectData, options) {
options = options || {};
options.materials = options.materials || [];
options.materials = options.materials || {};
options.enableWTextureCoord = !!options.enableWTextureCoord;
options.indicesPerMaterial = !!options.indicesPerMaterial;
let self = this;
self.has_materials = !!options.materials;
// maps material name to Material() instance
self.materials = {};
// the list of unique vertex, normal, texture, attributes

@@ -36,2 +44,3 @@ self.vertices = [];

self.indices = [];
self.textureStride = options.enableWTextureCoord ? 3 : 2;

@@ -111,3 +120,3 @@ /*

*/
this.name = '';
this.name = "";
const verts = [];

@@ -121,2 +130,4 @@ const vertNormals = [];

let currentMaterialIndex = -1;
// keep track if pushing indices by materials - otherwise not used
let currentObjectByMaterialIndex = 0;
// unpacking stuff

@@ -127,3 +138,3 @@ unpacked.verts = [];

unpacked.hashindices = {};
unpacked.indices = [];
unpacked.indices = [[]];
unpacked.materialIndices = [];

@@ -140,7 +151,7 @@ unpacked.index = 0;

// array of lines separated by the newline
const lines = objectData.split('\n');
const lines = objectData.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (!line || line.startsWith('#')) {
if (!line || line.startsWith("#")) {
continue;

@@ -158,4 +169,18 @@ }

} else if (TEXTURE_RE.test(line)) {
// if this is a texture
textures.push(...elements);
let coords = elements;
// by default, the loader will only look at the U and V
// coordinates of the vt declaration. So, this truncates the
// elements to only those 2 values. If W texture coordinate
// support is enabled, then the texture coordinate is
// expected to have three values in it.
if (elements.length > 2 && !options.enableWTextureCoord) {
coords = elements.slice(0, 1);
} else if (elements.length === 2 && options.enableWTextureCoord) {
// If for some reason W texture coordinate support is enabled
// and only the U and V coordinates are given, then we supply
// the default value of 0 so that the stride length is correct
// when the textures are unpacked below.
coords.push(0);
}
textures.push(...coords);
} else if (USE_MATERIAL_RE.test(line)) {

@@ -169,6 +194,16 @@ const materialName = elements[0];

materialIndicesByName[materialName] = materialNamesByIndex.length - 1;
// push new array into indices
if (options.indicesPerMaterial) {
// already contains an array at index zero, don't add
if (materialIndicesByName[materialName] > 0) {
unpacked.indices.push([]);
}
}
}
// keep track of the current material index
currentMaterialIndex = materialIndicesByName[materialName];
// update current index array
if (options.indicesPerMaterial) {
currentObjectByMaterialIndex = currentMaterialIndex;
}
} else if (FACE_RE.test(line)) {

@@ -195,6 +230,6 @@ // if this is a face

}
const hash0 = elements[0] + ',' + currentMaterialIndex;
const hash = elements[j] + ',' + currentMaterialIndex;
const hash0 = elements[0] + "," + currentMaterialIndex;
const hash = elements[j] + "," + currentMaterialIndex;
if (hash in unpacked.hashindices) {
unpacked.indices.push(unpacked.hashindices[hash]);
unpacked.indices[currentObjectByMaterialIndex].push(unpacked.hashindices[hash]);
} else {

@@ -215,3 +250,3 @@ /*

*/
let vertex = elements[j].split('/');
let vertex = elements[j].split("/");
// it's possible for faces to only specify the vertex

@@ -247,4 +282,8 @@ // and the normal. In this case, vertex will only have

if (textures.length) {
unpacked.textures.push(+textures[(vertex[1] - 1) * 2 + 0]);
unpacked.textures.push(+textures[(vertex[1] - 1) * 2 + 1]);
let stride = options.enableWTextureCoord ? 3 : 2;
unpacked.textures.push(+textures[(vertex[1] - 1) * stride + 0]);
unpacked.textures.push(+textures[(vertex[1] - 1) * stride + 1]);
if (options.enableWTextureCoord) {
unpacked.textures.push(+textures[(vertex[1] - 1) * stride + 2]);
}
}

@@ -259,3 +298,3 @@ // Vertex normals

unpacked.hashindices[hash] = unpacked.index;
unpacked.indices.push(unpacked.index);
unpacked.indices[currentObjectByMaterialIndex].push(unpacked.hashindices[hash]);
// increment the counter

@@ -266,3 +305,3 @@ unpacked.index += 1;

// add v0/t0/vn0 onto the second triangle
unpacked.indices.push(unpacked.hashindices[hash0]);
unpacked.indices[currentObjectByMaterialIndex].push(unpacked.hashindices[hash0]);
}

@@ -276,3 +315,3 @@ }

self.vertexMaterialIndices = unpacked.materialIndices;
self.indices = unpacked.indices;
self.indices = options.indicesPerMaterial ? unpacked.indices : unpacked.indices[currentObjectByMaterialIndex];

@@ -282,5 +321,204 @@ self.materialNames = materialNamesByIndex;

self.materialsByIndex = {};
if (options.calcTangentsAndBitangents) {
this.calculateTangentsAndBitangents();
}
}
/**
* Calculates the tangents and bitangents of the mesh that forms an orthogonal basis together with the
* normal in the direction of the texture coordinates. These are useful for setting up the TBN matrix
* when distorting the normals through normal maps.
* Method derived from: http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/
*
* This method requires the normals and texture coordinates to be parsed and set up correctly.
* Adds the tangents and bitangents as members of the class instance.
*/
calculateTangentsAndBitangents() {
console.assert(
this.vertices &&
this.vertices.length &&
this.vertexNormals &&
this.vertexNormals.length &&
this.textures &&
this.textures.length,
"Missing attributes for calculating tangents and bitangents"
);
const unpacked = {};
unpacked.tangents = [...new Array(this.vertices.length)].map(v => 0);
unpacked.bitangents = [...new Array(this.vertices.length)].map(v => 0);
// Loop through all faces in the whole mesh
let indices;
// If sorted by material
if (Array.isArray(this.indices[0])) {
indices = [].concat.apply([], this.indices);
} else {
indices = this.indices;
}
const vertices = this.vertices;
const normals = this.vertexNormals;
const uvs = this.textures;
for (let i = 0; i < indices.length; i += 3) {
const i0 = indices[i + 0];
const i1 = indices[i + 1];
const i2 = indices[i + 2];
const x_v0 = vertices[i0 * 3 + 0];
const y_v0 = vertices[i0 * 3 + 1];
const z_v0 = vertices[i0 * 3 + 2];
const x_uv0 = uvs[i0 * 2 + 0];
const y_uv0 = uvs[i0 * 2 + 1];
const x_v1 = vertices[i1 * 3 + 0];
const y_v1 = vertices[i1 * 3 + 1];
const z_v1 = vertices[i1 * 3 + 2];
const x_uv1 = uvs[i1 * 2 + 0];
const y_uv1 = uvs[i1 * 2 + 1];
const x_v2 = vertices[i2 * 3 + 0];
const y_v2 = vertices[i2 * 3 + 1];
const z_v2 = vertices[i2 * 3 + 2];
const x_uv2 = uvs[i2 * 2 + 0];
const y_uv2 = uvs[i2 * 2 + 1];
const x_deltaPos1 = x_v1 - x_v0;
const y_deltaPos1 = y_v1 - y_v0;
const z_deltaPos1 = z_v1 - z_v0;
const x_deltaPos2 = x_v2 - x_v0;
const y_deltaPos2 = y_v2 - y_v0;
const z_deltaPos2 = z_v2 - z_v0;
const x_uvDeltaPos1 = x_uv1 - x_uv0;
const y_uvDeltaPos1 = y_uv1 - y_uv0;
const x_uvDeltaPos2 = x_uv2 - x_uv0;
const y_uvDeltaPos2 = y_uv2 - y_uv0;
const rInv = x_uvDeltaPos1 * y_uvDeltaPos2 - y_uvDeltaPos1 * x_uvDeltaPos2;
const r = 1.0 / (Math.abs(rInv < 0.0001) ? 1.0 : rInv);
// Tangent
const x_tangent = (x_deltaPos1 * y_uvDeltaPos2 - x_deltaPos2 * y_uvDeltaPos1) * r;
const y_tangent = (y_deltaPos1 * y_uvDeltaPos2 - y_deltaPos2 * y_uvDeltaPos1) * r;
const z_tangent = (z_deltaPos1 * y_uvDeltaPos2 - z_deltaPos2 * y_uvDeltaPos1) * r;
// Bitangent
const x_bitangent = (x_deltaPos2 * x_uvDeltaPos1 - x_deltaPos1 * x_uvDeltaPos2) * r;
const y_bitangent = (y_deltaPos2 * x_uvDeltaPos1 - y_deltaPos1 * x_uvDeltaPos2) * r;
const z_bitangent = (z_deltaPos2 * x_uvDeltaPos1 - z_deltaPos1 * x_uvDeltaPos2) * r;
// Gram-Schmidt orthogonalize
//t = glm::normalize(t - n * glm:: dot(n, t));
const x_n0 = normals[i0 * 3 + 0];
const y_n0 = normals[i0 * 3 + 1];
const z_n0 = normals[i0 * 3 + 2];
const x_n1 = normals[i1 * 3 + 0];
const y_n1 = normals[i1 * 3 + 1];
const z_n1 = normals[i1 * 3 + 2];
const x_n2 = normals[i2 * 3 + 0];
const y_n2 = normals[i2 * 3 + 1];
const z_n2 = normals[i2 * 3 + 2];
// Tangent
const n0_dot_t = x_tangent * x_n0 + y_tangent * y_n0 + z_tangent * z_n0;
const n1_dot_t = x_tangent * x_n1 + y_tangent * y_n1 + z_tangent * z_n1;
const n2_dot_t = x_tangent * x_n2 + y_tangent * y_n2 + z_tangent * z_n2;
const x_resTangent0 = x_tangent - x_n0 * n0_dot_t;
const y_resTangent0 = y_tangent - y_n0 * n0_dot_t;
const z_resTangent0 = z_tangent - z_n0 * n0_dot_t;
const x_resTangent1 = x_tangent - x_n1 * n1_dot_t;
const y_resTangent1 = y_tangent - y_n1 * n1_dot_t;
const z_resTangent1 = z_tangent - z_n1 * n1_dot_t;
const x_resTangent2 = x_tangent - x_n2 * n2_dot_t;
const y_resTangent2 = y_tangent - y_n2 * n2_dot_t;
const z_resTangent2 = z_tangent - z_n2 * n2_dot_t;
const magTangent0 = Math.sqrt(
x_resTangent0 * x_resTangent0 + y_resTangent0 * y_resTangent0 + z_resTangent0 * z_resTangent0
);
const magTangent1 = Math.sqrt(
x_resTangent1 * x_resTangent1 + y_resTangent1 * y_resTangent1 + z_resTangent1 * z_resTangent1
);
const magTangent2 = Math.sqrt(
x_resTangent2 * x_resTangent2 + y_resTangent2 * y_resTangent2 + z_resTangent2 * z_resTangent2
);
// Bitangent
const n0_dot_bt = x_bitangent * x_n0 + y_bitangent * y_n0 + z_bitangent * z_n0;
const n1_dot_bt = x_bitangent * x_n1 + y_bitangent * y_n1 + z_bitangent * z_n1;
const n2_dot_bt = x_bitangent * x_n2 + y_bitangent * y_n2 + z_bitangent * z_n2;
const x_resBitangent0 = x_bitangent - x_n0 * n0_dot_bt;
const y_resBitangent0 = y_bitangent - y_n0 * n0_dot_bt;
const z_resBitangent0 = z_bitangent - z_n0 * n0_dot_bt;
const x_resBitangent1 = x_bitangent - x_n1 * n1_dot_bt;
const y_resBitangent1 = y_bitangent - y_n1 * n1_dot_bt;
const z_resBitangent1 = z_bitangent - z_n1 * n1_dot_bt;
const x_resBitangent2 = x_bitangent - x_n2 * n2_dot_bt;
const y_resBitangent2 = y_bitangent - y_n2 * n2_dot_bt;
const z_resBitangent2 = z_bitangent - z_n2 * n2_dot_bt;
const magBitangent0 = Math.sqrt(
x_resBitangent0 * x_resBitangent0 +
y_resBitangent0 * y_resBitangent0 +
z_resBitangent0 * z_resBitangent0
);
const magBitangent1 = Math.sqrt(
x_resBitangent1 * x_resBitangent1 +
y_resBitangent1 * y_resBitangent1 +
z_resBitangent1 * z_resBitangent1
);
const magBitangent2 = Math.sqrt(
x_resBitangent2 * x_resBitangent2 +
y_resBitangent2 * y_resBitangent2 +
z_resBitangent2 * z_resBitangent2
);
unpacked.tangents[i0 * 3 + 0] += x_resTangent0 / magTangent0;
unpacked.tangents[i0 * 3 + 1] += y_resTangent0 / magTangent0;
unpacked.tangents[i0 * 3 + 2] += z_resTangent0 / magTangent0;
unpacked.tangents[i1 * 3 + 0] += x_resTangent1 / magTangent1;
unpacked.tangents[i1 * 3 + 1] += y_resTangent1 / magTangent1;
unpacked.tangents[i1 * 3 + 2] += z_resTangent1 / magTangent1;
unpacked.tangents[i2 * 3 + 0] += x_resTangent2 / magTangent2;
unpacked.tangents[i2 * 3 + 1] += y_resTangent2 / magTangent2;
unpacked.tangents[i2 * 3 + 2] += z_resTangent2 / magTangent2;
unpacked.bitangents[i0 * 3 + 0] += x_resBitangent0 / magBitangent0;
unpacked.bitangents[i0 * 3 + 1] += y_resBitangent0 / magBitangent0;
unpacked.bitangents[i0 * 3 + 2] += z_resBitangent0 / magBitangent0;
unpacked.bitangents[i1 * 3 + 0] += x_resBitangent1 / magBitangent1;
unpacked.bitangents[i1 * 3 + 1] += y_resBitangent1 / magBitangent1;
unpacked.bitangents[i1 * 3 + 2] += z_resBitangent1 / magBitangent1;
unpacked.bitangents[i2 * 3 + 0] += x_resBitangent2 / magBitangent2;
unpacked.bitangents[i2 * 3 + 1] += y_resBitangent2 / magBitangent2;
unpacked.bitangents[i2 * 3 + 2] += z_resBitangent2 / magBitangent2;
// TODO: check handedness
}
this.tangents = unpacked.tangents;
this.bitangents = unpacked.bitangents;
}
/**
* @param {Layout} layout - A {@link Layout} object that describes the

@@ -323,3 +561,7 @@ * desired memory layout of the generated buffer

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -336,3 +578,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -349,3 +595,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -362,3 +612,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -373,3 +627,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -386,3 +644,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -399,3 +661,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -410,3 +676,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -421,3 +691,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -432,3 +706,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -443,3 +721,7 @@ }

if (!material) {
console.warn('Material "' + this.materialNames[materialIndex] + '" not found in mesh. Did you forget to call addMaterialLibrary(...)?"');
console.warn(
'Material "' +
this.materialNames[materialIndex] +
'" not found in mesh. Did you forget to call addMaterialLibrary(...)?"'
);
break;

@@ -462,3 +744,3 @@ }

addMaterialLibrary (mtl) {
addMaterialLibrary(mtl) {
for (const name in mtl.materials) {

@@ -473,3 +755,3 @@ if (!(name in this.materialIndices)) {

// Find the material index for this material
const materialIndex = this.materialIndices[material.name]
const materialIndex = this.materialIndices[material.name];

@@ -476,0 +758,0 @@ // Put the material into the materialsByIndex object at the right

@@ -0,61 +1,58 @@

import Mesh from "./mesh";
import { Material, MaterialLibrary } from "./material";
import { Layout } from "./layout";
import Mesh from './mesh';
import {Material, MaterialLibrary} from './material';
import {Layout} from './layout';
function downloadMtlTextures (mtl, root) {
const mapAttributes = [
'mapDiffuse',
'mapAmbient',
'mapSpecular',
'mapDissolve',
'mapBump',
'mapDisplacement',
'mapDecal',
'mapEmissive',
];
if (!root.endsWith('/')) {
root += '/';
}
let textures = [];
for (let material in mtl.materials) {
if (!mtl.materials.hasOwnProperty(material)) {
continue;
function downloadMtlTextures(mtl, root) {
const mapAttributes = [
"mapDiffuse",
"mapAmbient",
"mapSpecular",
"mapDissolve",
"mapBump",
"mapDisplacement",
"mapDecal",
"mapEmissive"
];
if (!root.endsWith("/")) {
root += "/";
}
material = mtl.materials[material];
let textures = [];
for (let attr of mapAttributes) {
let mapData = material[attr];
if (!mapData) {
continue;
}
for (let material in mtl.materials) {
if (!mtl.materials.hasOwnProperty(material)) {
continue;
}
material = mtl.materials[material];
let url = root + mapData.filename;
textures.push(
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error()
for (let attr of mapAttributes) {
let mapData = material[attr];
if (!mapData) {
continue;
}
return response.blob();
})
.then(function(data) {
let image = new Image();
image.src = URL.createObjectURL(data);
mapData.texture = image;
})
.catch(() => {
console.error(`Unable to download texture: ${url}`);
})
);
let url = root + mapData.filename;
textures.push(
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error();
}
return response.blob();
})
.then(function(data) {
let image = new Image();
image.src = URL.createObjectURL(data);
mapData.texture = image;
return new Promise(resolve => (image.onload = () => Promise.resolve()));
})
.catch(() => {
console.error(`Unable to download texture: ${url}`);
})
);
}
}
}
return Promise.all(textures);
return Promise.all(textures);
}
/**

@@ -104,90 +101,89 @@ * Accepts a list of model request objects and returns a Promise that

*/
export function downloadModels (models) {
const finished = [];
export function downloadModels(models) {
const finished = [];
for (const model of models) {
const parsed = [];
for (const model of models) {
const parsed = [];
if (!model.obj) {
throw new Error(
'"obj" attribute of model object not set. The .obj file is required to be set ' +
'in order to use downloadModels()'
);
}
if (!model.obj) {
throw new Error(
'"obj" attribute of model object not set. The .obj file is required to be set ' +
"in order to use downloadModels()"
);
}
// if the name is not provided, dervive it from the given OBJ
let name = model.name;
if (!name) {
let parts = model.obj.split('/');
name = parts[parts.length - 1].replace('.obj', '');
}
parsed.push(Promise.resolve(name))
let options = {};
options.indicesPerMaterial = !!model.indicesPerMaterial;
options.calcTangentsAndBitangents = !!model.calcTangentsAndBitangents;
parsed.push(
fetch(model.obj)
.then((response) => response.text())
.then((data) => {
return new Mesh(data);
})
);
// if the name is not provided, dervive it from the given OBJ
let name = model.name;
if (!name) {
let parts = model.obj.split("/");
name = parts[parts.length - 1].replace(".obj", "");
}
parsed.push(Promise.resolve(name));
// Download MaterialLibrary file?
if (model.mtl) {
let mtl = model.mtl;
if (typeof mtl === 'boolean') {
mtl = model.obj.replace(/\.obj$/, '.mtl');
}
parsed.push(
fetch(model.obj)
.then(response => response.text())
.then(data => {
return new Mesh(data, options);
})
);
parsed.push(
fetch(mtl)
.then((response) => response.text())
.then((data) => {
let material = new MaterialLibrary(data);
if (model.downloadMtlTextures !== false) {
let root = model.mtlTextureRoot;
if (!root) {
// get the directory of the MTL file as default
root = mtl.substr(0, mtl.lastIndexOf("/"));
}
// downloadMtlTextures returns a Promise that
// is resolved once all of the images it
// contains are downloaded. These are then
// attached to the map data objects
return Promise.all([
Promise.resolve(material),
downloadMtlTextures(material, root)
]);
// Download MaterialLibrary file?
if (model.mtl) {
let mtl = model.mtl;
if (typeof mtl === "boolean") {
mtl = model.obj.replace(/\.obj$/, ".mtl");
}
return Promise.all(Promise.resolve(material));
})
.then((value) => {
return value[0];
})
);
parsed.push(
fetch(mtl)
.then(response => response.text())
.then(data => {
let material = new MaterialLibrary(data);
if (model.downloadMtlTextures !== false) {
let root = model.mtlTextureRoot;
if (!root) {
// get the directory of the MTL file as default
root = mtl.substr(0, mtl.lastIndexOf("/"));
}
// downloadMtlTextures returns a Promise that
// is resolved once all of the images it
// contains are downloaded. These are then
// attached to the map data objects
return Promise.all([Promise.resolve(material), downloadMtlTextures(material, root)]);
}
return Promise.all(Promise.resolve(material));
})
.then(value => {
return value[0];
})
);
}
finished.push(Promise.all(parsed));
}
finished.push(Promise.all(parsed));
}
return Promise.all(finished).then(ms => {
// the "finished" promise is a list of name, Mesh instance,
// and MaterialLibary instance. This unpacks and returns an
// object mapping name to Mesh (Mesh points to MTL).
const models = {};
return Promise.all(finished)
.then((ms) => {
// the "finished" promise is a list of name, Mesh instance,
// and MaterialLibary instance. This unpacks and returns an
// object mapping name to Mesh (Mesh points to MTL).
const models = {};
for (const model of ms) {
const [name, mesh, mtl] = model;
mesh.name = name;
if (mtl) {
mesh.addMaterialLibrary(mtl);
for (const model of ms) {
const [name, mesh, mtl] = model;
mesh.name = name;
if (mtl) {
mesh.addMaterialLibrary(mtl);
}
models[name] = mesh;
}
models[name] = mesh;
}
return models;
return models;
});
}
/**

@@ -210,41 +206,40 @@ * Takes in an object of `mesh_name`, `'/url/to/OBJ/file'` pairs and a callback

*/
export function downloadMeshes (nameAndURLs, completionCallback, meshes) {
if (meshes === undefined) {
meshes = {};
}
export function downloadMeshes(nameAndURLs, completionCallback, meshes) {
if (meshes === undefined) {
meshes = {};
}
const completed = [];
const completed = [];
for (const mesh_name in nameAndURLs) {
if (!nameAndURLs.hasOwnProperty(mesh_name)) {
continue;
for (const mesh_name in nameAndURLs) {
if (!nameAndURLs.hasOwnProperty(mesh_name)) {
continue;
}
const url = nameAndURLs[mesh_name];
completed.push(
fetch(url)
.then(response => response.text())
.then(data => {
return [mesh_name, new Mesh(data)];
})
);
}
const url = nameAndURLs[mesh_name];
completed.push(
fetch(url)
.then((response) => response.text())
.then((data) => {
return [mesh_name, new Mesh(data)];
})
);
}
Promise.all(completed)
.then((ms) => {
for (let [name, mesh] of ms) {
meshes[name] = mesh;
}
Promise.all(completed).then(ms => {
for (let [name, mesh] of ms) {
meshes[name] = mesh;
}
return completionCallback(meshes);
return completionCallback(meshes);
});
}
var _buildBuffer = function( gl, type, data, itemSize ){
var buffer = gl.createBuffer();
var arrayView = type === gl.ARRAY_BUFFER ? Float32Array : Uint16Array;
gl.bindBuffer(type, buffer);
gl.bufferData(type, new arrayView(data), gl.STATIC_DRAW);
buffer.itemSize = itemSize;
buffer.numItems = data.length / itemSize;
return buffer;
var _buildBuffer = function(gl, type, data, itemSize) {
var buffer = gl.createBuffer();
var arrayView = type === gl.ARRAY_BUFFER ? Float32Array : Uint16Array;
gl.bindBuffer(type, buffer);
gl.bufferData(type, new arrayView(data), gl.STATIC_DRAW);
buffer.itemSize = itemSize;
buffer.numItems = data.length / itemSize;
return buffer;
};

@@ -326,14 +321,14 @@

*/
export function initMeshBuffers ( gl, mesh ) {
mesh.normalBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertexNormals, 3);
mesh.textureBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.textures, 2);
mesh.vertexBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertices, 3);
mesh.indexBuffer = _buildBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, mesh.indices, 1);
};
export function initMeshBuffers(gl, mesh) {
mesh.normalBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertexNormals, 3);
mesh.textureBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.textures, mesh.textureStride);
mesh.vertexBuffer = _buildBuffer(gl, gl.ARRAY_BUFFER, mesh.vertices, 3);
mesh.indexBuffer = _buildBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, mesh.indices, 1);
}
export function deleteMeshBuffers ( gl, mesh ) {
gl.deleteBuffer(mesh.normalBuffer);
gl.deleteBuffer(mesh.textureBuffer);
gl.deleteBuffer(mesh.vertexBuffer);
gl.deleteBuffer(mesh.indexBuffer);
export function deleteMeshBuffers(gl, mesh) {
gl.deleteBuffer(mesh.normalBuffer);
gl.deleteBuffer(mesh.textureBuffer);
gl.deleteBuffer(mesh.vertexBuffer);
gl.deleteBuffer(mesh.indexBuffer);
}

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc