UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

6 lines (5 loc) • 15.3 kB
/* All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://js.arcgis.com/4.33/esri/copyright.txt for details. */ import e from"../../../../core/Error.js";import t from"../../../../core/Logger.js";import{getOrCreateMapValue as r}from"../../../../core/MapUtils.js";import{allSettledValues as o}from"../../../../core/promiseUtils.js";import{isDataProtocol as n,dataComponents as s,dataToArrayBuffer as a,makeAbsolute as i}from"../../../../core/urlUtils.js";import{Version as u}from"../../../../core/Version.js";import{fromXRotation as f,multiply as l,translate as d,rotate as c,scale as p}from"../../../../core/libs/gl-matrix-2/math/mat4.js";import{create as m,clone as h}from"../../../../core/libs/gl-matrix-2/factories/mat4f64.js";import{getAxisAngle as w}from"../../../../core/libs/gl-matrix-2/math/quat.js";import{create as b}from"../../../../core/libs/gl-matrix-2/factories/quatf64.js";import{BufferViewVec2f as T,BufferViewVec2u32 as y,BufferViewVec2u16 as g,BufferViewVec2i16 as x,BufferViewVec2u8 as _,BufferViewVec2i8 as O,BufferViewUint32 as N,BufferViewUint16 as S,BufferViewUint8 as C,BufferViewVec3f as A,BufferViewVec4f as E,BufferViewVec4u8 as L,BufferViewVec4u16 as R,BufferViewVec3u8 as v,BufferViewVec3u16 as I}from"../../../../geometry/support/buffer/BufferView.js";import{m as U}from"../../../../chunks/scalar.js";import"../../../../core/has.js";import{BinaryStreamReader as B}from"./BinaryStreamReader.js";import{AttributeType as D}from"./enums.js";import{material as j,textureSampler as M}from"./fillDefaults.js";import{splitURI as G}from"./pathUtils.js";import{jsonFromBinaryData as F,EncodedMeshTexture as H,imageFromBinaryData as P,isEncodedMeshTexture as V}from"./resourceUtils.js";import{DataType as K}from"../../../webgl/enums.js";const k={MAGIC:1179937895,CHUNK_TYPE_JSON:1313821514,CHUNK_TYPE_BIN:5130562};class Y{constructor(t,r,o,n){if(this._context=t,this.uri=r,this.json=o,this._glbBuffer=n,this._bufferLoaders=new Map,this._textureLoaders=new Map,this._textureCache=new Map,this._materialCache=new Map,this._nodeParentMap=new Map,this._nodeTransformCache=new Map,this._supportedExtensions=["KHR_texture_basisu","KHR_texture_transform"],this._baseUri=G(this.uri).dirPart,this._checkVersionSupported(),this._checkRequiredExtensionsSupported(),null==o.scenes)throw new e("gltf-loader-unsupported-feature","Scenes must be defined.");if(null==o.meshes)throw new e("gltf-loader-unsupported-feature","Meshes must be defined");if(null==o.nodes)throw new e("gltf-loader-unsupported-feature","Nodes must be defined.");this._computeNodeParents()}static async load(t,r,o){if(n(r)){const e=s(r);if(e&&"model/gltf-binary"!==e.mediaType)try{const o=JSON.parse(e.isBase64?atob(e.data):e.data);return new Y(t,r,o)}catch{}const o=a(r);if(Y._isGLBData(o))return this._fromGLBData(t,r,o)}if(ee.test(r)||"gltf"===o?.expectedType){const e=await t.loadJSON(r,o);return new Y(t,r,e)}const i=await t.loadBinary(r,o);if(Y._isGLBData(i))return this._fromGLBData(t,r,i);if(te.test(r)||"glb"===o?.expectedType)throw new e("gltf-loader-invalid-glb","This is not a valid glb file.");const u=await t.loadJSON(r,o);return new Y(t,r,u)}static _isGLBData(e){if(null==e)return!1;const t=new B(e);return t.remainingBytes()>=4&&t.readUint32()===k.MAGIC}static async _fromGLBData(e,t,r){const o=await Y._parseGLBData(r);return new Y(e,t,o.json,o.binaryData)}static async _parseGLBData(r){const o=new B(r);if(o.remainingBytes()<12)throw new e("gltf-loader-error","glb binary data is insufficiently large.");const n=o.readUint32(),s=o.readUint32(),a=o.readUint32();if(n!==k.MAGIC)throw new e("gltf-loader-error","Magic first 4 bytes do not fit to expected glb value.");if(r.byteLength<a)throw new e("gltf-loader-error","glb binary data is smaller than header specifies.");if(2!==s)throw new e("gltf-loader-unsupported-feature","An unsupported glb container version was detected. Only version 2 is supported.");let i,u,f=0;for(;o.remainingBytes()>=8;){const r=o.readUint32(),n=o.readUint32();if(0===f){if(n!==k.CHUNK_TYPE_JSON)throw new e("gltf-loader-error","First glb chunk must be JSON.");if(r<0)throw new e("gltf-loader-error","No JSON data found.");i=await F(o.readUint8Array(r))}else if(1===f){if(n!==k.CHUNK_TYPE_BIN)throw new e("gltf-loader-unsupported-feature","Second glb chunk expected to be BIN.");u=o.readUint8Array(r)}else t.getLogger("esri.views.3d.glTF").warn("[Unsupported Feature] More than 2 glb chunks detected. Skipping.");f+=1}if(!i)throw new e("gltf-loader-error","No glb JSON chunk detected.");return{json:i,binaryData:u}}async getBuffer(t,r){const o=this.json.buffers[t];if(null==o.uri){if(null==this._glbBuffer)throw new e("gltf-loader-error","glb buffer not present");return this._glbBuffer}const n=await this._getBufferLoader(t,r);if(n.byteLength!==o.byteLength)throw new e("gltf-loader-error","Buffer byte lengths should match.");return n}async _getBufferLoader(e,t){const r=this._bufferLoaders.get(e);if(r)return r;const o=this.json.buffers[e].uri,n=this._context.loadBinary(this._resolveUri(o),t).then((e=>new Uint8Array(e)));return this._bufferLoaders.set(e,n),n}async getAccessor(t,r){if(!this.json.accessors)throw new e("gltf-loader-unsupported-feature","Accessors missing.");const o=this.json.accessors[t];if(null==o?.bufferView)throw new e("gltf-loader-unsupported-feature","Some accessor does not specify a bufferView.");if(o.type in[D.MAT2,D.MAT3,D.MAT4])throw new e("gltf-loader-unsupported-feature",`AttributeType ${o.type} is not supported`);const n=this.json.bufferViews[o.bufferView],s=await this.getBuffer(n.buffer,r),a=X[o.type],i=$[o.componentType],u=a*i,f=n.byteStride||u;return{raw:s.buffer,byteStride:f,byteOffset:s.byteOffset+(n.byteOffset||0)+(o.byteOffset||0),entryCount:o.count,isDenselyPacked:f===u,componentCount:a,componentByteSize:i,componentType:o.componentType,min:o.min,max:o.max,normalized:!!o.normalized}}async getIndexData(e,t){if(null==e.indices)return;const r=await this.getAccessor(e.indices,t);if(r.isDenselyPacked)switch(r.componentType){case K.UNSIGNED_BYTE:return new Uint8Array(r.raw,r.byteOffset,r.entryCount);case K.UNSIGNED_SHORT:return new Uint16Array(r.raw,r.byteOffset,r.entryCount);case K.UNSIGNED_INT:return new Uint32Array(r.raw,r.byteOffset,r.entryCount)}else switch(r.componentType){case K.UNSIGNED_BYTE:return U(Q(C,r));case K.UNSIGNED_SHORT:return U(Q(S,r));case K.UNSIGNED_INT:return U(Q(N,r))}}async getPositionData(t,r){if(null==t.attributes.POSITION)throw new e("gltf-loader-unsupported-feature","No POSITION vertex data found.");const o=await this.getAccessor(t.attributes.POSITION,r);if(o.componentType!==K.FLOAT)throw new e("gltf-loader-unsupported-feature","Expected type FLOAT for POSITION vertex attribute, but found "+K[o.componentType]);if(3!==o.componentCount)throw new e("gltf-loader-unsupported-feature","POSITION vertex attribute must have 3 components, but found "+o.componentCount.toFixed());return Q(A,o)}async getNormalData(t,r){if(null==t.attributes.NORMAL)throw new e("gltf-loader-error","No NORMAL vertex data found.");const o=await this.getAccessor(t.attributes.NORMAL,r);if(o.componentType!==K.FLOAT)throw new e("gltf-loader-unsupported-feature","Expected type FLOAT for NORMAL vertex attribute, but found "+K[o.componentType]);if(3!==o.componentCount)throw new e("gltf-loader-unsupported-feature","NORMAL vertex attribute must have 3 components, but found "+o.componentCount.toFixed());return Q(A,o)}async getTangentData(t,r){if(null==t.attributes.TANGENT)throw new e("gltf-loader-error","No TANGENT vertex data found.");const o=await this.getAccessor(t.attributes.TANGENT,r);if(o.componentType!==K.FLOAT)throw new e("gltf-loader-unsupported-feature","Expected type FLOAT for TANGENT vertex attribute, but found "+K[o.componentType]);if(4!==o.componentCount)throw new e("gltf-loader-unsupported-feature","TANGENT vertex attribute must have 4 components, but found "+o.componentCount.toFixed());return Q(E,o)}async getTextureCoordinates(t,r){if(null==t.attributes.TEXCOORD_0)throw new e("gltf-loader-error","No TEXCOORD_0 vertex data found.");const o=await this.getAccessor(t.attributes.TEXCOORD_0,r);if(2!==o.componentCount)throw new e("gltf-loader-unsupported-feature","TEXCOORD_0 vertex attribute must have 2 components, but found "+o.componentCount.toFixed());if(o.componentType===K.FLOAT)return Q(T,o);if(!o.normalized)throw new e("gltf-loader-unsupported-feature","Integer component types are only supported for a normalized accessor for TEXCOORD_0.");return W(o)}async getVertexColors(t,r){if(null==t.attributes.COLOR_0)throw new e("gltf-loader-error","No COLOR_0 vertex data found.");const o=await this.getAccessor(t.attributes.COLOR_0,r);if(4!==o.componentCount&&3!==o.componentCount)throw new e("gltf-loader-unsupported-feature","COLOR_0 attribute must have 3 or 4 components, but found "+o.componentCount.toFixed());if(4===o.componentCount){if(o.componentType===K.FLOAT)return Q(E,o);if(o.componentType===K.UNSIGNED_BYTE)return Q(L,o);if(o.componentType===K.UNSIGNED_SHORT)return Q(R,o)}else if(3===o.componentCount){if(o.componentType===K.FLOAT)return Q(A,o);if(o.componentType===K.UNSIGNED_BYTE)return Q(v,o);if(o.componentType===K.UNSIGNED_SHORT)return Q(I,o)}throw new e("gltf-loader-unsupported-feature","Unsupported component type for COLOR_0 attribute: "+K[o.componentType])}hasPositions(e){return void 0!==e.attributes.POSITION}hasNormals(e){return void 0!==e.attributes.NORMAL}hasVertexColors(e){return void 0!==e.attributes.COLOR_0}hasTextureCoordinates(e){return void 0!==e.attributes.TEXCOORD_0}hasTangents(e){return void 0!==e.attributes.TANGENT}async getMaterial(e,t,r){let o=e.material?this._materialCache.get(e.material):void 0;if(!o){const n=null!=e.material?j(this.json.materials[e.material]):j(),s=n.pbrMetallicRoughness,a=this.hasVertexColors(e),i=this.getTexture(s.baseColorTexture,t),u=this.getTexture(n.normalTexture,t),f=r?this.getTexture(n.occlusionTexture,t):void 0,l=r?this.getTexture(n.emissiveTexture,t):void 0,d=r?this.getTexture(s.metallicRoughnessTexture,t):void 0,c=null!=e.material?e.material:-1;o={alphaMode:n.alphaMode,alphaCutoff:n.alphaCutoff,color:s.baseColorFactor,doubleSided:!!n.doubleSided,colorTexture:await i,normalTexture:await u,name:n.name,id:c,occlusionTexture:await f,emissiveTexture:await l,emissiveFactor:n.emissiveFactor,metallicFactor:s.metallicFactor,roughnessFactor:s.roughnessFactor,metallicRoughnessTexture:await d,hasVertexColors:a,ESRI_externalColorMixMode:n.extras.ESRI_externalColorMixMode,colorTextureTransform:s?.baseColorTexture?.extensions?.KHR_texture_transform,normalTextureTransform:n.normalTexture?.extensions?.KHR_texture_transform,occlusionTextureTransform:n.occlusionTexture?.extensions?.KHR_texture_transform,emissiveTextureTransform:n.emissiveTexture?.extensions?.KHR_texture_transform,metallicRoughnessTextureTransform:s?.metallicRoughnessTexture?.extensions?.KHR_texture_transform,receiveAmbientOcclusion:n.extras.ESRI_receiveAmbientOcclusion,receiveShadows:n.extras.ESRI_receiveShadows}}return o}async getTexture(t,o){if(!t)return;if(0!==(t.texCoord||0))throw new e("gltf-loader-unsupported-feature","Only TEXCOORD with index 0 is supported.");const n=t.index,s=this.json.textures[n],a=M(null!=s.sampler?this.json.samplers[s.sampler]:{}),i=Z(s),u=this.json.images[i],f=await this._loadTextureImageData(n,s,o);return r(this._textureCache,n,(()=>{const t=e=>33071===e||33648===e||10497===e,r=t=>{throw new e("gltf-loader-error",`Unexpected TextureSampler WrapMode: ${t}`)};return{data:f,wrapS:t(a.wrapS)?a.wrapS:r(a.wrapS),wrapT:t(a.wrapT)?a.wrapT:r(a.wrapT),minFilter:a.minFilter,name:u.name,id:n}}))}getNodeTransform(e){if(void 0===e)return q;let t=this._nodeTransformCache.get(e);if(!t){const r=this.getNodeTransform(this._getNodeParent(e)),o=this.json.nodes[e];o.matrix?t=l(m(),r,o.matrix):o.translation||o.rotation||o.scale?(t=h(r),o.translation&&d(t,t,o.translation),o.rotation&&(z[3]=w(z,o.rotation),c(t,t,z[3],z)),o.scale&&p(t,t,o.scale)):t=h(r),this._nodeTransformCache.set(e,t)}return t}_resolveUri(e){return i(e,this._baseUri)}_getNodeParent(e){return this._nodeParentMap.get(e)}_checkVersionSupported(){const e=u.parse(this.json.asset.version,"glTF");J.validate(e)}_checkRequiredExtensionsSupported(){const t=this.json;if(t.extensionsRequired){if(!t.extensionsRequired.every((e=>this._supportedExtensions.includes(e))))throw new e("gltf-loader-unsupported-feature","gltf loader was not able to load unsupported feature. Required extensions: "+t.extensionsRequired.join(", "))}}_computeNodeParents(){this.json.nodes.forEach(((e,t)=>{e.children&&e.children.forEach((e=>{this._nodeParentMap.set(e,t)}))}))}async _loadTextureImageData(e,t,r){const o=this._textureLoaders.get(e);if(o)return o;const n=this._createTextureLoader(t,r);return this._textureLoaders.set(e,n),n}async _createTextureLoader(t,r){const o=Z(t),n=this.json.images[o];if(n.uri){if(n.uri.endsWith(".ktx2")){const e=await this._context.loadBinary(this._resolveUri(n.uri),r);return new H(new Uint8Array(e))}return this._context.loadImage(this._resolveUri(n.uri),r)}if(null==n.bufferView)throw new e("gltf-loader-unsupported-feature","Image bufferView must be defined.");if(null==n.mimeType)throw new e("gltf-loader-unsupported-feature","Image mimeType must be defined.");const s=this.json.bufferViews[n.bufferView],a=await this.getBuffer(s.buffer,r);if(null!=s.byteStride)throw new e("gltf-loader-unsupported-feature","byteStride not supported for image buffer");const i=a.byteOffset+(s.byteOffset||0);return P(new Uint8Array(a.buffer,i,s.byteLength),n.mimeType)}async getLoadedBuffersSize(){if(this._glbBuffer)return this._glbBuffer.byteLength;const e=await o(Array.from(this._bufferLoaders.values())),t=await o(Array.from(this._textureLoaders.values()));return e.reduce(((e,t)=>e+(t?.byteLength??0)),0)+t.reduce(((e,t)=>e+(t?V(t)?t.data.byteLength:t.width*t.height*4:0)),0)}}const q=f(m(),Math.PI/2),J=new u(2,0,"glTF"),z=b(),X={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16},$={[K.BYTE]:1,[K.UNSIGNED_BYTE]:1,[K.SHORT]:2,[K.UNSIGNED_SHORT]:2,[K.HALF_FLOAT]:2,[K.FLOAT]:4,[K.INT]:4,[K.UNSIGNED_INT]:4};function W(e){switch(e.componentType){case K.BYTE:return new O(e.raw,e.byteOffset,e.byteStride,e.byteOffset+e.byteStride*e.entryCount);case K.UNSIGNED_BYTE:return new _(e.raw,e.byteOffset,e.byteStride,e.byteOffset+e.byteStride*e.entryCount);case K.SHORT:return new x(e.raw,e.byteOffset,e.byteStride,e.byteOffset+e.byteStride*e.entryCount);case K.UNSIGNED_SHORT:return new g(e.raw,e.byteOffset,e.byteStride,e.byteOffset+e.byteStride*e.entryCount);case K.UNSIGNED_INT:return new y(e.raw,e.byteOffset,e.byteStride,e.byteOffset+e.byteStride*e.entryCount);case K.FLOAT:return new T(e.raw,e.byteOffset,e.byteStride,e.byteOffset+e.byteStride*e.entryCount)}}function Q(e,t){return new e(t.raw,t.byteOffset,t.byteStride,t.byteOffset+t.byteStride*(t.entryCount-1)+t.componentByteSize*t.componentCount)}function Z(t){if(null!=t.extensions?.KHR_texture_basisu)return t.extensions.KHR_texture_basisu.source;if(null!==t.source)return t.source;throw new e("gltf-loader-unsupported-feature","Source is expected to be defined for a texture. It can also be omitted in favour of an KHR_texture_basisu extension tag.")}const ee=/\.gltf$/i,te=/\.glb$/i;export{Y as GLTFResource,q as transformGltfToEngine};