@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
6 lines (5 loc) • 9.87 kB
JavaScript
/*
All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/4.32/esri/copyright.txt for details.
*/
import{colorGamma as e}from"../../../../../colorUtils.js";import t from"../../../../../core/Logger.js";import{getOrCreateMapValue as s}from"../../../../../core/MapUtils.js";import{deg2rad as r}from"../../../../../core/mathUtils.js";import{exactEquals as i}from"../../../../../core/libs/gl-matrix-2/math/quat.js";import{IDENTITY as o,clone as a}from"../../../../../core/libs/gl-matrix-2/factories/quatf64.js";import{p as n}from"../../../../../chunks/vec32.js";import{ZEROS as l,clone as c,ONES as u}from"../../../../../core/libs/gl-matrix-2/factories/vec3f64.js";import h from"../../../MeshMaterialMetallicRoughness.js";import{convertVertexSpace as f}from"../../vertexSpaceConversion.js";import{Buffer as m}from"./buffer.js";import{smoothNormals as p}from"./geometry.js";import{BufferOutputType as d,ImageOutputType as g,AttributeType as x,TargetBuffer as _,AlphaMode as T}from"./types.js";import{imageToArrayBuffer as R,imageToDataURI as A}from"./imageutils.js";import{isEncodedMeshTexture as b}from"../../../../../views/3d/glTF/internal/resourceUtils.js";import{DataType as M,TextureWrapMode as E}from"../../../../../views/webgl/enums.js";const O=()=>t.getLogger("esri.geometry.support.meshUtils.exporters.gltf.gltf");class w{constructor(e,t){this.options=t,this._materialMap=new Array,this._imageMap=new Map,this._textureMap=new Map,this.gltf={asset:{version:"2.0",copyright:e.copyright,generator:e.generator},extras:{output:t.output,binChunkBuffer:null,promises:[]}},this._addScenes(e)}_addScenes(e){this.gltf.scene=e.defaultScene;const t=this.gltf.extras,s=t.output.buffer===d.GLB||t.output.image===g.GLB;s&&(t.binChunkBuffer=new m(this.gltf)),e.forEachScene((e=>{this._addScene(e)})),s&&t.binChunkBuffer.finalize()}_addScene(e){this.gltf.scenes||(this.gltf.scenes=[]);const t={};e.name&&(t.name=e.name),e.forEachNode((e=>{t.nodes||(t.nodes=[]),t.nodes.push(...this._addNodes(e))})),this.gltf.scenes.push(t)}_addNodes(e){this.gltf.nodes||(this.gltf.nodes=[]);const t={};e.name&&(t.name=e.name);const s=e.translation;n(s,l)||(t.translation=c(s));const r=e.rotation;i(r,o)||(t.rotation=a(r));const h=e.scale;n(h,u)||(t.scale=c(h));const f=this.gltf.nodes.length;if(this.gltf.nodes.push(t),e.mesh&&e.mesh.vertexAttributes.position){const s=this._createMeshes(e.mesh),r=[f];if(1===s.length)this._addMesh(t,s[0]);else for(const e of s){const t={};this._addMesh(t,e),r.push(this.gltf.nodes.length),this.gltf.nodes.push(t)}return r}return e.forEachNode((e=>{t.children||(t.children=[]),t.children.push(...this._addNodes(e))})),[f]}_addMesh(e,t){this.gltf.meshes??=[];const s=this.gltf.meshes.length;this.gltf.meshes.push(t),e.mesh=s}_createMeshes(e){const t=this.gltf.extras,s=t.output.buffer===d.GLB;let r;r=s?t.binChunkBuffer:new m(this.gltf);const i=this.options.origin,o=e.vertexSpace.clone();o.origin=[i.x,i.y,i.z??0];const a=f({vertexAttributes:e.vertexAttributes,vertexSpace:e.vertexSpace,transform:this.options?.ignoreLocalTransform?null:e.transform,spatialReference:e.spatialReference},o,{targetUnit:this.options.unitConversionDisabled?void 0:"meters"});if(!a)return[];p(e,a),v(a);const{position:n,normal:l,tangent:c}=a,{uv:u,color:h}=e.vertexAttributes,g=r.addBufferView(M.FLOAT,x.VEC3,_.ARRAY_BUFFER);let T,R,A,b;l&&(T=r.addBufferView(M.FLOAT,x.VEC3,_.ARRAY_BUFFER)),u&&(R=r.addBufferView(M.FLOAT,x.VEC2,_.ARRAY_BUFFER)),c&&(A=r.addBufferView(M.FLOAT,x.VEC4,_.ARRAY_BUFFER)),h&&(b=r.addBufferView(M.FLOAT,x.VEC4,_.ARRAY_BUFFER)),g.startAccessor("POSITION"),T&&T.startAccessor("NORMAL"),R&&R.startAccessor("TEXCOORD_0"),A&&A.startAccessor("TANGENT"),b&&b.startAccessor("COLOR_0");const E=a.position.length/3;for(let f=0;f<E;++f)g.push(n[3*f]),g.push(n[3*f+1]),g.push(n[3*f+2]),T&&null!=l&&(T.push(l[3*f]),T.push(l[3*f+1]),T.push(l[3*f+2])),R&&null!=u&&(R.push(u[2*f]),R.push(u[2*f+1])),A&&null!=c&&(A.push(c[4*f]),A.push(c[4*f+1]),A.push(c[4*f+2]),A.push(c[4*f+3])),b&&null!=h&&(b.push(I(h[4*f]/255)),b.push(I(h[4*f+1]/255)),b.push(I(h[4*f+2]/255)),b.push(h[4*f+3]/255));const O=g.endAccessor(),w=this._addAccessor(g.index,O);let C,N,L,S,B;if(T){const e=T.endAccessor();C=this._addAccessor(T.index,e)}if(R){const e=R.endAccessor();N=this._addAccessor(R.index,e)}if(A){const e=A.endAccessor();L=this._addAccessor(A.index,e)}if(b){const e=b.endAccessor();S=this._addAccessor(b.index,e)}const F=[];return e.components&&e.components.length>0&&e.components[0].faces?(B=r.addBufferView(M.UNSIGNED_INT,x.SCALAR,_.ELEMENT_ARRAY_BUFFER),this._addMeshVertexIndexed(B,e.components,F,w,C,N,L,S)):this._addMeshVertexNonIndexed(e.components,F,w,C,N,L,S),g.finalize(),T&&T.finalize(),R&&R.finalize(),A&&A.finalize(),B&&B.finalize(),b&&b.finalize(),s||r.finalize(),F}_addMaterial(e){if(null==e)return;const t=this._materialMap.indexOf(e);if(-1!==t)return t;this.gltf.materials||(this.gltf.materials=[]);const s={};switch(e.alphaMode){case"mask":s.alphaMode=T.MASK;break;case"auto":case"blend":s.alphaMode=T.BLEND}s.alphaCutoff=e.alphaCutoff,e.doubleSided&&(s.doubleSided=e.doubleSided),s.pbrMetallicRoughness={};const r=e=>{const t=e.toRgba();return t[0]=I(t[0]/255),t[1]=I(t[1]/255),t[2]=I(t[2]/255),t};if(null!=e.color&&(s.pbrMetallicRoughness.baseColorFactor=r(e.color)),null!=e.colorTexture&&(s.pbrMetallicRoughness.baseColorTexture=this._createTextureInfo(e.colorTexture,e.colorTextureTransform)),null!=e.normalTexture&&(s.normalTexture=this._createTextureInfo(e.normalTexture,e.normalTextureTransform)),e instanceof h){if(null!=e.emissiveTexture&&(s.emissiveTexture=this._createTextureInfo(e.emissiveTexture,e.emissiveTextureTransform)),null!=e.emissiveColor){const t=r(e.emissiveColor);s.emissiveFactor=[t[0],t[1],t[2]]}null!=e.occlusionTexture&&(s.occlusionTexture=this._createTextureInfo(e.occlusionTexture,e.occlusionTextureTransform)),null!=e.metallicRoughnessTexture&&(s.pbrMetallicRoughness.metallicRoughnessTexture=this._createTextureInfo(e.metallicRoughnessTexture,e.metallicRoughnessTextureTransform)),s.pbrMetallicRoughness.metallicFactor=e.metallic,s.pbrMetallicRoughness.roughnessFactor=e.roughness}else s.pbrMetallicRoughness.metallicFactor=1,s.pbrMetallicRoughness.roughnessFactor=1,O().warnOnce("Meshes exported to GLTF without MeshMaterialMetallicRoughness material will appear different when imported back.");const i=this.gltf.materials.length;return this.gltf.materials.push(s),this._materialMap.push(e),i}_createTextureInfo(e,t){const s={index:this._addTexture(e)};return t?(s.extensions||(s.extensions={}),s.extensions.KHR_texture_transform={scale:t.scale,offset:t.offset,rotation:r(t.rotation)},s):s}_addTexture(e){const t=this.gltf.textures??[];return this.gltf.textures=t,s(this._textureMap,e,(()=>{const s={sampler:this._addSampler(e),source:this._addImage(e)},r=t.length;return t.push(s),r}))}_addImage(e){const t=this._imageMap.get(e);if(null!=t)return t;this.gltf.images||(this.gltf.images=[]);const s={};if(e.url)s.uri=e.url;else{const t=e.data;s.extras=t;for(let e=0;e<this.gltf.images.length;++e)if(t===this.gltf.images[e].extras)return e;const r=this.gltf.extras;switch(r.output.image){case g.GLB:{const e=r.binChunkBuffer.addBufferView(M.UNSIGNED_BYTE,x.SCALAR);if(b(t))null!=t.data&&e.writeOutToBuffer(t.data,0);else{const i=R(t,this.options.signal).then((({data:e,type:t})=>(s.mimeType=t,e)));r.promises.push(e.writeAsync(i).then((()=>{e.finalize()})))}s.bufferView=e.index;break}case g.DataURI:if(b(t)){O().warnOnce("Image export for basis compressed textures not available.");break}s.uri=A(t);break;default:if(b(t)){O().warnOnce("Image export for basis compressed textures not available.");break}r.promises.push(R(t,this.options.signal).then((({data:e,type:t})=>{s.uri=e,s.mimeType=t})))}}const r=this.gltf.images.length;return this.gltf.images.push(s),this._imageMap.set(e,r),r}_addSampler(e){this.gltf.samplers||(this.gltf.samplers=[]);let t=E.REPEAT,s=E.REPEAT;if("string"==typeof e.wrap)switch(e.wrap){case"clamp":t=E.CLAMP_TO_EDGE,s=E.CLAMP_TO_EDGE;break;case"mirror":t=E.MIRRORED_REPEAT,s=E.MIRRORED_REPEAT}else{switch(e.wrap.vertical){case"clamp":s=E.CLAMP_TO_EDGE;break;case"mirror":s=E.MIRRORED_REPEAT}switch(e.wrap.horizontal){case"clamp":t=E.CLAMP_TO_EDGE;break;case"mirror":t=E.MIRRORED_REPEAT}}const r={wrapS:t,wrapT:s};for(let o=0;o<this.gltf.samplers.length;++o)if(JSON.stringify(r)===JSON.stringify(this.gltf.samplers[o]))return o;const i=this.gltf.samplers.length;return this.gltf.samplers.push(r),i}_addAccessor(e,t){this.gltf.accessors||(this.gltf.accessors=[]);const s={bufferView:e,byteOffset:t.byteOffset,componentType:t.componentType,count:t.count,type:t.type,min:t.min,max:t.max,name:t.name};t.normalized&&(s.normalized=!0);const r=this.gltf.accessors.length;return this.gltf.accessors.push(s),r}_addMeshVertexIndexed(e,t,s,r,i,o,a,n){const l=new Map;for(const c of t){if(e.startAccessor("INDICES"),c.faces)for(let s=0;s<c.faces.length;++s)e.push(c.faces[s]);const t=e.endAccessor(),u={attributes:{POSITION:r},indices:this._addAccessor(e.index,t),material:this._addMaterial(c.material)};i&&"flat"!==c.shading&&(u.attributes.NORMAL=i),o&&(u.attributes.TEXCOORD_0=o),a&&"flat"!==c.shading&&(u.attributes.TANGENT=a),n&&(u.attributes.COLOR_0=n);const h=l.get(c.name);if(h)h.primitives.push(u);else{const e={name:c.name,primitives:[u]};l.set(c.name,e),s.push(e)}}}_addMeshVertexNonIndexed(e,t,s,r,i,o,a){const n={primitives:[]};t.push(n);const l={attributes:{POSITION:s}};r&&(l.attributes.NORMAL=r),i&&(l.attributes.TEXCOORD_0=i),o&&(l.attributes.TANGENT=o),a&&(l.attributes.COLOR_0=a),e&&(l.material=this._addMaterial(e[0].material)),n.primitives.push(l)}}function v({position:e,normal:t,tangent:s}){C(e,3),C(t,3),C(s,4)}function C(e,t){if(null!=e)for(let s=1,r=2;s<e.length;s+=t,r+=t){const t=e[s],i=e[r];e[s]=i,e[r]=-t}}function I(t){return t**e}export{w as GLTF};