webgl-obj-loader
Advanced tools
Comparing version 1.0.0 to 1.0.2
@@ -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'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'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"); |
1298
src/material.js
@@ -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*/ | ||
} |
364
src/mesh.js
@@ -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 |
325
src/utils.js
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
820917
3686
291
12
14