diff --git a/build/wegeo.cjs b/build/wegeo.cjs index 0b3afaf..0a61719 100644 --- a/build/wegeo.cjs +++ b/build/wegeo.cjs @@ -5009,6 +5009,26 @@ class Element { } } +class AngleUtils { + /** + * 弧度转角度 + * @param {*} rad + * @returns + */ + static radToDeg(rad) { + return rad * (180 / Math.PI); + } + /** + * 角度转弧度 + * @param {*} deg + * @returns + */ + static degToRad(deg) { + return deg * (Math.PI / 180); + } + +} + // OrbitControls performs orbiting, dollying (zooming), and panning. // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). // @@ -9173,7 +9193,7 @@ class EffectOutline { * @param {number} drawMode * @return {BufferGeometry} */ -function toTrianglesDrawMode$1( geometry, drawMode ) { +function toTrianglesDrawMode( geometry, drawMode ) { if ( drawMode === three.TrianglesDrawMode ) { return geometry; @@ -9268,7 +9288,7 @@ function toTrianglesDrawMode$1( geometry, drawMode ) { } -let GLTFLoader$1 = class GLTFLoader extends three.Loader { +class GLTFLoader extends three.Loader { constructor( manager ) { @@ -9282,97 +9302,97 @@ let GLTFLoader$1 = class GLTFLoader extends three.Loader { this.register( function ( parser ) { - return new GLTFMaterialsClearcoatExtension$1( parser ); + return new GLTFMaterialsClearcoatExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureBasisUExtension$1( parser ); + return new GLTFTextureBasisUExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureWebPExtension$1( parser ); + return new GLTFTextureWebPExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureAVIFExtension$1( parser ); + return new GLTFTextureAVIFExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsSheenExtension$1( parser ); + return new GLTFMaterialsSheenExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsTransmissionExtension$1( parser ); + return new GLTFMaterialsTransmissionExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsVolumeExtension$1( parser ); + return new GLTFMaterialsVolumeExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsIorExtension$1( parser ); + return new GLTFMaterialsIorExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsEmissiveStrengthExtension$1( parser ); + return new GLTFMaterialsEmissiveStrengthExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsSpecularExtension$1( parser ); + return new GLTFMaterialsSpecularExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsIridescenceExtension$1( parser ); + return new GLTFMaterialsIridescenceExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsAnisotropyExtension$1( parser ); + return new GLTFMaterialsAnisotropyExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsBumpExtension$1( parser ); + return new GLTFMaterialsBumpExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFLightsExtension$1( parser ); + return new GLTFLightsExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMeshoptCompression$1( parser ); + return new GLTFMeshoptCompression( parser ); } ); this.register( function ( parser ) { - return new GLTFMeshGpuInstancing$1( parser ); + return new GLTFMeshGpuInstancing( parser ); } ); @@ -9521,11 +9541,11 @@ let GLTFLoader$1 = class GLTFLoader extends three.Loader { const magic = textDecoder.decode( new Uint8Array( data, 0, 4 ) ); - if ( magic === BINARY_EXTENSION_HEADER_MAGIC$1 ) { + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { try { - extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ] = new GLTFBinaryExtension$1( data ); + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); } catch ( error ) { @@ -9534,7 +9554,7 @@ let GLTFLoader$1 = class GLTFLoader extends three.Loader { } - json = JSON.parse( extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].content ); + json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content ); } else { @@ -9555,7 +9575,7 @@ let GLTFLoader$1 = class GLTFLoader extends three.Loader { } - const parser = new GLTFParser$1( json, { + const parser = new GLTFParser( json, { path: path || this.resourcePath || '', crossOrigin: this.crossOrigin, @@ -9593,20 +9613,20 @@ let GLTFLoader$1 = class GLTFLoader extends three.Loader { switch ( extensionName ) { - case EXTENSIONS$1.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension$1(); + case EXTENSIONS.KHR_MATERIALS_UNLIT: + extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); break; - case EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension$1( json, this.dracoLoader ); + case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: + extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); break; - case EXTENSIONS$1.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension$1(); + case EXTENSIONS.KHR_TEXTURE_TRANSFORM: + extensions[ extensionName ] = new GLTFTextureTransformExtension(); break; - case EXTENSIONS$1.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension$1(); + case EXTENSIONS.KHR_MESH_QUANTIZATION: + extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); break; default: @@ -9637,11 +9657,11 @@ let GLTFLoader$1 = class GLTFLoader extends three.Loader { } -}; +} /* GLTFREGISTRY */ -function GLTFRegistry$1() { +function GLTFRegistry() { let objects = {}; @@ -9679,7 +9699,7 @@ function GLTFRegistry$1() { /********** EXTENSIONS ***********/ /*********************************/ -const EXTENSIONS$1 = { +const EXTENSIONS = { KHR_BINARY_GLTF: 'KHR_binary_glTF', KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', @@ -9708,12 +9728,12 @@ const EXTENSIONS$1 = { * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual */ -let GLTFLightsExtension$1 = class GLTFLightsExtension { +class GLTFLightsExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL; + this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; // Object3D instance caches this.cache = { refs: {}, uses: {} }; @@ -9798,7 +9818,7 @@ let GLTFLightsExtension$1 = class GLTFLightsExtension { lightNode.decay = 2; - assignExtrasToUserData$1( lightNode, lightDef ); + assignExtrasToUserData( lightNode, lightDef ); if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; @@ -9839,18 +9859,18 @@ let GLTFLightsExtension$1 = class GLTFLightsExtension { } -}; +} /** * Unlit Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit */ -let GLTFMaterialsUnlitExtension$1 = class GLTFMaterialsUnlitExtension { +class GLTFMaterialsUnlitExtension { constructor() { - this.name = EXTENSIONS$1.KHR_MATERIALS_UNLIT; + this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; } @@ -9892,19 +9912,19 @@ let GLTFMaterialsUnlitExtension$1 = class GLTFMaterialsUnlitExtension { } -}; +} /** * Materials Emissive Strength Extension * * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md */ -let GLTFMaterialsEmissiveStrengthExtension$1 = class GLTFMaterialsEmissiveStrengthExtension { +class GLTFMaterialsEmissiveStrengthExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_EMISSIVE_STRENGTH; + this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH; } @@ -9931,19 +9951,19 @@ let GLTFMaterialsEmissiveStrengthExtension$1 = class GLTFMaterialsEmissiveStreng } -}; +} /** * Clearcoat Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat */ -let GLTFMaterialsClearcoatExtension$1 = class GLTFMaterialsClearcoatExtension { +class GLTFMaterialsClearcoatExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_CLEARCOAT; + this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; } @@ -10015,19 +10035,19 @@ let GLTFMaterialsClearcoatExtension$1 = class GLTFMaterialsClearcoatExtension { } -}; +} /** * Iridescence Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence */ -let GLTFMaterialsIridescenceExtension$1 = class GLTFMaterialsIridescenceExtension { +class GLTFMaterialsIridescenceExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_IRIDESCENCE; + this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE; } @@ -10103,19 +10123,19 @@ let GLTFMaterialsIridescenceExtension$1 = class GLTFMaterialsIridescenceExtensio } -}; +} /** * Sheen Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen */ -let GLTFMaterialsSheenExtension$1 = class GLTFMaterialsSheenExtension { +class GLTFMaterialsSheenExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_SHEEN; + this.name = EXTENSIONS.KHR_MATERIALS_SHEEN; } @@ -10178,7 +10198,7 @@ let GLTFMaterialsSheenExtension$1 = class GLTFMaterialsSheenExtension { } -}; +} /** * Transmission Materials Extension @@ -10186,12 +10206,12 @@ let GLTFMaterialsSheenExtension$1 = class GLTFMaterialsSheenExtension { * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission * Draft: https://github.com/KhronosGroup/glTF/pull/1698 */ -let GLTFMaterialsTransmissionExtension$1 = class GLTFMaterialsTransmissionExtension { +class GLTFMaterialsTransmissionExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_TRANSMISSION; + this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; } @@ -10237,19 +10257,19 @@ let GLTFMaterialsTransmissionExtension$1 = class GLTFMaterialsTransmissionExtens } -}; +} /** * Materials Volume Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume */ -let GLTFMaterialsVolumeExtension$1 = class GLTFMaterialsVolumeExtension { +class GLTFMaterialsVolumeExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_VOLUME; + this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; } @@ -10296,19 +10316,19 @@ let GLTFMaterialsVolumeExtension$1 = class GLTFMaterialsVolumeExtension { } -}; +} /** * Materials ior Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior */ -let GLTFMaterialsIorExtension$1 = class GLTFMaterialsIorExtension { +class GLTFMaterialsIorExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_IOR; + this.name = EXTENSIONS.KHR_MATERIALS_IOR; } @@ -10342,19 +10362,19 @@ let GLTFMaterialsIorExtension$1 = class GLTFMaterialsIorExtension { } -}; +} /** * Materials specular Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular */ -let GLTFMaterialsSpecularExtension$1 = class GLTFMaterialsSpecularExtension { +class GLTFMaterialsSpecularExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_SPECULAR; + this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; } @@ -10405,7 +10425,7 @@ let GLTFMaterialsSpecularExtension$1 = class GLTFMaterialsSpecularExtension { } -}; +} /** @@ -10413,12 +10433,12 @@ let GLTFMaterialsSpecularExtension$1 = class GLTFMaterialsSpecularExtension { * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/EXT_materials_bump */ -let GLTFMaterialsBumpExtension$1 = class GLTFMaterialsBumpExtension { +class GLTFMaterialsBumpExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_MATERIALS_BUMP; + this.name = EXTENSIONS.EXT_MATERIALS_BUMP; } @@ -10460,19 +10480,19 @@ let GLTFMaterialsBumpExtension$1 = class GLTFMaterialsBumpExtension { } -}; +} /** * Materials anisotropy Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_anisotropy */ -let GLTFMaterialsAnisotropyExtension$1 = class GLTFMaterialsAnisotropyExtension { +class GLTFMaterialsAnisotropyExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_ANISOTROPY; + this.name = EXTENSIONS.KHR_MATERIALS_ANISOTROPY; } @@ -10524,19 +10544,19 @@ let GLTFMaterialsAnisotropyExtension$1 = class GLTFMaterialsAnisotropyExtension } -}; +} /** * BasisU Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu */ -let GLTFTextureBasisUExtension$1 = class GLTFTextureBasisUExtension { +class GLTFTextureBasisUExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_TEXTURE_BASISU; + this.name = EXTENSIONS.KHR_TEXTURE_BASISU; } @@ -10575,19 +10595,19 @@ let GLTFTextureBasisUExtension$1 = class GLTFTextureBasisUExtension { } -}; +} /** * WebP Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp */ -let GLTFTextureWebPExtension$1 = class GLTFTextureWebPExtension { +class GLTFTextureWebPExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_WEBP; + this.name = EXTENSIONS.EXT_TEXTURE_WEBP; this.isSupported = null; } @@ -10660,19 +10680,19 @@ let GLTFTextureWebPExtension$1 = class GLTFTextureWebPExtension { } -}; +} /** * AVIF Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_avif */ -let GLTFTextureAVIFExtension$1 = class GLTFTextureAVIFExtension { +class GLTFTextureAVIFExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_AVIF; + this.name = EXTENSIONS.EXT_TEXTURE_AVIF; this.isSupported = null; } @@ -10743,18 +10763,18 @@ let GLTFTextureAVIFExtension$1 = class GLTFTextureAVIFExtension { } -}; +} /** * meshopt BufferView Compression Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression */ -let GLTFMeshoptCompression$1 = class GLTFMeshoptCompression { +class GLTFMeshoptCompression { constructor( parser ) { - this.name = EXTENSIONS$1.EXT_MESHOPT_COMPRESSION; + this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; this.parser = parser; } @@ -10827,7 +10847,7 @@ let GLTFMeshoptCompression$1 = class GLTFMeshoptCompression { } -}; +} /** * GPU Instancing Extension @@ -10835,11 +10855,11 @@ let GLTFMeshoptCompression$1 = class GLTFMeshoptCompression { * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing * */ -let GLTFMeshGpuInstancing$1 = class GLTFMeshGpuInstancing { +class GLTFMeshGpuInstancing { constructor( parser ) { - this.name = EXTENSIONS$1.EXT_MESH_GPU_INSTANCING; + this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING; this.parser = parser; } @@ -10862,9 +10882,9 @@ let GLTFMeshGpuInstancing$1 = class GLTFMeshGpuInstancing { for ( const primitive of meshDef.primitives ) { - if ( primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLES && - primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLE_STRIP && - primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLE_FAN && + if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES && + primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP && + primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN && primitive.mode !== undefined ) { return null; @@ -10984,22 +11004,22 @@ let GLTFMeshGpuInstancing$1 = class GLTFMeshGpuInstancing { } -}; +} /* BINARY EXTENSION */ -const BINARY_EXTENSION_HEADER_MAGIC$1 = 'glTF'; -const BINARY_EXTENSION_HEADER_LENGTH$1 = 12; -const BINARY_EXTENSION_CHUNK_TYPES$1 = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; +const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; +const BINARY_EXTENSION_HEADER_LENGTH = 12; +const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; -let GLTFBinaryExtension$1 = class GLTFBinaryExtension { +class GLTFBinaryExtension { constructor( data ) { - this.name = EXTENSIONS$1.KHR_BINARY_GLTF; + this.name = EXTENSIONS.KHR_BINARY_GLTF; this.content = null; this.body = null; - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH$1 ); + const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); const textDecoder = new TextDecoder(); this.header = { @@ -11008,7 +11028,7 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { length: headerView.getUint32( 8, true ) }; - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC$1 ) { + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); @@ -11018,8 +11038,8 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { } - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH$1; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH$1 ); + const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; + const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); let chunkIndex = 0; while ( chunkIndex < chunkContentsLength ) { @@ -11030,14 +11050,14 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { const chunkType = chunkView.getUint32( chunkIndex, true ); chunkIndex += 4; - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES$1.JSON ) { + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex, chunkLength ); + const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); this.content = textDecoder.decode( contentArray ); - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES$1.BIN ) { + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex; + const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; this.body = data.slice( byteOffset, byteOffset + chunkLength ); } @@ -11056,14 +11076,14 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { } -}; +} /** * DRACO Mesh Compression Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression */ -let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtension { +class GLTFDracoMeshCompressionExtension { constructor( json, dracoLoader ) { @@ -11073,7 +11093,7 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio } - this.name = EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION; + this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; this.json = json; this.dracoLoader = dracoLoader; this.dracoLoader.preload(); @@ -11092,7 +11112,7 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio for ( const attributeName in gltfAttributeMap ) { - const threeAttributeName = ATTRIBUTES$1[ attributeName ] || attributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; @@ -11100,12 +11120,12 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio for ( const attributeName in primitive.attributes ) { - const threeAttributeName = ATTRIBUTES$1[ attributeName ] || attributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); if ( gltfAttributeMap[ attributeName ] !== undefined ) { const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; attributeTypeMap[ threeAttributeName ] = componentType.name; attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; @@ -11139,18 +11159,18 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio } -}; +} /** * Texture Transform Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform */ -let GLTFTextureTransformExtension$1 = class GLTFTextureTransformExtension { +class GLTFTextureTransformExtension { constructor() { - this.name = EXTENSIONS$1.KHR_TEXTURE_TRANSFORM; + this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; } @@ -11198,22 +11218,22 @@ let GLTFTextureTransformExtension$1 = class GLTFTextureTransformExtension { } -}; +} /** * Mesh Quantization Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization */ -let GLTFMeshQuantizationExtension$1 = class GLTFMeshQuantizationExtension { +class GLTFMeshQuantizationExtension { constructor() { - this.name = EXTENSIONS$1.KHR_MESH_QUANTIZATION; + this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; } -}; +} /*********************************/ /********** INTERPOLATION ********/ @@ -11221,7 +11241,7 @@ let GLTFMeshQuantizationExtension$1 = class GLTFMeshQuantizationExtension { // Spline Interpolation // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation -let GLTFCubicSplineInterpolant$1 = class GLTFCubicSplineInterpolant extends three.Interpolant { +class GLTFCubicSplineInterpolant extends three.Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { @@ -11289,23 +11309,23 @@ let GLTFCubicSplineInterpolant$1 = class GLTFCubicSplineInterpolant extends thre } -}; +} -const _q$1 = new three.Quaternion(); +const _q = new three.Quaternion(); -let GLTFCubicSplineQuaternionInterpolant$1 = class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant$1 { +class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { interpolate_( i1, t0, t, t1 ) { const result = super.interpolate_( i1, t0, t, t1 ); - _q$1.fromArray( result ).normalize().toArray( result ); + _q.fromArray( result ).normalize().toArray( result ); return result; } -}; +} /*********************************/ @@ -11314,7 +11334,7 @@ let GLTFCubicSplineQuaternionInterpolant$1 = class GLTFCubicSplineQuaternionInte /* CONSTANTS */ -const WEBGL_CONSTANTS$1 = { +const WEBGL_CONSTANTS = { FLOAT: 5126, //FLOAT_MAT2: 35674, FLOAT_MAT3: 35675, @@ -11336,7 +11356,7 @@ const WEBGL_CONSTANTS$1 = { UNSIGNED_SHORT: 5123 }; -const WEBGL_COMPONENT_TYPES$1 = { +const WEBGL_COMPONENT_TYPES = { 5120: Int8Array, 5121: Uint8Array, 5122: Int16Array, @@ -11345,7 +11365,7 @@ const WEBGL_COMPONENT_TYPES$1 = { 5126: Float32Array }; -const WEBGL_FILTERS$1 = { +const WEBGL_FILTERS = { 9728: three.NearestFilter, 9729: three.LinearFilter, 9984: three.NearestMipmapNearestFilter, @@ -11354,13 +11374,13 @@ const WEBGL_FILTERS$1 = { 9987: three.LinearMipmapLinearFilter }; -const WEBGL_WRAPPINGS$1 = { +const WEBGL_WRAPPINGS = { 33071: three.ClampToEdgeWrapping, 33648: three.MirroredRepeatWrapping, 10497: three.RepeatWrapping }; -const WEBGL_TYPE_SIZES$1 = { +const WEBGL_TYPE_SIZES = { 'SCALAR': 1, 'VEC2': 2, 'VEC3': 3, @@ -11370,7 +11390,7 @@ const WEBGL_TYPE_SIZES$1 = { 'MAT4': 16 }; -const ATTRIBUTES$1 = { +const ATTRIBUTES = { POSITION: 'position', NORMAL: 'normal', TANGENT: 'tangent', @@ -11383,21 +11403,21 @@ const ATTRIBUTES$1 = { JOINTS_0: 'skinIndex', }; -const PATH_PROPERTIES$1 = { +const PATH_PROPERTIES = { scale: 'scale', translation: 'position', rotation: 'quaternion', weights: 'morphTargetInfluences' }; -const INTERPOLATION$1 = { +const INTERPOLATION = { CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each // keyframe track will be initialized with a default interpolation type, then modified. LINEAR: three.InterpolateLinear, STEP: three.InterpolateDiscrete }; -const ALPHA_MODES$1 = { +const ALPHA_MODES = { OPAQUE: 'OPAQUE', MASK: 'MASK', BLEND: 'BLEND' @@ -11406,7 +11426,7 @@ const ALPHA_MODES$1 = { /** * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material */ -function createDefaultMaterial$1( cache ) { +function createDefaultMaterial( cache ) { if ( cache[ 'DefaultMaterial' ] === undefined ) { @@ -11426,7 +11446,7 @@ function createDefaultMaterial$1( cache ) { } -function addUnknownExtensionsToUserData$1( knownExtensions, object, objectDef ) { +function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { // Add unknown glTF extensions to an object's userData. @@ -11447,7 +11467,7 @@ function addUnknownExtensionsToUserData$1( knownExtensions, object, objectDef ) * @param {Object3D|Material|BufferGeometry} object * @param {GLTF.definition} gltfDef */ -function assignExtrasToUserData$1( object, gltfDef ) { +function assignExtrasToUserData( object, gltfDef ) { if ( gltfDef.extras !== undefined ) { @@ -11469,7 +11489,7 @@ function assignExtrasToUserData$1( object, gltfDef ) { * @param {GLTFParser} parser * @return {Promise} */ -function addMorphTargets$1( geometry, targets, parser ) { +function addMorphTargets( geometry, targets, parser ) { let hasMorphPosition = false; let hasMorphNormal = false; @@ -11554,7 +11574,7 @@ function addMorphTargets$1( geometry, targets, parser ) { * @param {Mesh} mesh * @param {GLTF.Mesh} meshDef */ -function updateMorphTargets$1( mesh, meshDef ) { +function updateMorphTargets( mesh, meshDef ) { mesh.updateMorphTargets(); @@ -11589,21 +11609,21 @@ function updateMorphTargets$1( mesh, meshDef ) { } -function createPrimitiveKey$1( primitiveDef ) { +function createPrimitiveKey( primitiveDef ) { let geometryKey; - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ]; + const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; if ( dracoExtension ) { geometryKey = 'draco:' + dracoExtension.bufferView + ':' + dracoExtension.indices - + ':' + createAttributesKey$1( dracoExtension.attributes ); + + ':' + createAttributesKey( dracoExtension.attributes ); } else { - geometryKey = primitiveDef.indices + ':' + createAttributesKey$1( primitiveDef.attributes ) + ':' + primitiveDef.mode; + geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; } @@ -11611,7 +11631,7 @@ function createPrimitiveKey$1( primitiveDef ) { for ( let i = 0, il = primitiveDef.targets.length; i < il; i ++ ) { - geometryKey += ':' + createAttributesKey$1( primitiveDef.targets[ i ] ); + geometryKey += ':' + createAttributesKey( primitiveDef.targets[ i ] ); } @@ -11621,7 +11641,7 @@ function createPrimitiveKey$1( primitiveDef ) { } -function createAttributesKey$1( attributes ) { +function createAttributesKey( attributes ) { let attributesKey = ''; @@ -11637,7 +11657,7 @@ function createAttributesKey$1( attributes ) { } -function getNormalizedComponentScale$1( constructor ) { +function getNormalizedComponentScale( constructor ) { // Reference: // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data @@ -11663,7 +11683,7 @@ function getNormalizedComponentScale$1( constructor ) { } -function getImageURIMimeType$1( uri ) { +function getImageURIMimeType( uri ) { if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; @@ -11672,11 +11692,11 @@ function getImageURIMimeType$1( uri ) { } -const _identityMatrix$1 = new three.Matrix4(); +const _identityMatrix = new three.Matrix4(); /* GLTF PARSER */ -let GLTFParser$1 = class GLTFParser { +class GLTFParser { constructor( json = {}, options = {} ) { @@ -11686,7 +11706,7 @@ let GLTFParser$1 = class GLTFParser { this.options = options; // loader object cache - this.cache = new GLTFRegistry$1(); + this.cache = new GLTFRegistry(); // associations between Three.js objects and glTF elements this.associations = new Map(); @@ -11802,9 +11822,9 @@ let GLTFParser$1 = class GLTFParser { userData: {} }; - addUnknownExtensionsToUserData$1( extensions, result, json ); + addUnknownExtensionsToUserData( extensions, result, json ); - assignExtrasToUserData$1( result, json ); + assignExtrasToUserData( result, json ); return Promise.all( parser._invokeAll( function ( ext ) { @@ -12122,7 +12142,7 @@ let GLTFParser$1 = class GLTFParser { // If present, GLB container is required to be the first buffer. if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - return Promise.resolve( this.extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].body ); + return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); } @@ -12173,8 +12193,8 @@ let GLTFParser$1 = class GLTFParser { if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; const normalized = accessorDef.normalized === true; const array = new TypedArray( accessorDef.count * itemSize ); @@ -12205,8 +12225,8 @@ let GLTFParser$1 = class GLTFParser { const bufferView = bufferViews[ 0 ]; - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. const elementBytes = TypedArray.BYTES_PER_ELEMENT; @@ -12257,8 +12277,8 @@ let GLTFParser$1 = class GLTFParser { // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors if ( accessorDef.sparse !== undefined ) { - const itemSizeIndices = WEBGL_TYPE_SIZES$1.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES$1[ accessorDef.sparse.indices.componentType ]; + const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; + const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; @@ -12351,10 +12371,10 @@ let GLTFParser$1 = class GLTFParser { const samplers = json.samplers || {}; const sampler = samplers[ textureDef.sampler ] || {}; - texture.magFilter = WEBGL_FILTERS$1[ sampler.magFilter ] || three.LinearFilter; - texture.minFilter = WEBGL_FILTERS$1[ sampler.minFilter ] || three.LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS$1[ sampler.wrapS ] || three.RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS$1[ sampler.wrapT ] || three.RepeatWrapping; + texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || three.LinearFilter; + texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || three.LinearMipmapLinearFilter; + texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || three.RepeatWrapping; + texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || three.RepeatWrapping; parser.associations.set( texture, { textures: textureIndex } ); @@ -12443,7 +12463,7 @@ let GLTFParser$1 = class GLTFParser { } - texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType$1( sourceDef.uri ); + texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); return texture; @@ -12479,14 +12499,14 @@ let GLTFParser$1 = class GLTFParser { } - if ( parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] ) { + if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] : undefined; + const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; if ( transform ) { const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); + texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); parser.associations.set( texture, gltfReference ); } @@ -12629,9 +12649,9 @@ let GLTFParser$1 = class GLTFParser { const pending = []; - if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ] ) { + if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - const kmuExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ]; + const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; materialType = kmuExtension.getMaterialType(); pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); @@ -12690,9 +12710,9 @@ let GLTFParser$1 = class GLTFParser { } - const alphaMode = materialDef.alphaMode || ALPHA_MODES$1.OPAQUE; + const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - if ( alphaMode === ALPHA_MODES$1.BLEND ) { + if ( alphaMode === ALPHA_MODES.BLEND ) { materialParams.transparent = true; @@ -12703,7 +12723,7 @@ let GLTFParser$1 = class GLTFParser { materialParams.transparent = false; - if ( alphaMode === ALPHA_MODES$1.MASK ) { + if ( alphaMode === ALPHA_MODES.MASK ) { materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; @@ -12758,11 +12778,11 @@ let GLTFParser$1 = class GLTFParser { if ( materialDef.name ) material.name = materialDef.name; - assignExtrasToUserData$1( material, materialDef ); + assignExtrasToUserData( material, materialDef ); parser.associations.set( material, { materials: materialIndex } ); - if ( materialDef.extensions ) addUnknownExtensionsToUserData$1( extensions, material, materialDef ); + if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); return material; @@ -12805,11 +12825,11 @@ let GLTFParser$1 = class GLTFParser { function createDracoPrimitive( primitive ) { - return extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] + return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] .decodePrimitive( primitive, parser ) .then( function ( geometry ) { - return addPrimitiveAttributes$1( geometry, primitive, parser ); + return addPrimitiveAttributes( geometry, primitive, parser ); } ); @@ -12820,7 +12840,7 @@ let GLTFParser$1 = class GLTFParser { for ( let i = 0, il = primitives.length; i < il; i ++ ) { const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey$1( primitive ); + const cacheKey = createPrimitiveKey( primitive ); // See if we've already created this geometry const cached = cache[ cacheKey ]; @@ -12834,7 +12854,7 @@ let GLTFParser$1 = class GLTFParser { let geometryPromise; - if ( primitive.extensions && primitive.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] ) { + if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { // Use DRACO geometry if available geometryPromise = createDracoPrimitive( primitive ); @@ -12842,7 +12862,7 @@ let GLTFParser$1 = class GLTFParser { } else { // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes$1( new three.BufferGeometry(), primitive, parser ); + geometryPromise = addPrimitiveAttributes( new three.BufferGeometry(), primitive, parser ); } @@ -12878,7 +12898,7 @@ let GLTFParser$1 = class GLTFParser { for ( let i = 0, il = primitives.length; i < il; i ++ ) { const material = primitives[ i ].material === undefined - ? createDefaultMaterial$1( this.cache ) + ? createDefaultMaterial( this.cache ) : this.getDependency( 'material', primitives[ i ].material ); pending.push( material ); @@ -12905,9 +12925,9 @@ let GLTFParser$1 = class GLTFParser { const material = materials[ i ]; - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || primitive.mode === undefined ) { // .isSkinnedMesh isn't in glTF spec. See ._markDefs() @@ -12922,29 +12942,29 @@ let GLTFParser$1 = class GLTFParser { } - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - mesh.geometry = toTrianglesDrawMode$1( mesh.geometry, three.TriangleStripDrawMode ); + mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleStripDrawMode ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - mesh.geometry = toTrianglesDrawMode$1( mesh.geometry, three.TriangleFanDrawMode ); + mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleFanDrawMode ); } - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { mesh = new three.LineSegments( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { mesh = new three.Line( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { mesh = new three.LineLoop( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { mesh = new three.Points( geometry, material ); @@ -12956,15 +12976,15 @@ let GLTFParser$1 = class GLTFParser { if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - updateMorphTargets$1( mesh, meshDef ); + updateMorphTargets( mesh, meshDef ); } mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); - assignExtrasToUserData$1( mesh, meshDef ); + assignExtrasToUserData( mesh, meshDef ); - if ( primitive.extensions ) addUnknownExtensionsToUserData$1( extensions, mesh, primitive ); + if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); parser.assignFinalMaterial( mesh ); @@ -12983,7 +13003,7 @@ let GLTFParser$1 = class GLTFParser { if ( meshes.length === 1 ) { - if ( meshDef.extensions ) addUnknownExtensionsToUserData$1( extensions, meshes[ 0 ], meshDef ); + if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, meshes[ 0 ], meshDef ); return meshes[ 0 ]; @@ -12991,7 +13011,7 @@ let GLTFParser$1 = class GLTFParser { const group = new three.Group(); - if ( meshDef.extensions ) addUnknownExtensionsToUserData$1( extensions, group, meshDef ); + if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, group, meshDef ); parser.associations.set( group, { meshes: meshIndex } ); @@ -13035,7 +13055,7 @@ let GLTFParser$1 = class GLTFParser { if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); - assignExtrasToUserData$1( camera, cameraDef ); + assignExtrasToUserData( camera, cameraDef ); return Promise.resolve( camera ); @@ -13279,7 +13299,7 @@ let GLTFParser$1 = class GLTFParser { if ( ! mesh.isSkinnedMesh ) return; - mesh.bind( skeleton, _identityMatrix$1 ); + mesh.bind( skeleton, _identityMatrix ); } ); @@ -13393,9 +13413,9 @@ let GLTFParser$1 = class GLTFParser { } - assignExtrasToUserData$1( node, nodeDef ); + assignExtrasToUserData( node, nodeDef ); - if ( nodeDef.extensions ) addUnknownExtensionsToUserData$1( extensions, node, nodeDef ); + if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); if ( nodeDef.matrix !== undefined ) { @@ -13457,9 +13477,9 @@ let GLTFParser$1 = class GLTFParser { const scene = new three.Group(); if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); - assignExtrasToUserData$1( scene, sceneDef ); + assignExtrasToUserData( scene, sceneDef ); - if ( sceneDef.extensions ) addUnknownExtensionsToUserData$1( extensions, scene, sceneDef ); + if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); const nodeIds = sceneDef.nodes || []; @@ -13526,7 +13546,7 @@ let GLTFParser$1 = class GLTFParser { const targetName = node.name ? node.name : node.uuid; const targetNames = []; - if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { + if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { node.traverse( function ( object ) { @@ -13546,20 +13566,20 @@ let GLTFParser$1 = class GLTFParser { let TypedKeyframeTrack; - switch ( PATH_PROPERTIES$1[ target.path ] ) { + switch ( PATH_PROPERTIES[ target.path ] ) { - case PATH_PROPERTIES$1.weights: + case PATH_PROPERTIES.weights: TypedKeyframeTrack = three.NumberKeyframeTrack; break; - case PATH_PROPERTIES$1.rotation: + case PATH_PROPERTIES.rotation: TypedKeyframeTrack = three.QuaternionKeyframeTrack; break; - case PATH_PROPERTIES$1.position: - case PATH_PROPERTIES$1.scale: + case PATH_PROPERTIES.position: + case PATH_PROPERTIES.scale: TypedKeyframeTrack = three.VectorKeyframeTrack; break; @@ -13583,7 +13603,7 @@ let GLTFParser$1 = class GLTFParser { } - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION$1[ sampler.interpolation ] : three.InterpolateLinear; + const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : three.InterpolateLinear; const outputArray = this._getArrayFromAccessor( outputAccessor ); @@ -13591,7 +13611,7 @@ let GLTFParser$1 = class GLTFParser { for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], + targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], inputAccessor.array, outputArray, interpolation @@ -13618,7 +13638,7 @@ let GLTFParser$1 = class GLTFParser { if ( accessor.normalized ) { - const scale = getNormalizedComponentScale$1( outputArray.constructor ); + const scale = getNormalizedComponentScale( outputArray.constructor ); const scaled = new Float32Array( outputArray.length ); for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { @@ -13643,7 +13663,7 @@ let GLTFParser$1 = class GLTFParser { // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() // must be divided by three to get the interpolant's sampleSize argument. - const interpolantType = ( this instanceof three.QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant$1 : GLTFCubicSplineInterpolant$1; + const interpolantType = ( this instanceof three.QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); @@ -13654,14 +13674,14 @@ let GLTFParser$1 = class GLTFParser { } -}; +} /** * @param {BufferGeometry} geometry * @param {GLTF.Primitive} primitiveDef * @param {GLTFParser} parser */ -function computeBounds$1( geometry, primitiveDef, parser ) { +function computeBounds( geometry, primitiveDef, parser ) { const attributes = primitiveDef.attributes; @@ -13685,7 +13705,7 @@ function computeBounds$1( geometry, primitiveDef, parser ) { if ( accessor.normalized ) { - const boxScale = getNormalizedComponentScale$1( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); box.min.multiplyScalar( boxScale ); box.max.multiplyScalar( boxScale ); @@ -13732,7 +13752,7 @@ function computeBounds$1( geometry, primitiveDef, parser ) { if ( accessor.normalized ) { - const boxScale = getNormalizedComponentScale$1( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); vector.multiplyScalar( boxScale ); } @@ -13771,7 +13791,7 @@ function computeBounds$1( geometry, primitiveDef, parser ) { * @param {GLTFParser} parser * @return {Promise} */ -function addPrimitiveAttributes$1( geometry, primitiveDef, parser ) { +function addPrimitiveAttributes( geometry, primitiveDef, parser ) { const attributes = primitiveDef.attributes; @@ -13790,7 +13810,7 @@ function addPrimitiveAttributes$1( geometry, primitiveDef, parser ) { for ( const gltfAttributeName in attributes ) { - const threeAttributeName = ATTRIBUTES$1[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); // Skip attributes already provided by e.g. Draco extension. if ( threeAttributeName in geometry.attributes ) continue; @@ -13813,23 +13833,23 @@ function addPrimitiveAttributes$1( geometry, primitiveDef, parser ) { if ( three.ColorManagement.workingColorSpace !== three.LinearSRGBColorSpace && 'COLOR_0' in attributes ) ; - assignExtrasToUserData$1( geometry, primitiveDef ); + assignExtrasToUserData( geometry, primitiveDef ); - computeBounds$1( geometry, primitiveDef, parser ); + computeBounds( geometry, primitiveDef, parser ); return Promise.all( pending ).then( function () { return primitiveDef.targets !== undefined - ? addMorphTargets$1( geometry, primitiveDef.targets, parser ) + ? addMorphTargets( geometry, primitiveDef.targets, parser ) : geometry; } ); } -const _taskCache$2 = new WeakMap(); +const _taskCache$1 = new WeakMap(); -let DRACOLoader$1 = class DRACOLoader extends three.Loader { +class DRACOLoader extends three.Loader { constructor( manager ) { @@ -13927,9 +13947,9 @@ let DRACOLoader$1 = class DRACOLoader extends three.Loader { // Check for an existing task using this buffer. A transferred buffer cannot be transferred // again from this thread. - if ( _taskCache$2.has( buffer ) ) { + if ( _taskCache$1.has( buffer ) ) { - const cachedTask = _taskCache$2.get( buffer ); + const cachedTask = _taskCache$1.get( buffer ); if ( cachedTask.key === taskKey ) { @@ -13995,7 +14015,7 @@ let DRACOLoader$1 = class DRACOLoader extends three.Loader { } ); // Cache the task result. - _taskCache$2.set( buffer, { + _taskCache$1.set( buffer, { key: taskKey, promise: geometryPending @@ -14113,7 +14133,7 @@ let DRACOLoader$1 = class DRACOLoader extends three.Loader { } - const fn = DRACOWorker$1.toString(); + const fn = DRACOWorker.toString(); const body = [ '/* draco decoder */', @@ -14216,11 +14236,11 @@ let DRACOLoader$1 = class DRACOLoader extends three.Loader { } -}; +} /* WEB WORKER */ -function DRACOWorker$1() { +function DRACOWorker() { let decoderConfig; let decoderPending; @@ -18439,10 +18459,10 @@ class ModelLoader { } class Lorder{ constructor() { - this.gltfLoader = new GLTFLoader$1(); + this.gltfLoader = new GLTFLoader(); // Optional: Provide a DRACOLoader instance to decode compressed mesh data - const dracoLoader = new DRACOLoader$1(); + const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath( Config.DRACOPath ); this.gltfLoader.setDRACOLoader( dracoLoader ); this.objLoader = new OBJLoader(); // obj模型 @@ -18688,5725 +18708,471 @@ Sky.SkyShader = { uniform float mieCoefficient; uniform vec3 up; - varying vec3 vWorldPosition; - varying vec3 vSunDirection; - varying float vSunfade; - varying vec3 vBetaR; - varying vec3 vBetaM; - varying float vSunE; - - // constants for atmospheric scattering - const float e = 2.71828182845904523536028747135266249775724709369995957; - const float pi = 3.141592653589793238462643383279502884197169; - - // wavelength of used primaries, according to preetham - const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); - // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: - // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) - const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); - - // mie stuff - // K coefficient for the primaries - const float v = 4.0; - const vec3 K = vec3( 0.686, 0.678, 0.666 ); - // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K - const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); - - // earth shadow hack - // cutoffAngle = pi / 1.95; - const float cutoffAngle = 1.6110731556870734; - const float steepness = 1.5; - const float EE = 1000.0; - - float sunIntensity( float zenithAngleCos ) { - zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); - return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); - } - - vec3 totalMie( float T ) { - float c = ( 0.2 * T ) * 10E-18; - return 0.434 * c * MieConst; - } - - void main() { - - vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); - vWorldPosition = worldPosition.xyz; - - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - gl_Position.z = gl_Position.w; // set z to camera.far - - vSunDirection = normalize( sunPosition ); - - vSunE = sunIntensity( dot( vSunDirection, up ) ); - - vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); - - float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); - - // extinction (absorbtion + out scattering) - // rayleigh coefficients - vBetaR = totalRayleigh * rayleighCoefficient; - - // mie coefficients - vBetaM = totalMie( turbidity ) * mieCoefficient; - - }`, - - fragmentShader: /* glsl */` - varying vec3 vWorldPosition; - varying vec3 vSunDirection; - varying float vSunfade; - varying vec3 vBetaR; - varying vec3 vBetaM; - varying float vSunE; - - uniform float mieDirectionalG; - uniform vec3 up; - - // constants for atmospheric scattering - const float pi = 3.141592653589793238462643383279502884197169; - - const float n = 1.0003; // refractive index of air - const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius) - - // optical length at zenith for molecules - const float rayleighZenithLength = 8.4E3; - const float mieZenithLength = 1.25E3; - // 66 arc seconds -> degrees, and the cosine of that - const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; - - // 3.0 / ( 16.0 * pi ) - const float THREE_OVER_SIXTEENPI = 0.05968310365946075; - // 1.0 / ( 4.0 * pi ) - const float ONE_OVER_FOURPI = 0.07957747154594767; - - float rayleighPhase( float cosTheta ) { - return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); - } - - float hgPhase( float cosTheta, float g ) { - float g2 = pow( g, 2.0 ); - float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); - return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse ); - } - - void main() { - - vec3 direction = normalize( vWorldPosition - cameraPosition ); - - // optical length - // cutoff angle at 90 to avoid singularity in next formula. - float zenithAngle = acos( max( 0.0, dot( up, direction ) ) ); - float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); - float sR = rayleighZenithLength * inverse; - float sM = mieZenithLength * inverse; - - // combined extinction factor - vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); - - // in scattering - float cosTheta = dot( direction, vSunDirection ); - - float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); - vec3 betaRTheta = vBetaR * rPhase; - - float mPhase = hgPhase( cosTheta, mieDirectionalG ); - vec3 betaMTheta = vBetaM * mPhase; - - vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); - Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); - - // nightsky - float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] - float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] - vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); - vec3 L0 = vec3( 0.1 ) * Fex; - - // composition + solar disc - float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); - L0 += ( vSunE * 19000.0 * Fex ) * sundisk; - - vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); - - vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); - - gl_FragColor = vec4( retColor, 1.0 ); - - #include - #include - - }` - -}; - -// 多个canvas并没有id,只有父节点 - - -class Layer extends BasLayer{ - id; // 唯一标识 - layerContainer; // div#layer 容器 - zIndex=1;//默认为1 - opacity=1;//默认为1 - canvas;//canvas - dispose = false; - renderer;//canvas上下文 - scene;//场景 - visible=true;//是否可见 - mapView;//地图视图 - camera;//相机 - controls;//控件 - animateId;//动画事件id - base = false; // 是否为底图 - ambientLight; // 环境光 - directionalLight; // 方向光 - modelLayer = false; // 模型图层 - imageLayer = false; // 影像图层 - vectorLayer = false; // 矢量图层,如路网、行政区划,地名等图层 - waters = []; // 水面集合 - constructor(id, layerContainer, canvas, mapView, plane = true, camera = new three.PerspectiveCamera(80, 1, 0.1, 1e12)) { - super(); - this.id = id; - this.layerContainer = layerContainer; - this.canvas = canvas; - this.renderer = new three.WebGLRenderer({ - canvas: this.canvas, - antialias: true, - alpha: true, - logarithmicDepthBuffer: true, - precision: "highp", - }); - this.renderer.sortObjects = true; - this.renderer.setPixelRatio(window.devicePixelRatio); - this.renderer.setClearColor(0xFFFFFF, 0.0); - this.scene = new three.Scene(); - this.mapView = mapView; - this.camera = camera; - if(this.mapView){ - this.scene.add(this.mapView); - this.mapView.updateMatrixWorld(true); - } - if (plane){ - this.controls = new MapControls(this.camera, this.canvas); - this.controls.minDistance = 1e1; - this.controls.zoomSpeed = 2.0; - } else { - this.controls = new OrbitControls(this.camera, this.canvas); - this.controls.enablePan = false; - this.controls.minDistance = UnitsUtils.EARTH_RADIUS + 2; - this.controls.maxDistance = UnitsUtils.EARTH_RADIUS * 1e1; - } - this._raycaster = new three.Raycaster(); - if(Config.outLine.on){ - this.effectOutline = new EffectOutline(this.renderer, this.scene, this.camera, this.canvas.width, this.canvas.height); - } - if (Config.layer.map.ambientLight.add){ - this.scene.add(new three.AmbientLight(Config.layer.map.ambientLight.color, Config.layer.map.ambientLight.intensity)); - } - if (Config.layer.map.directionalLight.add){ - this.scene.add(new three.DirectionalLight(Config.layer.map.directionalLight.color, Config.layer.map.directionalLight.intensity)); - } - if (Config.layer.map.pointLight.add){ - let pointLight = new three.PointLight(Config.layer.map.pointLight.color, Config.layer.map.pointLight.intensity, Config.layer.map.pointLight.distance); - pointLight.position.set(...Config.layer.map.pointLight.position); - this.scene.add(pointLight); - } - } - - moveTo(lat, lon, height = 38472.48763833733){ - // var coords = UnitsUtils.datumsToSpherical(44.266119,90.139228); - var coords = UnitsUtils.datumsToSpherical(lat,lon); - this.camera.position.set(coords.x, height, -coords.y); - this.controls.target.set(this.camera.position.x, 0, this.camera.position.z); - } - - moveToByCoords(coords){ - let offset = 50; - this.camera.position.set(coords.x, coords.y+offset, coords.z); - this.controls.target.set(coords.x, coords.y, coords.z); - } - - moveToByLL(lat, lon, distance = 384720){ - let dir = UnitsUtils.datumsToVector(lat, lon); - dir.multiplyScalar(UnitsUtils.EARTH_RADIUS + distance); - this.camera.position.copy(dir); - } - - - on(eventName, callback){ - this.listener.on(eventName, callback); - } - - setSceneBackground(color) { - this.scene.background = color; - } - - clearSceneBackground() { - this.scene.background = null; - } - - // 可用于添加灯光mesh等元素 - add(Object3D) { - if(Object3D ==null || Object3D ==undefined){ - return; - } - this.scene.add(Object3D); - } - - - remove(Object3D) { - if(Object3D ==null || Object3D ==undefined){ - return; - } - this.scene.remove(Object3D); - } - - openWaterConfig(){ - /** - * 打开渲染水系配置 - */ - // this.renderer.setPixelRatio( window.devicePixelRatio ); - this.renderer.toneMapping = three.ACESFilmicToneMapping; - this.renderer.toneMappingExposure = 0.5; - - // 添加天空 - this.sky = new Sky(); - this.sky.translateX = true; - this.sky.translateY = true; - this.sky.translateZ = true; - this.sky.rotateX = Math.PI / 2; - this.sky.scale.setScalar( Config.EARTH_RADIUS * 2 * Math.PI ); // 天空放大倍数 - this.scene.add( this.sky ); - const skyUniforms = this.sky.material.uniforms; - // 天空的配置 - skyUniforms[ 'turbidity' ].value = 10; - skyUniforms[ 'rayleigh' ].value = 2; - skyUniforms[ 'mieCoefficient' ].value = 0.005; - skyUniforms[ 'mieDirectionalG' ].value = 0.8; - // 旋转 设置为y朝上 - // sky.material.uniforms["up"].value = new THREE.Vector3(0, 1, 0); - - // 天空映射, 更新太阳位置 - this.pmremGenerator = new three.PMREMGenerator( this.renderer ); - this.sceneEnv = new three.Scene(); - this.renderTarget = null; - this.sun = new three.Vector3(); - this.updateSun(Config.SUNDEGREE, Config.SUNAZIMUTH); - } - - updateSun(elevation, azimuth) { - - const phi = three.MathUtils.degToRad( 90 - elevation ); - const theta = three.MathUtils.degToRad( azimuth ); - - this.sun.setFromSphericalCoords( 1, phi, theta ); - - this.sky.material.uniforms[ 'sunPosition' ].value.copy( this.sun ); - for (let water of this.waters){ - water.material.uniforms[ 'sunDirection' ].value.copy( this.sun ).normalize(); - } - if ( this.renderTarget !== null ) this.renderTarget.dispose(); - - this.sceneEnv.add( this.sky ); - this.renderTarget = this.pmremGenerator.fromScene( this.sceneEnv ); - this.scene.add( this.sky ); - - this.scene.environment = this.renderTarget.texture; - - - } - - /** - * 添加水系 - * @param {*} water - * @returns - */ - addWater(water) { - if(water ==null || water ==undefined){ - return; - } - this.scene.add(water); - this.waters.push(water); - } - - removeWater(water) { - if(water ==null || water ==undefined){ - return; - } - this.scene.remove(water); - let index = this.waters.indexOf(water); - if (index > -1) { - this.waters.splice(index, 1); - } - } - - setVisible(visible) { - this.visible = this.visible; - this.layerContainer.style.display = visible ? 'block' : 'none'; - } - /** - * @deprecated 不建议用 - * @param {number} opacity - */ - setOpacity(opacity) { - this.opacity = opacity; - this.layerContainer.style.opacity = opacity; - } - - /** - * 修改显示层级,默认越靠下的dom元素,显示上越靠上 - * @param {number} zIndex - */ - setZIndex(zIndex) { - this.zIndex = zIndex; - this.layerContainer.style.zIndex = this.zIndex; - } - - dispose() { - Element.removeLayer(id); - this.dispose = true; - this.animateId && cancelAnimationFrame(this.animateId); - this.mapView.root.dispose(); - this.mapView.dispose(); - } - - selectModel(insect){ - this.effectOutline.selectModel(insect); - } - - resize(){ - var width = window.innerWidth; - var height = window.innerHeight; - this.renderer.setSize(width, height); - this.camera.aspect = width / height; - this.camera.updateProjectionMatrix(); - if(Config.outLine.on){ - this.effectOutline.resize(width, height); - } - } - - animate(){ - this.animateId = requestAnimationFrame(this.animate.bind(this)); - if(this.base){ - this.controls.update(); - } - if (this.base){ - update(); //目前只有在基础地图中添加相机移动的动画,所以暂且只在base地图中进行渲染,后期可改成每个图层都进行渲染,或者必要时进行渲染。 - } - for(let water of this.waters){ - water.material.uniforms[ 'time' ].value += 1.0 / 60.0; - } - if (Config.outLine.on){ - this.effectOutline.render(); // 合成器渲染 - } else { - this.renderer.autoClear = true; - } - this.renderer.render(this.scene, this.camera); - } - - _raycast(meshes, recursive, faceExclude) { - const isects = this._raycaster.intersectObjects(meshes, recursive); - if (faceExclude) { - for (let i = 0; i < isects.length; i++) { - if (isects[i].face !== faceExclude) { - return isects[i]; - } - } - return null; - } - return isects.length > 0 ? isects[0] : null; - } - - _raycastFromMouse(mx, my, width, height, cam, meshes, recursive=false) { - const mouse = new three.Vector2( // normalized (-1 to +1) - (mx / width) * 2 - 1, - - (my / height) * 2 + 1); - // https://threejs.org/docs/#api/core/Raycaster - // update the picking ray with the camera and mouse position - this._raycaster.setFromCamera(mouse, cam); - return this._raycast(meshes, recursive, null); - } - - /** - * - * @param {*} mx 屏幕坐标x - * @param {*} my 屏幕坐标y - * @param {boolean} recursive 是否检查子节点,true 递归检查 - * @returns mesh - */ - raycastFromMouse(mx, my, recursive=false) { - //---- NG: 2x when starting with Chrome's inspector mobile - // const {width, height} = this.renderer.domElement; - // const {width, height} = this.canvas; - //---- OK - const {clientWidth, clientHeight} = this.canvas; - - return this._raycastFromMouse( - mx, my, clientWidth, clientHeight, this.camera, - this.mapView.children, recursive); - } - - insectALL(mx, my, recursive=false) { - const {clientWidth, clientHeight} = this.canvas; - - return this._raycastFromMouse( - mx, my, clientWidth, clientHeight, this.camera, - this.scene.children, recursive); - } - -} - -/** - * @param {BufferGeometry} geometry - * @param {number} drawMode - * @return {BufferGeometry} - */ -function toTrianglesDrawMode( geometry, drawMode ) { - - if ( drawMode === three.TrianglesDrawMode ) { - return geometry; - - } - - if ( drawMode === three.TriangleFanDrawMode || drawMode === three.TriangleStripDrawMode ) { - - let index = geometry.getIndex(); - - // generate index if not present - - if ( index === null ) { - - const indices = []; - - const position = geometry.getAttribute( 'position' ); - - if ( position !== undefined ) { - - for ( let i = 0; i < position.count; i ++ ) { - - indices.push( i ); - - } - - geometry.setIndex( indices ); - index = geometry.getIndex(); - - } else { - return geometry; - - } - - } - - // - - const numberOfTriangles = index.count - 2; - const newIndices = []; - - if ( drawMode === three.TriangleFanDrawMode ) { - - // gl.TRIANGLE_FAN - - for ( let i = 1; i <= numberOfTriangles; i ++ ) { - - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - - } - - } else { - - // gl.TRIANGLE_STRIP - - for ( let i = 0; i < numberOfTriangles; i ++ ) { - - if ( i % 2 === 0 ) { - - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); - - } else { - - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); - - } - - } - - } - - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) ; - - // build final geometry - - const newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); - newGeometry.clearGroups(); - - return newGeometry; - - } else { - return geometry; - - } - -} - -class GLTFLoader extends three.Loader { - - constructor( manager ) { - - super( manager ); - - this.dracoLoader = null; - this.ktx2Loader = null; - this.meshoptDecoder = null; - - this.pluginCallbacks = []; - - this.register( function ( parser ) { - - return new GLTFMaterialsClearcoatExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureBasisUExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureWebPExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureAVIFExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsSheenExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsTransmissionExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsVolumeExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsIorExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsEmissiveStrengthExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsSpecularExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsIridescenceExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsAnisotropyExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsBumpExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFLightsExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMeshoptCompression( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMeshGpuInstancing( parser ); - - } ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - let resourcePath; - - if ( this.resourcePath !== '' ) { - - resourcePath = this.resourcePath; - - } else if ( this.path !== '' ) { - - // If a base path is set, resources will be relative paths from that plus the relative path of the gltf file - // Example path = 'https://my-cnd-server.com/', url = 'assets/models/model.gltf' - // resourcePath = 'https://my-cnd-server.com/assets/models/' - // referenced resource 'model.bin' will be loaded from 'https://my-cnd-server.com/assets/models/model.bin' - // referenced resource '../textures/texture.png' will be loaded from 'https://my-cnd-server.com/assets/textures/texture.png' - const relativeUrl = three.LoaderUtils.extractUrlBase( url ); - resourcePath = three.LoaderUtils.resolveURL( relativeUrl, this.path ); - - } else { - - resourcePath = three.LoaderUtils.extractUrlBase( url ); - - } - - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - this.manager.itemStart( url ); - - const _onError = function ( e ) { - - if ( onError ) { - - onError( e ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }; - - const loader = new three.FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, function ( data ) { - - try { - - scope.parse( data, resourcePath, function ( gltf ) { - - onLoad( gltf ); - - scope.manager.itemEnd( url ); - - }, _onError ); - - } catch ( e ) { - - _onError( e ); - - } - - }, onProgress, _onError ); - - } - - setDRACOLoader( dracoLoader ) { - - this.dracoLoader = dracoLoader; - return this; - - } - - setDDSLoader() { - - throw new Error( - - 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' - - ); - - } - - setKTX2Loader( ktx2Loader ) { - - this.ktx2Loader = ktx2Loader; - return this; - - } - - setMeshoptDecoder( meshoptDecoder ) { - - this.meshoptDecoder = meshoptDecoder; - return this; - - } - - register( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - - this.pluginCallbacks.push( callback ); - - } - - return this; - - } - - unregister( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - - } - - return this; - - } - - parse( data, path, onLoad, onError ) { - - let json; - const extensions = {}; - const plugins = {}; - const textDecoder = new TextDecoder(); - - if ( typeof data === 'string' ) { - - json = JSON.parse( data ); - - } else if ( data instanceof ArrayBuffer ) { - - const magic = textDecoder.decode( new Uint8Array( data, 0, 4 ) ); - - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - - try { - - extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); - - } catch ( error ) { - - if ( onError ) onError( error ); - return; - - } - - json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content ); - - } else { - - json = JSON.parse( textDecoder.decode( data ) ); - - } - - } else { - - json = data; - - } - - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { - - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; - - } - - const parser = new GLTFParser( json, { - - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - requestHeader: this.requestHeader, - manager: this.manager, - ktx2Loader: this.ktx2Loader, - meshoptDecoder: this.meshoptDecoder - - } ); - - parser.fileLoader.setRequestHeader( this.requestHeader ); - - for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { - - const plugin = this.pluginCallbacks[ i ]( parser ); - - if ( ! plugin.name ) ; - - plugins[ plugin.name ] = plugin; - - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; - - } - - if ( json.extensionsUsed ) { - - for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { - - const extensionName = json.extensionsUsed[ i ]; - const extensionsRequired = json.extensionsRequired || []; - - switch ( extensionName ) { - - case EXTENSIONS.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); - break; - - case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; - - case EXTENSIONS.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; - - case EXTENSIONS.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; - - default: - - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) ; - - } - - } - - } - - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); - - } - - parseAsync( data, path ) { - - const scope = this; - - return new Promise( function ( resolve, reject ) { - - scope.parse( data, path, resolve, reject ); - - } ); - - } - -} - -/* GLTFREGISTRY */ - -function GLTFRegistry() { - - let objects = {}; - - return { - - get: function ( key ) { - - return objects[ key ]; - - }, - - add: function ( key, object ) { - - objects[ key ] = object; - - }, - - remove: function ( key ) { - - delete objects[ key ]; - - }, - - removeAll: function () { - - objects = {}; - - } - - }; - -} - -/*********************************/ -/********** EXTENSIONS ***********/ -/*********************************/ - -const EXTENSIONS = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_IOR: 'KHR_materials_ior', - KHR_MATERIALS_SHEEN: 'KHR_materials_sheen', - KHR_MATERIALS_SPECULAR: 'KHR_materials_specular', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_IRIDESCENCE: 'KHR_materials_iridescence', - KHR_MATERIALS_ANISOTROPY: 'KHR_materials_anisotropy', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_MATERIALS_VOLUME: 'KHR_materials_volume', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - KHR_MATERIALS_EMISSIVE_STRENGTH: 'KHR_materials_emissive_strength', - EXT_MATERIALS_BUMP: 'EXT_materials_bump', - EXT_TEXTURE_WEBP: 'EXT_texture_webp', - EXT_TEXTURE_AVIF: 'EXT_texture_avif', - EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression', - EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing' -}; - -/** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ -class GLTFLightsExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; - - // Object3D instance caches - this.cache = { refs: {}, uses: {} }; - - } - - _markDefs() { - - const parser = this.parser; - const nodeDefs = this.parser.json.nodes || []; - - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.extensions - && nodeDef.extensions[ this.name ] - && nodeDef.extensions[ this.name ].light !== undefined ) { - - parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); - - } - - } - - } - - _loadLight( lightIndex ) { - - const parser = this.parser; - const cacheKey = 'light:' + lightIndex; - let dependency = parser.cache.get( cacheKey ); - - if ( dependency ) return dependency; - - const json = parser.json; - const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; - const lightDefs = extensions.lights || []; - const lightDef = lightDefs[ lightIndex ]; - let lightNode; - - const color = new three.Color( 0xffffff ); - - if ( lightDef.color !== undefined ) color.setRGB( lightDef.color[ 0 ], lightDef.color[ 1 ], lightDef.color[ 2 ], three.LinearSRGBColorSpace ); - - const range = lightDef.range !== undefined ? lightDef.range : 0; - - switch ( lightDef.type ) { - - case 'directional': - lightNode = new three.DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - case 'point': - lightNode = new three.PointLight( color ); - lightNode.distance = range; - break; - - case 'spot': - lightNode = new three.SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); - - } - - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); - - lightNode.decay = 2; - - assignExtrasToUserData( lightNode, lightDef ); - - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - - lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); - - dependency = Promise.resolve( lightNode ); - - parser.cache.add( cacheKey, dependency ); - - return dependency; - - } - - getDependency( type, index ) { - - if ( type !== 'light' ) return; - - return this._loadLight( index ); - - } - - createNodeAttachment( nodeIndex ) { - - const self = this; - const parser = this.parser; - const json = parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; - const lightIndex = lightDef.light; - - if ( lightIndex === undefined ) return null; - - return this._loadLight( lightIndex ).then( function ( light ) { - - return parser._getNodeRef( self.cache, lightIndex, light ); - - } ); - - } - -} - -/** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ -class GLTFMaterialsUnlitExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; - - } - - getMaterialType() { - - return three.MeshBasicMaterial; - - } - - extendParams( materialParams, materialDef, parser ) { - - const pending = []; - - materialParams.color = new three.Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - const metallicRoughness = materialDef.pbrMetallicRoughness; - - if ( metallicRoughness ) { - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.setRGB( array[ 0 ], array[ 1 ], array[ 2 ], three.LinearSRGBColorSpace ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, three.SRGBColorSpace ) ); - - } - - } - - return Promise.all( pending ); - - } - -} - -/** - * Materials Emissive Strength Extension - * - * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md - */ -class GLTFMaterialsEmissiveStrengthExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const emissiveStrength = materialDef.extensions[ this.name ].emissiveStrength; - - if ( emissiveStrength !== undefined ) { - - materialParams.emissiveIntensity = emissiveStrength; - - } - - return Promise.resolve(); - - } - -} - -/** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ -class GLTFMaterialsClearcoatExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.clearcoatFactor !== undefined ) { - - materialParams.clearcoat = extension.clearcoatFactor; - - } - - if ( extension.clearcoatTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); - - } - - if ( extension.clearcoatRoughnessFactor !== undefined ) { - - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; - - } - - if ( extension.clearcoatRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - - } - - if ( extension.clearcoatNormalTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - - if ( extension.clearcoatNormalTexture.scale !== undefined ) { - - const scale = extension.clearcoatNormalTexture.scale; - - materialParams.clearcoatNormalScale = new three.Vector2( scale, scale ); - - } - - } - - return Promise.all( pending ); - - } - -} - -/** - * Iridescence Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence - */ -class GLTFMaterialsIridescenceExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.iridescenceFactor !== undefined ) { - - materialParams.iridescence = extension.iridescenceFactor; - - } - - if ( extension.iridescenceTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'iridescenceMap', extension.iridescenceTexture ) ); - - } - - if ( extension.iridescenceIor !== undefined ) { - - materialParams.iridescenceIOR = extension.iridescenceIor; - - } - - if ( materialParams.iridescenceThicknessRange === undefined ) { - - materialParams.iridescenceThicknessRange = [ 100, 400 ]; - - } - - if ( extension.iridescenceThicknessMinimum !== undefined ) { - - materialParams.iridescenceThicknessRange[ 0 ] = extension.iridescenceThicknessMinimum; - - } - - if ( extension.iridescenceThicknessMaximum !== undefined ) { - - materialParams.iridescenceThicknessRange[ 1 ] = extension.iridescenceThicknessMaximum; - - } - - if ( extension.iridescenceThicknessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'iridescenceThicknessMap', extension.iridescenceThicknessTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Sheen Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen - */ -class GLTFMaterialsSheenExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_SHEEN; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - materialParams.sheenColor = new three.Color( 0, 0, 0 ); - materialParams.sheenRoughness = 0; - materialParams.sheen = 1; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.sheenColorFactor !== undefined ) { - - const colorFactor = extension.sheenColorFactor; - materialParams.sheenColor.setRGB( colorFactor[ 0 ], colorFactor[ 1 ], colorFactor[ 2 ], three.LinearSRGBColorSpace ); - - } - - if ( extension.sheenRoughnessFactor !== undefined ) { - - materialParams.sheenRoughness = extension.sheenRoughnessFactor; - - } - - if ( extension.sheenColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, three.SRGBColorSpace ) ); - - } - - if ( extension.sheenRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ -class GLTFMaterialsTransmissionExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.transmissionFactor !== undefined ) { - - materialParams.transmission = extension.transmissionFactor; - - } - - if ( extension.transmissionTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Materials Volume Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume - */ -class GLTFMaterialsVolumeExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0; - - if ( extension.thicknessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) ); - - } - - materialParams.attenuationDistance = extension.attenuationDistance || Infinity; - - const colorArray = extension.attenuationColor || [ 1, 1, 1 ]; - materialParams.attenuationColor = new three.Color().setRGB( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ], three.LinearSRGBColorSpace ); - - return Promise.all( pending ); - - } - -} - -/** - * Materials ior Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior - */ -class GLTFMaterialsIorExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_IOR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const extension = materialDef.extensions[ this.name ]; - - materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5; - - return Promise.resolve(); - - } - -} - -/** - * Materials specular Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular - */ -class GLTFMaterialsSpecularExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0; - - if ( extension.specularTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) ); - - } - - const colorArray = extension.specularColorFactor || [ 1, 1, 1 ]; - materialParams.specularColor = new three.Color().setRGB( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ], three.LinearSRGBColorSpace ); - - if ( extension.specularColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, three.SRGBColorSpace ) ); - - } - - return Promise.all( pending ); - - } - -} - - -/** - * Materials bump Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/EXT_materials_bump - */ -class GLTFMaterialsBumpExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_MATERIALS_BUMP; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.bumpScale = extension.bumpFactor !== undefined ? extension.bumpFactor : 1.0; - - if ( extension.bumpTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'bumpMap', extension.bumpTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Materials anisotropy Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_anisotropy - */ -class GLTFMaterialsAnisotropyExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_ANISOTROPY; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.anisotropyStrength !== undefined ) { - - materialParams.anisotropy = extension.anisotropyStrength; - - } - - if ( extension.anisotropyRotation !== undefined ) { - - materialParams.anisotropyRotation = extension.anisotropyRotation; - - } - - if ( extension.anisotropyTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'anisotropyMap', extension.anisotropyTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - */ -class GLTFTextureBasisUExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_TEXTURE_BASISU; - - } - - loadTexture( textureIndex ) { - - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ this.name ]; - const loader = parser.options.ktx2Loader; - - if ( ! loader ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - - } else { - - // Assumes that the extension is optional and that a fallback texture is present - return null; - - } - - } - - return parser.loadTextureImage( textureIndex, extension.source, loader ); - - } - -} - -/** - * WebP Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp - */ -class GLTFTextureWebPExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_TEXTURE_WEBP; - this.isSupported = null; - - } - - loadTexture( textureIndex ) { - - const name = this.name; - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; - - let loader = parser.textureLoader; - if ( source.uri ) { - - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.detectSupport().then( function ( isSupported ) { - - if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); - - } - - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); - - } ); - - } - - detectSupport() { - - if ( ! this.isSupported ) { - - this.isSupported = new Promise( function ( resolve ) { - - const image = new Image(); - - // Lossy test image. Support for lossy images doesn't guarantee support for all - // WebP images, unfortunately. - image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; - - image.onload = image.onerror = function () { - - resolve( image.height === 1 ); - - }; - - } ); - - } - - return this.isSupported; - - } - -} - -/** - * AVIF Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_avif - */ -class GLTFTextureAVIFExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_TEXTURE_AVIF; - this.isSupported = null; - - } - - loadTexture( textureIndex ) { - - const name = this.name; - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; - - let loader = parser.textureLoader; - if ( source.uri ) { - - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.detectSupport().then( function ( isSupported ) { - - if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: AVIF required by asset but unsupported.' ); - - } - - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); - - } ); - - } - - detectSupport() { - - if ( ! this.isSupported ) { - - this.isSupported = new Promise( function ( resolve ) { - - const image = new Image(); - - // Lossy test image. - image.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkQAAAAB8dSLfI='; - image.onload = image.onerror = function () { - - resolve( image.height === 1 ); - - }; - - } ); - - } - - return this.isSupported; - - } - -} - -/** - * meshopt BufferView Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression - */ -class GLTFMeshoptCompression { - - constructor( parser ) { - - this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; - this.parser = parser; - - } - - loadBufferView( index ) { - - const json = this.parser.json; - const bufferView = json.bufferViews[ index ]; - - if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { - - const extensionDef = bufferView.extensions[ this.name ]; - - const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); - const decoder = this.parser.options.meshoptDecoder; - - if ( ! decoder || ! decoder.supported ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); - - } else { - - // Assumes that the extension is optional and that fallback buffer data is present - return null; - - } - - } - - return buffer.then( function ( res ) { - - const byteOffset = extensionDef.byteOffset || 0; - const byteLength = extensionDef.byteLength || 0; - - const count = extensionDef.count; - const stride = extensionDef.byteStride; - - const source = new Uint8Array( res, byteOffset, byteLength ); - - if ( decoder.decodeGltfBufferAsync ) { - - return decoder.decodeGltfBufferAsync( count, stride, source, extensionDef.mode, extensionDef.filter ).then( function ( res ) { - - return res.buffer; - - } ); - - } else { - - // Support for MeshoptDecoder 0.18 or earlier, without decodeGltfBufferAsync - return decoder.ready.then( function () { - - const result = new ArrayBuffer( count * stride ); - decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); - return result; - - } ); - - } - - } ); - - } else { - - return null; - - } - - } - -} - -/** - * GPU Instancing Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing - * - */ -class GLTFMeshGpuInstancing { - - constructor( parser ) { - - this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING; - this.parser = parser; - - } - - createNodeMesh( nodeIndex ) { - - const json = this.parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - - if ( ! nodeDef.extensions || ! nodeDef.extensions[ this.name ] || - nodeDef.mesh === undefined ) { - - return null; - - } - - const meshDef = json.meshes[ nodeDef.mesh ]; - - // No Points or Lines + Instancing support yet - - for ( const primitive of meshDef.primitives ) { - - if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES && - primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP && - primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN && - primitive.mode !== undefined ) { - - return null; - - } - - } - - const extensionDef = nodeDef.extensions[ this.name ]; - const attributesDef = extensionDef.attributes; - - // @TODO: Can we support InstancedMesh + SkinnedMesh? - - const pending = []; - const attributes = {}; - - for ( const key in attributesDef ) { - - pending.push( this.parser.getDependency( 'accessor', attributesDef[ key ] ).then( accessor => { - - attributes[ key ] = accessor; - return attributes[ key ]; - - } ) ); - - } - - if ( pending.length < 1 ) { - - return null; - - } - - pending.push( this.parser.createNodeMesh( nodeIndex ) ); - - return Promise.all( pending ).then( results => { - - const nodeObject = results.pop(); - const meshes = nodeObject.isGroup ? nodeObject.children : [ nodeObject ]; - const count = results[ 0 ].count; // All attribute counts should be same - const instancedMeshes = []; - - for ( const mesh of meshes ) { - - // Temporal variables - const m = new three.Matrix4(); - const p = new three.Vector3(); - const q = new three.Quaternion(); - const s = new three.Vector3( 1, 1, 1 ); - - const instancedMesh = new three.InstancedMesh( mesh.geometry, mesh.material, count ); - - for ( let i = 0; i < count; i ++ ) { - - if ( attributes.TRANSLATION ) { - - p.fromBufferAttribute( attributes.TRANSLATION, i ); - - } - - if ( attributes.ROTATION ) { - - q.fromBufferAttribute( attributes.ROTATION, i ); - - } - - if ( attributes.SCALE ) { - - s.fromBufferAttribute( attributes.SCALE, i ); - - } - - instancedMesh.setMatrixAt( i, m.compose( p, q, s ) ); - - } - - // Add instance attributes to the geometry, excluding TRS. - for ( const attributeName in attributes ) { - - if ( attributeName === '_COLOR_0' ) { - - const attr = attributes[ attributeName ]; - instancedMesh.instanceColor = new three.InstancedBufferAttribute( attr.array, attr.itemSize, attr.normalized ); - - } else if ( attributeName !== 'TRANSLATION' && - attributeName !== 'ROTATION' && - attributeName !== 'SCALE' ) { - - mesh.geometry.setAttribute( attributeName, attributes[ attributeName ] ); - - } - - } - - // Just in case - three.Object3D.prototype.copy.call( instancedMesh, mesh ); - - this.parser.assignFinalMaterial( instancedMesh ); - - instancedMeshes.push( instancedMesh ); - - } - - if ( nodeObject.isGroup ) { - - nodeObject.clear(); - - nodeObject.add( ... instancedMeshes ); - - return nodeObject; - - } - - return instancedMeshes[ 0 ]; - - } ); - - } - -} - -/* BINARY EXTENSION */ -const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; -const BINARY_EXTENSION_HEADER_LENGTH = 12; -const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - -class GLTFBinaryExtension { - - constructor( data ) { - - this.name = EXTENSIONS.KHR_BINARY_GLTF; - this.content = null; - this.body = null; - - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); - const textDecoder = new TextDecoder(); - - this.header = { - magic: textDecoder.decode( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; - - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - - } else if ( this.header.version < 2.0 ) { - - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - - } - - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); - let chunkIndex = 0; - - while ( chunkIndex < chunkContentsLength ) { - - const chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - const chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); - this.content = textDecoder.decode( contentArray ); - - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); - - } - - // Clients must ignore chunks with unknown types. - - chunkIndex += chunkLength; - - } - - if ( this.content === null ) { - - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); - - } - - } - -} - -/** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ -class GLTFDracoMeshCompressionExtension { - - constructor( json, dracoLoader ) { - - if ( ! dracoLoader ) { - - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); - - } - - this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); - - } - - decodePrimitive( primitive, parser ) { - - const json = this.json; - const dracoLoader = this.dracoLoader; - const bufferViewIndex = primitive.extensions[ this.name ].bufferView; - const gltfAttributeMap = primitive.extensions[ this.name ].attributes; - const threeAttributeMap = {}; - const attributeNormalizedMap = {}; - const attributeTypeMap = {}; - - for ( const attributeName in gltfAttributeMap ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; - - } - - for ( const attributeName in primitive.attributes ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - if ( gltfAttributeMap[ attributeName ] !== undefined ) { - - const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - attributeTypeMap[ threeAttributeName ] = componentType.name; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; - - } - - } - - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { - - return new Promise( function ( resolve, reject ) { - - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { - - for ( const attributeName in geometry.attributes ) { - - const attribute = geometry.attributes[ attributeName ]; - const normalized = attributeNormalizedMap[ attributeName ]; - - if ( normalized !== undefined ) attribute.normalized = normalized; - - } - - resolve( geometry ); - - }, threeAttributeMap, attributeTypeMap, three.LinearSRGBColorSpace, reject ); - - } ); - - } ); - - } - -} - -/** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ -class GLTFTextureTransformExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; - - } - - extendTexture( texture, transform ) { - - if ( ( transform.texCoord === undefined || transform.texCoord === texture.channel ) - && transform.offset === undefined - && transform.rotation === undefined - && transform.scale === undefined ) { - - // See https://github.com/mrdoob/three.js/issues/21819. - return texture; - - } - - texture = texture.clone(); - - if ( transform.texCoord !== undefined ) { - - texture.channel = transform.texCoord; - - } - - if ( transform.offset !== undefined ) { - - texture.offset.fromArray( transform.offset ); - - } - - if ( transform.rotation !== undefined ) { - - texture.rotation = transform.rotation; - - } - - if ( transform.scale !== undefined ) { - - texture.repeat.fromArray( transform.scale ); - - } - - texture.needsUpdate = true; - - return texture; - - } - -} - -/** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ -class GLTFMeshQuantizationExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; - - } - -} - -/*********************************/ -/********** INTERPOLATION ********/ -/*********************************/ - -// Spline Interpolation -// Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation -class GLTFCubicSplineInterpolant extends three.Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - copySampleValue_( index ) { - - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. - - const result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; - - for ( let i = 0; i !== valueSize; i ++ ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - } - - interpolate_( i1, t0, t, t1 ) { - - const result = this.resultBuffer; - const values = this.sampleValues; - const stride = this.valueSize; - - const stride2 = stride * 2; - const stride3 = stride * 3; - - const td = t1 - t0; - - const p = ( t - t0 ) / td; - const pp = p * p; - const ppp = pp * p; - - const offset1 = i1 * stride3; - const offset0 = offset1 - stride3; - - const s2 = - 2 * ppp + 3 * pp; - const s3 = ppp - pp; - const s0 = 1 - s2; - const s1 = s3 - pp + p; - - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( let i = 0; i !== stride; i ++ ) { - - const p0 = values[ offset0 + i + stride ]; // splineVertex_k - const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) - - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; - - } - - return result; - - } - -} - -const _q = new three.Quaternion(); - -class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { - - interpolate_( i1, t0, t, t1 ) { - - const result = super.interpolate_( i1, t0, t, t1 ); - - _q.fromArray( result ).normalize().toArray( result ); - - return result; - - } - -} - - -/*********************************/ -/********** INTERNALS ************/ -/*********************************/ - -/* CONSTANTS */ - -const WEBGL_CONSTANTS = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 -}; - -const WEBGL_COMPONENT_TYPES = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array -}; - -const WEBGL_FILTERS = { - 9728: three.NearestFilter, - 9729: three.LinearFilter, - 9984: three.NearestMipmapNearestFilter, - 9985: three.LinearMipmapNearestFilter, - 9986: three.NearestMipmapLinearFilter, - 9987: three.LinearMipmapLinearFilter -}; - -const WEBGL_WRAPPINGS = { - 33071: three.ClampToEdgeWrapping, - 33648: three.MirroredRepeatWrapping, - 10497: three.RepeatWrapping -}; - -const WEBGL_TYPE_SIZES = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 -}; - -const ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv1', - TEXCOORD_2: 'uv2', - TEXCOORD_3: 'uv3', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', -}; - -const PATH_PROPERTIES = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' -}; - -const INTERPOLATION = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: three.InterpolateLinear, - STEP: three.InterpolateDiscrete -}; - -const ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' -}; - -/** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ -function createDefaultMaterial( cache ) { - - if ( cache[ 'DefaultMaterial' ] === undefined ) { - - cache[ 'DefaultMaterial' ] = new three.MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: three.FrontSide - } ); - - } - - return cache[ 'DefaultMaterial' ]; - -} - -function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { - - // Add unknown glTF extensions to an object's userData. - - for ( const name in objectDef.extensions ) { - - if ( knownExtensions[ name ] === undefined ) { - - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; - - } - - } - -} - -/** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ -function assignExtrasToUserData( object, gltfDef ) { - - if ( gltfDef.extras !== undefined ) { - - if ( typeof gltfDef.extras === 'object' ) { - - Object.assign( object.userData, gltfDef.extras ); - - } - - } - -} - -/** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ -function addMorphTargets( geometry, targets, parser ) { - - let hasMorphPosition = false; - let hasMorphNormal = false; - let hasMorphColor = false; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; - if ( target.COLOR_0 !== undefined ) hasMorphColor = true; - - if ( hasMorphPosition && hasMorphNormal && hasMorphColor ) break; - - } - - if ( ! hasMorphPosition && ! hasMorphNormal && ! hasMorphColor ) return Promise.resolve( geometry ); - - const pendingPositionAccessors = []; - const pendingNormalAccessors = []; - const pendingColorAccessors = []; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( hasMorphPosition ) { - - const pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; - - pendingPositionAccessors.push( pendingAccessor ); - - } - - if ( hasMorphNormal ) { - - const pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; - - pendingNormalAccessors.push( pendingAccessor ); - - } - - if ( hasMorphColor ) { - - const pendingAccessor = target.COLOR_0 !== undefined - ? parser.getDependency( 'accessor', target.COLOR_0 ) - : geometry.attributes.color; - - pendingColorAccessors.push( pendingAccessor ); - - } - - } - - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ), - Promise.all( pendingColorAccessors ) - ] ).then( function ( accessors ) { - - const morphPositions = accessors[ 0 ]; - const morphNormals = accessors[ 1 ]; - const morphColors = accessors[ 2 ]; - - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - if ( hasMorphColor ) geometry.morphAttributes.color = morphColors; - geometry.morphTargetsRelative = true; - - return geometry; - - } ); - -} - -/** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ -function updateMorphTargets( mesh, meshDef ) { - - mesh.updateMorphTargets(); - - if ( meshDef.weights !== undefined ) { - - for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { - - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; - - } - - } - - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { - - const targetNames = meshDef.extras.targetNames; - - if ( mesh.morphTargetInfluences.length === targetNames.length ) { - - mesh.morphTargetDictionary = {}; - - for ( let i = 0, il = targetNames.length; i < il; i ++ ) { - - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; - - } - - } - - } - -} - -function createPrimitiveKey( primitiveDef ) { - - let geometryKey; - - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; - - if ( dracoExtension ) { - - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); - - } else { - - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; - - } - - if ( primitiveDef.targets !== undefined ) { - - for ( let i = 0, il = primitiveDef.targets.length; i < il; i ++ ) { - - geometryKey += ':' + createAttributesKey( primitiveDef.targets[ i ] ); - - } - - } - - return geometryKey; - -} - -function createAttributesKey( attributes ) { - - let attributesKey = ''; - - const keys = Object.keys( attributes ).sort(); - - for ( let i = 0, il = keys.length; i < il; i ++ ) { - - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; - - } - - return attributesKey; - -} - -function getNormalizedComponentScale( constructor ) { - - // Reference: - // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data - - switch ( constructor ) { - - case Int8Array: - return 1 / 127; - - case Uint8Array: - return 1 / 255; - - case Int16Array: - return 1 / 32767; - - case Uint16Array: - return 1 / 65535; - - default: - throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); - - } - -} - -function getImageURIMimeType( uri ) { - - if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; - if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; - - return 'image/png'; - -} - -const _identityMatrix = new three.Matrix4(); - -/* GLTF PARSER */ - -class GLTFParser { - - constructor( json = {}, options = {} ) { - - this.json = json; - this.extensions = {}; - this.plugins = {}; - this.options = options; - - // loader object cache - this.cache = new GLTFRegistry(); - - // associations between Three.js objects and glTF elements - this.associations = new Map(); - - // BufferGeometry caching - this.primitiveCache = {}; - - // Node cache - this.nodeCache = {}; - - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; - - this.sourceCache = {}; - this.textureCache = {}; - - // Track node names, to ensure no duplicates - this.nodeNamesUsed = {}; - - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - - let isSafari = false; - let isFirefox = false; - let firefoxVersion = - 1; - - if ( typeof navigator !== 'undefined' ) { - - isSafari = /^((?!chrome|android).)*safari/i.test( navigator.userAgent ) === true; - isFirefox = navigator.userAgent.indexOf( 'Firefox' ) > - 1; - firefoxVersion = isFirefox ? navigator.userAgent.match( /Firefox\/([0-9]+)\./ )[ 1 ] : - 1; - - } - - if ( typeof createImageBitmap === 'undefined' || isSafari || ( isFirefox && firefoxVersion < 98 ) ) { - - this.textureLoader = new three.TextureLoader( this.options.manager ); - - } else { - - this.textureLoader = new three.ImageBitmapLoader( this.options.manager ); - - } - - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - this.textureLoader.setRequestHeader( this.options.requestHeader ); - - this.fileLoader = new three.FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); - - if ( this.options.crossOrigin === 'use-credentials' ) { - - this.fileLoader.setWithCredentials( true ); - - } - - } - - setExtensions( extensions ) { - - this.extensions = extensions; - - } - - setPlugins( plugins ) { - - this.plugins = plugins; - - } - - parse( onLoad, onError ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - // Clear the loader cache - this.cache.removeAll(); - this.nodeCache = {}; - - // Mark the special nodes/meshes in json for efficient parse - this._invokeAll( function ( ext ) { - - return ext._markDefs && ext._markDefs(); - - } ); - - Promise.all( this._invokeAll( function ( ext ) { - - return ext.beforeRoot && ext.beforeRoot(); - - } ) ).then( function () { - - return Promise.all( [ - - parser.getDependencies( 'scene' ), - parser.getDependencies( 'animation' ), - parser.getDependencies( 'camera' ), - - ] ); - - } ).then( function ( dependencies ) { - - const result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; - - addUnknownExtensionsToUserData( extensions, result, json ); - - assignExtrasToUserData( result, json ); - - return Promise.all( parser._invokeAll( function ( ext ) { - - return ext.afterRoot && ext.afterRoot( result ); - - } ) ).then( function () { - - onLoad( result ); - - } ); - - } ).catch( onError ); - - } - - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - _markDefs() { - - const nodeDefs = this.json.nodes || []; - const skinDefs = this.json.skins || []; - const meshDefs = this.json.meshes || []; - - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { - - const joints = skinDefs[ skinIndex ].joints; - - for ( let i = 0, il = joints.length; i < il; i ++ ) { - - nodeDefs[ joints[ i ] ].isBone = true; - - } - - } - - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.mesh !== undefined ) { - - this._addNodeRef( this.meshCache, nodeDef.mesh ); - - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { - - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; - - } - - } - - if ( nodeDef.camera !== undefined ) { - - this._addNodeRef( this.cameraCache, nodeDef.camera ); - - } - - } - - } - - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - _addNodeRef( cache, index ) { - - if ( index === undefined ) return; - - if ( cache.refs[ index ] === undefined ) { - - cache.refs[ index ] = cache.uses[ index ] = 0; - - } - - cache.refs[ index ] ++; - - } - - /** Returns a reference to a shared resource, cloning it if necessary. */ - _getNodeRef( cache, index, object ) { - - if ( cache.refs[ index ] <= 1 ) return object; - - const ref = object.clone(); - - // Propagates mappings to the cloned object, prevents mappings on the - // original object from being lost. - const updateMappings = ( original, clone ) => { - - const mappings = this.associations.get( original ); - if ( mappings != null ) { - - this.associations.set( clone, mappings ); - - } - - for ( const [ i, child ] of original.children.entries() ) { - - updateMappings( child, clone.children[ i ] ); - - } - - }; - - updateMappings( object, ref ); - - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); - - return ref; - - } - - _invokeOne( func ) { - - const extensions = Object.values( this.plugins ); - extensions.push( this ); - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) return result; - - } - - return null; - - } - - _invokeAll( func ) { - - const extensions = Object.values( this.plugins ); - extensions.unshift( this ); - - const pending = []; - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) pending.push( result ); - - } - - return pending; - - } - - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - getDependency( type, index ) { - - const cacheKey = type + ':' + index; - let dependency = this.cache.get( cacheKey ); - - if ( ! dependency ) { - - switch ( type ) { - - case 'scene': - dependency = this.loadScene( index ); - break; - - case 'node': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadNode && ext.loadNode( index ); - - } ); - break; - - case 'mesh': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMesh && ext.loadMesh( index ); - - } ); - break; - - case 'accessor': - dependency = this.loadAccessor( index ); - break; - - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadBufferView && ext.loadBufferView( index ); - - } ); - break; - - case 'buffer': - dependency = this.loadBuffer( index ); - break; - - case 'material': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMaterial && ext.loadMaterial( index ); - - } ); - break; - - case 'texture': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadTexture && ext.loadTexture( index ); - - } ); - break; - - case 'skin': - dependency = this.loadSkin( index ); - break; - - case 'animation': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadAnimation && ext.loadAnimation( index ); - - } ); - break; - - case 'camera': - dependency = this.loadCamera( index ); - break; - - default: - dependency = this._invokeOne( function ( ext ) { - - return ext != this && ext.getDependency && ext.getDependency( type, index ); - - } ); - - if ( ! dependency ) { - - throw new Error( 'Unknown type: ' + type ); - - } - - break; - - } - - this.cache.add( cacheKey, dependency ); - - } - - return dependency; - - } - - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - getDependencies( type ) { - - let dependencies = this.cache.get( type ); - - if ( ! dependencies ) { - - const parser = this; - const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; - - dependencies = Promise.all( defs.map( function ( def, index ) { - - return parser.getDependency( type, index ); - - } ) ); - - this.cache.add( type, dependencies ); - - } - - return dependencies; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - loadBuffer( bufferIndex ) { - - const bufferDef = this.json.buffers[ bufferIndex ]; - const loader = this.fileLoader; - - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { - - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); - - } - - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - - return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); - - } - - const options = this.options; - - return new Promise( function ( resolve, reject ) { - - loader.load( three.LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { - - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); - - } ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - loadBufferView( bufferViewIndex ) { - - const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; - - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { - - const byteLength = bufferViewDef.byteLength || 0; - const byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - loadAccessor( accessorIndex ) { - - const parser = this; - const json = this.json; - - const accessorDef = this.json.accessors[ accessorIndex ]; - - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - - const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - const normalized = accessorDef.normalized === true; - - const array = new TypedArray( accessorDef.count * itemSize ); - return Promise.resolve( new three.BufferAttribute( array, itemSize, normalized ) ); - - } - - const pendingBufferViews = []; - - if ( accessorDef.bufferView !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); - - } else { - - pendingBufferViews.push( null ); - - } - - if ( accessorDef.sparse !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); - - } - - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { - - const bufferView = bufferViews[ 0 ]; - - const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - const elementBytes = TypedArray.BYTES_PER_ELEMENT; - const itemBytes = elementBytes * itemSize; - const byteOffset = accessorDef.byteOffset || 0; - const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - const normalized = accessorDef.normalized === true; - let array, bufferAttribute; - - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { - - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - const ibSlice = Math.floor( byteOffset / byteStride ); - const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - let ib = parser.cache.get( ibCacheKey ); - - if ( ! ib ) { - - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); - - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new three.InterleavedBuffer( array, byteStride / elementBytes ); - - parser.cache.add( ibCacheKey, ib ); - - } - - bufferAttribute = new three.InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); - - } else { - - if ( bufferView === null ) { - - array = new TypedArray( accessorDef.count * itemSize ); - - } else { - - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); - - } - - bufferAttribute = new three.BufferAttribute( array, itemSize, normalized ); - - } - - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { - - const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; - - const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; - - const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); - - if ( bufferView !== null ) { - - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new three.BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); - - } - - for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { - - const index = sparseIndices[ i ]; - - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); - - } - - } - - return bufferAttribute; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - loadTexture( textureIndex ) { - - const json = this.json; - const options = this.options; - const textureDef = json.textures[ textureIndex ]; - const sourceIndex = textureDef.source; - const sourceDef = json.images[ sourceIndex ]; - - let loader = this.textureLoader; - - if ( sourceDef.uri ) { - - const handler = options.manager.getHandler( sourceDef.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.loadTextureImage( textureIndex, sourceIndex, loader ); - - } - - loadTextureImage( textureIndex, sourceIndex, loader ) { - - const parser = this; - const json = this.json; - - const textureDef = json.textures[ textureIndex ]; - const sourceDef = json.images[ sourceIndex ]; - - const cacheKey = ( sourceDef.uri || sourceDef.bufferView ) + ':' + textureDef.sampler; - - if ( this.textureCache[ cacheKey ] ) { - - // See https://github.com/mrdoob/three.js/issues/21559. - return this.textureCache[ cacheKey ]; - - } - - const promise = this.loadImageSource( sourceIndex, loader ).then( function ( texture ) { - - texture.flipY = false; - - texture.name = textureDef.name || sourceDef.name || ''; - - if ( texture.name === '' && typeof sourceDef.uri === 'string' && sourceDef.uri.startsWith( 'data:image/' ) === false ) { - - texture.name = sourceDef.uri; - - } - - const samplers = json.samplers || {}; - const sampler = samplers[ textureDef.sampler ] || {}; - - texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || three.LinearFilter; - texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || three.LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || three.RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || three.RepeatWrapping; - - parser.associations.set( texture, { textures: textureIndex } ); - - return texture; - - } ).catch( function () { - - return null; - - } ); - - this.textureCache[ cacheKey ] = promise; - - return promise; - - } - - loadImageSource( sourceIndex, loader ) { - - const parser = this; - const json = this.json; - const options = this.options; - - if ( this.sourceCache[ sourceIndex ] !== undefined ) { - - return this.sourceCache[ sourceIndex ].then( ( texture ) => texture.clone() ); - - } - - const sourceDef = json.images[ sourceIndex ]; - - const URL = self.URL || self.webkitURL; - - let sourceURI = sourceDef.uri || ''; - let isObjectURL = false; - - if ( sourceDef.bufferView !== undefined ) { - - // Load binary image data from bufferView, if provided. - - sourceURI = parser.getDependency( 'bufferView', sourceDef.bufferView ).then( function ( bufferView ) { - - isObjectURL = true; - const blob = new Blob( [ bufferView ], { type: sourceDef.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; - - } ); - - } else if ( sourceDef.uri === undefined ) { - - throw new Error( 'THREE.GLTFLoader: Image ' + sourceIndex + ' is missing URI and bufferView' ); - - } - - const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) { - - return new Promise( function ( resolve, reject ) { - - let onLoad = resolve; - - if ( loader.isImageBitmapLoader === true ) { - - onLoad = function ( imageBitmap ) { - - const texture = new three.Texture( imageBitmap ); - texture.needsUpdate = true; - - resolve( texture ); - - }; - - } - - loader.load( three.LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); - - } ); - - } ).then( function ( texture ) { - - // Clean up resources and configure Texture. - - if ( isObjectURL === true ) { - - URL.revokeObjectURL( sourceURI ); - - } - - texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); - - return texture; - - } ).catch( function ( error ) { - throw error; - - } ); - - this.sourceCache[ sourceIndex ] = promise; - return promise; - - } - - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - assignTexture( materialParams, mapName, mapDef, colorSpace ) { - - const parser = this; - - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { - - if ( ! texture ) return null; - - if ( mapDef.texCoord !== undefined && mapDef.texCoord > 0 ) { - - texture = texture.clone(); - texture.channel = mapDef.texCoord; - - } - - if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; - - if ( transform ) { - - const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); - - } - - } - - if ( colorSpace !== undefined ) { - - texture.colorSpace = colorSpace; - - } - - materialParams[ mapName ] = texture; - - return texture; - - } ); - - } - - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accommodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - assignFinalMaterial( mesh ) { - - const geometry = mesh.geometry; - let material = mesh.material; - - const useDerivativeTangents = geometry.attributes.tangent === undefined; - const useVertexColors = geometry.attributes.color !== undefined; - const useFlatShading = geometry.attributes.normal === undefined; - - if ( mesh.isPoints ) { - - const cacheKey = 'PointsMaterial:' + material.uuid; - - let pointsMaterial = this.cache.get( cacheKey ); - - if ( ! pointsMaterial ) { - - pointsMaterial = new three.PointsMaterial(); - three.Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px - - this.cache.add( cacheKey, pointsMaterial ); - - } - - material = pointsMaterial; - - } else if ( mesh.isLine ) { - - const cacheKey = 'LineBasicMaterial:' + material.uuid; - - let lineMaterial = this.cache.get( cacheKey ); - - if ( ! lineMaterial ) { - - lineMaterial = new three.LineBasicMaterial(); - three.Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); - lineMaterial.map = material.map; - - this.cache.add( cacheKey, lineMaterial ); - - } - - material = lineMaterial; - - } - - // Clone the material if it will be modified - if ( useDerivativeTangents || useVertexColors || useFlatShading ) { - - let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; - - if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - - let cachedMaterial = this.cache.get( cacheKey ); - - if ( ! cachedMaterial ) { - - cachedMaterial = material.clone(); - - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - - if ( useDerivativeTangents ) { - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; - if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; - - } - - this.cache.add( cacheKey, cachedMaterial ); - - this.associations.set( cachedMaterial, this.associations.get( material ) ); - - } - - material = cachedMaterial; - - } - - mesh.material = material; - - } - - getMaterialType( /* materialIndex */ ) { - - return three.MeshStandardMaterial; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - loadMaterial( materialIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - const materialDef = json.materials[ materialIndex ]; - - let materialType; - const materialParams = {}; - const materialExtensions = materialDef.extensions || {}; - - const pending = []; - - if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - - const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); - - } else { - - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - - const metallicRoughness = materialDef.pbrMetallicRoughness || {}; - - materialParams.color = new three.Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.setRGB( array[ 0 ], array[ 1 ], array[ 2 ], three.LinearSRGBColorSpace ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, three.SRGBColorSpace ) ); - - } - - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; - - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); - - } - - materialType = this._invokeOne( function ( ext ) { - - return ext.getMaterialType && ext.getMaterialType( materialIndex ); - - } ); - - pending.push( Promise.all( this._invokeAll( function ( ext ) { - - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); - - } ) ) ); - - } - - if ( materialDef.doubleSided === true ) { - - materialParams.side = three.DoubleSide; - - } - - const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - - if ( alphaMode === ALPHA_MODES.BLEND ) { - - materialParams.transparent = true; - - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; - - } else { - - materialParams.transparent = false; - - if ( alphaMode === ALPHA_MODES.MASK ) { - - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; - - } - - } - - if ( materialDef.normalTexture !== undefined && materialType !== three.MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); - - materialParams.normalScale = new three.Vector2( 1, 1 ); - - if ( materialDef.normalTexture.scale !== undefined ) { - - const scale = materialDef.normalTexture.scale; - - materialParams.normalScale.set( scale, scale ); - - } - - } - - if ( materialDef.occlusionTexture !== undefined && materialType !== three.MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); - - if ( materialDef.occlusionTexture.strength !== undefined ) { - - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; - - } - - } - - if ( materialDef.emissiveFactor !== undefined && materialType !== three.MeshBasicMaterial ) { - - const emissiveFactor = materialDef.emissiveFactor; - materialParams.emissive = new three.Color().setRGB( emissiveFactor[ 0 ], emissiveFactor[ 1 ], emissiveFactor[ 2 ], three.LinearSRGBColorSpace ); - - } - - if ( materialDef.emissiveTexture !== undefined && materialType !== three.MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, three.SRGBColorSpace ) ); - - } - - return Promise.all( pending ).then( function () { - - const material = new materialType( materialParams ); - - if ( materialDef.name ) material.name = materialDef.name; - - assignExtrasToUserData( material, materialDef ); - - parser.associations.set( material, { materials: materialIndex } ); - - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); - - return material; - - } ); - - } - - /** When Object3D instances are targeted by animation, they need unique names. */ - createUniqueName( originalName ) { - - const sanitizedName = three.PropertyBinding.sanitizeNodeName( originalName || '' ); - - if ( sanitizedName in this.nodeNamesUsed ) { - - return sanitizedName + '_' + ( ++ this.nodeNamesUsed[ sanitizedName ] ); - - } else { - - this.nodeNamesUsed[ sanitizedName ] = 0; - - return sanitizedName; - - } - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - loadGeometries( primitives ) { - - const parser = this; - const extensions = this.extensions; - const cache = this.primitiveCache; - - function createDracoPrimitive( primitive ) { - - return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { - - return addPrimitiveAttributes( geometry, primitive, parser ); - - } ); - - } - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey( primitive ); - - // See if we've already created this geometry - const cached = cache[ cacheKey ]; - - if ( cached ) { - - // Use the cached geometry if it exists - pending.push( cached.promise ); - - } else { - - let geometryPromise; - - if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { - - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); - - } else { - - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new three.BufferGeometry(), primitive, parser ); - - } - - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; - - pending.push( geometryPromise ); - - } - - } - - return Promise.all( pending ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - loadMesh( meshIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - const meshDef = json.meshes[ meshIndex ]; - const primitives = meshDef.primitives; - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const material = primitives[ i ].material === undefined - ? createDefaultMaterial( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); - - pending.push( material ); - - } - - pending.push( parser.loadGeometries( primitives ) ); - - return Promise.all( pending ).then( function ( results ) { - - const materials = results.slice( 0, results.length - 1 ); - const geometries = results[ results.length - 1 ]; - - const meshes = []; - - for ( let i = 0, il = geometries.length; i < il; i ++ ) { - - const geometry = geometries[ i ]; - const primitive = primitives[ i ]; - - // 1. create Mesh - - let mesh; - - const material = materials[ i ]; - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || - primitive.mode === undefined ) { - - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new three.SkinnedMesh( geometry, material ) - : new three.Mesh( geometry, material ); - - if ( mesh.isSkinnedMesh === true ) { - - // normalize skin weights to fix malformed assets (see #15319) - mesh.normalizeSkinWeights(); - - } - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleStripDrawMode ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleFanDrawMode ); - - } - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { - - mesh = new three.LineSegments( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { - - mesh = new three.Line( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { - - mesh = new three.LineLoop( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { - - mesh = new three.Points( geometry, material ); - - } else { - - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); - - } - - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - - updateMorphTargets( mesh, meshDef ); - - } - - mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); - - assignExtrasToUserData( mesh, meshDef ); - - if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); - - parser.assignFinalMaterial( mesh ); - - meshes.push( mesh ); - - } - - for ( let i = 0, il = meshes.length; i < il; i ++ ) { - - parser.associations.set( meshes[ i ], { - meshes: meshIndex, - primitives: i - } ); - - } - - if ( meshes.length === 1 ) { - - if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, meshes[ 0 ], meshDef ); - - return meshes[ 0 ]; - - } - - const group = new three.Group(); - - if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, group, meshDef ); - - parser.associations.set( group, { meshes: meshIndex } ); - - for ( let i = 0, il = meshes.length; i < il; i ++ ) { - - group.add( meshes[ i ] ); - - } - - return group; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - loadCamera( cameraIndex ) { - - let camera; - const cameraDef = this.json.cameras[ cameraIndex ]; - const params = cameraDef[ cameraDef.type ]; - - if ( ! params ) { - return; - - } - - if ( cameraDef.type === 'perspective' ) { - - camera = new three.PerspectiveCamera( three.MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); - - } else if ( cameraDef.type === 'orthographic' ) { - - camera = new three.OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); - - } - - if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); - - assignExtrasToUserData( camera, cameraDef ); - - return Promise.resolve( camera ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - loadSkin( skinIndex ) { - - const skinDef = this.json.skins[ skinIndex ]; - - const pending = []; - - for ( let i = 0, il = skinDef.joints.length; i < il; i ++ ) { - - pending.push( this._loadNodeShallow( skinDef.joints[ i ] ) ); - - } - - if ( skinDef.inverseBindMatrices !== undefined ) { - - pending.push( this.getDependency( 'accessor', skinDef.inverseBindMatrices ) ); - - } else { - - pending.push( null ); - - } - - return Promise.all( pending ).then( function ( results ) { - - const inverseBindMatrices = results.pop(); - const jointNodes = results; - - // Note that bones (joint nodes) may or may not be in the - // scene graph at this time. - - const bones = []; - const boneInverses = []; - - for ( let i = 0, il = jointNodes.length; i < il; i ++ ) { - - const jointNode = jointNodes[ i ]; - - if ( jointNode ) { - - bones.push( jointNode ); - - const mat = new three.Matrix4(); - - if ( inverseBindMatrices !== null ) { - - mat.fromArray( inverseBindMatrices.array, i * 16 ); - - } - - boneInverses.push( mat ); - - } - - } - - return new three.Skeleton( bones, boneInverses ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - loadAnimation( animationIndex ) { - - const json = this.json; - const parser = this; - - const animationDef = json.animations[ animationIndex ]; - const animationName = animationDef.name ? animationDef.name : 'animation_' + animationIndex; - - const pendingNodes = []; - const pendingInputAccessors = []; - const pendingOutputAccessors = []; - const pendingSamplers = []; - const pendingTargets = []; - - for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { - - const channel = animationDef.channels[ i ]; - const sampler = animationDef.samplers[ channel.sampler ]; - const target = channel.target; - const name = target.node; - const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; - - if ( target.node === undefined ) continue; - - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); - - } - - return Promise.all( [ - - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) - - ] ).then( function ( dependencies ) { - - const nodes = dependencies[ 0 ]; - const inputAccessors = dependencies[ 1 ]; - const outputAccessors = dependencies[ 2 ]; - const samplers = dependencies[ 3 ]; - const targets = dependencies[ 4 ]; - - const tracks = []; - - for ( let i = 0, il = nodes.length; i < il; i ++ ) { - - const node = nodes[ i ]; - const inputAccessor = inputAccessors[ i ]; - const outputAccessor = outputAccessors[ i ]; - const sampler = samplers[ i ]; - const target = targets[ i ]; - - if ( node === undefined ) continue; - - if ( node.updateMatrix ) { - - node.updateMatrix(); - - } - - const createdTracks = parser._createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target ); - - if ( createdTracks ) { - - for ( let k = 0; k < createdTracks.length; k ++ ) { - - tracks.push( createdTracks[ k ] ); - - } - - } - - } - - return new three.AnimationClip( animationName, undefined, tracks ); - - } ); - - } - - createNodeMesh( nodeIndex ) { - - const json = this.json; - const parser = this; - const nodeDef = json.nodes[ nodeIndex ]; - - if ( nodeDef.mesh === undefined ) return null; - - return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { - - const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); - - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { - - node.traverse( function ( o ) { - - if ( ! o.isMesh ) return; - - for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { - - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; - - } - - } ); - - } - - return node; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - loadNode( nodeIndex ) { - - const json = this.json; - const parser = this; - - const nodeDef = json.nodes[ nodeIndex ]; - - const nodePending = parser._loadNodeShallow( nodeIndex ); - - const childPending = []; - const childrenDef = nodeDef.children || []; - - for ( let i = 0, il = childrenDef.length; i < il; i ++ ) { - - childPending.push( parser.getDependency( 'node', childrenDef[ i ] ) ); - - } - - const skeletonPending = nodeDef.skin === undefined - ? Promise.resolve( null ) - : parser.getDependency( 'skin', nodeDef.skin ); - - return Promise.all( [ - nodePending, - Promise.all( childPending ), - skeletonPending - ] ).then( function ( results ) { - - const node = results[ 0 ]; - const children = results[ 1 ]; - const skeleton = results[ 2 ]; - - if ( skeleton !== null ) { - - // This full traverse should be fine because - // child glTF nodes have not been added to this node yet. - node.traverse( function ( mesh ) { - - if ( ! mesh.isSkinnedMesh ) return; - - mesh.bind( skeleton, _identityMatrix ); - - } ); - - } - - for ( let i = 0, il = children.length; i < il; i ++ ) { - - node.add( children[ i ] ); - - } - - return node; - - } ); - - } - - // ._loadNodeShallow() parses a single node. - // skin and child nodes are created and added in .loadNode() (no '_' prefix). - _loadNodeShallow( nodeIndex ) { - - const json = this.json; - const extensions = this.extensions; - const parser = this; - - // This method is called from .loadNode() and .loadSkin(). - // Cache a node to avoid duplication. - - if ( this.nodeCache[ nodeIndex ] !== undefined ) { - - return this.nodeCache[ nodeIndex ]; - - } - - const nodeDef = json.nodes[ nodeIndex ]; - - // reserve node's name before its dependencies, so the root has the intended name. - const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; - - const pending = []; - - const meshPromise = parser._invokeOne( function ( ext ) { - - return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); - - } ); - - if ( meshPromise ) { - - pending.push( meshPromise ); - - } - - if ( nodeDef.camera !== undefined ) { - - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { - - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); - - } ) ); - - } - - parser._invokeAll( function ( ext ) { - - return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); - - } ).forEach( function ( promise ) { - - pending.push( promise ); - - } ); - - this.nodeCache[ nodeIndex ] = Promise.all( pending ).then( function ( objects ) { - - let node; - - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { - - node = new three.Bone(); - - } else if ( objects.length > 1 ) { - - node = new three.Group(); - - } else if ( objects.length === 1 ) { - - node = objects[ 0 ]; - - } else { - - node = new three.Object3D(); - - } - - if ( node !== objects[ 0 ] ) { - - for ( let i = 0, il = objects.length; i < il; i ++ ) { - - node.add( objects[ i ] ); - - } - - } - - if ( nodeDef.name ) { - - node.userData.name = nodeDef.name; - node.name = nodeName; - - } - - assignExtrasToUserData( node, nodeDef ); - - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); - - if ( nodeDef.matrix !== undefined ) { - - const matrix = new three.Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); - - } else { - - if ( nodeDef.translation !== undefined ) { - - node.position.fromArray( nodeDef.translation ); - - } - - if ( nodeDef.rotation !== undefined ) { - - node.quaternion.fromArray( nodeDef.rotation ); - - } - - if ( nodeDef.scale !== undefined ) { - - node.scale.fromArray( nodeDef.scale ); - - } - - } - - if ( ! parser.associations.has( node ) ) { - - parser.associations.set( node, {} ); - - } - - parser.associations.get( node ).nodes = nodeIndex; - - return node; - - } ); - - return this.nodeCache[ nodeIndex ]; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - loadScene( sceneIndex ) { - - const extensions = this.extensions; - const sceneDef = this.json.scenes[ sceneIndex ]; - const parser = this; - - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - const scene = new three.Group(); - if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); - - assignExtrasToUserData( scene, sceneDef ); - - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); - - const nodeIds = sceneDef.nodes || []; - - const pending = []; - - for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { - - pending.push( parser.getDependency( 'node', nodeIds[ i ] ) ); - - } - - return Promise.all( pending ).then( function ( nodes ) { - - for ( let i = 0, il = nodes.length; i < il; i ++ ) { - - scene.add( nodes[ i ] ); - - } - - // Removes dangling associations, associations that reference a node that - // didn't make it into the scene. - const reduceAssociations = ( node ) => { - - const reducedAssociations = new Map(); - - for ( const [ key, value ] of parser.associations ) { - - if ( key instanceof three.Material || key instanceof three.Texture ) { - - reducedAssociations.set( key, value ); - - } - - } - - node.traverse( ( node ) => { - - const mappings = parser.associations.get( node ); - - if ( mappings != null ) { - - reducedAssociations.set( node, mappings ); - - } - - } ); - - return reducedAssociations; - - }; - - parser.associations = reduceAssociations( scene ); - - return scene; - - } ); - - } - - _createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target ) { - - const tracks = []; - - const targetName = node.name ? node.name : node.uuid; - const targetNames = []; - - if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { - - node.traverse( function ( object ) { - - if ( object.morphTargetInfluences ) { - - targetNames.push( object.name ? object.name : object.uuid ); - - } - - } ); - - } else { - - targetNames.push( targetName ); - - } - - let TypedKeyframeTrack; - - switch ( PATH_PROPERTIES[ target.path ] ) { - - case PATH_PROPERTIES.weights: - - TypedKeyframeTrack = three.NumberKeyframeTrack; - break; - - case PATH_PROPERTIES.rotation: - - TypedKeyframeTrack = three.QuaternionKeyframeTrack; - break; - - case PATH_PROPERTIES.position: - case PATH_PROPERTIES.scale: - - TypedKeyframeTrack = three.VectorKeyframeTrack; - break; - - default: - - switch ( outputAccessor.itemSize ) { - - case 1: - TypedKeyframeTrack = three.NumberKeyframeTrack; - break; - case 2: - case 3: - default: - TypedKeyframeTrack = three.VectorKeyframeTrack; - break; - - } - - break; - - } - - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : three.InterpolateLinear; - - - const outputArray = this._getArrayFromAccessor( outputAccessor ); - - for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { - - const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); - - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { - - this._createCubicSplineTrackInterpolant( track ); - - } - - tracks.push( track ); - - } - - return tracks; - - } - - _getArrayFromAccessor( accessor ) { - - let outputArray = accessor.array; - - if ( accessor.normalized ) { - - const scale = getNormalizedComponentScale( outputArray.constructor ); - const scaled = new Float32Array( outputArray.length ); - - for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { - - scaled[ j ] = outputArray[ j ] * scale; - - } - - outputArray = scaled; - - } - - return outputArray; - - } - - _createCubicSplineTrackInterpolant( track ) { - - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { - - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. - - const interpolantType = ( this instanceof three.QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; - - return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); - - }; - - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; - - } - -} - -/** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ -function computeBounds( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const box = new three.Box3(); - - if ( attributes.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ attributes.POSITION ]; - - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - box.set( - new three.Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new three.Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) - ); - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - box.min.multiplyScalar( boxScale ); - box.max.multiplyScalar( boxScale ); - - } - - } else { - - return; - - } - - } else { - - return; - - } - - const targets = primitiveDef.targets; - - if ( targets !== undefined ) { - - const maxDisplacement = new three.Vector3(); - const vector = new three.Vector3(); - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ target.POSITION ]; - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); - - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - vector.multiplyScalar( boxScale ); - - } - - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); - - } - - } - - } - - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); - - } - - geometry.boundingBox = box; - - const sphere = new three.Sphere(); - - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; - - geometry.boundingSphere = sphere; - -} - -/** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} - */ -function addPrimitiveAttributes( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const pending = []; - - function assignAttributeAccessor( accessorIndex, attributeName ) { - - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { - - geometry.setAttribute( attributeName, accessor ); - - } ); - - } - - for ( const gltfAttributeName in attributes ) { - - const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); - - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; - - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); - - } - - if ( primitiveDef.indices !== undefined && ! geometry.index ) { - - const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { - - geometry.setIndex( accessor ); - - } ); - - pending.push( accessor ); - - } - - if ( three.ColorManagement.workingColorSpace !== three.LinearSRGBColorSpace && 'COLOR_0' in attributes ) ; - - assignExtrasToUserData( geometry, primitiveDef ); - - computeBounds( geometry, primitiveDef, parser ); - - return Promise.all( pending ).then( function () { - - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; - - } ); - -} - -const _taskCache$1 = new WeakMap(); - -class DRACOLoader extends three.Loader { - - constructor( manager ) { - - super( manager ); - - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; - - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; - - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' - }; - - } - - setDecoderPath( path ) { - - this.decoderPath = path; - - return this; - - } - - setDecoderConfig( config ) { - - this.decoderConfig = config; - - return this; - - } - - setWorkerLimit( workerLimit ) { - - this.workerLimit = workerLimit; - - return this; - - } - - load( url, onLoad, onProgress, onError ) { - - const loader = new three.FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, ( buffer ) => { - - this.parse( buffer, onLoad, onError ); - - }, onProgress, onError ); - - } - - - parse( buffer, onLoad, onError = ()=>{} ) { - - this.decodeDracoFile( buffer, onLoad, null, null, three.SRGBColorSpace ).catch( onError ); - - } - - decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = three.LinearSRGBColorSpace, onError = () => {} ) { - - const taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs, - vertexColorSpace: vertexColorSpace, - }; - - return this.decodeGeometry( buffer, taskConfig ).then( callback ).catch( onError ); - - } - - decodeGeometry( buffer, taskConfig ) { - - const taskKey = JSON.stringify( taskConfig ); - - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( _taskCache$1.has( buffer ) ) { - - const cachedTask = _taskCache$1.get( buffer ); - - if ( cachedTask.key === taskKey ) { - - return cachedTask.promise; - - } else if ( buffer.byteLength === 0 ) { - - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( - - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' - - ); - - } - - } - - // - - let worker; - const taskID = this.workerNextTaskID ++; - const taskCost = buffer.byteLength; - - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - const geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { - - worker = _worker; - - return new Promise( ( resolve, reject ) => { - - worker._callbacks[ taskID ] = { resolve, reject }; - - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - - // this.debug(); - - } ); - - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); - - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { - - if ( worker && taskID ) { - - this._releaseTask( worker, taskID ); - - // this.debug(); - - } - - } ); - - // Cache the task result. - _taskCache$1.set( buffer, { - - key: taskKey, - promise: geometryPending - - } ); - - return geometryPending; - - } - - _createGeometry( geometryData ) { - - const geometry = new three.BufferGeometry(); - - if ( geometryData.index ) { - - geometry.setIndex( new three.BufferAttribute( geometryData.index.array, 1 ) ); - - } - - for ( let i = 0; i < geometryData.attributes.length; i ++ ) { - - const result = geometryData.attributes[ i ]; - const name = result.name; - const array = result.array; - const itemSize = result.itemSize; - - const attribute = new three.BufferAttribute( array, itemSize ); - - if ( name === 'color' ) { - - this._assignVertexColorSpace( attribute, result.vertexColorSpace ); - - attribute.normalized = ( array instanceof Float32Array ) === false; - - } - - geometry.setAttribute( name, attribute ); - - } - - return geometry; - - } - - _assignVertexColorSpace( attribute, inputColorSpace ) { - - // While .drc files do not specify colorspace, the only 'official' tooling - // is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc - // file is passed into .load() or .parse(). GLTFLoader uses internal APIs - // to decode geometry, and vertex colors are already Linear-sRGB in there. - - if ( inputColorSpace !== three.SRGBColorSpace ) return; - - const _color = new three.Color(); - - for ( let i = 0, il = attribute.count; i < il; i ++ ) { - - _color.fromBufferAttribute( attribute, i ).convertSRGBToLinear(); - attribute.setXYZ( i, _color.r, _color.g, _color.b ); - - } - - } - - _loadLibrary( url, responseType ) { - - const loader = new three.FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - loader.setWithCredentials( this.withCredentials ); - - return new Promise( ( resolve, reject ) => { - - loader.load( url, resolve, undefined, reject ); - - } ); - - } - - preload() { - - this._initDecoder(); - - return this; - - } - - _initDecoder() { - - if ( this.decoderPending ) return this.decoderPending; - - const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - const librariesPending = []; - - if ( useJS ) { - - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - - } else { - - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); - - } - - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { - - const jsContent = libraries[ 0 ]; - - if ( ! useJS ) { - - this.decoderConfig.wasmBinary = libraries[ 1 ]; - - } - - const fn = DRACOWorker.toString(); - - const body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); - - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); - - } ); - - return this.decoderPending; - - } - - _getWorker( taskID, taskCost ) { - - return this._initDecoder().then( () => { - - if ( this.workerPool.length < this.workerLimit ) { - - const worker = new Worker( this.workerSourceURL ); - - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; - - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - - worker.onmessage = function ( e ) { - - const message = e.data; - - switch ( message.type ) { - - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; - - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; - - } - - }; - - this.workerPool.push( worker ); - - } else { - - this.workerPool.sort( function ( a, b ) { - - return a._taskLoad > b._taskLoad ? - 1 : 1; - - } ); - - } - - const worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; - - } ); - - } - - _releaseTask( worker, taskID ) { - - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; - - } - - debug() { - - } - - dispose() { - - for ( let i = 0; i < this.workerPool.length; ++ i ) { - - this.workerPool[ i ].terminate(); - - } - - this.workerPool.length = 0; - - if ( this.workerSourceURL !== '' ) { - - URL.revokeObjectURL( this.workerSourceURL ); - - } - - return this; - - } - -} - -/* WEB WORKER */ - -function DRACOWorker() { - - let decoderConfig; - let decoderPending; + varying vec3 vWorldPosition; + varying vec3 vSunDirection; + varying float vSunfade; + varying vec3 vBetaR; + varying vec3 vBetaM; + varying float vSunE; - onmessage = function ( e ) { + // constants for atmospheric scattering + const float e = 2.71828182845904523536028747135266249775724709369995957; + const float pi = 3.141592653589793238462643383279502884197169; - const message = e.data; + // wavelength of used primaries, according to preetham + const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); + // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: + // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) + const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); - switch ( message.type ) { + // mie stuff + // K coefficient for the primaries + const float v = 4.0; + const vec3 K = vec3( 0.686, 0.678, 0.666 ); + // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K + const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { + // earth shadow hack + // cutoffAngle = pi / 1.95; + const float cutoffAngle = 1.6110731556870734; + const float steepness = 1.5; + const float EE = 1000.0; - decoderConfig.onModuleLoaded = function ( draco ) { + float sunIntensity( float zenithAngleCos ) { + zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); + return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); + } - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); + vec3 totalMie( float T ) { + float c = ( 0.2 * T ) * 10E-18; + return 0.434 * c * MieConst; + } - }; + void main() { - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef + vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); + vWorldPosition = worldPosition.xyz; - } ); - break; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + gl_Position.z = gl_Position.w; // set z to camera.far - case 'decode': - const buffer = message.buffer; - const taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { + vSunDirection = normalize( sunPosition ); - const draco = module.draco; - const decoder = new draco.Decoder(); + vSunE = sunIntensity( dot( vSunDirection, up ) ); - try { + vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); - const geometry = decodeGeometry( draco, decoder, new Int8Array( buffer ), taskConfig ); + float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); - const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); + // extinction (absorbtion + out scattering) + // rayleigh coefficients + vBetaR = totalRayleigh * rayleighCoefficient; - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); + // mie coefficients + vBetaM = totalMie( turbidity ) * mieCoefficient; - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); + }`, - } catch ( error ) { + fragmentShader: /* glsl */` + varying vec3 vWorldPosition; + varying vec3 vSunDirection; + varying float vSunfade; + varying vec3 vBetaR; + varying vec3 vBetaM; + varying float vSunE; - self.postMessage( { type: 'error', id: message.id, error: error.message } ); + uniform float mieDirectionalG; + uniform vec3 up; - } finally { + // constants for atmospheric scattering + const float pi = 3.141592653589793238462643383279502884197169; - draco.destroy( decoder ); + const float n = 1.0003; // refractive index of air + const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius) - } + // optical length at zenith for molecules + const float rayleighZenithLength = 8.4E3; + const float mieZenithLength = 1.25E3; + // 66 arc seconds -> degrees, and the cosine of that + const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; - } ); - break; + // 3.0 / ( 16.0 * pi ) + const float THREE_OVER_SIXTEENPI = 0.05968310365946075; + // 1.0 / ( 4.0 * pi ) + const float ONE_OVER_FOURPI = 0.07957747154594767; + float rayleighPhase( float cosTheta ) { + return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); } - }; + float hgPhase( float cosTheta, float g ) { + float g2 = pow( g, 2.0 ); + float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); + return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse ); + } - function decodeGeometry( draco, decoder, array, taskConfig ) { + void main() { - const attributeIDs = taskConfig.attributeIDs; - const attributeTypes = taskConfig.attributeTypes; + vec3 direction = normalize( vWorldPosition - cameraPosition ); - let dracoGeometry; - let decodingStatus; + // optical length + // cutoff angle at 90 to avoid singularity in next formula. + float zenithAngle = acos( max( 0.0, dot( up, direction ) ) ); + float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); + float sR = rayleighZenithLength * inverse; + float sM = mieZenithLength * inverse; - const geometryType = decoder.GetEncodedGeometryType( array ); + // combined extinction factor + vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); - if ( geometryType === draco.TRIANGULAR_MESH ) { + // in scattering + float cosTheta = dot( direction, vSunDirection ); - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeArrayToMesh( array, array.byteLength, dracoGeometry ); + float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); + vec3 betaRTheta = vBetaR * rPhase; - } else if ( geometryType === draco.POINT_CLOUD ) { + float mPhase = hgPhase( cosTheta, mieDirectionalG ); + vec3 betaMTheta = vBetaM * mPhase; - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeArrayToPointCloud( array, array.byteLength, dracoGeometry ); + vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); + Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); - } else { + // nightsky + float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] + float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] + vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); + vec3 L0 = vec3( 0.1 ) * Fex; - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); + // composition + solar disc + float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); + L0 += ( vSunE * 19000.0 * Fex ) * sundisk; - } + vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { + vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); + gl_FragColor = vec4( retColor, 1.0 ); - } + #include + #include - const geometry = { index: null, attributes: [] }; + }` - // Gather all vertex attributes. - for ( const attributeName in attributeIDs ) { +}; - const attributeType = self[ attributeTypes[ attributeName ] ]; +// 多个canvas并没有id,只有父节点 - let attribute; - let attributeID; - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { +class Layer extends BasLayer{ + id; // 唯一标识 + layerContainer; // div#layer 容器 + zIndex=1;//默认为1 + opacity=1;//默认为1 + canvas;//canvas + dispose = false; + renderer;//canvas上下文 + scene;//场景 + visible=true;//是否可见 + mapView;//地图视图 + camera;//相机 + controls;//控件 + animateId;//动画事件id + base = false; // 是否为底图 + ambientLight; // 环境光 + directionalLight; // 方向光 + modelLayer = false; // 模型图层 + imageLayer = false; // 影像图层 + vectorLayer = false; // 矢量图层,如路网、行政区划,地名等图层 + waters = []; // 水面集合 + constructor(id, layerContainer, canvas, mapView, plane = true, camera = new three.PerspectiveCamera(80, 1, 0.1, 1e12)) { + super(); + this.id = id; + this.layerContainer = layerContainer; + this.canvas = canvas; + this.renderer = new three.WebGLRenderer({ + canvas: this.canvas, + antialias: true, + alpha: true, + logarithmicDepthBuffer: true, + precision: "highp", + }); + this.renderer.sortObjects = true; + this.renderer.setPixelRatio(window.devicePixelRatio); + this.renderer.setClearColor(0xFFFFFF, 0.0); + this.scene = new three.Scene(); + this.mapView = mapView; + this.camera = camera; + if(this.mapView){ + this.scene.add(this.mapView); + this.mapView.updateMatrixWorld(true); + } + if (plane){ + this.controls = new MapControls(this.camera, this.canvas); + this.controls.minDistance = 1e1; + this.controls.zoomSpeed = 2.0; + } else { + this.controls = new OrbitControls(this.camera, this.canvas); + this.controls.enablePan = false; + this.controls.minDistance = UnitsUtils.EARTH_RADIUS + 2; + this.controls.maxDistance = UnitsUtils.EARTH_RADIUS * 1e1; + } + this._raycaster = new three.Raycaster(); + if(Config.outLine.on){ + this.effectOutline = new EffectOutline(this.renderer, this.scene, this.camera, this.canvas.width, this.canvas.height); + } + if (Config.layer.map.ambientLight.add){ + this.scene.add(new three.AmbientLight(Config.layer.map.ambientLight.color, Config.layer.map.ambientLight.intensity)); + } + if (Config.layer.map.directionalLight.add){ + this.scene.add(new three.DirectionalLight(Config.layer.map.directionalLight.color, Config.layer.map.directionalLight.intensity)); + } + if (Config.layer.map.pointLight.add){ + let pointLight = new three.PointLight(Config.layer.map.pointLight.color, Config.layer.map.pointLight.intensity, Config.layer.map.pointLight.distance); + pointLight.position.set(...Config.layer.map.pointLight.position); + this.scene.add(pointLight); + } + } - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); + moveTo(lat, lon, height = 38472.48763833733){ + // var coords = UnitsUtils.datumsToSpherical(44.266119,90.139228); + var coords = UnitsUtils.datumsToSpherical(lat,lon); + this.camera.position.set(coords.x, height, -coords.y); + this.controls.target.set(this.camera.position.x, 0, this.camera.position.z); + } - } else { + moveToByCoords(coords){ + let offset = 50; + this.camera.position.set(coords.x, coords.y+offset, coords.z); + this.controls.target.set(coords.x, coords.y, coords.z); + } - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); + moveToByLL(lat, lon, distance = 384720){ + let dir = UnitsUtils.datumsToVector(lat, lon); + dir.multiplyScalar(UnitsUtils.EARTH_RADIUS + distance); + this.camera.position.copy(dir); + } - if ( attributeID === - 1 ) continue; - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); + on(eventName, callback){ + this.listener.on(eventName, callback); + } - } + setSceneBackground(color) { + this.scene.background = color; + } - const attributeResult = decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ); + clearSceneBackground() { + this.scene.background = null; + } - if ( attributeName === 'color' ) { + // 可用于添加灯光mesh等元素 + add(Object3D) { + if(Object3D ==null || Object3D ==undefined){ + return; + } + this.scene.add(Object3D); + } - attributeResult.vertexColorSpace = taskConfig.vertexColorSpace; - } + remove(Object3D) { + if(Object3D ==null || Object3D ==undefined){ + return; + } + this.scene.remove(Object3D); + } - geometry.attributes.push( attributeResult ); + openWaterConfig(){ + /** + * 打开渲染水系配置 + */ + // this.renderer.setPixelRatio( window.devicePixelRatio ); + this.renderer.toneMapping = three.ACESFilmicToneMapping; + this.renderer.toneMappingExposure = 0.5; + + // 添加天空 + this.sky = new Sky(); + this.sky.translateX = true; + this.sky.translateY = true; + this.sky.translateZ = true; + this.sky.rotateX = Math.PI / 2; + this.sky.scale.setScalar( Config.EARTH_RADIUS * 2 * Math.PI ); // 天空放大倍数 + this.scene.add( this.sky ); + const skyUniforms = this.sky.material.uniforms; + // 天空的配置 + skyUniforms[ 'turbidity' ].value = 10; + skyUniforms[ 'rayleigh' ].value = 2; + skyUniforms[ 'mieCoefficient' ].value = 0.005; + skyUniforms[ 'mieDirectionalG' ].value = 0.8; + // 旋转 设置为y朝上 + // sky.material.uniforms["up"].value = new THREE.Vector3(0, 1, 0); + + // 天空映射, 更新太阳位置 + this.pmremGenerator = new three.PMREMGenerator( this.renderer ); + this.sceneEnv = new three.Scene(); + this.renderTarget = null; + this.sun = new three.Vector3(); + this.updateSun(Config.SUNDEGREE, Config.SUNAZIMUTH); + } - } + updateSun(elevation, azimuth) { - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { + const phi = three.MathUtils.degToRad( 90 - elevation ); + const theta = three.MathUtils.degToRad( azimuth ); - geometry.index = decodeIndex( draco, decoder, dracoGeometry ); + this.sun.setFromSphericalCoords( 1, phi, theta ); - } + this.sky.material.uniforms[ 'sunPosition' ].value.copy( this.sun ); + for (let water of this.waters){ + water.material.uniforms[ 'sunDirection' ].value.copy( this.sun ).normalize(); + } + if ( this.renderTarget !== null ) this.renderTarget.dispose(); - draco.destroy( dracoGeometry ); + this.sceneEnv.add( this.sky ); + this.renderTarget = this.pmremGenerator.fromScene( this.sceneEnv ); + this.scene.add( this.sky ); - return geometry; + this.scene.environment = this.renderTarget.texture; - } - function decodeIndex( draco, decoder, dracoGeometry ) { + } - const numFaces = dracoGeometry.num_faces(); - const numIndices = numFaces * 3; - const byteLength = numIndices * 4; + /** + * 添加水系 + * @param {*} water + * @returns + */ + addWater(water) { + if(water ==null || water ==undefined){ + return; + } + this.scene.add(water); + this.waters.push(water); + } - const ptr = draco._malloc( byteLength ); - decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); - const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); - draco._free( ptr ); + removeWater(water) { + if(water ==null || water ==undefined){ + return; + } + this.scene.remove(water); + let index = this.waters.indexOf(water); + if (index > -1) { + this.waters.splice(index, 1); + } + } - return { array: index, itemSize: 1 }; + setVisible(visible) { + this.visible = this.visible; + this.layerContainer.style.display = visible ? 'block' : 'none'; + } + /** + * @deprecated 不建议用 + * @param {number} opacity + */ + setOpacity(opacity) { + this.opacity = opacity; + this.layerContainer.style.opacity = opacity; + } - } + /** + * 修改显示层级,默认越靠下的dom元素,显示上越靠上 + * @param {number} zIndex + */ + setZIndex(zIndex) { + this.zIndex = zIndex; + this.layerContainer.style.zIndex = this.zIndex; + } - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { + dispose() { + Element.removeLayer(id); + this.dispose = true; + this.animateId && cancelAnimationFrame(this.animateId); + this.mapView.root.dispose(); + this.mapView.dispose(); + } - const numComponents = attribute.num_components(); - const numPoints = dracoGeometry.num_points(); - const numValues = numPoints * numComponents; - const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; - const dataType = getDracoDataType( draco, attributeType ); + selectModel(insect){ + this.effectOutline.selectModel(insect); + } - const ptr = draco._malloc( byteLength ); - decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); - const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); - draco._free( ptr ); + resize(){ + var width = window.innerWidth; + var height = window.innerHeight; + this.renderer.setSize(width, height); + this.camera.aspect = width / height; + this.camera.updateProjectionMatrix(); + if(Config.outLine.on){ + this.effectOutline.resize(width, height); + } + } - return { - name: attributeName, - array: array, - itemSize: numComponents - }; + animate(){ + this.animateId = requestAnimationFrame(this.animate.bind(this)); + if(this.base){ + this.controls.update(); + } + if (this.base){ + update(); //目前只有在基础地图中添加相机移动的动画,所以暂且只在base地图中进行渲染,后期可改成每个图层都进行渲染,或者必要时进行渲染。 + } + for(let water of this.waters){ + water.material.uniforms[ 'time' ].value += 1.0 / 60.0; + } + if (Config.outLine.on){ + this.effectOutline.render(); // 合成器渲染 + } else { + this.renderer.autoClear = true; + } + this.renderer.render(this.scene, this.camera); + } - } + _raycast(meshes, recursive, faceExclude) { + const isects = this._raycaster.intersectObjects(meshes, recursive); + if (faceExclude) { + for (let i = 0; i < isects.length; i++) { + if (isects[i].face !== faceExclude) { + return isects[i]; + } + } + return null; + } + return isects.length > 0 ? isects[0] : null; + } - function getDracoDataType( draco, attributeType ) { + _raycastFromMouse(mx, my, width, height, cam, meshes, recursive=false) { + const mouse = new three.Vector2( // normalized (-1 to +1) + (mx / width) * 2 - 1, + - (my / height) * 2 + 1); + // https://threejs.org/docs/#api/core/Raycaster + // update the picking ray with the camera and mouse position + this._raycaster.setFromCamera(mouse, cam); + return this._raycast(meshes, recursive, null); + } - switch ( attributeType ) { + /** + * + * @param {*} mx 屏幕坐标x + * @param {*} my 屏幕坐标y + * @param {boolean} recursive 是否检查子节点,true 递归检查 + * @returns mesh + */ + raycastFromMouse(mx, my, recursive=false) { + //---- NG: 2x when starting with Chrome's inspector mobile + // const {width, height} = this.renderer.domElement; + // const {width, height} = this.canvas; + //---- OK + const {clientWidth, clientHeight} = this.canvas; - case Float32Array: return draco.DT_FLOAT32; - case Int8Array: return draco.DT_INT8; - case Int16Array: return draco.DT_INT16; - case Int32Array: return draco.DT_INT32; - case Uint8Array: return draco.DT_UINT8; - case Uint16Array: return draco.DT_UINT16; - case Uint32Array: return draco.DT_UINT32; + return this._raycastFromMouse( + mx, my, clientWidth, clientHeight, this.camera, + this.mapView.children, recursive); + } - } + insectALL(mx, my, recursive=false) { + const {clientWidth, clientHeight} = this.canvas; - } + return this._raycastFromMouse( + mx, my, clientWidth, clientHeight, this.camera, + this.scene.children, recursive); + } } @@ -41527,11 +36293,17 @@ class WegeoMap { this.baseMap.moveToByCoords(coords); } - moveToByLL(lat, lon){ + /** + * 跳转到指定位置,用于球形地图 + * @param {*} lat + * @param {*} lon + * @returns + */ + moveToByLL(lat, lon, distance = 384720){ if(!this.baseMap){ return; } - this.baseMap.moveToByLL(lat, lon); + this.baseMap.moveToByLL(lat, lon, distance); } // 鼠标点击获取模型 @@ -41895,12 +36667,12 @@ class Skybox { loadSkyBox(scale) { var aCubeMap = new three.CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); aCubeMap.format = three.RGBAFormat; @@ -41923,17 +36695,18 @@ class Skybox { } loadBox(){ var cube = new three.CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); return cube; } } +exports.AngleUtils = AngleUtils; exports.Animate = Animate; exports.BingMapsProvider = BingMapsProvider; exports.CancelablePromise = CancelablePromise; diff --git a/build/wegeo.js b/build/wegeo.js index fb795a6..3c6a8ff 100644 --- a/build/wegeo.js +++ b/build/wegeo.js @@ -5011,6 +5011,26 @@ } } + class AngleUtils { + /** + * 弧度转角度 + * @param {*} rad + * @returns + */ + static radToDeg(rad) { + return rad * (180 / Math.PI); + } + /** + * 角度转弧度 + * @param {*} deg + * @returns + */ + static degToRad(deg) { + return deg * (Math.PI / 180); + } + + } + // OrbitControls performs orbiting, dollying (zooming), and panning. // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). // @@ -9175,7 +9195,7 @@ * @param {number} drawMode * @return {BufferGeometry} */ - function toTrianglesDrawMode$1( geometry, drawMode ) { + function toTrianglesDrawMode( geometry, drawMode ) { if ( drawMode === three.TrianglesDrawMode ) { return geometry; @@ -9270,7 +9290,7 @@ } - let GLTFLoader$1 = class GLTFLoader extends three.Loader { + class GLTFLoader extends three.Loader { constructor( manager ) { @@ -9284,97 +9304,97 @@ this.register( function ( parser ) { - return new GLTFMaterialsClearcoatExtension$1( parser ); + return new GLTFMaterialsClearcoatExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureBasisUExtension$1( parser ); + return new GLTFTextureBasisUExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureWebPExtension$1( parser ); + return new GLTFTextureWebPExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureAVIFExtension$1( parser ); + return new GLTFTextureAVIFExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsSheenExtension$1( parser ); + return new GLTFMaterialsSheenExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsTransmissionExtension$1( parser ); + return new GLTFMaterialsTransmissionExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsVolumeExtension$1( parser ); + return new GLTFMaterialsVolumeExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsIorExtension$1( parser ); + return new GLTFMaterialsIorExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsEmissiveStrengthExtension$1( parser ); + return new GLTFMaterialsEmissiveStrengthExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsSpecularExtension$1( parser ); + return new GLTFMaterialsSpecularExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsIridescenceExtension$1( parser ); + return new GLTFMaterialsIridescenceExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsAnisotropyExtension$1( parser ); + return new GLTFMaterialsAnisotropyExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsBumpExtension$1( parser ); + return new GLTFMaterialsBumpExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFLightsExtension$1( parser ); + return new GLTFLightsExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMeshoptCompression$1( parser ); + return new GLTFMeshoptCompression( parser ); } ); this.register( function ( parser ) { - return new GLTFMeshGpuInstancing$1( parser ); + return new GLTFMeshGpuInstancing( parser ); } ); @@ -9523,11 +9543,11 @@ const magic = textDecoder.decode( new Uint8Array( data, 0, 4 ) ); - if ( magic === BINARY_EXTENSION_HEADER_MAGIC$1 ) { + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { try { - extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ] = new GLTFBinaryExtension$1( data ); + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); } catch ( error ) { @@ -9536,7 +9556,7 @@ } - json = JSON.parse( extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].content ); + json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content ); } else { @@ -9557,7 +9577,7 @@ } - const parser = new GLTFParser$1( json, { + const parser = new GLTFParser( json, { path: path || this.resourcePath || '', crossOrigin: this.crossOrigin, @@ -9595,20 +9615,20 @@ switch ( extensionName ) { - case EXTENSIONS$1.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension$1(); + case EXTENSIONS.KHR_MATERIALS_UNLIT: + extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); break; - case EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension$1( json, this.dracoLoader ); + case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: + extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); break; - case EXTENSIONS$1.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension$1(); + case EXTENSIONS.KHR_TEXTURE_TRANSFORM: + extensions[ extensionName ] = new GLTFTextureTransformExtension(); break; - case EXTENSIONS$1.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension$1(); + case EXTENSIONS.KHR_MESH_QUANTIZATION: + extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); break; default: @@ -9639,11 +9659,11 @@ } - }; + } /* GLTFREGISTRY */ - function GLTFRegistry$1() { + function GLTFRegistry() { let objects = {}; @@ -9681,7 +9701,7 @@ /********** EXTENSIONS ***********/ /*********************************/ - const EXTENSIONS$1 = { + const EXTENSIONS = { KHR_BINARY_GLTF: 'KHR_binary_glTF', KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', @@ -9710,12 +9730,12 @@ * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual */ - let GLTFLightsExtension$1 = class GLTFLightsExtension { + class GLTFLightsExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL; + this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; // Object3D instance caches this.cache = { refs: {}, uses: {} }; @@ -9800,7 +9820,7 @@ lightNode.decay = 2; - assignExtrasToUserData$1( lightNode, lightDef ); + assignExtrasToUserData( lightNode, lightDef ); if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; @@ -9841,18 +9861,18 @@ } - }; + } /** * Unlit Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit */ - let GLTFMaterialsUnlitExtension$1 = class GLTFMaterialsUnlitExtension { + class GLTFMaterialsUnlitExtension { constructor() { - this.name = EXTENSIONS$1.KHR_MATERIALS_UNLIT; + this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; } @@ -9894,19 +9914,19 @@ } - }; + } /** * Materials Emissive Strength Extension * * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md */ - let GLTFMaterialsEmissiveStrengthExtension$1 = class GLTFMaterialsEmissiveStrengthExtension { + class GLTFMaterialsEmissiveStrengthExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_EMISSIVE_STRENGTH; + this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH; } @@ -9933,19 +9953,19 @@ } - }; + } /** * Clearcoat Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat */ - let GLTFMaterialsClearcoatExtension$1 = class GLTFMaterialsClearcoatExtension { + class GLTFMaterialsClearcoatExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_CLEARCOAT; + this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; } @@ -10017,19 +10037,19 @@ } - }; + } /** * Iridescence Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence */ - let GLTFMaterialsIridescenceExtension$1 = class GLTFMaterialsIridescenceExtension { + class GLTFMaterialsIridescenceExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_IRIDESCENCE; + this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE; } @@ -10105,19 +10125,19 @@ } - }; + } /** * Sheen Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen */ - let GLTFMaterialsSheenExtension$1 = class GLTFMaterialsSheenExtension { + class GLTFMaterialsSheenExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_SHEEN; + this.name = EXTENSIONS.KHR_MATERIALS_SHEEN; } @@ -10180,7 +10200,7 @@ } - }; + } /** * Transmission Materials Extension @@ -10188,12 +10208,12 @@ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission * Draft: https://github.com/KhronosGroup/glTF/pull/1698 */ - let GLTFMaterialsTransmissionExtension$1 = class GLTFMaterialsTransmissionExtension { + class GLTFMaterialsTransmissionExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_TRANSMISSION; + this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; } @@ -10239,19 +10259,19 @@ } - }; + } /** * Materials Volume Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume */ - let GLTFMaterialsVolumeExtension$1 = class GLTFMaterialsVolumeExtension { + class GLTFMaterialsVolumeExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_VOLUME; + this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; } @@ -10298,19 +10318,19 @@ } - }; + } /** * Materials ior Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior */ - let GLTFMaterialsIorExtension$1 = class GLTFMaterialsIorExtension { + class GLTFMaterialsIorExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_IOR; + this.name = EXTENSIONS.KHR_MATERIALS_IOR; } @@ -10344,19 +10364,19 @@ } - }; + } /** * Materials specular Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular */ - let GLTFMaterialsSpecularExtension$1 = class GLTFMaterialsSpecularExtension { + class GLTFMaterialsSpecularExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_SPECULAR; + this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; } @@ -10407,7 +10427,7 @@ } - }; + } /** @@ -10415,12 +10435,12 @@ * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/EXT_materials_bump */ - let GLTFMaterialsBumpExtension$1 = class GLTFMaterialsBumpExtension { + class GLTFMaterialsBumpExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_MATERIALS_BUMP; + this.name = EXTENSIONS.EXT_MATERIALS_BUMP; } @@ -10462,19 +10482,19 @@ } - }; + } /** * Materials anisotropy Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_anisotropy */ - let GLTFMaterialsAnisotropyExtension$1 = class GLTFMaterialsAnisotropyExtension { + class GLTFMaterialsAnisotropyExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_ANISOTROPY; + this.name = EXTENSIONS.KHR_MATERIALS_ANISOTROPY; } @@ -10526,19 +10546,19 @@ } - }; + } /** * BasisU Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu */ - let GLTFTextureBasisUExtension$1 = class GLTFTextureBasisUExtension { + class GLTFTextureBasisUExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_TEXTURE_BASISU; + this.name = EXTENSIONS.KHR_TEXTURE_BASISU; } @@ -10577,19 +10597,19 @@ } - }; + } /** * WebP Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp */ - let GLTFTextureWebPExtension$1 = class GLTFTextureWebPExtension { + class GLTFTextureWebPExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_WEBP; + this.name = EXTENSIONS.EXT_TEXTURE_WEBP; this.isSupported = null; } @@ -10662,19 +10682,19 @@ } - }; + } /** * AVIF Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_avif */ - let GLTFTextureAVIFExtension$1 = class GLTFTextureAVIFExtension { + class GLTFTextureAVIFExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_AVIF; + this.name = EXTENSIONS.EXT_TEXTURE_AVIF; this.isSupported = null; } @@ -10745,18 +10765,18 @@ } - }; + } /** * meshopt BufferView Compression Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression */ - let GLTFMeshoptCompression$1 = class GLTFMeshoptCompression { + class GLTFMeshoptCompression { constructor( parser ) { - this.name = EXTENSIONS$1.EXT_MESHOPT_COMPRESSION; + this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; this.parser = parser; } @@ -10829,7 +10849,7 @@ } - }; + } /** * GPU Instancing Extension @@ -10837,11 +10857,11 @@ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing * */ - let GLTFMeshGpuInstancing$1 = class GLTFMeshGpuInstancing { + class GLTFMeshGpuInstancing { constructor( parser ) { - this.name = EXTENSIONS$1.EXT_MESH_GPU_INSTANCING; + this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING; this.parser = parser; } @@ -10864,9 +10884,9 @@ for ( const primitive of meshDef.primitives ) { - if ( primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLES && - primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLE_STRIP && - primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLE_FAN && + if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES && + primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP && + primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN && primitive.mode !== undefined ) { return null; @@ -10986,22 +11006,22 @@ } - }; + } /* BINARY EXTENSION */ - const BINARY_EXTENSION_HEADER_MAGIC$1 = 'glTF'; - const BINARY_EXTENSION_HEADER_LENGTH$1 = 12; - const BINARY_EXTENSION_CHUNK_TYPES$1 = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; + const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; + const BINARY_EXTENSION_HEADER_LENGTH = 12; + const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - let GLTFBinaryExtension$1 = class GLTFBinaryExtension { + class GLTFBinaryExtension { constructor( data ) { - this.name = EXTENSIONS$1.KHR_BINARY_GLTF; + this.name = EXTENSIONS.KHR_BINARY_GLTF; this.content = null; this.body = null; - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH$1 ); + const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); const textDecoder = new TextDecoder(); this.header = { @@ -11010,7 +11030,7 @@ length: headerView.getUint32( 8, true ) }; - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC$1 ) { + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); @@ -11020,8 +11040,8 @@ } - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH$1; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH$1 ); + const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; + const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); let chunkIndex = 0; while ( chunkIndex < chunkContentsLength ) { @@ -11032,14 +11052,14 @@ const chunkType = chunkView.getUint32( chunkIndex, true ); chunkIndex += 4; - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES$1.JSON ) { + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex, chunkLength ); + const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); this.content = textDecoder.decode( contentArray ); - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES$1.BIN ) { + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex; + const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; this.body = data.slice( byteOffset, byteOffset + chunkLength ); } @@ -11058,14 +11078,14 @@ } - }; + } /** * DRACO Mesh Compression Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression */ - let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtension { + class GLTFDracoMeshCompressionExtension { constructor( json, dracoLoader ) { @@ -11075,7 +11095,7 @@ } - this.name = EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION; + this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; this.json = json; this.dracoLoader = dracoLoader; this.dracoLoader.preload(); @@ -11094,7 +11114,7 @@ for ( const attributeName in gltfAttributeMap ) { - const threeAttributeName = ATTRIBUTES$1[ attributeName ] || attributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; @@ -11102,12 +11122,12 @@ for ( const attributeName in primitive.attributes ) { - const threeAttributeName = ATTRIBUTES$1[ attributeName ] || attributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); if ( gltfAttributeMap[ attributeName ] !== undefined ) { const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; attributeTypeMap[ threeAttributeName ] = componentType.name; attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; @@ -11141,18 +11161,18 @@ } - }; + } /** * Texture Transform Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform */ - let GLTFTextureTransformExtension$1 = class GLTFTextureTransformExtension { + class GLTFTextureTransformExtension { constructor() { - this.name = EXTENSIONS$1.KHR_TEXTURE_TRANSFORM; + this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; } @@ -11200,22 +11220,22 @@ } - }; + } /** * Mesh Quantization Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization */ - let GLTFMeshQuantizationExtension$1 = class GLTFMeshQuantizationExtension { + class GLTFMeshQuantizationExtension { constructor() { - this.name = EXTENSIONS$1.KHR_MESH_QUANTIZATION; + this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; } - }; + } /*********************************/ /********** INTERPOLATION ********/ @@ -11223,7 +11243,7 @@ // Spline Interpolation // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation - let GLTFCubicSplineInterpolant$1 = class GLTFCubicSplineInterpolant extends three.Interpolant { + class GLTFCubicSplineInterpolant extends three.Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { @@ -11291,23 +11311,23 @@ } - }; + } - const _q$1 = new three.Quaternion(); + const _q = new three.Quaternion(); - let GLTFCubicSplineQuaternionInterpolant$1 = class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant$1 { + class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { interpolate_( i1, t0, t, t1 ) { const result = super.interpolate_( i1, t0, t, t1 ); - _q$1.fromArray( result ).normalize().toArray( result ); + _q.fromArray( result ).normalize().toArray( result ); return result; } - }; + } /*********************************/ @@ -11316,7 +11336,7 @@ /* CONSTANTS */ - const WEBGL_CONSTANTS$1 = { + const WEBGL_CONSTANTS = { FLOAT: 5126, //FLOAT_MAT2: 35674, FLOAT_MAT3: 35675, @@ -11338,7 +11358,7 @@ UNSIGNED_SHORT: 5123 }; - const WEBGL_COMPONENT_TYPES$1 = { + const WEBGL_COMPONENT_TYPES = { 5120: Int8Array, 5121: Uint8Array, 5122: Int16Array, @@ -11347,7 +11367,7 @@ 5126: Float32Array }; - const WEBGL_FILTERS$1 = { + const WEBGL_FILTERS = { 9728: three.NearestFilter, 9729: three.LinearFilter, 9984: three.NearestMipmapNearestFilter, @@ -11356,13 +11376,13 @@ 9987: three.LinearMipmapLinearFilter }; - const WEBGL_WRAPPINGS$1 = { + const WEBGL_WRAPPINGS = { 33071: three.ClampToEdgeWrapping, 33648: three.MirroredRepeatWrapping, 10497: three.RepeatWrapping }; - const WEBGL_TYPE_SIZES$1 = { + const WEBGL_TYPE_SIZES = { 'SCALAR': 1, 'VEC2': 2, 'VEC3': 3, @@ -11372,7 +11392,7 @@ 'MAT4': 16 }; - const ATTRIBUTES$1 = { + const ATTRIBUTES = { POSITION: 'position', NORMAL: 'normal', TANGENT: 'tangent', @@ -11385,21 +11405,21 @@ JOINTS_0: 'skinIndex', }; - const PATH_PROPERTIES$1 = { + const PATH_PROPERTIES = { scale: 'scale', translation: 'position', rotation: 'quaternion', weights: 'morphTargetInfluences' }; - const INTERPOLATION$1 = { + const INTERPOLATION = { CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each // keyframe track will be initialized with a default interpolation type, then modified. LINEAR: three.InterpolateLinear, STEP: three.InterpolateDiscrete }; - const ALPHA_MODES$1 = { + const ALPHA_MODES = { OPAQUE: 'OPAQUE', MASK: 'MASK', BLEND: 'BLEND' @@ -11408,7 +11428,7 @@ /** * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material */ - function createDefaultMaterial$1( cache ) { + function createDefaultMaterial( cache ) { if ( cache[ 'DefaultMaterial' ] === undefined ) { @@ -11428,7 +11448,7 @@ } - function addUnknownExtensionsToUserData$1( knownExtensions, object, objectDef ) { + function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { // Add unknown glTF extensions to an object's userData. @@ -11449,7 +11469,7 @@ * @param {Object3D|Material|BufferGeometry} object * @param {GLTF.definition} gltfDef */ - function assignExtrasToUserData$1( object, gltfDef ) { + function assignExtrasToUserData( object, gltfDef ) { if ( gltfDef.extras !== undefined ) { @@ -11471,7 +11491,7 @@ * @param {GLTFParser} parser * @return {Promise} */ - function addMorphTargets$1( geometry, targets, parser ) { + function addMorphTargets( geometry, targets, parser ) { let hasMorphPosition = false; let hasMorphNormal = false; @@ -11556,7 +11576,7 @@ * @param {Mesh} mesh * @param {GLTF.Mesh} meshDef */ - function updateMorphTargets$1( mesh, meshDef ) { + function updateMorphTargets( mesh, meshDef ) { mesh.updateMorphTargets(); @@ -11591,21 +11611,21 @@ } - function createPrimitiveKey$1( primitiveDef ) { + function createPrimitiveKey( primitiveDef ) { let geometryKey; - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ]; + const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; if ( dracoExtension ) { geometryKey = 'draco:' + dracoExtension.bufferView + ':' + dracoExtension.indices - + ':' + createAttributesKey$1( dracoExtension.attributes ); + + ':' + createAttributesKey( dracoExtension.attributes ); } else { - geometryKey = primitiveDef.indices + ':' + createAttributesKey$1( primitiveDef.attributes ) + ':' + primitiveDef.mode; + geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; } @@ -11613,7 +11633,7 @@ for ( let i = 0, il = primitiveDef.targets.length; i < il; i ++ ) { - geometryKey += ':' + createAttributesKey$1( primitiveDef.targets[ i ] ); + geometryKey += ':' + createAttributesKey( primitiveDef.targets[ i ] ); } @@ -11623,7 +11643,7 @@ } - function createAttributesKey$1( attributes ) { + function createAttributesKey( attributes ) { let attributesKey = ''; @@ -11639,7 +11659,7 @@ } - function getNormalizedComponentScale$1( constructor ) { + function getNormalizedComponentScale( constructor ) { // Reference: // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data @@ -11665,7 +11685,7 @@ } - function getImageURIMimeType$1( uri ) { + function getImageURIMimeType( uri ) { if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; @@ -11674,11 +11694,11 @@ } - const _identityMatrix$1 = new three.Matrix4(); + const _identityMatrix = new three.Matrix4(); /* GLTF PARSER */ - let GLTFParser$1 = class GLTFParser { + class GLTFParser { constructor( json = {}, options = {} ) { @@ -11688,7 +11708,7 @@ this.options = options; // loader object cache - this.cache = new GLTFRegistry$1(); + this.cache = new GLTFRegistry(); // associations between Three.js objects and glTF elements this.associations = new Map(); @@ -11804,9 +11824,9 @@ userData: {} }; - addUnknownExtensionsToUserData$1( extensions, result, json ); + addUnknownExtensionsToUserData( extensions, result, json ); - assignExtrasToUserData$1( result, json ); + assignExtrasToUserData( result, json ); return Promise.all( parser._invokeAll( function ( ext ) { @@ -12124,7 +12144,7 @@ // If present, GLB container is required to be the first buffer. if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - return Promise.resolve( this.extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].body ); + return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); } @@ -12175,8 +12195,8 @@ if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; const normalized = accessorDef.normalized === true; const array = new TypedArray( accessorDef.count * itemSize ); @@ -12207,8 +12227,8 @@ const bufferView = bufferViews[ 0 ]; - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. const elementBytes = TypedArray.BYTES_PER_ELEMENT; @@ -12259,8 +12279,8 @@ // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors if ( accessorDef.sparse !== undefined ) { - const itemSizeIndices = WEBGL_TYPE_SIZES$1.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES$1[ accessorDef.sparse.indices.componentType ]; + const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; + const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; @@ -12353,10 +12373,10 @@ const samplers = json.samplers || {}; const sampler = samplers[ textureDef.sampler ] || {}; - texture.magFilter = WEBGL_FILTERS$1[ sampler.magFilter ] || three.LinearFilter; - texture.minFilter = WEBGL_FILTERS$1[ sampler.minFilter ] || three.LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS$1[ sampler.wrapS ] || three.RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS$1[ sampler.wrapT ] || three.RepeatWrapping; + texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || three.LinearFilter; + texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || three.LinearMipmapLinearFilter; + texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || three.RepeatWrapping; + texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || three.RepeatWrapping; parser.associations.set( texture, { textures: textureIndex } ); @@ -12445,7 +12465,7 @@ } - texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType$1( sourceDef.uri ); + texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); return texture; @@ -12481,14 +12501,14 @@ } - if ( parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] ) { + if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] : undefined; + const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; if ( transform ) { const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); + texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); parser.associations.set( texture, gltfReference ); } @@ -12631,9 +12651,9 @@ const pending = []; - if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ] ) { + if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - const kmuExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ]; + const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; materialType = kmuExtension.getMaterialType(); pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); @@ -12692,9 +12712,9 @@ } - const alphaMode = materialDef.alphaMode || ALPHA_MODES$1.OPAQUE; + const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - if ( alphaMode === ALPHA_MODES$1.BLEND ) { + if ( alphaMode === ALPHA_MODES.BLEND ) { materialParams.transparent = true; @@ -12705,7 +12725,7 @@ materialParams.transparent = false; - if ( alphaMode === ALPHA_MODES$1.MASK ) { + if ( alphaMode === ALPHA_MODES.MASK ) { materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; @@ -12760,11 +12780,11 @@ if ( materialDef.name ) material.name = materialDef.name; - assignExtrasToUserData$1( material, materialDef ); + assignExtrasToUserData( material, materialDef ); parser.associations.set( material, { materials: materialIndex } ); - if ( materialDef.extensions ) addUnknownExtensionsToUserData$1( extensions, material, materialDef ); + if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); return material; @@ -12807,11 +12827,11 @@ function createDracoPrimitive( primitive ) { - return extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] + return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] .decodePrimitive( primitive, parser ) .then( function ( geometry ) { - return addPrimitiveAttributes$1( geometry, primitive, parser ); + return addPrimitiveAttributes( geometry, primitive, parser ); } ); @@ -12822,7 +12842,7 @@ for ( let i = 0, il = primitives.length; i < il; i ++ ) { const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey$1( primitive ); + const cacheKey = createPrimitiveKey( primitive ); // See if we've already created this geometry const cached = cache[ cacheKey ]; @@ -12836,7 +12856,7 @@ let geometryPromise; - if ( primitive.extensions && primitive.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] ) { + if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { // Use DRACO geometry if available geometryPromise = createDracoPrimitive( primitive ); @@ -12844,7 +12864,7 @@ } else { // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes$1( new three.BufferGeometry(), primitive, parser ); + geometryPromise = addPrimitiveAttributes( new three.BufferGeometry(), primitive, parser ); } @@ -12880,7 +12900,7 @@ for ( let i = 0, il = primitives.length; i < il; i ++ ) { const material = primitives[ i ].material === undefined - ? createDefaultMaterial$1( this.cache ) + ? createDefaultMaterial( this.cache ) : this.getDependency( 'material', primitives[ i ].material ); pending.push( material ); @@ -12907,9 +12927,9 @@ const material = materials[ i ]; - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || primitive.mode === undefined ) { // .isSkinnedMesh isn't in glTF spec. See ._markDefs() @@ -12924,29 +12944,29 @@ } - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - mesh.geometry = toTrianglesDrawMode$1( mesh.geometry, three.TriangleStripDrawMode ); + mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleStripDrawMode ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - mesh.geometry = toTrianglesDrawMode$1( mesh.geometry, three.TriangleFanDrawMode ); + mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleFanDrawMode ); } - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { mesh = new three.LineSegments( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { mesh = new three.Line( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { mesh = new three.LineLoop( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { mesh = new three.Points( geometry, material ); @@ -12958,15 +12978,15 @@ if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - updateMorphTargets$1( mesh, meshDef ); + updateMorphTargets( mesh, meshDef ); } mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); - assignExtrasToUserData$1( mesh, meshDef ); + assignExtrasToUserData( mesh, meshDef ); - if ( primitive.extensions ) addUnknownExtensionsToUserData$1( extensions, mesh, primitive ); + if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); parser.assignFinalMaterial( mesh ); @@ -12985,7 +13005,7 @@ if ( meshes.length === 1 ) { - if ( meshDef.extensions ) addUnknownExtensionsToUserData$1( extensions, meshes[ 0 ], meshDef ); + if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, meshes[ 0 ], meshDef ); return meshes[ 0 ]; @@ -12993,7 +13013,7 @@ const group = new three.Group(); - if ( meshDef.extensions ) addUnknownExtensionsToUserData$1( extensions, group, meshDef ); + if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, group, meshDef ); parser.associations.set( group, { meshes: meshIndex } ); @@ -13037,7 +13057,7 @@ if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); - assignExtrasToUserData$1( camera, cameraDef ); + assignExtrasToUserData( camera, cameraDef ); return Promise.resolve( camera ); @@ -13281,7 +13301,7 @@ if ( ! mesh.isSkinnedMesh ) return; - mesh.bind( skeleton, _identityMatrix$1 ); + mesh.bind( skeleton, _identityMatrix ); } ); @@ -13395,9 +13415,9 @@ } - assignExtrasToUserData$1( node, nodeDef ); + assignExtrasToUserData( node, nodeDef ); - if ( nodeDef.extensions ) addUnknownExtensionsToUserData$1( extensions, node, nodeDef ); + if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); if ( nodeDef.matrix !== undefined ) { @@ -13459,9 +13479,9 @@ const scene = new three.Group(); if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); - assignExtrasToUserData$1( scene, sceneDef ); + assignExtrasToUserData( scene, sceneDef ); - if ( sceneDef.extensions ) addUnknownExtensionsToUserData$1( extensions, scene, sceneDef ); + if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); const nodeIds = sceneDef.nodes || []; @@ -13528,7 +13548,7 @@ const targetName = node.name ? node.name : node.uuid; const targetNames = []; - if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { + if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { node.traverse( function ( object ) { @@ -13548,20 +13568,20 @@ let TypedKeyframeTrack; - switch ( PATH_PROPERTIES$1[ target.path ] ) { + switch ( PATH_PROPERTIES[ target.path ] ) { - case PATH_PROPERTIES$1.weights: + case PATH_PROPERTIES.weights: TypedKeyframeTrack = three.NumberKeyframeTrack; break; - case PATH_PROPERTIES$1.rotation: + case PATH_PROPERTIES.rotation: TypedKeyframeTrack = three.QuaternionKeyframeTrack; break; - case PATH_PROPERTIES$1.position: - case PATH_PROPERTIES$1.scale: + case PATH_PROPERTIES.position: + case PATH_PROPERTIES.scale: TypedKeyframeTrack = three.VectorKeyframeTrack; break; @@ -13585,7 +13605,7 @@ } - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION$1[ sampler.interpolation ] : three.InterpolateLinear; + const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : three.InterpolateLinear; const outputArray = this._getArrayFromAccessor( outputAccessor ); @@ -13593,7 +13613,7 @@ for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], + targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], inputAccessor.array, outputArray, interpolation @@ -13620,7 +13640,7 @@ if ( accessor.normalized ) { - const scale = getNormalizedComponentScale$1( outputArray.constructor ); + const scale = getNormalizedComponentScale( outputArray.constructor ); const scaled = new Float32Array( outputArray.length ); for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { @@ -13645,7 +13665,7 @@ // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() // must be divided by three to get the interpolant's sampleSize argument. - const interpolantType = ( this instanceof three.QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant$1 : GLTFCubicSplineInterpolant$1; + const interpolantType = ( this instanceof three.QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); @@ -13656,14 +13676,14 @@ } - }; + } /** * @param {BufferGeometry} geometry * @param {GLTF.Primitive} primitiveDef * @param {GLTFParser} parser */ - function computeBounds$1( geometry, primitiveDef, parser ) { + function computeBounds( geometry, primitiveDef, parser ) { const attributes = primitiveDef.attributes; @@ -13687,7 +13707,7 @@ if ( accessor.normalized ) { - const boxScale = getNormalizedComponentScale$1( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); box.min.multiplyScalar( boxScale ); box.max.multiplyScalar( boxScale ); @@ -13734,7 +13754,7 @@ if ( accessor.normalized ) { - const boxScale = getNormalizedComponentScale$1( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); vector.multiplyScalar( boxScale ); } @@ -13773,7 +13793,7 @@ * @param {GLTFParser} parser * @return {Promise} */ - function addPrimitiveAttributes$1( geometry, primitiveDef, parser ) { + function addPrimitiveAttributes( geometry, primitiveDef, parser ) { const attributes = primitiveDef.attributes; @@ -13792,7 +13812,7 @@ for ( const gltfAttributeName in attributes ) { - const threeAttributeName = ATTRIBUTES$1[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); // Skip attributes already provided by e.g. Draco extension. if ( threeAttributeName in geometry.attributes ) continue; @@ -13815,23 +13835,23 @@ if ( three.ColorManagement.workingColorSpace !== three.LinearSRGBColorSpace && 'COLOR_0' in attributes ) ; - assignExtrasToUserData$1( geometry, primitiveDef ); + assignExtrasToUserData( geometry, primitiveDef ); - computeBounds$1( geometry, primitiveDef, parser ); + computeBounds( geometry, primitiveDef, parser ); return Promise.all( pending ).then( function () { return primitiveDef.targets !== undefined - ? addMorphTargets$1( geometry, primitiveDef.targets, parser ) + ? addMorphTargets( geometry, primitiveDef.targets, parser ) : geometry; } ); } - const _taskCache$2 = new WeakMap(); + const _taskCache$1 = new WeakMap(); - let DRACOLoader$1 = class DRACOLoader extends three.Loader { + class DRACOLoader extends three.Loader { constructor( manager ) { @@ -13929,9 +13949,9 @@ // Check for an existing task using this buffer. A transferred buffer cannot be transferred // again from this thread. - if ( _taskCache$2.has( buffer ) ) { + if ( _taskCache$1.has( buffer ) ) { - const cachedTask = _taskCache$2.get( buffer ); + const cachedTask = _taskCache$1.get( buffer ); if ( cachedTask.key === taskKey ) { @@ -13997,7 +14017,7 @@ } ); // Cache the task result. - _taskCache$2.set( buffer, { + _taskCache$1.set( buffer, { key: taskKey, promise: geometryPending @@ -14115,7 +14135,7 @@ } - const fn = DRACOWorker$1.toString(); + const fn = DRACOWorker.toString(); const body = [ '/* draco decoder */', @@ -14218,11 +14238,11 @@ } - }; + } /* WEB WORKER */ - function DRACOWorker$1() { + function DRACOWorker() { let decoderConfig; let decoderPending; @@ -18441,10 +18461,10 @@ } class Lorder{ constructor() { - this.gltfLoader = new GLTFLoader$1(); + this.gltfLoader = new GLTFLoader(); // Optional: Provide a DRACOLoader instance to decode compressed mesh data - const dracoLoader = new DRACOLoader$1(); + const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath( Config.DRACOPath ); this.gltfLoader.setDRACOLoader( dracoLoader ); this.objLoader = new OBJLoader(); // obj模型 @@ -18690,5725 +18710,471 @@ uniform float mieCoefficient; uniform vec3 up; - varying vec3 vWorldPosition; - varying vec3 vSunDirection; - varying float vSunfade; - varying vec3 vBetaR; - varying vec3 vBetaM; - varying float vSunE; - - // constants for atmospheric scattering - const float e = 2.71828182845904523536028747135266249775724709369995957; - const float pi = 3.141592653589793238462643383279502884197169; - - // wavelength of used primaries, according to preetham - const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); - // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: - // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) - const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); - - // mie stuff - // K coefficient for the primaries - const float v = 4.0; - const vec3 K = vec3( 0.686, 0.678, 0.666 ); - // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K - const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); - - // earth shadow hack - // cutoffAngle = pi / 1.95; - const float cutoffAngle = 1.6110731556870734; - const float steepness = 1.5; - const float EE = 1000.0; - - float sunIntensity( float zenithAngleCos ) { - zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); - return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); - } - - vec3 totalMie( float T ) { - float c = ( 0.2 * T ) * 10E-18; - return 0.434 * c * MieConst; - } - - void main() { - - vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); - vWorldPosition = worldPosition.xyz; - - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - gl_Position.z = gl_Position.w; // set z to camera.far - - vSunDirection = normalize( sunPosition ); - - vSunE = sunIntensity( dot( vSunDirection, up ) ); - - vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); - - float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); - - // extinction (absorbtion + out scattering) - // rayleigh coefficients - vBetaR = totalRayleigh * rayleighCoefficient; - - // mie coefficients - vBetaM = totalMie( turbidity ) * mieCoefficient; - - }`, - - fragmentShader: /* glsl */` - varying vec3 vWorldPosition; - varying vec3 vSunDirection; - varying float vSunfade; - varying vec3 vBetaR; - varying vec3 vBetaM; - varying float vSunE; - - uniform float mieDirectionalG; - uniform vec3 up; - - // constants for atmospheric scattering - const float pi = 3.141592653589793238462643383279502884197169; - - const float n = 1.0003; // refractive index of air - const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius) - - // optical length at zenith for molecules - const float rayleighZenithLength = 8.4E3; - const float mieZenithLength = 1.25E3; - // 66 arc seconds -> degrees, and the cosine of that - const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; - - // 3.0 / ( 16.0 * pi ) - const float THREE_OVER_SIXTEENPI = 0.05968310365946075; - // 1.0 / ( 4.0 * pi ) - const float ONE_OVER_FOURPI = 0.07957747154594767; - - float rayleighPhase( float cosTheta ) { - return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); - } - - float hgPhase( float cosTheta, float g ) { - float g2 = pow( g, 2.0 ); - float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); - return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse ); - } - - void main() { - - vec3 direction = normalize( vWorldPosition - cameraPosition ); - - // optical length - // cutoff angle at 90 to avoid singularity in next formula. - float zenithAngle = acos( max( 0.0, dot( up, direction ) ) ); - float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); - float sR = rayleighZenithLength * inverse; - float sM = mieZenithLength * inverse; - - // combined extinction factor - vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); - - // in scattering - float cosTheta = dot( direction, vSunDirection ); - - float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); - vec3 betaRTheta = vBetaR * rPhase; - - float mPhase = hgPhase( cosTheta, mieDirectionalG ); - vec3 betaMTheta = vBetaM * mPhase; - - vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); - Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); - - // nightsky - float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] - float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] - vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); - vec3 L0 = vec3( 0.1 ) * Fex; - - // composition + solar disc - float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); - L0 += ( vSunE * 19000.0 * Fex ) * sundisk; - - vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); - - vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); - - gl_FragColor = vec4( retColor, 1.0 ); - - #include - #include - - }` - - }; - - // 多个canvas并没有id,只有父节点 - - - class Layer extends BasLayer{ - id; // 唯一标识 - layerContainer; // div#layer 容器 - zIndex=1;//默认为1 - opacity=1;//默认为1 - canvas;//canvas - dispose = false; - renderer;//canvas上下文 - scene;//场景 - visible=true;//是否可见 - mapView;//地图视图 - camera;//相机 - controls;//控件 - animateId;//动画事件id - base = false; // 是否为底图 - ambientLight; // 环境光 - directionalLight; // 方向光 - modelLayer = false; // 模型图层 - imageLayer = false; // 影像图层 - vectorLayer = false; // 矢量图层,如路网、行政区划,地名等图层 - waters = []; // 水面集合 - constructor(id, layerContainer, canvas, mapView, plane = true, camera = new three.PerspectiveCamera(80, 1, 0.1, 1e12)) { - super(); - this.id = id; - this.layerContainer = layerContainer; - this.canvas = canvas; - this.renderer = new three.WebGLRenderer({ - canvas: this.canvas, - antialias: true, - alpha: true, - logarithmicDepthBuffer: true, - precision: "highp", - }); - this.renderer.sortObjects = true; - this.renderer.setPixelRatio(window.devicePixelRatio); - this.renderer.setClearColor(0xFFFFFF, 0.0); - this.scene = new three.Scene(); - this.mapView = mapView; - this.camera = camera; - if(this.mapView){ - this.scene.add(this.mapView); - this.mapView.updateMatrixWorld(true); - } - if (plane){ - this.controls = new MapControls(this.camera, this.canvas); - this.controls.minDistance = 1e1; - this.controls.zoomSpeed = 2.0; - } else { - this.controls = new OrbitControls(this.camera, this.canvas); - this.controls.enablePan = false; - this.controls.minDistance = UnitsUtils.EARTH_RADIUS + 2; - this.controls.maxDistance = UnitsUtils.EARTH_RADIUS * 1e1; - } - this._raycaster = new three.Raycaster(); - if(Config.outLine.on){ - this.effectOutline = new EffectOutline(this.renderer, this.scene, this.camera, this.canvas.width, this.canvas.height); - } - if (Config.layer.map.ambientLight.add){ - this.scene.add(new three.AmbientLight(Config.layer.map.ambientLight.color, Config.layer.map.ambientLight.intensity)); - } - if (Config.layer.map.directionalLight.add){ - this.scene.add(new three.DirectionalLight(Config.layer.map.directionalLight.color, Config.layer.map.directionalLight.intensity)); - } - if (Config.layer.map.pointLight.add){ - let pointLight = new three.PointLight(Config.layer.map.pointLight.color, Config.layer.map.pointLight.intensity, Config.layer.map.pointLight.distance); - pointLight.position.set(...Config.layer.map.pointLight.position); - this.scene.add(pointLight); - } - } - - moveTo(lat, lon, height = 38472.48763833733){ - // var coords = UnitsUtils.datumsToSpherical(44.266119,90.139228); - var coords = UnitsUtils.datumsToSpherical(lat,lon); - this.camera.position.set(coords.x, height, -coords.y); - this.controls.target.set(this.camera.position.x, 0, this.camera.position.z); - } - - moveToByCoords(coords){ - let offset = 50; - this.camera.position.set(coords.x, coords.y+offset, coords.z); - this.controls.target.set(coords.x, coords.y, coords.z); - } - - moveToByLL(lat, lon, distance = 384720){ - let dir = UnitsUtils.datumsToVector(lat, lon); - dir.multiplyScalar(UnitsUtils.EARTH_RADIUS + distance); - this.camera.position.copy(dir); - } - - - on(eventName, callback){ - this.listener.on(eventName, callback); - } - - setSceneBackground(color) { - this.scene.background = color; - } - - clearSceneBackground() { - this.scene.background = null; - } - - // 可用于添加灯光mesh等元素 - add(Object3D) { - if(Object3D ==null || Object3D ==undefined){ - return; - } - this.scene.add(Object3D); - } - - - remove(Object3D) { - if(Object3D ==null || Object3D ==undefined){ - return; - } - this.scene.remove(Object3D); - } - - openWaterConfig(){ - /** - * 打开渲染水系配置 - */ - // this.renderer.setPixelRatio( window.devicePixelRatio ); - this.renderer.toneMapping = three.ACESFilmicToneMapping; - this.renderer.toneMappingExposure = 0.5; - - // 添加天空 - this.sky = new Sky(); - this.sky.translateX = true; - this.sky.translateY = true; - this.sky.translateZ = true; - this.sky.rotateX = Math.PI / 2; - this.sky.scale.setScalar( Config.EARTH_RADIUS * 2 * Math.PI ); // 天空放大倍数 - this.scene.add( this.sky ); - const skyUniforms = this.sky.material.uniforms; - // 天空的配置 - skyUniforms[ 'turbidity' ].value = 10; - skyUniforms[ 'rayleigh' ].value = 2; - skyUniforms[ 'mieCoefficient' ].value = 0.005; - skyUniforms[ 'mieDirectionalG' ].value = 0.8; - // 旋转 设置为y朝上 - // sky.material.uniforms["up"].value = new THREE.Vector3(0, 1, 0); - - // 天空映射, 更新太阳位置 - this.pmremGenerator = new three.PMREMGenerator( this.renderer ); - this.sceneEnv = new three.Scene(); - this.renderTarget = null; - this.sun = new three.Vector3(); - this.updateSun(Config.SUNDEGREE, Config.SUNAZIMUTH); - } - - updateSun(elevation, azimuth) { - - const phi = three.MathUtils.degToRad( 90 - elevation ); - const theta = three.MathUtils.degToRad( azimuth ); - - this.sun.setFromSphericalCoords( 1, phi, theta ); - - this.sky.material.uniforms[ 'sunPosition' ].value.copy( this.sun ); - for (let water of this.waters){ - water.material.uniforms[ 'sunDirection' ].value.copy( this.sun ).normalize(); - } - if ( this.renderTarget !== null ) this.renderTarget.dispose(); - - this.sceneEnv.add( this.sky ); - this.renderTarget = this.pmremGenerator.fromScene( this.sceneEnv ); - this.scene.add( this.sky ); - - this.scene.environment = this.renderTarget.texture; - - - } - - /** - * 添加水系 - * @param {*} water - * @returns - */ - addWater(water) { - if(water ==null || water ==undefined){ - return; - } - this.scene.add(water); - this.waters.push(water); - } - - removeWater(water) { - if(water ==null || water ==undefined){ - return; - } - this.scene.remove(water); - let index = this.waters.indexOf(water); - if (index > -1) { - this.waters.splice(index, 1); - } - } - - setVisible(visible) { - this.visible = this.visible; - this.layerContainer.style.display = visible ? 'block' : 'none'; - } - /** - * @deprecated 不建议用 - * @param {number} opacity - */ - setOpacity(opacity) { - this.opacity = opacity; - this.layerContainer.style.opacity = opacity; - } - - /** - * 修改显示层级,默认越靠下的dom元素,显示上越靠上 - * @param {number} zIndex - */ - setZIndex(zIndex) { - this.zIndex = zIndex; - this.layerContainer.style.zIndex = this.zIndex; - } - - dispose() { - Element.removeLayer(id); - this.dispose = true; - this.animateId && cancelAnimationFrame(this.animateId); - this.mapView.root.dispose(); - this.mapView.dispose(); - } - - selectModel(insect){ - this.effectOutline.selectModel(insect); - } - - resize(){ - var width = window.innerWidth; - var height = window.innerHeight; - this.renderer.setSize(width, height); - this.camera.aspect = width / height; - this.camera.updateProjectionMatrix(); - if(Config.outLine.on){ - this.effectOutline.resize(width, height); - } - } - - animate(){ - this.animateId = requestAnimationFrame(this.animate.bind(this)); - if(this.base){ - this.controls.update(); - } - if (this.base){ - update(); //目前只有在基础地图中添加相机移动的动画,所以暂且只在base地图中进行渲染,后期可改成每个图层都进行渲染,或者必要时进行渲染。 - } - for(let water of this.waters){ - water.material.uniforms[ 'time' ].value += 1.0 / 60.0; - } - if (Config.outLine.on){ - this.effectOutline.render(); // 合成器渲染 - } else { - this.renderer.autoClear = true; - } - this.renderer.render(this.scene, this.camera); - } - - _raycast(meshes, recursive, faceExclude) { - const isects = this._raycaster.intersectObjects(meshes, recursive); - if (faceExclude) { - for (let i = 0; i < isects.length; i++) { - if (isects[i].face !== faceExclude) { - return isects[i]; - } - } - return null; - } - return isects.length > 0 ? isects[0] : null; - } - - _raycastFromMouse(mx, my, width, height, cam, meshes, recursive=false) { - const mouse = new three.Vector2( // normalized (-1 to +1) - (mx / width) * 2 - 1, - - (my / height) * 2 + 1); - // https://threejs.org/docs/#api/core/Raycaster - // update the picking ray with the camera and mouse position - this._raycaster.setFromCamera(mouse, cam); - return this._raycast(meshes, recursive, null); - } - - /** - * - * @param {*} mx 屏幕坐标x - * @param {*} my 屏幕坐标y - * @param {boolean} recursive 是否检查子节点,true 递归检查 - * @returns mesh - */ - raycastFromMouse(mx, my, recursive=false) { - //---- NG: 2x when starting with Chrome's inspector mobile - // const {width, height} = this.renderer.domElement; - // const {width, height} = this.canvas; - //---- OK - const {clientWidth, clientHeight} = this.canvas; - - return this._raycastFromMouse( - mx, my, clientWidth, clientHeight, this.camera, - this.mapView.children, recursive); - } - - insectALL(mx, my, recursive=false) { - const {clientWidth, clientHeight} = this.canvas; - - return this._raycastFromMouse( - mx, my, clientWidth, clientHeight, this.camera, - this.scene.children, recursive); - } - - } - - /** - * @param {BufferGeometry} geometry - * @param {number} drawMode - * @return {BufferGeometry} - */ - function toTrianglesDrawMode( geometry, drawMode ) { - - if ( drawMode === three.TrianglesDrawMode ) { - return geometry; - - } - - if ( drawMode === three.TriangleFanDrawMode || drawMode === three.TriangleStripDrawMode ) { - - let index = geometry.getIndex(); - - // generate index if not present - - if ( index === null ) { - - const indices = []; - - const position = geometry.getAttribute( 'position' ); - - if ( position !== undefined ) { - - for ( let i = 0; i < position.count; i ++ ) { - - indices.push( i ); - - } - - geometry.setIndex( indices ); - index = geometry.getIndex(); - - } else { - return geometry; - - } - - } - - // - - const numberOfTriangles = index.count - 2; - const newIndices = []; - - if ( drawMode === three.TriangleFanDrawMode ) { - - // gl.TRIANGLE_FAN - - for ( let i = 1; i <= numberOfTriangles; i ++ ) { - - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - - } - - } else { - - // gl.TRIANGLE_STRIP - - for ( let i = 0; i < numberOfTriangles; i ++ ) { - - if ( i % 2 === 0 ) { - - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); - - } else { - - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); - - } - - } - - } - - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) ; - - // build final geometry - - const newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); - newGeometry.clearGroups(); - - return newGeometry; - - } else { - return geometry; - - } - - } - - class GLTFLoader extends three.Loader { - - constructor( manager ) { - - super( manager ); - - this.dracoLoader = null; - this.ktx2Loader = null; - this.meshoptDecoder = null; - - this.pluginCallbacks = []; - - this.register( function ( parser ) { - - return new GLTFMaterialsClearcoatExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureBasisUExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureWebPExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureAVIFExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsSheenExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsTransmissionExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsVolumeExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsIorExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsEmissiveStrengthExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsSpecularExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsIridescenceExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsAnisotropyExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsBumpExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFLightsExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMeshoptCompression( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMeshGpuInstancing( parser ); - - } ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - let resourcePath; - - if ( this.resourcePath !== '' ) { - - resourcePath = this.resourcePath; - - } else if ( this.path !== '' ) { - - // If a base path is set, resources will be relative paths from that plus the relative path of the gltf file - // Example path = 'https://my-cnd-server.com/', url = 'assets/models/model.gltf' - // resourcePath = 'https://my-cnd-server.com/assets/models/' - // referenced resource 'model.bin' will be loaded from 'https://my-cnd-server.com/assets/models/model.bin' - // referenced resource '../textures/texture.png' will be loaded from 'https://my-cnd-server.com/assets/textures/texture.png' - const relativeUrl = three.LoaderUtils.extractUrlBase( url ); - resourcePath = three.LoaderUtils.resolveURL( relativeUrl, this.path ); - - } else { - - resourcePath = three.LoaderUtils.extractUrlBase( url ); - - } - - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - this.manager.itemStart( url ); - - const _onError = function ( e ) { - - if ( onError ) { - - onError( e ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }; - - const loader = new three.FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, function ( data ) { - - try { - - scope.parse( data, resourcePath, function ( gltf ) { - - onLoad( gltf ); - - scope.manager.itemEnd( url ); - - }, _onError ); - - } catch ( e ) { - - _onError( e ); - - } - - }, onProgress, _onError ); - - } - - setDRACOLoader( dracoLoader ) { - - this.dracoLoader = dracoLoader; - return this; - - } - - setDDSLoader() { - - throw new Error( - - 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' - - ); - - } - - setKTX2Loader( ktx2Loader ) { - - this.ktx2Loader = ktx2Loader; - return this; - - } - - setMeshoptDecoder( meshoptDecoder ) { - - this.meshoptDecoder = meshoptDecoder; - return this; - - } - - register( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - - this.pluginCallbacks.push( callback ); - - } - - return this; - - } - - unregister( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - - } - - return this; - - } - - parse( data, path, onLoad, onError ) { - - let json; - const extensions = {}; - const plugins = {}; - const textDecoder = new TextDecoder(); - - if ( typeof data === 'string' ) { - - json = JSON.parse( data ); - - } else if ( data instanceof ArrayBuffer ) { - - const magic = textDecoder.decode( new Uint8Array( data, 0, 4 ) ); - - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - - try { - - extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); - - } catch ( error ) { - - if ( onError ) onError( error ); - return; - - } - - json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content ); - - } else { - - json = JSON.parse( textDecoder.decode( data ) ); - - } - - } else { - - json = data; - - } - - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { - - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; - - } - - const parser = new GLTFParser( json, { - - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - requestHeader: this.requestHeader, - manager: this.manager, - ktx2Loader: this.ktx2Loader, - meshoptDecoder: this.meshoptDecoder - - } ); - - parser.fileLoader.setRequestHeader( this.requestHeader ); - - for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { - - const plugin = this.pluginCallbacks[ i ]( parser ); - - if ( ! plugin.name ) ; - - plugins[ plugin.name ] = plugin; - - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; - - } - - if ( json.extensionsUsed ) { - - for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { - - const extensionName = json.extensionsUsed[ i ]; - const extensionsRequired = json.extensionsRequired || []; - - switch ( extensionName ) { - - case EXTENSIONS.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); - break; - - case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; - - case EXTENSIONS.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; - - case EXTENSIONS.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; - - default: - - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) ; - - } - - } - - } - - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); - - } - - parseAsync( data, path ) { - - const scope = this; - - return new Promise( function ( resolve, reject ) { - - scope.parse( data, path, resolve, reject ); - - } ); - - } - - } - - /* GLTFREGISTRY */ - - function GLTFRegistry() { - - let objects = {}; - - return { - - get: function ( key ) { - - return objects[ key ]; - - }, - - add: function ( key, object ) { - - objects[ key ] = object; - - }, - - remove: function ( key ) { - - delete objects[ key ]; - - }, - - removeAll: function () { - - objects = {}; - - } - - }; - - } - - /*********************************/ - /********** EXTENSIONS ***********/ - /*********************************/ - - const EXTENSIONS = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_IOR: 'KHR_materials_ior', - KHR_MATERIALS_SHEEN: 'KHR_materials_sheen', - KHR_MATERIALS_SPECULAR: 'KHR_materials_specular', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_IRIDESCENCE: 'KHR_materials_iridescence', - KHR_MATERIALS_ANISOTROPY: 'KHR_materials_anisotropy', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_MATERIALS_VOLUME: 'KHR_materials_volume', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - KHR_MATERIALS_EMISSIVE_STRENGTH: 'KHR_materials_emissive_strength', - EXT_MATERIALS_BUMP: 'EXT_materials_bump', - EXT_TEXTURE_WEBP: 'EXT_texture_webp', - EXT_TEXTURE_AVIF: 'EXT_texture_avif', - EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression', - EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing' - }; - - /** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ - class GLTFLightsExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; - - // Object3D instance caches - this.cache = { refs: {}, uses: {} }; - - } - - _markDefs() { - - const parser = this.parser; - const nodeDefs = this.parser.json.nodes || []; - - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.extensions - && nodeDef.extensions[ this.name ] - && nodeDef.extensions[ this.name ].light !== undefined ) { - - parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); - - } - - } - - } - - _loadLight( lightIndex ) { - - const parser = this.parser; - const cacheKey = 'light:' + lightIndex; - let dependency = parser.cache.get( cacheKey ); - - if ( dependency ) return dependency; - - const json = parser.json; - const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; - const lightDefs = extensions.lights || []; - const lightDef = lightDefs[ lightIndex ]; - let lightNode; - - const color = new three.Color( 0xffffff ); - - if ( lightDef.color !== undefined ) color.setRGB( lightDef.color[ 0 ], lightDef.color[ 1 ], lightDef.color[ 2 ], three.LinearSRGBColorSpace ); - - const range = lightDef.range !== undefined ? lightDef.range : 0; - - switch ( lightDef.type ) { - - case 'directional': - lightNode = new three.DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - case 'point': - lightNode = new three.PointLight( color ); - lightNode.distance = range; - break; - - case 'spot': - lightNode = new three.SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); - - } - - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); - - lightNode.decay = 2; - - assignExtrasToUserData( lightNode, lightDef ); - - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - - lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); - - dependency = Promise.resolve( lightNode ); - - parser.cache.add( cacheKey, dependency ); - - return dependency; - - } - - getDependency( type, index ) { - - if ( type !== 'light' ) return; - - return this._loadLight( index ); - - } - - createNodeAttachment( nodeIndex ) { - - const self = this; - const parser = this.parser; - const json = parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; - const lightIndex = lightDef.light; - - if ( lightIndex === undefined ) return null; - - return this._loadLight( lightIndex ).then( function ( light ) { - - return parser._getNodeRef( self.cache, lightIndex, light ); - - } ); - - } - - } - - /** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ - class GLTFMaterialsUnlitExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; - - } - - getMaterialType() { - - return three.MeshBasicMaterial; - - } - - extendParams( materialParams, materialDef, parser ) { - - const pending = []; - - materialParams.color = new three.Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - const metallicRoughness = materialDef.pbrMetallicRoughness; - - if ( metallicRoughness ) { - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.setRGB( array[ 0 ], array[ 1 ], array[ 2 ], three.LinearSRGBColorSpace ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, three.SRGBColorSpace ) ); - - } - - } - - return Promise.all( pending ); - - } - - } - - /** - * Materials Emissive Strength Extension - * - * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md - */ - class GLTFMaterialsEmissiveStrengthExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const emissiveStrength = materialDef.extensions[ this.name ].emissiveStrength; - - if ( emissiveStrength !== undefined ) { - - materialParams.emissiveIntensity = emissiveStrength; - - } - - return Promise.resolve(); - - } - - } - - /** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ - class GLTFMaterialsClearcoatExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.clearcoatFactor !== undefined ) { - - materialParams.clearcoat = extension.clearcoatFactor; - - } - - if ( extension.clearcoatTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); - - } - - if ( extension.clearcoatRoughnessFactor !== undefined ) { - - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; - - } - - if ( extension.clearcoatRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - - } - - if ( extension.clearcoatNormalTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - - if ( extension.clearcoatNormalTexture.scale !== undefined ) { - - const scale = extension.clearcoatNormalTexture.scale; - - materialParams.clearcoatNormalScale = new three.Vector2( scale, scale ); - - } - - } - - return Promise.all( pending ); - - } - - } - - /** - * Iridescence Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence - */ - class GLTFMaterialsIridescenceExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.iridescenceFactor !== undefined ) { - - materialParams.iridescence = extension.iridescenceFactor; - - } - - if ( extension.iridescenceTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'iridescenceMap', extension.iridescenceTexture ) ); - - } - - if ( extension.iridescenceIor !== undefined ) { - - materialParams.iridescenceIOR = extension.iridescenceIor; - - } - - if ( materialParams.iridescenceThicknessRange === undefined ) { - - materialParams.iridescenceThicknessRange = [ 100, 400 ]; - - } - - if ( extension.iridescenceThicknessMinimum !== undefined ) { - - materialParams.iridescenceThicknessRange[ 0 ] = extension.iridescenceThicknessMinimum; - - } - - if ( extension.iridescenceThicknessMaximum !== undefined ) { - - materialParams.iridescenceThicknessRange[ 1 ] = extension.iridescenceThicknessMaximum; - - } - - if ( extension.iridescenceThicknessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'iridescenceThicknessMap', extension.iridescenceThicknessTexture ) ); - - } - - return Promise.all( pending ); - - } - - } - - /** - * Sheen Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen - */ - class GLTFMaterialsSheenExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_SHEEN; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - materialParams.sheenColor = new three.Color( 0, 0, 0 ); - materialParams.sheenRoughness = 0; - materialParams.sheen = 1; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.sheenColorFactor !== undefined ) { - - const colorFactor = extension.sheenColorFactor; - materialParams.sheenColor.setRGB( colorFactor[ 0 ], colorFactor[ 1 ], colorFactor[ 2 ], three.LinearSRGBColorSpace ); - - } - - if ( extension.sheenRoughnessFactor !== undefined ) { - - materialParams.sheenRoughness = extension.sheenRoughnessFactor; - - } - - if ( extension.sheenColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, three.SRGBColorSpace ) ); - - } - - if ( extension.sheenRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) ); - - } - - return Promise.all( pending ); - - } - - } - - /** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ - class GLTFMaterialsTransmissionExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.transmissionFactor !== undefined ) { - - materialParams.transmission = extension.transmissionFactor; - - } - - if ( extension.transmissionTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - - } - - return Promise.all( pending ); - - } - - } - - /** - * Materials Volume Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume - */ - class GLTFMaterialsVolumeExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0; - - if ( extension.thicknessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) ); - - } - - materialParams.attenuationDistance = extension.attenuationDistance || Infinity; - - const colorArray = extension.attenuationColor || [ 1, 1, 1 ]; - materialParams.attenuationColor = new three.Color().setRGB( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ], three.LinearSRGBColorSpace ); - - return Promise.all( pending ); - - } - - } - - /** - * Materials ior Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior - */ - class GLTFMaterialsIorExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_IOR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const extension = materialDef.extensions[ this.name ]; - - materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5; - - return Promise.resolve(); - - } - - } - - /** - * Materials specular Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular - */ - class GLTFMaterialsSpecularExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0; - - if ( extension.specularTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) ); - - } - - const colorArray = extension.specularColorFactor || [ 1, 1, 1 ]; - materialParams.specularColor = new three.Color().setRGB( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ], three.LinearSRGBColorSpace ); - - if ( extension.specularColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, three.SRGBColorSpace ) ); - - } - - return Promise.all( pending ); - - } - - } - - - /** - * Materials bump Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/EXT_materials_bump - */ - class GLTFMaterialsBumpExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_MATERIALS_BUMP; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.bumpScale = extension.bumpFactor !== undefined ? extension.bumpFactor : 1.0; - - if ( extension.bumpTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'bumpMap', extension.bumpTexture ) ); - - } - - return Promise.all( pending ); - - } - - } - - /** - * Materials anisotropy Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_anisotropy - */ - class GLTFMaterialsAnisotropyExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_ANISOTROPY; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return three.MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.anisotropyStrength !== undefined ) { - - materialParams.anisotropy = extension.anisotropyStrength; - - } - - if ( extension.anisotropyRotation !== undefined ) { - - materialParams.anisotropyRotation = extension.anisotropyRotation; - - } - - if ( extension.anisotropyTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'anisotropyMap', extension.anisotropyTexture ) ); - - } - - return Promise.all( pending ); - - } - - } - - /** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - */ - class GLTFTextureBasisUExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_TEXTURE_BASISU; - - } - - loadTexture( textureIndex ) { - - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ this.name ]; - const loader = parser.options.ktx2Loader; - - if ( ! loader ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - - } else { - - // Assumes that the extension is optional and that a fallback texture is present - return null; - - } - - } - - return parser.loadTextureImage( textureIndex, extension.source, loader ); - - } - - } - - /** - * WebP Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp - */ - class GLTFTextureWebPExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_TEXTURE_WEBP; - this.isSupported = null; - - } - - loadTexture( textureIndex ) { - - const name = this.name; - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; - - let loader = parser.textureLoader; - if ( source.uri ) { - - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.detectSupport().then( function ( isSupported ) { - - if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); - - } - - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); - - } ); - - } - - detectSupport() { - - if ( ! this.isSupported ) { - - this.isSupported = new Promise( function ( resolve ) { - - const image = new Image(); - - // Lossy test image. Support for lossy images doesn't guarantee support for all - // WebP images, unfortunately. - image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; - - image.onload = image.onerror = function () { - - resolve( image.height === 1 ); - - }; - - } ); - - } - - return this.isSupported; - - } - - } - - /** - * AVIF Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_avif - */ - class GLTFTextureAVIFExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_TEXTURE_AVIF; - this.isSupported = null; - - } - - loadTexture( textureIndex ) { - - const name = this.name; - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; - - let loader = parser.textureLoader; - if ( source.uri ) { - - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.detectSupport().then( function ( isSupported ) { - - if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: AVIF required by asset but unsupported.' ); - - } - - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); - - } ); - - } - - detectSupport() { - - if ( ! this.isSupported ) { - - this.isSupported = new Promise( function ( resolve ) { - - const image = new Image(); - - // Lossy test image. - image.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkQAAAAB8dSLfI='; - image.onload = image.onerror = function () { - - resolve( image.height === 1 ); - - }; - - } ); - - } - - return this.isSupported; - - } - - } - - /** - * meshopt BufferView Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression - */ - class GLTFMeshoptCompression { - - constructor( parser ) { - - this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; - this.parser = parser; - - } - - loadBufferView( index ) { - - const json = this.parser.json; - const bufferView = json.bufferViews[ index ]; - - if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { - - const extensionDef = bufferView.extensions[ this.name ]; - - const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); - const decoder = this.parser.options.meshoptDecoder; - - if ( ! decoder || ! decoder.supported ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); - - } else { - - // Assumes that the extension is optional and that fallback buffer data is present - return null; - - } - - } - - return buffer.then( function ( res ) { - - const byteOffset = extensionDef.byteOffset || 0; - const byteLength = extensionDef.byteLength || 0; - - const count = extensionDef.count; - const stride = extensionDef.byteStride; - - const source = new Uint8Array( res, byteOffset, byteLength ); - - if ( decoder.decodeGltfBufferAsync ) { - - return decoder.decodeGltfBufferAsync( count, stride, source, extensionDef.mode, extensionDef.filter ).then( function ( res ) { - - return res.buffer; - - } ); - - } else { - - // Support for MeshoptDecoder 0.18 or earlier, without decodeGltfBufferAsync - return decoder.ready.then( function () { - - const result = new ArrayBuffer( count * stride ); - decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); - return result; - - } ); - - } - - } ); - - } else { - - return null; - - } - - } - - } - - /** - * GPU Instancing Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing - * - */ - class GLTFMeshGpuInstancing { - - constructor( parser ) { - - this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING; - this.parser = parser; - - } - - createNodeMesh( nodeIndex ) { - - const json = this.parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - - if ( ! nodeDef.extensions || ! nodeDef.extensions[ this.name ] || - nodeDef.mesh === undefined ) { - - return null; - - } - - const meshDef = json.meshes[ nodeDef.mesh ]; - - // No Points or Lines + Instancing support yet - - for ( const primitive of meshDef.primitives ) { - - if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES && - primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP && - primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN && - primitive.mode !== undefined ) { - - return null; - - } - - } - - const extensionDef = nodeDef.extensions[ this.name ]; - const attributesDef = extensionDef.attributes; - - // @TODO: Can we support InstancedMesh + SkinnedMesh? - - const pending = []; - const attributes = {}; - - for ( const key in attributesDef ) { - - pending.push( this.parser.getDependency( 'accessor', attributesDef[ key ] ).then( accessor => { - - attributes[ key ] = accessor; - return attributes[ key ]; - - } ) ); - - } - - if ( pending.length < 1 ) { - - return null; - - } - - pending.push( this.parser.createNodeMesh( nodeIndex ) ); - - return Promise.all( pending ).then( results => { - - const nodeObject = results.pop(); - const meshes = nodeObject.isGroup ? nodeObject.children : [ nodeObject ]; - const count = results[ 0 ].count; // All attribute counts should be same - const instancedMeshes = []; - - for ( const mesh of meshes ) { - - // Temporal variables - const m = new three.Matrix4(); - const p = new three.Vector3(); - const q = new three.Quaternion(); - const s = new three.Vector3( 1, 1, 1 ); - - const instancedMesh = new three.InstancedMesh( mesh.geometry, mesh.material, count ); - - for ( let i = 0; i < count; i ++ ) { - - if ( attributes.TRANSLATION ) { - - p.fromBufferAttribute( attributes.TRANSLATION, i ); - - } - - if ( attributes.ROTATION ) { - - q.fromBufferAttribute( attributes.ROTATION, i ); - - } - - if ( attributes.SCALE ) { - - s.fromBufferAttribute( attributes.SCALE, i ); - - } - - instancedMesh.setMatrixAt( i, m.compose( p, q, s ) ); - - } - - // Add instance attributes to the geometry, excluding TRS. - for ( const attributeName in attributes ) { - - if ( attributeName === '_COLOR_0' ) { - - const attr = attributes[ attributeName ]; - instancedMesh.instanceColor = new three.InstancedBufferAttribute( attr.array, attr.itemSize, attr.normalized ); - - } else if ( attributeName !== 'TRANSLATION' && - attributeName !== 'ROTATION' && - attributeName !== 'SCALE' ) { - - mesh.geometry.setAttribute( attributeName, attributes[ attributeName ] ); - - } - - } - - // Just in case - three.Object3D.prototype.copy.call( instancedMesh, mesh ); - - this.parser.assignFinalMaterial( instancedMesh ); - - instancedMeshes.push( instancedMesh ); - - } - - if ( nodeObject.isGroup ) { - - nodeObject.clear(); - - nodeObject.add( ... instancedMeshes ); - - return nodeObject; - - } - - return instancedMeshes[ 0 ]; - - } ); - - } - - } - - /* BINARY EXTENSION */ - const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; - const BINARY_EXTENSION_HEADER_LENGTH = 12; - const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - - class GLTFBinaryExtension { - - constructor( data ) { - - this.name = EXTENSIONS.KHR_BINARY_GLTF; - this.content = null; - this.body = null; - - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); - const textDecoder = new TextDecoder(); - - this.header = { - magic: textDecoder.decode( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; - - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - - } else if ( this.header.version < 2.0 ) { - - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - - } - - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); - let chunkIndex = 0; - - while ( chunkIndex < chunkContentsLength ) { - - const chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - const chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); - this.content = textDecoder.decode( contentArray ); - - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); - - } - - // Clients must ignore chunks with unknown types. - - chunkIndex += chunkLength; - - } - - if ( this.content === null ) { - - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); - - } - - } - - } - - /** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ - class GLTFDracoMeshCompressionExtension { - - constructor( json, dracoLoader ) { - - if ( ! dracoLoader ) { - - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); - - } - - this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); - - } - - decodePrimitive( primitive, parser ) { - - const json = this.json; - const dracoLoader = this.dracoLoader; - const bufferViewIndex = primitive.extensions[ this.name ].bufferView; - const gltfAttributeMap = primitive.extensions[ this.name ].attributes; - const threeAttributeMap = {}; - const attributeNormalizedMap = {}; - const attributeTypeMap = {}; - - for ( const attributeName in gltfAttributeMap ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; - - } - - for ( const attributeName in primitive.attributes ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - if ( gltfAttributeMap[ attributeName ] !== undefined ) { - - const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - attributeTypeMap[ threeAttributeName ] = componentType.name; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; - - } - - } - - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { - - return new Promise( function ( resolve, reject ) { - - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { - - for ( const attributeName in geometry.attributes ) { - - const attribute = geometry.attributes[ attributeName ]; - const normalized = attributeNormalizedMap[ attributeName ]; - - if ( normalized !== undefined ) attribute.normalized = normalized; - - } - - resolve( geometry ); - - }, threeAttributeMap, attributeTypeMap, three.LinearSRGBColorSpace, reject ); - - } ); - - } ); - - } - - } - - /** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ - class GLTFTextureTransformExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; - - } - - extendTexture( texture, transform ) { - - if ( ( transform.texCoord === undefined || transform.texCoord === texture.channel ) - && transform.offset === undefined - && transform.rotation === undefined - && transform.scale === undefined ) { - - // See https://github.com/mrdoob/three.js/issues/21819. - return texture; - - } - - texture = texture.clone(); - - if ( transform.texCoord !== undefined ) { - - texture.channel = transform.texCoord; - - } - - if ( transform.offset !== undefined ) { - - texture.offset.fromArray( transform.offset ); - - } - - if ( transform.rotation !== undefined ) { - - texture.rotation = transform.rotation; - - } - - if ( transform.scale !== undefined ) { - - texture.repeat.fromArray( transform.scale ); - - } - - texture.needsUpdate = true; - - return texture; - - } - - } - - /** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ - class GLTFMeshQuantizationExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; - - } - - } - - /*********************************/ - /********** INTERPOLATION ********/ - /*********************************/ - - // Spline Interpolation - // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation - class GLTFCubicSplineInterpolant extends three.Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - copySampleValue_( index ) { - - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. - - const result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; - - for ( let i = 0; i !== valueSize; i ++ ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - } - - interpolate_( i1, t0, t, t1 ) { - - const result = this.resultBuffer; - const values = this.sampleValues; - const stride = this.valueSize; - - const stride2 = stride * 2; - const stride3 = stride * 3; - - const td = t1 - t0; - - const p = ( t - t0 ) / td; - const pp = p * p; - const ppp = pp * p; - - const offset1 = i1 * stride3; - const offset0 = offset1 - stride3; - - const s2 = - 2 * ppp + 3 * pp; - const s3 = ppp - pp; - const s0 = 1 - s2; - const s1 = s3 - pp + p; - - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( let i = 0; i !== stride; i ++ ) { - - const p0 = values[ offset0 + i + stride ]; // splineVertex_k - const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) - - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; - - } - - return result; - - } - - } - - const _q = new three.Quaternion(); - - class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { - - interpolate_( i1, t0, t, t1 ) { - - const result = super.interpolate_( i1, t0, t, t1 ); - - _q.fromArray( result ).normalize().toArray( result ); - - return result; - - } - - } - - - /*********************************/ - /********** INTERNALS ************/ - /*********************************/ - - /* CONSTANTS */ - - const WEBGL_CONSTANTS = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 - }; - - const WEBGL_COMPONENT_TYPES = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array - }; - - const WEBGL_FILTERS = { - 9728: three.NearestFilter, - 9729: three.LinearFilter, - 9984: three.NearestMipmapNearestFilter, - 9985: three.LinearMipmapNearestFilter, - 9986: three.NearestMipmapLinearFilter, - 9987: three.LinearMipmapLinearFilter - }; - - const WEBGL_WRAPPINGS = { - 33071: three.ClampToEdgeWrapping, - 33648: three.MirroredRepeatWrapping, - 10497: three.RepeatWrapping - }; - - const WEBGL_TYPE_SIZES = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 - }; - - const ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv1', - TEXCOORD_2: 'uv2', - TEXCOORD_3: 'uv3', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', - }; - - const PATH_PROPERTIES = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' - }; - - const INTERPOLATION = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: three.InterpolateLinear, - STEP: three.InterpolateDiscrete - }; - - const ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' - }; - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ - function createDefaultMaterial( cache ) { - - if ( cache[ 'DefaultMaterial' ] === undefined ) { - - cache[ 'DefaultMaterial' ] = new three.MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: three.FrontSide - } ); - - } - - return cache[ 'DefaultMaterial' ]; - - } - - function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { - - // Add unknown glTF extensions to an object's userData. - - for ( const name in objectDef.extensions ) { - - if ( knownExtensions[ name ] === undefined ) { - - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; - - } - - } - - } - - /** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ - function assignExtrasToUserData( object, gltfDef ) { - - if ( gltfDef.extras !== undefined ) { - - if ( typeof gltfDef.extras === 'object' ) { - - Object.assign( object.userData, gltfDef.extras ); - - } - - } - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ - function addMorphTargets( geometry, targets, parser ) { - - let hasMorphPosition = false; - let hasMorphNormal = false; - let hasMorphColor = false; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; - if ( target.COLOR_0 !== undefined ) hasMorphColor = true; - - if ( hasMorphPosition && hasMorphNormal && hasMorphColor ) break; - - } - - if ( ! hasMorphPosition && ! hasMorphNormal && ! hasMorphColor ) return Promise.resolve( geometry ); - - const pendingPositionAccessors = []; - const pendingNormalAccessors = []; - const pendingColorAccessors = []; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( hasMorphPosition ) { - - const pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; - - pendingPositionAccessors.push( pendingAccessor ); - - } - - if ( hasMorphNormal ) { - - const pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; - - pendingNormalAccessors.push( pendingAccessor ); - - } - - if ( hasMorphColor ) { - - const pendingAccessor = target.COLOR_0 !== undefined - ? parser.getDependency( 'accessor', target.COLOR_0 ) - : geometry.attributes.color; - - pendingColorAccessors.push( pendingAccessor ); - - } - - } - - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ), - Promise.all( pendingColorAccessors ) - ] ).then( function ( accessors ) { - - const morphPositions = accessors[ 0 ]; - const morphNormals = accessors[ 1 ]; - const morphColors = accessors[ 2 ]; - - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - if ( hasMorphColor ) geometry.morphAttributes.color = morphColors; - geometry.morphTargetsRelative = true; - - return geometry; - - } ); - - } - - /** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ - function updateMorphTargets( mesh, meshDef ) { - - mesh.updateMorphTargets(); - - if ( meshDef.weights !== undefined ) { - - for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { - - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; - - } - - } - - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { - - const targetNames = meshDef.extras.targetNames; - - if ( mesh.morphTargetInfluences.length === targetNames.length ) { - - mesh.morphTargetDictionary = {}; - - for ( let i = 0, il = targetNames.length; i < il; i ++ ) { - - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; - - } - - } - - } - - } - - function createPrimitiveKey( primitiveDef ) { - - let geometryKey; - - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; - - if ( dracoExtension ) { - - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); - - } else { - - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; - - } - - if ( primitiveDef.targets !== undefined ) { - - for ( let i = 0, il = primitiveDef.targets.length; i < il; i ++ ) { - - geometryKey += ':' + createAttributesKey( primitiveDef.targets[ i ] ); - - } - - } - - return geometryKey; - - } - - function createAttributesKey( attributes ) { - - let attributesKey = ''; - - const keys = Object.keys( attributes ).sort(); - - for ( let i = 0, il = keys.length; i < il; i ++ ) { - - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; - - } - - return attributesKey; - - } - - function getNormalizedComponentScale( constructor ) { - - // Reference: - // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data - - switch ( constructor ) { - - case Int8Array: - return 1 / 127; - - case Uint8Array: - return 1 / 255; - - case Int16Array: - return 1 / 32767; - - case Uint16Array: - return 1 / 65535; - - default: - throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); - - } - - } - - function getImageURIMimeType( uri ) { - - if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; - if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; - - return 'image/png'; - - } - - const _identityMatrix = new three.Matrix4(); - - /* GLTF PARSER */ - - class GLTFParser { - - constructor( json = {}, options = {} ) { - - this.json = json; - this.extensions = {}; - this.plugins = {}; - this.options = options; - - // loader object cache - this.cache = new GLTFRegistry(); - - // associations between Three.js objects and glTF elements - this.associations = new Map(); - - // BufferGeometry caching - this.primitiveCache = {}; - - // Node cache - this.nodeCache = {}; - - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; - - this.sourceCache = {}; - this.textureCache = {}; - - // Track node names, to ensure no duplicates - this.nodeNamesUsed = {}; - - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - - let isSafari = false; - let isFirefox = false; - let firefoxVersion = - 1; - - if ( typeof navigator !== 'undefined' ) { - - isSafari = /^((?!chrome|android).)*safari/i.test( navigator.userAgent ) === true; - isFirefox = navigator.userAgent.indexOf( 'Firefox' ) > - 1; - firefoxVersion = isFirefox ? navigator.userAgent.match( /Firefox\/([0-9]+)\./ )[ 1 ] : - 1; - - } - - if ( typeof createImageBitmap === 'undefined' || isSafari || ( isFirefox && firefoxVersion < 98 ) ) { - - this.textureLoader = new three.TextureLoader( this.options.manager ); - - } else { - - this.textureLoader = new three.ImageBitmapLoader( this.options.manager ); - - } - - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - this.textureLoader.setRequestHeader( this.options.requestHeader ); - - this.fileLoader = new three.FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); - - if ( this.options.crossOrigin === 'use-credentials' ) { - - this.fileLoader.setWithCredentials( true ); - - } - - } - - setExtensions( extensions ) { - - this.extensions = extensions; - - } - - setPlugins( plugins ) { - - this.plugins = plugins; - - } - - parse( onLoad, onError ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - // Clear the loader cache - this.cache.removeAll(); - this.nodeCache = {}; - - // Mark the special nodes/meshes in json for efficient parse - this._invokeAll( function ( ext ) { - - return ext._markDefs && ext._markDefs(); - - } ); - - Promise.all( this._invokeAll( function ( ext ) { - - return ext.beforeRoot && ext.beforeRoot(); - - } ) ).then( function () { - - return Promise.all( [ - - parser.getDependencies( 'scene' ), - parser.getDependencies( 'animation' ), - parser.getDependencies( 'camera' ), - - ] ); - - } ).then( function ( dependencies ) { - - const result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; - - addUnknownExtensionsToUserData( extensions, result, json ); - - assignExtrasToUserData( result, json ); - - return Promise.all( parser._invokeAll( function ( ext ) { - - return ext.afterRoot && ext.afterRoot( result ); - - } ) ).then( function () { - - onLoad( result ); - - } ); - - } ).catch( onError ); - - } - - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - _markDefs() { - - const nodeDefs = this.json.nodes || []; - const skinDefs = this.json.skins || []; - const meshDefs = this.json.meshes || []; - - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { - - const joints = skinDefs[ skinIndex ].joints; - - for ( let i = 0, il = joints.length; i < il; i ++ ) { - - nodeDefs[ joints[ i ] ].isBone = true; - - } - - } - - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.mesh !== undefined ) { - - this._addNodeRef( this.meshCache, nodeDef.mesh ); - - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { - - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; - - } - - } - - if ( nodeDef.camera !== undefined ) { - - this._addNodeRef( this.cameraCache, nodeDef.camera ); - - } - - } - - } - - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - _addNodeRef( cache, index ) { - - if ( index === undefined ) return; - - if ( cache.refs[ index ] === undefined ) { - - cache.refs[ index ] = cache.uses[ index ] = 0; - - } - - cache.refs[ index ] ++; - - } - - /** Returns a reference to a shared resource, cloning it if necessary. */ - _getNodeRef( cache, index, object ) { - - if ( cache.refs[ index ] <= 1 ) return object; - - const ref = object.clone(); - - // Propagates mappings to the cloned object, prevents mappings on the - // original object from being lost. - const updateMappings = ( original, clone ) => { - - const mappings = this.associations.get( original ); - if ( mappings != null ) { - - this.associations.set( clone, mappings ); - - } - - for ( const [ i, child ] of original.children.entries() ) { - - updateMappings( child, clone.children[ i ] ); - - } - - }; - - updateMappings( object, ref ); - - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); - - return ref; - - } - - _invokeOne( func ) { - - const extensions = Object.values( this.plugins ); - extensions.push( this ); - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) return result; - - } - - return null; - - } - - _invokeAll( func ) { - - const extensions = Object.values( this.plugins ); - extensions.unshift( this ); - - const pending = []; - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) pending.push( result ); - - } - - return pending; - - } - - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - getDependency( type, index ) { - - const cacheKey = type + ':' + index; - let dependency = this.cache.get( cacheKey ); - - if ( ! dependency ) { - - switch ( type ) { - - case 'scene': - dependency = this.loadScene( index ); - break; - - case 'node': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadNode && ext.loadNode( index ); - - } ); - break; - - case 'mesh': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMesh && ext.loadMesh( index ); - - } ); - break; - - case 'accessor': - dependency = this.loadAccessor( index ); - break; - - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadBufferView && ext.loadBufferView( index ); - - } ); - break; - - case 'buffer': - dependency = this.loadBuffer( index ); - break; - - case 'material': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMaterial && ext.loadMaterial( index ); - - } ); - break; - - case 'texture': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadTexture && ext.loadTexture( index ); - - } ); - break; - - case 'skin': - dependency = this.loadSkin( index ); - break; - - case 'animation': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadAnimation && ext.loadAnimation( index ); - - } ); - break; - - case 'camera': - dependency = this.loadCamera( index ); - break; - - default: - dependency = this._invokeOne( function ( ext ) { - - return ext != this && ext.getDependency && ext.getDependency( type, index ); - - } ); - - if ( ! dependency ) { - - throw new Error( 'Unknown type: ' + type ); - - } - - break; - - } - - this.cache.add( cacheKey, dependency ); - - } - - return dependency; - - } - - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - getDependencies( type ) { - - let dependencies = this.cache.get( type ); - - if ( ! dependencies ) { - - const parser = this; - const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; - - dependencies = Promise.all( defs.map( function ( def, index ) { - - return parser.getDependency( type, index ); - - } ) ); - - this.cache.add( type, dependencies ); - - } - - return dependencies; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - loadBuffer( bufferIndex ) { - - const bufferDef = this.json.buffers[ bufferIndex ]; - const loader = this.fileLoader; - - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { - - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); - - } - - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - - return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); - - } - - const options = this.options; - - return new Promise( function ( resolve, reject ) { - - loader.load( three.LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { - - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); - - } ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - loadBufferView( bufferViewIndex ) { - - const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; - - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { - - const byteLength = bufferViewDef.byteLength || 0; - const byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - loadAccessor( accessorIndex ) { - - const parser = this; - const json = this.json; - - const accessorDef = this.json.accessors[ accessorIndex ]; - - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - - const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - const normalized = accessorDef.normalized === true; - - const array = new TypedArray( accessorDef.count * itemSize ); - return Promise.resolve( new three.BufferAttribute( array, itemSize, normalized ) ); - - } - - const pendingBufferViews = []; - - if ( accessorDef.bufferView !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); - - } else { - - pendingBufferViews.push( null ); - - } - - if ( accessorDef.sparse !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); - - } - - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { - - const bufferView = bufferViews[ 0 ]; - - const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - const elementBytes = TypedArray.BYTES_PER_ELEMENT; - const itemBytes = elementBytes * itemSize; - const byteOffset = accessorDef.byteOffset || 0; - const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - const normalized = accessorDef.normalized === true; - let array, bufferAttribute; - - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { - - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - const ibSlice = Math.floor( byteOffset / byteStride ); - const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - let ib = parser.cache.get( ibCacheKey ); - - if ( ! ib ) { - - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); - - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new three.InterleavedBuffer( array, byteStride / elementBytes ); - - parser.cache.add( ibCacheKey, ib ); - - } - - bufferAttribute = new three.InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); - - } else { - - if ( bufferView === null ) { - - array = new TypedArray( accessorDef.count * itemSize ); - - } else { - - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); - - } - - bufferAttribute = new three.BufferAttribute( array, itemSize, normalized ); - - } - - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { - - const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; - - const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; - - const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); - - if ( bufferView !== null ) { - - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new three.BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); - - } - - for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { - - const index = sparseIndices[ i ]; - - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); - - } - - } - - return bufferAttribute; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - loadTexture( textureIndex ) { - - const json = this.json; - const options = this.options; - const textureDef = json.textures[ textureIndex ]; - const sourceIndex = textureDef.source; - const sourceDef = json.images[ sourceIndex ]; - - let loader = this.textureLoader; - - if ( sourceDef.uri ) { - - const handler = options.manager.getHandler( sourceDef.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.loadTextureImage( textureIndex, sourceIndex, loader ); - - } - - loadTextureImage( textureIndex, sourceIndex, loader ) { - - const parser = this; - const json = this.json; - - const textureDef = json.textures[ textureIndex ]; - const sourceDef = json.images[ sourceIndex ]; - - const cacheKey = ( sourceDef.uri || sourceDef.bufferView ) + ':' + textureDef.sampler; - - if ( this.textureCache[ cacheKey ] ) { - - // See https://github.com/mrdoob/three.js/issues/21559. - return this.textureCache[ cacheKey ]; - - } - - const promise = this.loadImageSource( sourceIndex, loader ).then( function ( texture ) { - - texture.flipY = false; - - texture.name = textureDef.name || sourceDef.name || ''; - - if ( texture.name === '' && typeof sourceDef.uri === 'string' && sourceDef.uri.startsWith( 'data:image/' ) === false ) { - - texture.name = sourceDef.uri; - - } - - const samplers = json.samplers || {}; - const sampler = samplers[ textureDef.sampler ] || {}; - - texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || three.LinearFilter; - texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || three.LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || three.RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || three.RepeatWrapping; - - parser.associations.set( texture, { textures: textureIndex } ); - - return texture; - - } ).catch( function () { - - return null; - - } ); - - this.textureCache[ cacheKey ] = promise; - - return promise; - - } - - loadImageSource( sourceIndex, loader ) { - - const parser = this; - const json = this.json; - const options = this.options; - - if ( this.sourceCache[ sourceIndex ] !== undefined ) { - - return this.sourceCache[ sourceIndex ].then( ( texture ) => texture.clone() ); - - } - - const sourceDef = json.images[ sourceIndex ]; - - const URL = self.URL || self.webkitURL; - - let sourceURI = sourceDef.uri || ''; - let isObjectURL = false; - - if ( sourceDef.bufferView !== undefined ) { - - // Load binary image data from bufferView, if provided. - - sourceURI = parser.getDependency( 'bufferView', sourceDef.bufferView ).then( function ( bufferView ) { - - isObjectURL = true; - const blob = new Blob( [ bufferView ], { type: sourceDef.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; - - } ); - - } else if ( sourceDef.uri === undefined ) { - - throw new Error( 'THREE.GLTFLoader: Image ' + sourceIndex + ' is missing URI and bufferView' ); - - } - - const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) { - - return new Promise( function ( resolve, reject ) { - - let onLoad = resolve; - - if ( loader.isImageBitmapLoader === true ) { - - onLoad = function ( imageBitmap ) { - - const texture = new three.Texture( imageBitmap ); - texture.needsUpdate = true; - - resolve( texture ); - - }; - - } - - loader.load( three.LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); - - } ); - - } ).then( function ( texture ) { - - // Clean up resources and configure Texture. - - if ( isObjectURL === true ) { - - URL.revokeObjectURL( sourceURI ); - - } - - texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); - - return texture; - - } ).catch( function ( error ) { - throw error; - - } ); - - this.sourceCache[ sourceIndex ] = promise; - return promise; - - } - - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - assignTexture( materialParams, mapName, mapDef, colorSpace ) { - - const parser = this; - - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { - - if ( ! texture ) return null; - - if ( mapDef.texCoord !== undefined && mapDef.texCoord > 0 ) { - - texture = texture.clone(); - texture.channel = mapDef.texCoord; - - } - - if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; - - if ( transform ) { - - const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); - - } - - } - - if ( colorSpace !== undefined ) { - - texture.colorSpace = colorSpace; - - } - - materialParams[ mapName ] = texture; - - return texture; - - } ); - - } - - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accommodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - assignFinalMaterial( mesh ) { - - const geometry = mesh.geometry; - let material = mesh.material; - - const useDerivativeTangents = geometry.attributes.tangent === undefined; - const useVertexColors = geometry.attributes.color !== undefined; - const useFlatShading = geometry.attributes.normal === undefined; - - if ( mesh.isPoints ) { - - const cacheKey = 'PointsMaterial:' + material.uuid; - - let pointsMaterial = this.cache.get( cacheKey ); - - if ( ! pointsMaterial ) { - - pointsMaterial = new three.PointsMaterial(); - three.Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px - - this.cache.add( cacheKey, pointsMaterial ); - - } - - material = pointsMaterial; - - } else if ( mesh.isLine ) { - - const cacheKey = 'LineBasicMaterial:' + material.uuid; - - let lineMaterial = this.cache.get( cacheKey ); - - if ( ! lineMaterial ) { - - lineMaterial = new three.LineBasicMaterial(); - three.Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); - lineMaterial.map = material.map; - - this.cache.add( cacheKey, lineMaterial ); - - } - - material = lineMaterial; - - } - - // Clone the material if it will be modified - if ( useDerivativeTangents || useVertexColors || useFlatShading ) { - - let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; - - if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - - let cachedMaterial = this.cache.get( cacheKey ); - - if ( ! cachedMaterial ) { - - cachedMaterial = material.clone(); - - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - - if ( useDerivativeTangents ) { - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; - if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; - - } - - this.cache.add( cacheKey, cachedMaterial ); - - this.associations.set( cachedMaterial, this.associations.get( material ) ); - - } - - material = cachedMaterial; - - } - - mesh.material = material; - - } - - getMaterialType( /* materialIndex */ ) { - - return three.MeshStandardMaterial; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - loadMaterial( materialIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - const materialDef = json.materials[ materialIndex ]; - - let materialType; - const materialParams = {}; - const materialExtensions = materialDef.extensions || {}; - - const pending = []; - - if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - - const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); - - } else { - - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - - const metallicRoughness = materialDef.pbrMetallicRoughness || {}; - - materialParams.color = new three.Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.setRGB( array[ 0 ], array[ 1 ], array[ 2 ], three.LinearSRGBColorSpace ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, three.SRGBColorSpace ) ); - - } - - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; - - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); - - } - - materialType = this._invokeOne( function ( ext ) { - - return ext.getMaterialType && ext.getMaterialType( materialIndex ); - - } ); - - pending.push( Promise.all( this._invokeAll( function ( ext ) { - - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); - - } ) ) ); - - } - - if ( materialDef.doubleSided === true ) { - - materialParams.side = three.DoubleSide; - - } - - const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - - if ( alphaMode === ALPHA_MODES.BLEND ) { - - materialParams.transparent = true; - - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; - - } else { - - materialParams.transparent = false; - - if ( alphaMode === ALPHA_MODES.MASK ) { - - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; - - } - - } - - if ( materialDef.normalTexture !== undefined && materialType !== three.MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); - - materialParams.normalScale = new three.Vector2( 1, 1 ); - - if ( materialDef.normalTexture.scale !== undefined ) { - - const scale = materialDef.normalTexture.scale; - - materialParams.normalScale.set( scale, scale ); - - } - - } - - if ( materialDef.occlusionTexture !== undefined && materialType !== three.MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); - - if ( materialDef.occlusionTexture.strength !== undefined ) { - - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; - - } - - } - - if ( materialDef.emissiveFactor !== undefined && materialType !== three.MeshBasicMaterial ) { - - const emissiveFactor = materialDef.emissiveFactor; - materialParams.emissive = new three.Color().setRGB( emissiveFactor[ 0 ], emissiveFactor[ 1 ], emissiveFactor[ 2 ], three.LinearSRGBColorSpace ); - - } - - if ( materialDef.emissiveTexture !== undefined && materialType !== three.MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, three.SRGBColorSpace ) ); - - } - - return Promise.all( pending ).then( function () { - - const material = new materialType( materialParams ); - - if ( materialDef.name ) material.name = materialDef.name; - - assignExtrasToUserData( material, materialDef ); - - parser.associations.set( material, { materials: materialIndex } ); - - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); - - return material; - - } ); - - } - - /** When Object3D instances are targeted by animation, they need unique names. */ - createUniqueName( originalName ) { - - const sanitizedName = three.PropertyBinding.sanitizeNodeName( originalName || '' ); - - if ( sanitizedName in this.nodeNamesUsed ) { - - return sanitizedName + '_' + ( ++ this.nodeNamesUsed[ sanitizedName ] ); - - } else { - - this.nodeNamesUsed[ sanitizedName ] = 0; - - return sanitizedName; - - } - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - loadGeometries( primitives ) { - - const parser = this; - const extensions = this.extensions; - const cache = this.primitiveCache; - - function createDracoPrimitive( primitive ) { - - return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { - - return addPrimitiveAttributes( geometry, primitive, parser ); - - } ); - - } - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey( primitive ); - - // See if we've already created this geometry - const cached = cache[ cacheKey ]; - - if ( cached ) { - - // Use the cached geometry if it exists - pending.push( cached.promise ); - - } else { - - let geometryPromise; - - if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { - - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); - - } else { - - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new three.BufferGeometry(), primitive, parser ); - - } - - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; - - pending.push( geometryPromise ); - - } - - } - - return Promise.all( pending ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - loadMesh( meshIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - const meshDef = json.meshes[ meshIndex ]; - const primitives = meshDef.primitives; - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const material = primitives[ i ].material === undefined - ? createDefaultMaterial( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); - - pending.push( material ); - - } - - pending.push( parser.loadGeometries( primitives ) ); - - return Promise.all( pending ).then( function ( results ) { - - const materials = results.slice( 0, results.length - 1 ); - const geometries = results[ results.length - 1 ]; - - const meshes = []; - - for ( let i = 0, il = geometries.length; i < il; i ++ ) { - - const geometry = geometries[ i ]; - const primitive = primitives[ i ]; - - // 1. create Mesh - - let mesh; - - const material = materials[ i ]; - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || - primitive.mode === undefined ) { - - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new three.SkinnedMesh( geometry, material ) - : new three.Mesh( geometry, material ); - - if ( mesh.isSkinnedMesh === true ) { - - // normalize skin weights to fix malformed assets (see #15319) - mesh.normalizeSkinWeights(); - - } - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleStripDrawMode ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, three.TriangleFanDrawMode ); - - } - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { - - mesh = new three.LineSegments( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { - - mesh = new three.Line( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { - - mesh = new three.LineLoop( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { - - mesh = new three.Points( geometry, material ); - - } else { - - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); - - } - - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - - updateMorphTargets( mesh, meshDef ); - - } - - mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); - - assignExtrasToUserData( mesh, meshDef ); - - if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); - - parser.assignFinalMaterial( mesh ); - - meshes.push( mesh ); - - } - - for ( let i = 0, il = meshes.length; i < il; i ++ ) { - - parser.associations.set( meshes[ i ], { - meshes: meshIndex, - primitives: i - } ); - - } - - if ( meshes.length === 1 ) { - - if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, meshes[ 0 ], meshDef ); - - return meshes[ 0 ]; - - } - - const group = new three.Group(); - - if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, group, meshDef ); - - parser.associations.set( group, { meshes: meshIndex } ); - - for ( let i = 0, il = meshes.length; i < il; i ++ ) { - - group.add( meshes[ i ] ); - - } - - return group; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - loadCamera( cameraIndex ) { - - let camera; - const cameraDef = this.json.cameras[ cameraIndex ]; - const params = cameraDef[ cameraDef.type ]; - - if ( ! params ) { - return; - - } - - if ( cameraDef.type === 'perspective' ) { - - camera = new three.PerspectiveCamera( three.MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); - - } else if ( cameraDef.type === 'orthographic' ) { - - camera = new three.OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); - - } - - if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); - - assignExtrasToUserData( camera, cameraDef ); - - return Promise.resolve( camera ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - loadSkin( skinIndex ) { - - const skinDef = this.json.skins[ skinIndex ]; - - const pending = []; - - for ( let i = 0, il = skinDef.joints.length; i < il; i ++ ) { - - pending.push( this._loadNodeShallow( skinDef.joints[ i ] ) ); - - } - - if ( skinDef.inverseBindMatrices !== undefined ) { - - pending.push( this.getDependency( 'accessor', skinDef.inverseBindMatrices ) ); - - } else { - - pending.push( null ); - - } - - return Promise.all( pending ).then( function ( results ) { - - const inverseBindMatrices = results.pop(); - const jointNodes = results; - - // Note that bones (joint nodes) may or may not be in the - // scene graph at this time. - - const bones = []; - const boneInverses = []; - - for ( let i = 0, il = jointNodes.length; i < il; i ++ ) { - - const jointNode = jointNodes[ i ]; - - if ( jointNode ) { - - bones.push( jointNode ); - - const mat = new three.Matrix4(); - - if ( inverseBindMatrices !== null ) { - - mat.fromArray( inverseBindMatrices.array, i * 16 ); - - } - - boneInverses.push( mat ); - - } - - } - - return new three.Skeleton( bones, boneInverses ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - loadAnimation( animationIndex ) { - - const json = this.json; - const parser = this; - - const animationDef = json.animations[ animationIndex ]; - const animationName = animationDef.name ? animationDef.name : 'animation_' + animationIndex; - - const pendingNodes = []; - const pendingInputAccessors = []; - const pendingOutputAccessors = []; - const pendingSamplers = []; - const pendingTargets = []; - - for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { - - const channel = animationDef.channels[ i ]; - const sampler = animationDef.samplers[ channel.sampler ]; - const target = channel.target; - const name = target.node; - const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; - - if ( target.node === undefined ) continue; - - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); - - } - - return Promise.all( [ - - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) - - ] ).then( function ( dependencies ) { - - const nodes = dependencies[ 0 ]; - const inputAccessors = dependencies[ 1 ]; - const outputAccessors = dependencies[ 2 ]; - const samplers = dependencies[ 3 ]; - const targets = dependencies[ 4 ]; - - const tracks = []; - - for ( let i = 0, il = nodes.length; i < il; i ++ ) { - - const node = nodes[ i ]; - const inputAccessor = inputAccessors[ i ]; - const outputAccessor = outputAccessors[ i ]; - const sampler = samplers[ i ]; - const target = targets[ i ]; - - if ( node === undefined ) continue; - - if ( node.updateMatrix ) { - - node.updateMatrix(); - - } - - const createdTracks = parser._createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target ); - - if ( createdTracks ) { - - for ( let k = 0; k < createdTracks.length; k ++ ) { - - tracks.push( createdTracks[ k ] ); - - } - - } - - } - - return new three.AnimationClip( animationName, undefined, tracks ); - - } ); - - } - - createNodeMesh( nodeIndex ) { - - const json = this.json; - const parser = this; - const nodeDef = json.nodes[ nodeIndex ]; - - if ( nodeDef.mesh === undefined ) return null; - - return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { - - const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); - - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { - - node.traverse( function ( o ) { - - if ( ! o.isMesh ) return; - - for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { - - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; - - } - - } ); - - } - - return node; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - loadNode( nodeIndex ) { - - const json = this.json; - const parser = this; - - const nodeDef = json.nodes[ nodeIndex ]; - - const nodePending = parser._loadNodeShallow( nodeIndex ); - - const childPending = []; - const childrenDef = nodeDef.children || []; - - for ( let i = 0, il = childrenDef.length; i < il; i ++ ) { - - childPending.push( parser.getDependency( 'node', childrenDef[ i ] ) ); - - } - - const skeletonPending = nodeDef.skin === undefined - ? Promise.resolve( null ) - : parser.getDependency( 'skin', nodeDef.skin ); - - return Promise.all( [ - nodePending, - Promise.all( childPending ), - skeletonPending - ] ).then( function ( results ) { - - const node = results[ 0 ]; - const children = results[ 1 ]; - const skeleton = results[ 2 ]; - - if ( skeleton !== null ) { - - // This full traverse should be fine because - // child glTF nodes have not been added to this node yet. - node.traverse( function ( mesh ) { - - if ( ! mesh.isSkinnedMesh ) return; - - mesh.bind( skeleton, _identityMatrix ); - - } ); - - } - - for ( let i = 0, il = children.length; i < il; i ++ ) { - - node.add( children[ i ] ); - - } - - return node; - - } ); - - } - - // ._loadNodeShallow() parses a single node. - // skin and child nodes are created and added in .loadNode() (no '_' prefix). - _loadNodeShallow( nodeIndex ) { - - const json = this.json; - const extensions = this.extensions; - const parser = this; - - // This method is called from .loadNode() and .loadSkin(). - // Cache a node to avoid duplication. - - if ( this.nodeCache[ nodeIndex ] !== undefined ) { - - return this.nodeCache[ nodeIndex ]; - - } - - const nodeDef = json.nodes[ nodeIndex ]; - - // reserve node's name before its dependencies, so the root has the intended name. - const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; - - const pending = []; - - const meshPromise = parser._invokeOne( function ( ext ) { - - return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); - - } ); - - if ( meshPromise ) { - - pending.push( meshPromise ); - - } - - if ( nodeDef.camera !== undefined ) { - - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { - - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); - - } ) ); - - } - - parser._invokeAll( function ( ext ) { - - return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); - - } ).forEach( function ( promise ) { - - pending.push( promise ); - - } ); - - this.nodeCache[ nodeIndex ] = Promise.all( pending ).then( function ( objects ) { - - let node; - - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { - - node = new three.Bone(); - - } else if ( objects.length > 1 ) { - - node = new three.Group(); - - } else if ( objects.length === 1 ) { - - node = objects[ 0 ]; - - } else { - - node = new three.Object3D(); - - } - - if ( node !== objects[ 0 ] ) { - - for ( let i = 0, il = objects.length; i < il; i ++ ) { - - node.add( objects[ i ] ); - - } - - } - - if ( nodeDef.name ) { - - node.userData.name = nodeDef.name; - node.name = nodeName; - - } - - assignExtrasToUserData( node, nodeDef ); - - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); - - if ( nodeDef.matrix !== undefined ) { - - const matrix = new three.Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); - - } else { - - if ( nodeDef.translation !== undefined ) { - - node.position.fromArray( nodeDef.translation ); - - } - - if ( nodeDef.rotation !== undefined ) { - - node.quaternion.fromArray( nodeDef.rotation ); - - } - - if ( nodeDef.scale !== undefined ) { - - node.scale.fromArray( nodeDef.scale ); - - } - - } - - if ( ! parser.associations.has( node ) ) { - - parser.associations.set( node, {} ); - - } - - parser.associations.get( node ).nodes = nodeIndex; - - return node; - - } ); - - return this.nodeCache[ nodeIndex ]; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - loadScene( sceneIndex ) { - - const extensions = this.extensions; - const sceneDef = this.json.scenes[ sceneIndex ]; - const parser = this; - - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - const scene = new three.Group(); - if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); - - assignExtrasToUserData( scene, sceneDef ); - - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); - - const nodeIds = sceneDef.nodes || []; - - const pending = []; - - for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { - - pending.push( parser.getDependency( 'node', nodeIds[ i ] ) ); - - } - - return Promise.all( pending ).then( function ( nodes ) { - - for ( let i = 0, il = nodes.length; i < il; i ++ ) { - - scene.add( nodes[ i ] ); - - } - - // Removes dangling associations, associations that reference a node that - // didn't make it into the scene. - const reduceAssociations = ( node ) => { - - const reducedAssociations = new Map(); - - for ( const [ key, value ] of parser.associations ) { - - if ( key instanceof three.Material || key instanceof three.Texture ) { - - reducedAssociations.set( key, value ); - - } - - } - - node.traverse( ( node ) => { - - const mappings = parser.associations.get( node ); - - if ( mappings != null ) { - - reducedAssociations.set( node, mappings ); - - } - - } ); - - return reducedAssociations; - - }; - - parser.associations = reduceAssociations( scene ); - - return scene; - - } ); - - } - - _createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target ) { - - const tracks = []; - - const targetName = node.name ? node.name : node.uuid; - const targetNames = []; - - if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { - - node.traverse( function ( object ) { - - if ( object.morphTargetInfluences ) { - - targetNames.push( object.name ? object.name : object.uuid ); - - } - - } ); - - } else { - - targetNames.push( targetName ); - - } - - let TypedKeyframeTrack; - - switch ( PATH_PROPERTIES[ target.path ] ) { - - case PATH_PROPERTIES.weights: - - TypedKeyframeTrack = three.NumberKeyframeTrack; - break; - - case PATH_PROPERTIES.rotation: - - TypedKeyframeTrack = three.QuaternionKeyframeTrack; - break; - - case PATH_PROPERTIES.position: - case PATH_PROPERTIES.scale: - - TypedKeyframeTrack = three.VectorKeyframeTrack; - break; - - default: - - switch ( outputAccessor.itemSize ) { - - case 1: - TypedKeyframeTrack = three.NumberKeyframeTrack; - break; - case 2: - case 3: - default: - TypedKeyframeTrack = three.VectorKeyframeTrack; - break; - - } - - break; - - } - - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : three.InterpolateLinear; - - - const outputArray = this._getArrayFromAccessor( outputAccessor ); - - for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { - - const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); - - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { - - this._createCubicSplineTrackInterpolant( track ); - - } - - tracks.push( track ); - - } - - return tracks; - - } - - _getArrayFromAccessor( accessor ) { - - let outputArray = accessor.array; - - if ( accessor.normalized ) { - - const scale = getNormalizedComponentScale( outputArray.constructor ); - const scaled = new Float32Array( outputArray.length ); - - for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { - - scaled[ j ] = outputArray[ j ] * scale; - - } - - outputArray = scaled; - - } - - return outputArray; - - } - - _createCubicSplineTrackInterpolant( track ) { - - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { - - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. - - const interpolantType = ( this instanceof three.QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; - - return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); - - }; - - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; - - } - - } - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ - function computeBounds( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const box = new three.Box3(); - - if ( attributes.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ attributes.POSITION ]; - - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - box.set( - new three.Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new three.Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) - ); - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - box.min.multiplyScalar( boxScale ); - box.max.multiplyScalar( boxScale ); - - } - - } else { - - return; - - } - - } else { - - return; - - } - - const targets = primitiveDef.targets; - - if ( targets !== undefined ) { - - const maxDisplacement = new three.Vector3(); - const vector = new three.Vector3(); - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ target.POSITION ]; - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); - - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - vector.multiplyScalar( boxScale ); - - } - - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); - - } - - } - - } - - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); - - } - - geometry.boundingBox = box; - - const sphere = new three.Sphere(); - - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; - - geometry.boundingSphere = sphere; - - } - - /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} - */ - function addPrimitiveAttributes( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const pending = []; - - function assignAttributeAccessor( accessorIndex, attributeName ) { - - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { - - geometry.setAttribute( attributeName, accessor ); - - } ); - - } - - for ( const gltfAttributeName in attributes ) { - - const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); - - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; - - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); - - } - - if ( primitiveDef.indices !== undefined && ! geometry.index ) { - - const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { - - geometry.setIndex( accessor ); - - } ); - - pending.push( accessor ); - - } - - if ( three.ColorManagement.workingColorSpace !== three.LinearSRGBColorSpace && 'COLOR_0' in attributes ) ; - - assignExtrasToUserData( geometry, primitiveDef ); - - computeBounds( geometry, primitiveDef, parser ); - - return Promise.all( pending ).then( function () { - - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; - - } ); - - } - - const _taskCache$1 = new WeakMap(); - - class DRACOLoader extends three.Loader { - - constructor( manager ) { - - super( manager ); - - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; - - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; - - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' - }; - - } - - setDecoderPath( path ) { - - this.decoderPath = path; - - return this; - - } - - setDecoderConfig( config ) { - - this.decoderConfig = config; - - return this; - - } - - setWorkerLimit( workerLimit ) { - - this.workerLimit = workerLimit; - - return this; - - } - - load( url, onLoad, onProgress, onError ) { - - const loader = new three.FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, ( buffer ) => { - - this.parse( buffer, onLoad, onError ); - - }, onProgress, onError ); - - } - - - parse( buffer, onLoad, onError = ()=>{} ) { - - this.decodeDracoFile( buffer, onLoad, null, null, three.SRGBColorSpace ).catch( onError ); - - } - - decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = three.LinearSRGBColorSpace, onError = () => {} ) { - - const taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs, - vertexColorSpace: vertexColorSpace, - }; - - return this.decodeGeometry( buffer, taskConfig ).then( callback ).catch( onError ); - - } - - decodeGeometry( buffer, taskConfig ) { - - const taskKey = JSON.stringify( taskConfig ); - - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( _taskCache$1.has( buffer ) ) { - - const cachedTask = _taskCache$1.get( buffer ); - - if ( cachedTask.key === taskKey ) { - - return cachedTask.promise; - - } else if ( buffer.byteLength === 0 ) { - - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( - - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' - - ); - - } - - } - - // - - let worker; - const taskID = this.workerNextTaskID ++; - const taskCost = buffer.byteLength; - - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - const geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { - - worker = _worker; - - return new Promise( ( resolve, reject ) => { - - worker._callbacks[ taskID ] = { resolve, reject }; - - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - - // this.debug(); - - } ); - - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); - - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { - - if ( worker && taskID ) { - - this._releaseTask( worker, taskID ); - - // this.debug(); - - } - - } ); - - // Cache the task result. - _taskCache$1.set( buffer, { - - key: taskKey, - promise: geometryPending - - } ); - - return geometryPending; - - } - - _createGeometry( geometryData ) { - - const geometry = new three.BufferGeometry(); - - if ( geometryData.index ) { - - geometry.setIndex( new three.BufferAttribute( geometryData.index.array, 1 ) ); - - } - - for ( let i = 0; i < geometryData.attributes.length; i ++ ) { - - const result = geometryData.attributes[ i ]; - const name = result.name; - const array = result.array; - const itemSize = result.itemSize; - - const attribute = new three.BufferAttribute( array, itemSize ); - - if ( name === 'color' ) { - - this._assignVertexColorSpace( attribute, result.vertexColorSpace ); - - attribute.normalized = ( array instanceof Float32Array ) === false; - - } - - geometry.setAttribute( name, attribute ); - - } - - return geometry; - - } - - _assignVertexColorSpace( attribute, inputColorSpace ) { - - // While .drc files do not specify colorspace, the only 'official' tooling - // is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc - // file is passed into .load() or .parse(). GLTFLoader uses internal APIs - // to decode geometry, and vertex colors are already Linear-sRGB in there. - - if ( inputColorSpace !== three.SRGBColorSpace ) return; - - const _color = new three.Color(); - - for ( let i = 0, il = attribute.count; i < il; i ++ ) { - - _color.fromBufferAttribute( attribute, i ).convertSRGBToLinear(); - attribute.setXYZ( i, _color.r, _color.g, _color.b ); - - } - - } - - _loadLibrary( url, responseType ) { - - const loader = new three.FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - loader.setWithCredentials( this.withCredentials ); - - return new Promise( ( resolve, reject ) => { - - loader.load( url, resolve, undefined, reject ); - - } ); - - } - - preload() { - - this._initDecoder(); - - return this; - - } - - _initDecoder() { - - if ( this.decoderPending ) return this.decoderPending; - - const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - const librariesPending = []; - - if ( useJS ) { - - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - - } else { - - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); - - } - - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { - - const jsContent = libraries[ 0 ]; - - if ( ! useJS ) { - - this.decoderConfig.wasmBinary = libraries[ 1 ]; - - } - - const fn = DRACOWorker.toString(); - - const body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); - - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); - - } ); - - return this.decoderPending; - - } - - _getWorker( taskID, taskCost ) { - - return this._initDecoder().then( () => { - - if ( this.workerPool.length < this.workerLimit ) { - - const worker = new Worker( this.workerSourceURL ); - - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; - - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - - worker.onmessage = function ( e ) { - - const message = e.data; - - switch ( message.type ) { - - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; - - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; - - } - - }; - - this.workerPool.push( worker ); - - } else { - - this.workerPool.sort( function ( a, b ) { - - return a._taskLoad > b._taskLoad ? - 1 : 1; - - } ); - - } - - const worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; - - } ); - - } - - _releaseTask( worker, taskID ) { - - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; - - } - - debug() { - - } - - dispose() { - - for ( let i = 0; i < this.workerPool.length; ++ i ) { - - this.workerPool[ i ].terminate(); - - } - - this.workerPool.length = 0; - - if ( this.workerSourceURL !== '' ) { - - URL.revokeObjectURL( this.workerSourceURL ); - - } - - return this; - - } - - } - - /* WEB WORKER */ - - function DRACOWorker() { - - let decoderConfig; - let decoderPending; + varying vec3 vWorldPosition; + varying vec3 vSunDirection; + varying float vSunfade; + varying vec3 vBetaR; + varying vec3 vBetaM; + varying float vSunE; - onmessage = function ( e ) { + // constants for atmospheric scattering + const float e = 2.71828182845904523536028747135266249775724709369995957; + const float pi = 3.141592653589793238462643383279502884197169; - const message = e.data; + // wavelength of used primaries, according to preetham + const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); + // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: + // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) + const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); - switch ( message.type ) { + // mie stuff + // K coefficient for the primaries + const float v = 4.0; + const vec3 K = vec3( 0.686, 0.678, 0.666 ); + // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K + const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { + // earth shadow hack + // cutoffAngle = pi / 1.95; + const float cutoffAngle = 1.6110731556870734; + const float steepness = 1.5; + const float EE = 1000.0; - decoderConfig.onModuleLoaded = function ( draco ) { + float sunIntensity( float zenithAngleCos ) { + zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); + return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); + } - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); + vec3 totalMie( float T ) { + float c = ( 0.2 * T ) * 10E-18; + return 0.434 * c * MieConst; + } - }; + void main() { - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef + vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); + vWorldPosition = worldPosition.xyz; - } ); - break; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + gl_Position.z = gl_Position.w; // set z to camera.far - case 'decode': - const buffer = message.buffer; - const taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { + vSunDirection = normalize( sunPosition ); - const draco = module.draco; - const decoder = new draco.Decoder(); + vSunE = sunIntensity( dot( vSunDirection, up ) ); - try { + vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); - const geometry = decodeGeometry( draco, decoder, new Int8Array( buffer ), taskConfig ); + float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); - const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); + // extinction (absorbtion + out scattering) + // rayleigh coefficients + vBetaR = totalRayleigh * rayleighCoefficient; - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); + // mie coefficients + vBetaM = totalMie( turbidity ) * mieCoefficient; - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); + }`, - } catch ( error ) { + fragmentShader: /* glsl */` + varying vec3 vWorldPosition; + varying vec3 vSunDirection; + varying float vSunfade; + varying vec3 vBetaR; + varying vec3 vBetaM; + varying float vSunE; - self.postMessage( { type: 'error', id: message.id, error: error.message } ); + uniform float mieDirectionalG; + uniform vec3 up; - } finally { + // constants for atmospheric scattering + const float pi = 3.141592653589793238462643383279502884197169; - draco.destroy( decoder ); + const float n = 1.0003; // refractive index of air + const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius) - } + // optical length at zenith for molecules + const float rayleighZenithLength = 8.4E3; + const float mieZenithLength = 1.25E3; + // 66 arc seconds -> degrees, and the cosine of that + const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; - } ); - break; + // 3.0 / ( 16.0 * pi ) + const float THREE_OVER_SIXTEENPI = 0.05968310365946075; + // 1.0 / ( 4.0 * pi ) + const float ONE_OVER_FOURPI = 0.07957747154594767; - } + float rayleighPhase( float cosTheta ) { + return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); + } - }; + float hgPhase( float cosTheta, float g ) { + float g2 = pow( g, 2.0 ); + float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); + return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse ); + } - function decodeGeometry( draco, decoder, array, taskConfig ) { + void main() { - const attributeIDs = taskConfig.attributeIDs; - const attributeTypes = taskConfig.attributeTypes; + vec3 direction = normalize( vWorldPosition - cameraPosition ); - let dracoGeometry; - let decodingStatus; + // optical length + // cutoff angle at 90 to avoid singularity in next formula. + float zenithAngle = acos( max( 0.0, dot( up, direction ) ) ); + float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); + float sR = rayleighZenithLength * inverse; + float sM = mieZenithLength * inverse; - const geometryType = decoder.GetEncodedGeometryType( array ); + // combined extinction factor + vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); - if ( geometryType === draco.TRIANGULAR_MESH ) { + // in scattering + float cosTheta = dot( direction, vSunDirection ); - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeArrayToMesh( array, array.byteLength, dracoGeometry ); + float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); + vec3 betaRTheta = vBetaR * rPhase; - } else if ( geometryType === draco.POINT_CLOUD ) { + float mPhase = hgPhase( cosTheta, mieDirectionalG ); + vec3 betaMTheta = vBetaM * mPhase; - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeArrayToPointCloud( array, array.byteLength, dracoGeometry ); + vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); + Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); - } else { + // nightsky + float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] + float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] + vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); + vec3 L0 = vec3( 0.1 ) * Fex; - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); + // composition + solar disc + float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); + L0 += ( vSunE * 19000.0 * Fex ) * sundisk; - } + vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { + vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); + gl_FragColor = vec4( retColor, 1.0 ); - } + #include + #include - const geometry = { index: null, attributes: [] }; + }` - // Gather all vertex attributes. - for ( const attributeName in attributeIDs ) { + }; - const attributeType = self[ attributeTypes[ attributeName ] ]; + // 多个canvas并没有id,只有父节点 - let attribute; - let attributeID; - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { + class Layer extends BasLayer{ + id; // 唯一标识 + layerContainer; // div#layer 容器 + zIndex=1;//默认为1 + opacity=1;//默认为1 + canvas;//canvas + dispose = false; + renderer;//canvas上下文 + scene;//场景 + visible=true;//是否可见 + mapView;//地图视图 + camera;//相机 + controls;//控件 + animateId;//动画事件id + base = false; // 是否为底图 + ambientLight; // 环境光 + directionalLight; // 方向光 + modelLayer = false; // 模型图层 + imageLayer = false; // 影像图层 + vectorLayer = false; // 矢量图层,如路网、行政区划,地名等图层 + waters = []; // 水面集合 + constructor(id, layerContainer, canvas, mapView, plane = true, camera = new three.PerspectiveCamera(80, 1, 0.1, 1e12)) { + super(); + this.id = id; + this.layerContainer = layerContainer; + this.canvas = canvas; + this.renderer = new three.WebGLRenderer({ + canvas: this.canvas, + antialias: true, + alpha: true, + logarithmicDepthBuffer: true, + precision: "highp", + }); + this.renderer.sortObjects = true; + this.renderer.setPixelRatio(window.devicePixelRatio); + this.renderer.setClearColor(0xFFFFFF, 0.0); + this.scene = new three.Scene(); + this.mapView = mapView; + this.camera = camera; + if(this.mapView){ + this.scene.add(this.mapView); + this.mapView.updateMatrixWorld(true); + } + if (plane){ + this.controls = new MapControls(this.camera, this.canvas); + this.controls.minDistance = 1e1; + this.controls.zoomSpeed = 2.0; + } else { + this.controls = new OrbitControls(this.camera, this.canvas); + this.controls.enablePan = false; + this.controls.minDistance = UnitsUtils.EARTH_RADIUS + 2; + this.controls.maxDistance = UnitsUtils.EARTH_RADIUS * 1e1; + } + this._raycaster = new three.Raycaster(); + if(Config.outLine.on){ + this.effectOutline = new EffectOutline(this.renderer, this.scene, this.camera, this.canvas.width, this.canvas.height); + } + if (Config.layer.map.ambientLight.add){ + this.scene.add(new three.AmbientLight(Config.layer.map.ambientLight.color, Config.layer.map.ambientLight.intensity)); + } + if (Config.layer.map.directionalLight.add){ + this.scene.add(new three.DirectionalLight(Config.layer.map.directionalLight.color, Config.layer.map.directionalLight.intensity)); + } + if (Config.layer.map.pointLight.add){ + let pointLight = new three.PointLight(Config.layer.map.pointLight.color, Config.layer.map.pointLight.intensity, Config.layer.map.pointLight.distance); + pointLight.position.set(...Config.layer.map.pointLight.position); + this.scene.add(pointLight); + } + } - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); + moveTo(lat, lon, height = 38472.48763833733){ + // var coords = UnitsUtils.datumsToSpherical(44.266119,90.139228); + var coords = UnitsUtils.datumsToSpherical(lat,lon); + this.camera.position.set(coords.x, height, -coords.y); + this.controls.target.set(this.camera.position.x, 0, this.camera.position.z); + } - } else { + moveToByCoords(coords){ + let offset = 50; + this.camera.position.set(coords.x, coords.y+offset, coords.z); + this.controls.target.set(coords.x, coords.y, coords.z); + } - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); + moveToByLL(lat, lon, distance = 384720){ + let dir = UnitsUtils.datumsToVector(lat, lon); + dir.multiplyScalar(UnitsUtils.EARTH_RADIUS + distance); + this.camera.position.copy(dir); + } - if ( attributeID === - 1 ) continue; - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); + on(eventName, callback){ + this.listener.on(eventName, callback); + } - } + setSceneBackground(color) { + this.scene.background = color; + } - const attributeResult = decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ); + clearSceneBackground() { + this.scene.background = null; + } - if ( attributeName === 'color' ) { + // 可用于添加灯光mesh等元素 + add(Object3D) { + if(Object3D ==null || Object3D ==undefined){ + return; + } + this.scene.add(Object3D); + } - attributeResult.vertexColorSpace = taskConfig.vertexColorSpace; - } + remove(Object3D) { + if(Object3D ==null || Object3D ==undefined){ + return; + } + this.scene.remove(Object3D); + } - geometry.attributes.push( attributeResult ); + openWaterConfig(){ + /** + * 打开渲染水系配置 + */ + // this.renderer.setPixelRatio( window.devicePixelRatio ); + this.renderer.toneMapping = three.ACESFilmicToneMapping; + this.renderer.toneMappingExposure = 0.5; + + // 添加天空 + this.sky = new Sky(); + this.sky.translateX = true; + this.sky.translateY = true; + this.sky.translateZ = true; + this.sky.rotateX = Math.PI / 2; + this.sky.scale.setScalar( Config.EARTH_RADIUS * 2 * Math.PI ); // 天空放大倍数 + this.scene.add( this.sky ); + const skyUniforms = this.sky.material.uniforms; + // 天空的配置 + skyUniforms[ 'turbidity' ].value = 10; + skyUniforms[ 'rayleigh' ].value = 2; + skyUniforms[ 'mieCoefficient' ].value = 0.005; + skyUniforms[ 'mieDirectionalG' ].value = 0.8; + // 旋转 设置为y朝上 + // sky.material.uniforms["up"].value = new THREE.Vector3(0, 1, 0); + + // 天空映射, 更新太阳位置 + this.pmremGenerator = new three.PMREMGenerator( this.renderer ); + this.sceneEnv = new three.Scene(); + this.renderTarget = null; + this.sun = new three.Vector3(); + this.updateSun(Config.SUNDEGREE, Config.SUNAZIMUTH); + } - } + updateSun(elevation, azimuth) { - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { + const phi = three.MathUtils.degToRad( 90 - elevation ); + const theta = three.MathUtils.degToRad( azimuth ); - geometry.index = decodeIndex( draco, decoder, dracoGeometry ); + this.sun.setFromSphericalCoords( 1, phi, theta ); - } + this.sky.material.uniforms[ 'sunPosition' ].value.copy( this.sun ); + for (let water of this.waters){ + water.material.uniforms[ 'sunDirection' ].value.copy( this.sun ).normalize(); + } + if ( this.renderTarget !== null ) this.renderTarget.dispose(); - draco.destroy( dracoGeometry ); + this.sceneEnv.add( this.sky ); + this.renderTarget = this.pmremGenerator.fromScene( this.sceneEnv ); + this.scene.add( this.sky ); - return geometry; + this.scene.environment = this.renderTarget.texture; - } - function decodeIndex( draco, decoder, dracoGeometry ) { + } - const numFaces = dracoGeometry.num_faces(); - const numIndices = numFaces * 3; - const byteLength = numIndices * 4; + /** + * 添加水系 + * @param {*} water + * @returns + */ + addWater(water) { + if(water ==null || water ==undefined){ + return; + } + this.scene.add(water); + this.waters.push(water); + } - const ptr = draco._malloc( byteLength ); - decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); - const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); - draco._free( ptr ); + removeWater(water) { + if(water ==null || water ==undefined){ + return; + } + this.scene.remove(water); + let index = this.waters.indexOf(water); + if (index > -1) { + this.waters.splice(index, 1); + } + } - return { array: index, itemSize: 1 }; + setVisible(visible) { + this.visible = this.visible; + this.layerContainer.style.display = visible ? 'block' : 'none'; + } + /** + * @deprecated 不建议用 + * @param {number} opacity + */ + setOpacity(opacity) { + this.opacity = opacity; + this.layerContainer.style.opacity = opacity; + } - } + /** + * 修改显示层级,默认越靠下的dom元素,显示上越靠上 + * @param {number} zIndex + */ + setZIndex(zIndex) { + this.zIndex = zIndex; + this.layerContainer.style.zIndex = this.zIndex; + } - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { + dispose() { + Element.removeLayer(id); + this.dispose = true; + this.animateId && cancelAnimationFrame(this.animateId); + this.mapView.root.dispose(); + this.mapView.dispose(); + } - const numComponents = attribute.num_components(); - const numPoints = dracoGeometry.num_points(); - const numValues = numPoints * numComponents; - const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; - const dataType = getDracoDataType( draco, attributeType ); + selectModel(insect){ + this.effectOutline.selectModel(insect); + } - const ptr = draco._malloc( byteLength ); - decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); - const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); - draco._free( ptr ); + resize(){ + var width = window.innerWidth; + var height = window.innerHeight; + this.renderer.setSize(width, height); + this.camera.aspect = width / height; + this.camera.updateProjectionMatrix(); + if(Config.outLine.on){ + this.effectOutline.resize(width, height); + } + } - return { - name: attributeName, - array: array, - itemSize: numComponents - }; + animate(){ + this.animateId = requestAnimationFrame(this.animate.bind(this)); + if(this.base){ + this.controls.update(); + } + if (this.base){ + update(); //目前只有在基础地图中添加相机移动的动画,所以暂且只在base地图中进行渲染,后期可改成每个图层都进行渲染,或者必要时进行渲染。 + } + for(let water of this.waters){ + water.material.uniforms[ 'time' ].value += 1.0 / 60.0; + } + if (Config.outLine.on){ + this.effectOutline.render(); // 合成器渲染 + } else { + this.renderer.autoClear = true; + } + this.renderer.render(this.scene, this.camera); + } - } + _raycast(meshes, recursive, faceExclude) { + const isects = this._raycaster.intersectObjects(meshes, recursive); + if (faceExclude) { + for (let i = 0; i < isects.length; i++) { + if (isects[i].face !== faceExclude) { + return isects[i]; + } + } + return null; + } + return isects.length > 0 ? isects[0] : null; + } - function getDracoDataType( draco, attributeType ) { + _raycastFromMouse(mx, my, width, height, cam, meshes, recursive=false) { + const mouse = new three.Vector2( // normalized (-1 to +1) + (mx / width) * 2 - 1, + - (my / height) * 2 + 1); + // https://threejs.org/docs/#api/core/Raycaster + // update the picking ray with the camera and mouse position + this._raycaster.setFromCamera(mouse, cam); + return this._raycast(meshes, recursive, null); + } - switch ( attributeType ) { + /** + * + * @param {*} mx 屏幕坐标x + * @param {*} my 屏幕坐标y + * @param {boolean} recursive 是否检查子节点,true 递归检查 + * @returns mesh + */ + raycastFromMouse(mx, my, recursive=false) { + //---- NG: 2x when starting with Chrome's inspector mobile + // const {width, height} = this.renderer.domElement; + // const {width, height} = this.canvas; + //---- OK + const {clientWidth, clientHeight} = this.canvas; - case Float32Array: return draco.DT_FLOAT32; - case Int8Array: return draco.DT_INT8; - case Int16Array: return draco.DT_INT16; - case Int32Array: return draco.DT_INT32; - case Uint8Array: return draco.DT_UINT8; - case Uint16Array: return draco.DT_UINT16; - case Uint32Array: return draco.DT_UINT32; + return this._raycastFromMouse( + mx, my, clientWidth, clientHeight, this.camera, + this.mapView.children, recursive); + } - } + insectALL(mx, my, recursive=false) { + const {clientWidth, clientHeight} = this.canvas; - } + return this._raycastFromMouse( + mx, my, clientWidth, clientHeight, this.camera, + this.scene.children, recursive); + } } @@ -41529,11 +36295,17 @@ Char: ${this.c}`; this.baseMap.moveToByCoords(coords); } - moveToByLL(lat, lon){ + /** + * 跳转到指定位置,用于球形地图 + * @param {*} lat + * @param {*} lon + * @returns + */ + moveToByLL(lat, lon, distance = 384720){ if(!this.baseMap){ return; } - this.baseMap.moveToByLL(lat, lon); + this.baseMap.moveToByLL(lat, lon, distance); } // 鼠标点击获取模型 @@ -41897,12 +36669,12 @@ Char: ${this.c}`; loadSkyBox(scale) { var aCubeMap = new three.CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); aCubeMap.format = three.RGBAFormat; @@ -41925,17 +36697,18 @@ Char: ${this.c}`; } loadBox(){ var cube = new three.CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); return cube; } } + exports.AngleUtils = AngleUtils; exports.Animate = Animate; exports.BingMapsProvider = BingMapsProvider; exports.CancelablePromise = CancelablePromise; diff --git a/build/wegeo.module.js b/build/wegeo.module.js index 364bef3..136bd6a 100644 --- a/build/wegeo.module.js +++ b/build/wegeo.module.js @@ -5007,6 +5007,26 @@ class Element { } } +class AngleUtils { + /** + * 弧度转角度 + * @param {*} rad + * @returns + */ + static radToDeg(rad) { + return rad * (180 / Math.PI); + } + /** + * 角度转弧度 + * @param {*} deg + * @returns + */ + static degToRad(deg) { + return deg * (Math.PI / 180); + } + +} + // OrbitControls performs orbiting, dollying (zooming), and panning. // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). // @@ -9171,7 +9191,7 @@ class EffectOutline { * @param {number} drawMode * @return {BufferGeometry} */ -function toTrianglesDrawMode$1( geometry, drawMode ) { +function toTrianglesDrawMode( geometry, drawMode ) { if ( drawMode === TrianglesDrawMode ) { return geometry; @@ -9266,7 +9286,7 @@ function toTrianglesDrawMode$1( geometry, drawMode ) { } -let GLTFLoader$1 = class GLTFLoader extends Loader { +class GLTFLoader extends Loader { constructor( manager ) { @@ -9280,97 +9300,97 @@ let GLTFLoader$1 = class GLTFLoader extends Loader { this.register( function ( parser ) { - return new GLTFMaterialsClearcoatExtension$1( parser ); + return new GLTFMaterialsClearcoatExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureBasisUExtension$1( parser ); + return new GLTFTextureBasisUExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureWebPExtension$1( parser ); + return new GLTFTextureWebPExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFTextureAVIFExtension$1( parser ); + return new GLTFTextureAVIFExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsSheenExtension$1( parser ); + return new GLTFMaterialsSheenExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsTransmissionExtension$1( parser ); + return new GLTFMaterialsTransmissionExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsVolumeExtension$1( parser ); + return new GLTFMaterialsVolumeExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsIorExtension$1( parser ); + return new GLTFMaterialsIorExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsEmissiveStrengthExtension$1( parser ); + return new GLTFMaterialsEmissiveStrengthExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsSpecularExtension$1( parser ); + return new GLTFMaterialsSpecularExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsIridescenceExtension$1( parser ); + return new GLTFMaterialsIridescenceExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsAnisotropyExtension$1( parser ); + return new GLTFMaterialsAnisotropyExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMaterialsBumpExtension$1( parser ); + return new GLTFMaterialsBumpExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFLightsExtension$1( parser ); + return new GLTFLightsExtension( parser ); } ); this.register( function ( parser ) { - return new GLTFMeshoptCompression$1( parser ); + return new GLTFMeshoptCompression( parser ); } ); this.register( function ( parser ) { - return new GLTFMeshGpuInstancing$1( parser ); + return new GLTFMeshGpuInstancing( parser ); } ); @@ -9519,11 +9539,11 @@ let GLTFLoader$1 = class GLTFLoader extends Loader { const magic = textDecoder.decode( new Uint8Array( data, 0, 4 ) ); - if ( magic === BINARY_EXTENSION_HEADER_MAGIC$1 ) { + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { try { - extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ] = new GLTFBinaryExtension$1( data ); + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); } catch ( error ) { @@ -9532,7 +9552,7 @@ let GLTFLoader$1 = class GLTFLoader extends Loader { } - json = JSON.parse( extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].content ); + json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content ); } else { @@ -9553,7 +9573,7 @@ let GLTFLoader$1 = class GLTFLoader extends Loader { } - const parser = new GLTFParser$1( json, { + const parser = new GLTFParser( json, { path: path || this.resourcePath || '', crossOrigin: this.crossOrigin, @@ -9591,20 +9611,20 @@ let GLTFLoader$1 = class GLTFLoader extends Loader { switch ( extensionName ) { - case EXTENSIONS$1.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension$1(); + case EXTENSIONS.KHR_MATERIALS_UNLIT: + extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); break; - case EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension$1( json, this.dracoLoader ); + case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: + extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); break; - case EXTENSIONS$1.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension$1(); + case EXTENSIONS.KHR_TEXTURE_TRANSFORM: + extensions[ extensionName ] = new GLTFTextureTransformExtension(); break; - case EXTENSIONS$1.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension$1(); + case EXTENSIONS.KHR_MESH_QUANTIZATION: + extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); break; default: @@ -9635,11 +9655,11 @@ let GLTFLoader$1 = class GLTFLoader extends Loader { } -}; +} /* GLTFREGISTRY */ -function GLTFRegistry$1() { +function GLTFRegistry() { let objects = {}; @@ -9677,7 +9697,7 @@ function GLTFRegistry$1() { /********** EXTENSIONS ***********/ /*********************************/ -const EXTENSIONS$1 = { +const EXTENSIONS = { KHR_BINARY_GLTF: 'KHR_binary_glTF', KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', @@ -9706,12 +9726,12 @@ const EXTENSIONS$1 = { * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual */ -let GLTFLightsExtension$1 = class GLTFLightsExtension { +class GLTFLightsExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL; + this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; // Object3D instance caches this.cache = { refs: {}, uses: {} }; @@ -9796,7 +9816,7 @@ let GLTFLightsExtension$1 = class GLTFLightsExtension { lightNode.decay = 2; - assignExtrasToUserData$1( lightNode, lightDef ); + assignExtrasToUserData( lightNode, lightDef ); if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; @@ -9837,18 +9857,18 @@ let GLTFLightsExtension$1 = class GLTFLightsExtension { } -}; +} /** * Unlit Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit */ -let GLTFMaterialsUnlitExtension$1 = class GLTFMaterialsUnlitExtension { +class GLTFMaterialsUnlitExtension { constructor() { - this.name = EXTENSIONS$1.KHR_MATERIALS_UNLIT; + this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; } @@ -9890,19 +9910,19 @@ let GLTFMaterialsUnlitExtension$1 = class GLTFMaterialsUnlitExtension { } -}; +} /** * Materials Emissive Strength Extension * * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md */ -let GLTFMaterialsEmissiveStrengthExtension$1 = class GLTFMaterialsEmissiveStrengthExtension { +class GLTFMaterialsEmissiveStrengthExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_EMISSIVE_STRENGTH; + this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH; } @@ -9929,19 +9949,19 @@ let GLTFMaterialsEmissiveStrengthExtension$1 = class GLTFMaterialsEmissiveStreng } -}; +} /** * Clearcoat Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat */ -let GLTFMaterialsClearcoatExtension$1 = class GLTFMaterialsClearcoatExtension { +class GLTFMaterialsClearcoatExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_CLEARCOAT; + this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; } @@ -10013,19 +10033,19 @@ let GLTFMaterialsClearcoatExtension$1 = class GLTFMaterialsClearcoatExtension { } -}; +} /** * Iridescence Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence */ -let GLTFMaterialsIridescenceExtension$1 = class GLTFMaterialsIridescenceExtension { +class GLTFMaterialsIridescenceExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_IRIDESCENCE; + this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE; } @@ -10101,19 +10121,19 @@ let GLTFMaterialsIridescenceExtension$1 = class GLTFMaterialsIridescenceExtensio } -}; +} /** * Sheen Materials Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen */ -let GLTFMaterialsSheenExtension$1 = class GLTFMaterialsSheenExtension { +class GLTFMaterialsSheenExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_SHEEN; + this.name = EXTENSIONS.KHR_MATERIALS_SHEEN; } @@ -10176,7 +10196,7 @@ let GLTFMaterialsSheenExtension$1 = class GLTFMaterialsSheenExtension { } -}; +} /** * Transmission Materials Extension @@ -10184,12 +10204,12 @@ let GLTFMaterialsSheenExtension$1 = class GLTFMaterialsSheenExtension { * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission * Draft: https://github.com/KhronosGroup/glTF/pull/1698 */ -let GLTFMaterialsTransmissionExtension$1 = class GLTFMaterialsTransmissionExtension { +class GLTFMaterialsTransmissionExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_TRANSMISSION; + this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; } @@ -10235,19 +10255,19 @@ let GLTFMaterialsTransmissionExtension$1 = class GLTFMaterialsTransmissionExtens } -}; +} /** * Materials Volume Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume */ -let GLTFMaterialsVolumeExtension$1 = class GLTFMaterialsVolumeExtension { +class GLTFMaterialsVolumeExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_VOLUME; + this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; } @@ -10294,19 +10314,19 @@ let GLTFMaterialsVolumeExtension$1 = class GLTFMaterialsVolumeExtension { } -}; +} /** * Materials ior Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior */ -let GLTFMaterialsIorExtension$1 = class GLTFMaterialsIorExtension { +class GLTFMaterialsIorExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_IOR; + this.name = EXTENSIONS.KHR_MATERIALS_IOR; } @@ -10340,19 +10360,19 @@ let GLTFMaterialsIorExtension$1 = class GLTFMaterialsIorExtension { } -}; +} /** * Materials specular Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular */ -let GLTFMaterialsSpecularExtension$1 = class GLTFMaterialsSpecularExtension { +class GLTFMaterialsSpecularExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_SPECULAR; + this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; } @@ -10403,7 +10423,7 @@ let GLTFMaterialsSpecularExtension$1 = class GLTFMaterialsSpecularExtension { } -}; +} /** @@ -10411,12 +10431,12 @@ let GLTFMaterialsSpecularExtension$1 = class GLTFMaterialsSpecularExtension { * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/EXT_materials_bump */ -let GLTFMaterialsBumpExtension$1 = class GLTFMaterialsBumpExtension { +class GLTFMaterialsBumpExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_MATERIALS_BUMP; + this.name = EXTENSIONS.EXT_MATERIALS_BUMP; } @@ -10458,19 +10478,19 @@ let GLTFMaterialsBumpExtension$1 = class GLTFMaterialsBumpExtension { } -}; +} /** * Materials anisotropy Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_anisotropy */ -let GLTFMaterialsAnisotropyExtension$1 = class GLTFMaterialsAnisotropyExtension { +class GLTFMaterialsAnisotropyExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_ANISOTROPY; + this.name = EXTENSIONS.KHR_MATERIALS_ANISOTROPY; } @@ -10522,19 +10542,19 @@ let GLTFMaterialsAnisotropyExtension$1 = class GLTFMaterialsAnisotropyExtension } -}; +} /** * BasisU Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu */ -let GLTFTextureBasisUExtension$1 = class GLTFTextureBasisUExtension { +class GLTFTextureBasisUExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.KHR_TEXTURE_BASISU; + this.name = EXTENSIONS.KHR_TEXTURE_BASISU; } @@ -10573,19 +10593,19 @@ let GLTFTextureBasisUExtension$1 = class GLTFTextureBasisUExtension { } -}; +} /** * WebP Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp */ -let GLTFTextureWebPExtension$1 = class GLTFTextureWebPExtension { +class GLTFTextureWebPExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_WEBP; + this.name = EXTENSIONS.EXT_TEXTURE_WEBP; this.isSupported = null; } @@ -10658,19 +10678,19 @@ let GLTFTextureWebPExtension$1 = class GLTFTextureWebPExtension { } -}; +} /** * AVIF Texture Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_avif */ -let GLTFTextureAVIFExtension$1 = class GLTFTextureAVIFExtension { +class GLTFTextureAVIFExtension { constructor( parser ) { this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_AVIF; + this.name = EXTENSIONS.EXT_TEXTURE_AVIF; this.isSupported = null; } @@ -10741,18 +10761,18 @@ let GLTFTextureAVIFExtension$1 = class GLTFTextureAVIFExtension { } -}; +} /** * meshopt BufferView Compression Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression */ -let GLTFMeshoptCompression$1 = class GLTFMeshoptCompression { +class GLTFMeshoptCompression { constructor( parser ) { - this.name = EXTENSIONS$1.EXT_MESHOPT_COMPRESSION; + this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; this.parser = parser; } @@ -10825,7 +10845,7 @@ let GLTFMeshoptCompression$1 = class GLTFMeshoptCompression { } -}; +} /** * GPU Instancing Extension @@ -10833,11 +10853,11 @@ let GLTFMeshoptCompression$1 = class GLTFMeshoptCompression { * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing * */ -let GLTFMeshGpuInstancing$1 = class GLTFMeshGpuInstancing { +class GLTFMeshGpuInstancing { constructor( parser ) { - this.name = EXTENSIONS$1.EXT_MESH_GPU_INSTANCING; + this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING; this.parser = parser; } @@ -10860,9 +10880,9 @@ let GLTFMeshGpuInstancing$1 = class GLTFMeshGpuInstancing { for ( const primitive of meshDef.primitives ) { - if ( primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLES && - primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLE_STRIP && - primitive.mode !== WEBGL_CONSTANTS$1.TRIANGLE_FAN && + if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES && + primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP && + primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN && primitive.mode !== undefined ) { return null; @@ -10982,22 +11002,22 @@ let GLTFMeshGpuInstancing$1 = class GLTFMeshGpuInstancing { } -}; +} /* BINARY EXTENSION */ -const BINARY_EXTENSION_HEADER_MAGIC$1 = 'glTF'; -const BINARY_EXTENSION_HEADER_LENGTH$1 = 12; -const BINARY_EXTENSION_CHUNK_TYPES$1 = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; +const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; +const BINARY_EXTENSION_HEADER_LENGTH = 12; +const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; -let GLTFBinaryExtension$1 = class GLTFBinaryExtension { +class GLTFBinaryExtension { constructor( data ) { - this.name = EXTENSIONS$1.KHR_BINARY_GLTF; + this.name = EXTENSIONS.KHR_BINARY_GLTF; this.content = null; this.body = null; - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH$1 ); + const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); const textDecoder = new TextDecoder(); this.header = { @@ -11006,7 +11026,7 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { length: headerView.getUint32( 8, true ) }; - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC$1 ) { + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); @@ -11016,8 +11036,8 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { } - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH$1; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH$1 ); + const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; + const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); let chunkIndex = 0; while ( chunkIndex < chunkContentsLength ) { @@ -11028,14 +11048,14 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { const chunkType = chunkView.getUint32( chunkIndex, true ); chunkIndex += 4; - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES$1.JSON ) { + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex, chunkLength ); + const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); this.content = textDecoder.decode( contentArray ); - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES$1.BIN ) { + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex; + const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; this.body = data.slice( byteOffset, byteOffset + chunkLength ); } @@ -11054,14 +11074,14 @@ let GLTFBinaryExtension$1 = class GLTFBinaryExtension { } -}; +} /** * DRACO Mesh Compression Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression */ -let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtension { +class GLTFDracoMeshCompressionExtension { constructor( json, dracoLoader ) { @@ -11071,7 +11091,7 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio } - this.name = EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION; + this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; this.json = json; this.dracoLoader = dracoLoader; this.dracoLoader.preload(); @@ -11090,7 +11110,7 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio for ( const attributeName in gltfAttributeMap ) { - const threeAttributeName = ATTRIBUTES$1[ attributeName ] || attributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; @@ -11098,12 +11118,12 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio for ( const attributeName in primitive.attributes ) { - const threeAttributeName = ATTRIBUTES$1[ attributeName ] || attributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); if ( gltfAttributeMap[ attributeName ] !== undefined ) { const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; attributeTypeMap[ threeAttributeName ] = componentType.name; attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; @@ -11137,18 +11157,18 @@ let GLTFDracoMeshCompressionExtension$1 = class GLTFDracoMeshCompressionExtensio } -}; +} /** * Texture Transform Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform */ -let GLTFTextureTransformExtension$1 = class GLTFTextureTransformExtension { +class GLTFTextureTransformExtension { constructor() { - this.name = EXTENSIONS$1.KHR_TEXTURE_TRANSFORM; + this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; } @@ -11196,22 +11216,22 @@ let GLTFTextureTransformExtension$1 = class GLTFTextureTransformExtension { } -}; +} /** * Mesh Quantization Extension * * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization */ -let GLTFMeshQuantizationExtension$1 = class GLTFMeshQuantizationExtension { +class GLTFMeshQuantizationExtension { constructor() { - this.name = EXTENSIONS$1.KHR_MESH_QUANTIZATION; + this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; } -}; +} /*********************************/ /********** INTERPOLATION ********/ @@ -11219,7 +11239,7 @@ let GLTFMeshQuantizationExtension$1 = class GLTFMeshQuantizationExtension { // Spline Interpolation // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation -let GLTFCubicSplineInterpolant$1 = class GLTFCubicSplineInterpolant extends Interpolant { +class GLTFCubicSplineInterpolant extends Interpolant { constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { @@ -11287,23 +11307,23 @@ let GLTFCubicSplineInterpolant$1 = class GLTFCubicSplineInterpolant extends Inte } -}; +} -const _q$1 = new Quaternion(); +const _q = new Quaternion(); -let GLTFCubicSplineQuaternionInterpolant$1 = class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant$1 { +class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { interpolate_( i1, t0, t, t1 ) { const result = super.interpolate_( i1, t0, t, t1 ); - _q$1.fromArray( result ).normalize().toArray( result ); + _q.fromArray( result ).normalize().toArray( result ); return result; } -}; +} /*********************************/ @@ -11312,7 +11332,7 @@ let GLTFCubicSplineQuaternionInterpolant$1 = class GLTFCubicSplineQuaternionInte /* CONSTANTS */ -const WEBGL_CONSTANTS$1 = { +const WEBGL_CONSTANTS = { FLOAT: 5126, //FLOAT_MAT2: 35674, FLOAT_MAT3: 35675, @@ -11334,7 +11354,7 @@ const WEBGL_CONSTANTS$1 = { UNSIGNED_SHORT: 5123 }; -const WEBGL_COMPONENT_TYPES$1 = { +const WEBGL_COMPONENT_TYPES = { 5120: Int8Array, 5121: Uint8Array, 5122: Int16Array, @@ -11343,7 +11363,7 @@ const WEBGL_COMPONENT_TYPES$1 = { 5126: Float32Array }; -const WEBGL_FILTERS$1 = { +const WEBGL_FILTERS = { 9728: NearestFilter, 9729: LinearFilter, 9984: NearestMipmapNearestFilter, @@ -11352,13 +11372,13 @@ const WEBGL_FILTERS$1 = { 9987: LinearMipmapLinearFilter }; -const WEBGL_WRAPPINGS$1 = { +const WEBGL_WRAPPINGS = { 33071: ClampToEdgeWrapping, 33648: MirroredRepeatWrapping, 10497: RepeatWrapping }; -const WEBGL_TYPE_SIZES$1 = { +const WEBGL_TYPE_SIZES = { 'SCALAR': 1, 'VEC2': 2, 'VEC3': 3, @@ -11368,7 +11388,7 @@ const WEBGL_TYPE_SIZES$1 = { 'MAT4': 16 }; -const ATTRIBUTES$1 = { +const ATTRIBUTES = { POSITION: 'position', NORMAL: 'normal', TANGENT: 'tangent', @@ -11381,21 +11401,21 @@ const ATTRIBUTES$1 = { JOINTS_0: 'skinIndex', }; -const PATH_PROPERTIES$1 = { +const PATH_PROPERTIES = { scale: 'scale', translation: 'position', rotation: 'quaternion', weights: 'morphTargetInfluences' }; -const INTERPOLATION$1 = { +const INTERPOLATION = { CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each // keyframe track will be initialized with a default interpolation type, then modified. LINEAR: InterpolateLinear, STEP: InterpolateDiscrete }; -const ALPHA_MODES$1 = { +const ALPHA_MODES = { OPAQUE: 'OPAQUE', MASK: 'MASK', BLEND: 'BLEND' @@ -11404,7 +11424,7 @@ const ALPHA_MODES$1 = { /** * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material */ -function createDefaultMaterial$1( cache ) { +function createDefaultMaterial( cache ) { if ( cache[ 'DefaultMaterial' ] === undefined ) { @@ -11424,7 +11444,7 @@ function createDefaultMaterial$1( cache ) { } -function addUnknownExtensionsToUserData$1( knownExtensions, object, objectDef ) { +function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { // Add unknown glTF extensions to an object's userData. @@ -11445,7 +11465,7 @@ function addUnknownExtensionsToUserData$1( knownExtensions, object, objectDef ) * @param {Object3D|Material|BufferGeometry} object * @param {GLTF.definition} gltfDef */ -function assignExtrasToUserData$1( object, gltfDef ) { +function assignExtrasToUserData( object, gltfDef ) { if ( gltfDef.extras !== undefined ) { @@ -11467,7 +11487,7 @@ function assignExtrasToUserData$1( object, gltfDef ) { * @param {GLTFParser} parser * @return {Promise} */ -function addMorphTargets$1( geometry, targets, parser ) { +function addMorphTargets( geometry, targets, parser ) { let hasMorphPosition = false; let hasMorphNormal = false; @@ -11552,7 +11572,7 @@ function addMorphTargets$1( geometry, targets, parser ) { * @param {Mesh} mesh * @param {GLTF.Mesh} meshDef */ -function updateMorphTargets$1( mesh, meshDef ) { +function updateMorphTargets( mesh, meshDef ) { mesh.updateMorphTargets(); @@ -11587,21 +11607,21 @@ function updateMorphTargets$1( mesh, meshDef ) { } -function createPrimitiveKey$1( primitiveDef ) { +function createPrimitiveKey( primitiveDef ) { let geometryKey; - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ]; + const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; if ( dracoExtension ) { geometryKey = 'draco:' + dracoExtension.bufferView + ':' + dracoExtension.indices - + ':' + createAttributesKey$1( dracoExtension.attributes ); + + ':' + createAttributesKey( dracoExtension.attributes ); } else { - geometryKey = primitiveDef.indices + ':' + createAttributesKey$1( primitiveDef.attributes ) + ':' + primitiveDef.mode; + geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; } @@ -11609,7 +11629,7 @@ function createPrimitiveKey$1( primitiveDef ) { for ( let i = 0, il = primitiveDef.targets.length; i < il; i ++ ) { - geometryKey += ':' + createAttributesKey$1( primitiveDef.targets[ i ] ); + geometryKey += ':' + createAttributesKey( primitiveDef.targets[ i ] ); } @@ -11619,7 +11639,7 @@ function createPrimitiveKey$1( primitiveDef ) { } -function createAttributesKey$1( attributes ) { +function createAttributesKey( attributes ) { let attributesKey = ''; @@ -11635,7 +11655,7 @@ function createAttributesKey$1( attributes ) { } -function getNormalizedComponentScale$1( constructor ) { +function getNormalizedComponentScale( constructor ) { // Reference: // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data @@ -11661,7 +11681,7 @@ function getNormalizedComponentScale$1( constructor ) { } -function getImageURIMimeType$1( uri ) { +function getImageURIMimeType( uri ) { if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; @@ -11670,11 +11690,11 @@ function getImageURIMimeType$1( uri ) { } -const _identityMatrix$1 = new Matrix4(); +const _identityMatrix = new Matrix4(); /* GLTF PARSER */ -let GLTFParser$1 = class GLTFParser { +class GLTFParser { constructor( json = {}, options = {} ) { @@ -11684,7 +11704,7 @@ let GLTFParser$1 = class GLTFParser { this.options = options; // loader object cache - this.cache = new GLTFRegistry$1(); + this.cache = new GLTFRegistry(); // associations between Three.js objects and glTF elements this.associations = new Map(); @@ -11800,9 +11820,9 @@ let GLTFParser$1 = class GLTFParser { userData: {} }; - addUnknownExtensionsToUserData$1( extensions, result, json ); + addUnknownExtensionsToUserData( extensions, result, json ); - assignExtrasToUserData$1( result, json ); + assignExtrasToUserData( result, json ); return Promise.all( parser._invokeAll( function ( ext ) { @@ -12120,7 +12140,7 @@ let GLTFParser$1 = class GLTFParser { // If present, GLB container is required to be the first buffer. if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - return Promise.resolve( this.extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].body ); + return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); } @@ -12171,8 +12191,8 @@ let GLTFParser$1 = class GLTFParser { if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; const normalized = accessorDef.normalized === true; const array = new TypedArray( accessorDef.count * itemSize ); @@ -12203,8 +12223,8 @@ let GLTFParser$1 = class GLTFParser { const bufferView = bufferViews[ 0 ]; - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. const elementBytes = TypedArray.BYTES_PER_ELEMENT; @@ -12255,8 +12275,8 @@ let GLTFParser$1 = class GLTFParser { // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors if ( accessorDef.sparse !== undefined ) { - const itemSizeIndices = WEBGL_TYPE_SIZES$1.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES$1[ accessorDef.sparse.indices.componentType ]; + const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; + const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; @@ -12349,10 +12369,10 @@ let GLTFParser$1 = class GLTFParser { const samplers = json.samplers || {}; const sampler = samplers[ textureDef.sampler ] || {}; - texture.magFilter = WEBGL_FILTERS$1[ sampler.magFilter ] || LinearFilter; - texture.minFilter = WEBGL_FILTERS$1[ sampler.minFilter ] || LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS$1[ sampler.wrapS ] || RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS$1[ sampler.wrapT ] || RepeatWrapping; + texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || LinearFilter; + texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || LinearMipmapLinearFilter; + texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || RepeatWrapping; + texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || RepeatWrapping; parser.associations.set( texture, { textures: textureIndex } ); @@ -12441,7 +12461,7 @@ let GLTFParser$1 = class GLTFParser { } - texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType$1( sourceDef.uri ); + texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); return texture; @@ -12477,14 +12497,14 @@ let GLTFParser$1 = class GLTFParser { } - if ( parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] ) { + if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] : undefined; + const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; if ( transform ) { const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); + texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); parser.associations.set( texture, gltfReference ); } @@ -12627,9 +12647,9 @@ let GLTFParser$1 = class GLTFParser { const pending = []; - if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ] ) { + if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - const kmuExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ]; + const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; materialType = kmuExtension.getMaterialType(); pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); @@ -12688,9 +12708,9 @@ let GLTFParser$1 = class GLTFParser { } - const alphaMode = materialDef.alphaMode || ALPHA_MODES$1.OPAQUE; + const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - if ( alphaMode === ALPHA_MODES$1.BLEND ) { + if ( alphaMode === ALPHA_MODES.BLEND ) { materialParams.transparent = true; @@ -12701,7 +12721,7 @@ let GLTFParser$1 = class GLTFParser { materialParams.transparent = false; - if ( alphaMode === ALPHA_MODES$1.MASK ) { + if ( alphaMode === ALPHA_MODES.MASK ) { materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; @@ -12756,11 +12776,11 @@ let GLTFParser$1 = class GLTFParser { if ( materialDef.name ) material.name = materialDef.name; - assignExtrasToUserData$1( material, materialDef ); + assignExtrasToUserData( material, materialDef ); parser.associations.set( material, { materials: materialIndex } ); - if ( materialDef.extensions ) addUnknownExtensionsToUserData$1( extensions, material, materialDef ); + if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); return material; @@ -12803,11 +12823,11 @@ let GLTFParser$1 = class GLTFParser { function createDracoPrimitive( primitive ) { - return extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] + return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] .decodePrimitive( primitive, parser ) .then( function ( geometry ) { - return addPrimitiveAttributes$1( geometry, primitive, parser ); + return addPrimitiveAttributes( geometry, primitive, parser ); } ); @@ -12818,7 +12838,7 @@ let GLTFParser$1 = class GLTFParser { for ( let i = 0, il = primitives.length; i < il; i ++ ) { const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey$1( primitive ); + const cacheKey = createPrimitiveKey( primitive ); // See if we've already created this geometry const cached = cache[ cacheKey ]; @@ -12832,7 +12852,7 @@ let GLTFParser$1 = class GLTFParser { let geometryPromise; - if ( primitive.extensions && primitive.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] ) { + if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { // Use DRACO geometry if available geometryPromise = createDracoPrimitive( primitive ); @@ -12840,7 +12860,7 @@ let GLTFParser$1 = class GLTFParser { } else { // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes$1( new BufferGeometry(), primitive, parser ); + geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); } @@ -12876,7 +12896,7 @@ let GLTFParser$1 = class GLTFParser { for ( let i = 0, il = primitives.length; i < il; i ++ ) { const material = primitives[ i ].material === undefined - ? createDefaultMaterial$1( this.cache ) + ? createDefaultMaterial( this.cache ) : this.getDependency( 'material', primitives[ i ].material ); pending.push( material ); @@ -12903,9 +12923,9 @@ let GLTFParser$1 = class GLTFParser { const material = materials[ i ]; - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || primitive.mode === undefined ) { // .isSkinnedMesh isn't in glTF spec. See ._markDefs() @@ -12920,29 +12940,29 @@ let GLTFParser$1 = class GLTFParser { } - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - mesh.geometry = toTrianglesDrawMode$1( mesh.geometry, TriangleStripDrawMode ); + mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - mesh.geometry = toTrianglesDrawMode$1( mesh.geometry, TriangleFanDrawMode ); + mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); } - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { mesh = new LineSegments( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { mesh = new Line( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { mesh = new LineLoop( geometry, material ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { mesh = new Points( geometry, material ); @@ -12954,15 +12974,15 @@ let GLTFParser$1 = class GLTFParser { if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - updateMorphTargets$1( mesh, meshDef ); + updateMorphTargets( mesh, meshDef ); } mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); - assignExtrasToUserData$1( mesh, meshDef ); + assignExtrasToUserData( mesh, meshDef ); - if ( primitive.extensions ) addUnknownExtensionsToUserData$1( extensions, mesh, primitive ); + if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); parser.assignFinalMaterial( mesh ); @@ -12981,7 +13001,7 @@ let GLTFParser$1 = class GLTFParser { if ( meshes.length === 1 ) { - if ( meshDef.extensions ) addUnknownExtensionsToUserData$1( extensions, meshes[ 0 ], meshDef ); + if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, meshes[ 0 ], meshDef ); return meshes[ 0 ]; @@ -12989,7 +13009,7 @@ let GLTFParser$1 = class GLTFParser { const group = new Group$1(); - if ( meshDef.extensions ) addUnknownExtensionsToUserData$1( extensions, group, meshDef ); + if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, group, meshDef ); parser.associations.set( group, { meshes: meshIndex } ); @@ -13033,7 +13053,7 @@ let GLTFParser$1 = class GLTFParser { if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); - assignExtrasToUserData$1( camera, cameraDef ); + assignExtrasToUserData( camera, cameraDef ); return Promise.resolve( camera ); @@ -13277,7 +13297,7 @@ let GLTFParser$1 = class GLTFParser { if ( ! mesh.isSkinnedMesh ) return; - mesh.bind( skeleton, _identityMatrix$1 ); + mesh.bind( skeleton, _identityMatrix ); } ); @@ -13391,9 +13411,9 @@ let GLTFParser$1 = class GLTFParser { } - assignExtrasToUserData$1( node, nodeDef ); + assignExtrasToUserData( node, nodeDef ); - if ( nodeDef.extensions ) addUnknownExtensionsToUserData$1( extensions, node, nodeDef ); + if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); if ( nodeDef.matrix !== undefined ) { @@ -13455,9 +13475,9 @@ let GLTFParser$1 = class GLTFParser { const scene = new Group$1(); if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); - assignExtrasToUserData$1( scene, sceneDef ); + assignExtrasToUserData( scene, sceneDef ); - if ( sceneDef.extensions ) addUnknownExtensionsToUserData$1( extensions, scene, sceneDef ); + if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); const nodeIds = sceneDef.nodes || []; @@ -13524,7 +13544,7 @@ let GLTFParser$1 = class GLTFParser { const targetName = node.name ? node.name : node.uuid; const targetNames = []; - if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { + if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { node.traverse( function ( object ) { @@ -13544,20 +13564,20 @@ let GLTFParser$1 = class GLTFParser { let TypedKeyframeTrack; - switch ( PATH_PROPERTIES$1[ target.path ] ) { + switch ( PATH_PROPERTIES[ target.path ] ) { - case PATH_PROPERTIES$1.weights: + case PATH_PROPERTIES.weights: TypedKeyframeTrack = NumberKeyframeTrack; break; - case PATH_PROPERTIES$1.rotation: + case PATH_PROPERTIES.rotation: TypedKeyframeTrack = QuaternionKeyframeTrack; break; - case PATH_PROPERTIES$1.position: - case PATH_PROPERTIES$1.scale: + case PATH_PROPERTIES.position: + case PATH_PROPERTIES.scale: TypedKeyframeTrack = VectorKeyframeTrack; break; @@ -13581,7 +13601,7 @@ let GLTFParser$1 = class GLTFParser { } - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION$1[ sampler.interpolation ] : InterpolateLinear; + const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear; const outputArray = this._getArrayFromAccessor( outputAccessor ); @@ -13589,7 +13609,7 @@ let GLTFParser$1 = class GLTFParser { for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], + targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], inputAccessor.array, outputArray, interpolation @@ -13616,7 +13636,7 @@ let GLTFParser$1 = class GLTFParser { if ( accessor.normalized ) { - const scale = getNormalizedComponentScale$1( outputArray.constructor ); + const scale = getNormalizedComponentScale( outputArray.constructor ); const scaled = new Float32Array( outputArray.length ); for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { @@ -13641,7 +13661,7 @@ let GLTFParser$1 = class GLTFParser { // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() // must be divided by three to get the interpolant's sampleSize argument. - const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant$1 : GLTFCubicSplineInterpolant$1; + const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); @@ -13652,14 +13672,14 @@ let GLTFParser$1 = class GLTFParser { } -}; +} /** * @param {BufferGeometry} geometry * @param {GLTF.Primitive} primitiveDef * @param {GLTFParser} parser */ -function computeBounds$1( geometry, primitiveDef, parser ) { +function computeBounds( geometry, primitiveDef, parser ) { const attributes = primitiveDef.attributes; @@ -13683,7 +13703,7 @@ function computeBounds$1( geometry, primitiveDef, parser ) { if ( accessor.normalized ) { - const boxScale = getNormalizedComponentScale$1( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); box.min.multiplyScalar( boxScale ); box.max.multiplyScalar( boxScale ); @@ -13730,7 +13750,7 @@ function computeBounds$1( geometry, primitiveDef, parser ) { if ( accessor.normalized ) { - const boxScale = getNormalizedComponentScale$1( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); vector.multiplyScalar( boxScale ); } @@ -13769,7 +13789,7 @@ function computeBounds$1( geometry, primitiveDef, parser ) { * @param {GLTFParser} parser * @return {Promise} */ -function addPrimitiveAttributes$1( geometry, primitiveDef, parser ) { +function addPrimitiveAttributes( geometry, primitiveDef, parser ) { const attributes = primitiveDef.attributes; @@ -13788,7 +13808,7 @@ function addPrimitiveAttributes$1( geometry, primitiveDef, parser ) { for ( const gltfAttributeName in attributes ) { - const threeAttributeName = ATTRIBUTES$1[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); + const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); // Skip attributes already provided by e.g. Draco extension. if ( threeAttributeName in geometry.attributes ) continue; @@ -13811,23 +13831,23 @@ function addPrimitiveAttributes$1( geometry, primitiveDef, parser ) { if ( ColorManagement.workingColorSpace !== LinearSRGBColorSpace && 'COLOR_0' in attributes ) ; - assignExtrasToUserData$1( geometry, primitiveDef ); + assignExtrasToUserData( geometry, primitiveDef ); - computeBounds$1( geometry, primitiveDef, parser ); + computeBounds( geometry, primitiveDef, parser ); return Promise.all( pending ).then( function () { return primitiveDef.targets !== undefined - ? addMorphTargets$1( geometry, primitiveDef.targets, parser ) + ? addMorphTargets( geometry, primitiveDef.targets, parser ) : geometry; } ); } -const _taskCache$2 = new WeakMap(); +const _taskCache$1 = new WeakMap(); -let DRACOLoader$1 = class DRACOLoader extends Loader { +class DRACOLoader extends Loader { constructor( manager ) { @@ -13925,9 +13945,9 @@ let DRACOLoader$1 = class DRACOLoader extends Loader { // Check for an existing task using this buffer. A transferred buffer cannot be transferred // again from this thread. - if ( _taskCache$2.has( buffer ) ) { + if ( _taskCache$1.has( buffer ) ) { - const cachedTask = _taskCache$2.get( buffer ); + const cachedTask = _taskCache$1.get( buffer ); if ( cachedTask.key === taskKey ) { @@ -13993,7 +14013,7 @@ let DRACOLoader$1 = class DRACOLoader extends Loader { } ); // Cache the task result. - _taskCache$2.set( buffer, { + _taskCache$1.set( buffer, { key: taskKey, promise: geometryPending @@ -14111,7 +14131,7 @@ let DRACOLoader$1 = class DRACOLoader extends Loader { } - const fn = DRACOWorker$1.toString(); + const fn = DRACOWorker.toString(); const body = [ '/* draco decoder */', @@ -14214,11 +14234,11 @@ let DRACOLoader$1 = class DRACOLoader extends Loader { } -}; +} /* WEB WORKER */ -function DRACOWorker$1() { +function DRACOWorker() { let decoderConfig; let decoderPending; @@ -18437,10 +18457,10 @@ class ModelLoader { } class Lorder{ constructor() { - this.gltfLoader = new GLTFLoader$1(); + this.gltfLoader = new GLTFLoader(); // Optional: Provide a DRACOLoader instance to decode compressed mesh data - const dracoLoader = new DRACOLoader$1(); + const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath( Config.DRACOPath ); this.gltfLoader.setDRACOLoader( dracoLoader ); this.objLoader = new OBJLoader(); // obj模型 @@ -18686,5725 +18706,471 @@ Sky.SkyShader = { uniform float mieCoefficient; uniform vec3 up; - varying vec3 vWorldPosition; - varying vec3 vSunDirection; - varying float vSunfade; - varying vec3 vBetaR; - varying vec3 vBetaM; - varying float vSunE; - - // constants for atmospheric scattering - const float e = 2.71828182845904523536028747135266249775724709369995957; - const float pi = 3.141592653589793238462643383279502884197169; - - // wavelength of used primaries, according to preetham - const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); - // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: - // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) - const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); - - // mie stuff - // K coefficient for the primaries - const float v = 4.0; - const vec3 K = vec3( 0.686, 0.678, 0.666 ); - // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K - const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); - - // earth shadow hack - // cutoffAngle = pi / 1.95; - const float cutoffAngle = 1.6110731556870734; - const float steepness = 1.5; - const float EE = 1000.0; - - float sunIntensity( float zenithAngleCos ) { - zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); - return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); - } - - vec3 totalMie( float T ) { - float c = ( 0.2 * T ) * 10E-18; - return 0.434 * c * MieConst; - } - - void main() { - - vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); - vWorldPosition = worldPosition.xyz; - - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - gl_Position.z = gl_Position.w; // set z to camera.far - - vSunDirection = normalize( sunPosition ); - - vSunE = sunIntensity( dot( vSunDirection, up ) ); - - vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); - - float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); - - // extinction (absorbtion + out scattering) - // rayleigh coefficients - vBetaR = totalRayleigh * rayleighCoefficient; - - // mie coefficients - vBetaM = totalMie( turbidity ) * mieCoefficient; - - }`, - - fragmentShader: /* glsl */` - varying vec3 vWorldPosition; - varying vec3 vSunDirection; - varying float vSunfade; - varying vec3 vBetaR; - varying vec3 vBetaM; - varying float vSunE; - - uniform float mieDirectionalG; - uniform vec3 up; - - // constants for atmospheric scattering - const float pi = 3.141592653589793238462643383279502884197169; - - const float n = 1.0003; // refractive index of air - const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius) - - // optical length at zenith for molecules - const float rayleighZenithLength = 8.4E3; - const float mieZenithLength = 1.25E3; - // 66 arc seconds -> degrees, and the cosine of that - const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; - - // 3.0 / ( 16.0 * pi ) - const float THREE_OVER_SIXTEENPI = 0.05968310365946075; - // 1.0 / ( 4.0 * pi ) - const float ONE_OVER_FOURPI = 0.07957747154594767; - - float rayleighPhase( float cosTheta ) { - return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); - } - - float hgPhase( float cosTheta, float g ) { - float g2 = pow( g, 2.0 ); - float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); - return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse ); - } - - void main() { - - vec3 direction = normalize( vWorldPosition - cameraPosition ); - - // optical length - // cutoff angle at 90 to avoid singularity in next formula. - float zenithAngle = acos( max( 0.0, dot( up, direction ) ) ); - float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); - float sR = rayleighZenithLength * inverse; - float sM = mieZenithLength * inverse; - - // combined extinction factor - vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); - - // in scattering - float cosTheta = dot( direction, vSunDirection ); - - float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); - vec3 betaRTheta = vBetaR * rPhase; - - float mPhase = hgPhase( cosTheta, mieDirectionalG ); - vec3 betaMTheta = vBetaM * mPhase; - - vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); - Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); - - // nightsky - float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] - float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] - vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); - vec3 L0 = vec3( 0.1 ) * Fex; - - // composition + solar disc - float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); - L0 += ( vSunE * 19000.0 * Fex ) * sundisk; - - vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); - - vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); - - gl_FragColor = vec4( retColor, 1.0 ); - - #include - #include - - }` - -}; - -// 多个canvas并没有id,只有父节点 - - -class Layer extends BasLayer{ - id; // 唯一标识 - layerContainer; // div#layer 容器 - zIndex=1;//默认为1 - opacity=1;//默认为1 - canvas;//canvas - dispose = false; - renderer;//canvas上下文 - scene;//场景 - visible=true;//是否可见 - mapView;//地图视图 - camera;//相机 - controls;//控件 - animateId;//动画事件id - base = false; // 是否为底图 - ambientLight; // 环境光 - directionalLight; // 方向光 - modelLayer = false; // 模型图层 - imageLayer = false; // 影像图层 - vectorLayer = false; // 矢量图层,如路网、行政区划,地名等图层 - waters = []; // 水面集合 - constructor(id, layerContainer, canvas, mapView, plane = true, camera = new PerspectiveCamera(80, 1, 0.1, 1e12)) { - super(); - this.id = id; - this.layerContainer = layerContainer; - this.canvas = canvas; - this.renderer = new WebGLRenderer({ - canvas: this.canvas, - antialias: true, - alpha: true, - logarithmicDepthBuffer: true, - precision: "highp", - }); - this.renderer.sortObjects = true; - this.renderer.setPixelRatio(window.devicePixelRatio); - this.renderer.setClearColor(0xFFFFFF, 0.0); - this.scene = new Scene(); - this.mapView = mapView; - this.camera = camera; - if(this.mapView){ - this.scene.add(this.mapView); - this.mapView.updateMatrixWorld(true); - } - if (plane){ - this.controls = new MapControls(this.camera, this.canvas); - this.controls.minDistance = 1e1; - this.controls.zoomSpeed = 2.0; - } else { - this.controls = new OrbitControls(this.camera, this.canvas); - this.controls.enablePan = false; - this.controls.minDistance = UnitsUtils.EARTH_RADIUS + 2; - this.controls.maxDistance = UnitsUtils.EARTH_RADIUS * 1e1; - } - this._raycaster = new Raycaster(); - if(Config.outLine.on){ - this.effectOutline = new EffectOutline(this.renderer, this.scene, this.camera, this.canvas.width, this.canvas.height); - } - if (Config.layer.map.ambientLight.add){ - this.scene.add(new AmbientLight(Config.layer.map.ambientLight.color, Config.layer.map.ambientLight.intensity)); - } - if (Config.layer.map.directionalLight.add){ - this.scene.add(new DirectionalLight(Config.layer.map.directionalLight.color, Config.layer.map.directionalLight.intensity)); - } - if (Config.layer.map.pointLight.add){ - let pointLight = new PointLight(Config.layer.map.pointLight.color, Config.layer.map.pointLight.intensity, Config.layer.map.pointLight.distance); - pointLight.position.set(...Config.layer.map.pointLight.position); - this.scene.add(pointLight); - } - } - - moveTo(lat, lon, height = 38472.48763833733){ - // var coords = UnitsUtils.datumsToSpherical(44.266119,90.139228); - var coords = UnitsUtils.datumsToSpherical(lat,lon); - this.camera.position.set(coords.x, height, -coords.y); - this.controls.target.set(this.camera.position.x, 0, this.camera.position.z); - } - - moveToByCoords(coords){ - let offset = 50; - this.camera.position.set(coords.x, coords.y+offset, coords.z); - this.controls.target.set(coords.x, coords.y, coords.z); - } - - moveToByLL(lat, lon, distance = 384720){ - let dir = UnitsUtils.datumsToVector(lat, lon); - dir.multiplyScalar(UnitsUtils.EARTH_RADIUS + distance); - this.camera.position.copy(dir); - } - - - on(eventName, callback){ - this.listener.on(eventName, callback); - } - - setSceneBackground(color) { - this.scene.background = color; - } - - clearSceneBackground() { - this.scene.background = null; - } - - // 可用于添加灯光mesh等元素 - add(Object3D) { - if(Object3D ==null || Object3D ==undefined){ - return; - } - this.scene.add(Object3D); - } - - - remove(Object3D) { - if(Object3D ==null || Object3D ==undefined){ - return; - } - this.scene.remove(Object3D); - } - - openWaterConfig(){ - /** - * 打开渲染水系配置 - */ - // this.renderer.setPixelRatio( window.devicePixelRatio ); - this.renderer.toneMapping = ACESFilmicToneMapping; - this.renderer.toneMappingExposure = 0.5; - - // 添加天空 - this.sky = new Sky(); - this.sky.translateX = true; - this.sky.translateY = true; - this.sky.translateZ = true; - this.sky.rotateX = Math.PI / 2; - this.sky.scale.setScalar( Config.EARTH_RADIUS * 2 * Math.PI ); // 天空放大倍数 - this.scene.add( this.sky ); - const skyUniforms = this.sky.material.uniforms; - // 天空的配置 - skyUniforms[ 'turbidity' ].value = 10; - skyUniforms[ 'rayleigh' ].value = 2; - skyUniforms[ 'mieCoefficient' ].value = 0.005; - skyUniforms[ 'mieDirectionalG' ].value = 0.8; - // 旋转 设置为y朝上 - // sky.material.uniforms["up"].value = new THREE.Vector3(0, 1, 0); - - // 天空映射, 更新太阳位置 - this.pmremGenerator = new PMREMGenerator( this.renderer ); - this.sceneEnv = new Scene(); - this.renderTarget = null; - this.sun = new Vector3(); - this.updateSun(Config.SUNDEGREE, Config.SUNAZIMUTH); - } - - updateSun(elevation, azimuth) { - - const phi = MathUtils.degToRad( 90 - elevation ); - const theta = MathUtils.degToRad( azimuth ); - - this.sun.setFromSphericalCoords( 1, phi, theta ); - - this.sky.material.uniforms[ 'sunPosition' ].value.copy( this.sun ); - for (let water of this.waters){ - water.material.uniforms[ 'sunDirection' ].value.copy( this.sun ).normalize(); - } - if ( this.renderTarget !== null ) this.renderTarget.dispose(); - - this.sceneEnv.add( this.sky ); - this.renderTarget = this.pmremGenerator.fromScene( this.sceneEnv ); - this.scene.add( this.sky ); - - this.scene.environment = this.renderTarget.texture; - - - } - - /** - * 添加水系 - * @param {*} water - * @returns - */ - addWater(water) { - if(water ==null || water ==undefined){ - return; - } - this.scene.add(water); - this.waters.push(water); - } - - removeWater(water) { - if(water ==null || water ==undefined){ - return; - } - this.scene.remove(water); - let index = this.waters.indexOf(water); - if (index > -1) { - this.waters.splice(index, 1); - } - } - - setVisible(visible) { - this.visible = this.visible; - this.layerContainer.style.display = visible ? 'block' : 'none'; - } - /** - * @deprecated 不建议用 - * @param {number} opacity - */ - setOpacity(opacity) { - this.opacity = opacity; - this.layerContainer.style.opacity = opacity; - } - - /** - * 修改显示层级,默认越靠下的dom元素,显示上越靠上 - * @param {number} zIndex - */ - setZIndex(zIndex) { - this.zIndex = zIndex; - this.layerContainer.style.zIndex = this.zIndex; - } - - dispose() { - Element.removeLayer(id); - this.dispose = true; - this.animateId && cancelAnimationFrame(this.animateId); - this.mapView.root.dispose(); - this.mapView.dispose(); - } - - selectModel(insect){ - this.effectOutline.selectModel(insect); - } - - resize(){ - var width = window.innerWidth; - var height = window.innerHeight; - this.renderer.setSize(width, height); - this.camera.aspect = width / height; - this.camera.updateProjectionMatrix(); - if(Config.outLine.on){ - this.effectOutline.resize(width, height); - } - } - - animate(){ - this.animateId = requestAnimationFrame(this.animate.bind(this)); - if(this.base){ - this.controls.update(); - } - if (this.base){ - update(); //目前只有在基础地图中添加相机移动的动画,所以暂且只在base地图中进行渲染,后期可改成每个图层都进行渲染,或者必要时进行渲染。 - } - for(let water of this.waters){ - water.material.uniforms[ 'time' ].value += 1.0 / 60.0; - } - if (Config.outLine.on){ - this.effectOutline.render(); // 合成器渲染 - } else { - this.renderer.autoClear = true; - } - this.renderer.render(this.scene, this.camera); - } - - _raycast(meshes, recursive, faceExclude) { - const isects = this._raycaster.intersectObjects(meshes, recursive); - if (faceExclude) { - for (let i = 0; i < isects.length; i++) { - if (isects[i].face !== faceExclude) { - return isects[i]; - } - } - return null; - } - return isects.length > 0 ? isects[0] : null; - } - - _raycastFromMouse(mx, my, width, height, cam, meshes, recursive=false) { - const mouse = new Vector2( // normalized (-1 to +1) - (mx / width) * 2 - 1, - - (my / height) * 2 + 1); - // https://threejs.org/docs/#api/core/Raycaster - // update the picking ray with the camera and mouse position - this._raycaster.setFromCamera(mouse, cam); - return this._raycast(meshes, recursive, null); - } - - /** - * - * @param {*} mx 屏幕坐标x - * @param {*} my 屏幕坐标y - * @param {boolean} recursive 是否检查子节点,true 递归检查 - * @returns mesh - */ - raycastFromMouse(mx, my, recursive=false) { - //---- NG: 2x when starting with Chrome's inspector mobile - // const {width, height} = this.renderer.domElement; - // const {width, height} = this.canvas; - //---- OK - const {clientWidth, clientHeight} = this.canvas; - - return this._raycastFromMouse( - mx, my, clientWidth, clientHeight, this.camera, - this.mapView.children, recursive); - } - - insectALL(mx, my, recursive=false) { - const {clientWidth, clientHeight} = this.canvas; - - return this._raycastFromMouse( - mx, my, clientWidth, clientHeight, this.camera, - this.scene.children, recursive); - } - -} - -/** - * @param {BufferGeometry} geometry - * @param {number} drawMode - * @return {BufferGeometry} - */ -function toTrianglesDrawMode( geometry, drawMode ) { - - if ( drawMode === TrianglesDrawMode ) { - return geometry; - - } - - if ( drawMode === TriangleFanDrawMode || drawMode === TriangleStripDrawMode ) { - - let index = geometry.getIndex(); - - // generate index if not present - - if ( index === null ) { - - const indices = []; - - const position = geometry.getAttribute( 'position' ); - - if ( position !== undefined ) { - - for ( let i = 0; i < position.count; i ++ ) { - - indices.push( i ); - - } - - geometry.setIndex( indices ); - index = geometry.getIndex(); - - } else { - return geometry; - - } - - } - - // - - const numberOfTriangles = index.count - 2; - const newIndices = []; - - if ( drawMode === TriangleFanDrawMode ) { - - // gl.TRIANGLE_FAN - - for ( let i = 1; i <= numberOfTriangles; i ++ ) { - - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - - } - - } else { - - // gl.TRIANGLE_STRIP - - for ( let i = 0; i < numberOfTriangles; i ++ ) { - - if ( i % 2 === 0 ) { - - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); - - } else { - - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); - - } - - } - - } - - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) ; - - // build final geometry - - const newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); - newGeometry.clearGroups(); - - return newGeometry; - - } else { - return geometry; - - } - -} - -class GLTFLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - this.dracoLoader = null; - this.ktx2Loader = null; - this.meshoptDecoder = null; - - this.pluginCallbacks = []; - - this.register( function ( parser ) { - - return new GLTFMaterialsClearcoatExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureBasisUExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureWebPExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFTextureAVIFExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsSheenExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsTransmissionExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsVolumeExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsIorExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsEmissiveStrengthExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsSpecularExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsIridescenceExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsAnisotropyExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMaterialsBumpExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFLightsExtension( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMeshoptCompression( parser ); - - } ); - - this.register( function ( parser ) { - - return new GLTFMeshGpuInstancing( parser ); - - } ); - - } - - load( url, onLoad, onProgress, onError ) { - - const scope = this; - - let resourcePath; - - if ( this.resourcePath !== '' ) { - - resourcePath = this.resourcePath; - - } else if ( this.path !== '' ) { - - // If a base path is set, resources will be relative paths from that plus the relative path of the gltf file - // Example path = 'https://my-cnd-server.com/', url = 'assets/models/model.gltf' - // resourcePath = 'https://my-cnd-server.com/assets/models/' - // referenced resource 'model.bin' will be loaded from 'https://my-cnd-server.com/assets/models/model.bin' - // referenced resource '../textures/texture.png' will be loaded from 'https://my-cnd-server.com/assets/textures/texture.png' - const relativeUrl = LoaderUtils.extractUrlBase( url ); - resourcePath = LoaderUtils.resolveURL( relativeUrl, this.path ); - - } else { - - resourcePath = LoaderUtils.extractUrlBase( url ); - - } - - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - this.manager.itemStart( url ); - - const _onError = function ( e ) { - - if ( onError ) { - - onError( e ); - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }; - - const loader = new FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, function ( data ) { - - try { - - scope.parse( data, resourcePath, function ( gltf ) { - - onLoad( gltf ); - - scope.manager.itemEnd( url ); - - }, _onError ); - - } catch ( e ) { - - _onError( e ); - - } - - }, onProgress, _onError ); - - } - - setDRACOLoader( dracoLoader ) { - - this.dracoLoader = dracoLoader; - return this; - - } - - setDDSLoader() { - - throw new Error( - - 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' - - ); - - } - - setKTX2Loader( ktx2Loader ) { - - this.ktx2Loader = ktx2Loader; - return this; - - } - - setMeshoptDecoder( meshoptDecoder ) { - - this.meshoptDecoder = meshoptDecoder; - return this; - - } - - register( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - - this.pluginCallbacks.push( callback ); - - } - - return this; - - } - - unregister( callback ) { - - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - - } - - return this; - - } - - parse( data, path, onLoad, onError ) { - - let json; - const extensions = {}; - const plugins = {}; - const textDecoder = new TextDecoder(); - - if ( typeof data === 'string' ) { - - json = JSON.parse( data ); - - } else if ( data instanceof ArrayBuffer ) { - - const magic = textDecoder.decode( new Uint8Array( data, 0, 4 ) ); - - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - - try { - - extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); - - } catch ( error ) { - - if ( onError ) onError( error ); - return; - - } - - json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content ); - - } else { - - json = JSON.parse( textDecoder.decode( data ) ); - - } - - } else { - - json = data; - - } - - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { - - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; - - } - - const parser = new GLTFParser( json, { - - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - requestHeader: this.requestHeader, - manager: this.manager, - ktx2Loader: this.ktx2Loader, - meshoptDecoder: this.meshoptDecoder - - } ); - - parser.fileLoader.setRequestHeader( this.requestHeader ); - - for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { - - const plugin = this.pluginCallbacks[ i ]( parser ); - - if ( ! plugin.name ) ; - - plugins[ plugin.name ] = plugin; - - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; - - } - - if ( json.extensionsUsed ) { - - for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { - - const extensionName = json.extensionsUsed[ i ]; - const extensionsRequired = json.extensionsRequired || []; - - switch ( extensionName ) { - - case EXTENSIONS.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); - break; - - case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; - - case EXTENSIONS.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; - - case EXTENSIONS.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; - - default: - - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) ; - - } - - } - - } - - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); - - } - - parseAsync( data, path ) { - - const scope = this; - - return new Promise( function ( resolve, reject ) { - - scope.parse( data, path, resolve, reject ); - - } ); - - } - -} - -/* GLTFREGISTRY */ - -function GLTFRegistry() { - - let objects = {}; - - return { - - get: function ( key ) { - - return objects[ key ]; - - }, - - add: function ( key, object ) { - - objects[ key ] = object; - - }, - - remove: function ( key ) { - - delete objects[ key ]; - - }, - - removeAll: function () { - - objects = {}; - - } - - }; - -} - -/*********************************/ -/********** EXTENSIONS ***********/ -/*********************************/ - -const EXTENSIONS = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_IOR: 'KHR_materials_ior', - KHR_MATERIALS_SHEEN: 'KHR_materials_sheen', - KHR_MATERIALS_SPECULAR: 'KHR_materials_specular', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_IRIDESCENCE: 'KHR_materials_iridescence', - KHR_MATERIALS_ANISOTROPY: 'KHR_materials_anisotropy', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_MATERIALS_VOLUME: 'KHR_materials_volume', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - KHR_MATERIALS_EMISSIVE_STRENGTH: 'KHR_materials_emissive_strength', - EXT_MATERIALS_BUMP: 'EXT_materials_bump', - EXT_TEXTURE_WEBP: 'EXT_texture_webp', - EXT_TEXTURE_AVIF: 'EXT_texture_avif', - EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression', - EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing' -}; - -/** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ -class GLTFLightsExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; - - // Object3D instance caches - this.cache = { refs: {}, uses: {} }; - - } - - _markDefs() { - - const parser = this.parser; - const nodeDefs = this.parser.json.nodes || []; - - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.extensions - && nodeDef.extensions[ this.name ] - && nodeDef.extensions[ this.name ].light !== undefined ) { - - parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); - - } - - } - - } - - _loadLight( lightIndex ) { - - const parser = this.parser; - const cacheKey = 'light:' + lightIndex; - let dependency = parser.cache.get( cacheKey ); - - if ( dependency ) return dependency; - - const json = parser.json; - const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; - const lightDefs = extensions.lights || []; - const lightDef = lightDefs[ lightIndex ]; - let lightNode; - - const color = new Color( 0xffffff ); - - if ( lightDef.color !== undefined ) color.setRGB( lightDef.color[ 0 ], lightDef.color[ 1 ], lightDef.color[ 2 ], LinearSRGBColorSpace ); - - const range = lightDef.range !== undefined ? lightDef.range : 0; - - switch ( lightDef.type ) { - - case 'directional': - lightNode = new DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - case 'point': - lightNode = new PointLight( color ); - lightNode.distance = range; - break; - - case 'spot': - lightNode = new SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); - - } - - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); - - lightNode.decay = 2; - - assignExtrasToUserData( lightNode, lightDef ); - - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - - lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); - - dependency = Promise.resolve( lightNode ); - - parser.cache.add( cacheKey, dependency ); - - return dependency; - - } - - getDependency( type, index ) { - - if ( type !== 'light' ) return; - - return this._loadLight( index ); - - } - - createNodeAttachment( nodeIndex ) { - - const self = this; - const parser = this.parser; - const json = parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; - const lightIndex = lightDef.light; - - if ( lightIndex === undefined ) return null; - - return this._loadLight( lightIndex ).then( function ( light ) { - - return parser._getNodeRef( self.cache, lightIndex, light ); - - } ); - - } - -} - -/** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ -class GLTFMaterialsUnlitExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; - - } - - getMaterialType() { - - return MeshBasicMaterial; - - } - - extendParams( materialParams, materialDef, parser ) { - - const pending = []; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - const metallicRoughness = materialDef.pbrMetallicRoughness; - - if ( metallicRoughness ) { - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.setRGB( array[ 0 ], array[ 1 ], array[ 2 ], LinearSRGBColorSpace ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, SRGBColorSpace ) ); - - } - - } - - return Promise.all( pending ); - - } - -} - -/** - * Materials Emissive Strength Extension - * - * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md - */ -class GLTFMaterialsEmissiveStrengthExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const emissiveStrength = materialDef.extensions[ this.name ].emissiveStrength; - - if ( emissiveStrength !== undefined ) { - - materialParams.emissiveIntensity = emissiveStrength; - - } - - return Promise.resolve(); - - } - -} - -/** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ -class GLTFMaterialsClearcoatExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.clearcoatFactor !== undefined ) { - - materialParams.clearcoat = extension.clearcoatFactor; - - } - - if ( extension.clearcoatTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); - - } - - if ( extension.clearcoatRoughnessFactor !== undefined ) { - - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; - - } - - if ( extension.clearcoatRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - - } - - if ( extension.clearcoatNormalTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - - if ( extension.clearcoatNormalTexture.scale !== undefined ) { - - const scale = extension.clearcoatNormalTexture.scale; - - materialParams.clearcoatNormalScale = new Vector2( scale, scale ); - - } - - } - - return Promise.all( pending ); - - } - -} - -/** - * Iridescence Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence - */ -class GLTFMaterialsIridescenceExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.iridescenceFactor !== undefined ) { - - materialParams.iridescence = extension.iridescenceFactor; - - } - - if ( extension.iridescenceTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'iridescenceMap', extension.iridescenceTexture ) ); - - } - - if ( extension.iridescenceIor !== undefined ) { - - materialParams.iridescenceIOR = extension.iridescenceIor; - - } - - if ( materialParams.iridescenceThicknessRange === undefined ) { - - materialParams.iridescenceThicknessRange = [ 100, 400 ]; - - } - - if ( extension.iridescenceThicknessMinimum !== undefined ) { - - materialParams.iridescenceThicknessRange[ 0 ] = extension.iridescenceThicknessMinimum; - - } - - if ( extension.iridescenceThicknessMaximum !== undefined ) { - - materialParams.iridescenceThicknessRange[ 1 ] = extension.iridescenceThicknessMaximum; - - } - - if ( extension.iridescenceThicknessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'iridescenceThicknessMap', extension.iridescenceThicknessTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Sheen Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen - */ -class GLTFMaterialsSheenExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_SHEEN; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - materialParams.sheenColor = new Color( 0, 0, 0 ); - materialParams.sheenRoughness = 0; - materialParams.sheen = 1; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.sheenColorFactor !== undefined ) { - - const colorFactor = extension.sheenColorFactor; - materialParams.sheenColor.setRGB( colorFactor[ 0 ], colorFactor[ 1 ], colorFactor[ 2 ], LinearSRGBColorSpace ); - - } - - if ( extension.sheenRoughnessFactor !== undefined ) { - - materialParams.sheenRoughness = extension.sheenRoughnessFactor; - - } - - if ( extension.sheenColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, SRGBColorSpace ) ); - - } - - if ( extension.sheenRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ -class GLTFMaterialsTransmissionExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.transmissionFactor !== undefined ) { - - materialParams.transmission = extension.transmissionFactor; - - } - - if ( extension.transmissionTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Materials Volume Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume - */ -class GLTFMaterialsVolumeExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0; - - if ( extension.thicknessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) ); - - } - - materialParams.attenuationDistance = extension.attenuationDistance || Infinity; - - const colorArray = extension.attenuationColor || [ 1, 1, 1 ]; - materialParams.attenuationColor = new Color().setRGB( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ], LinearSRGBColorSpace ); - - return Promise.all( pending ); - - } - -} - -/** - * Materials ior Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior - */ -class GLTFMaterialsIorExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_IOR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const extension = materialDef.extensions[ this.name ]; - - materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5; - - return Promise.resolve(); - - } - -} - -/** - * Materials specular Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular - */ -class GLTFMaterialsSpecularExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0; - - if ( extension.specularTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) ); - - } - - const colorArray = extension.specularColorFactor || [ 1, 1, 1 ]; - materialParams.specularColor = new Color().setRGB( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ], LinearSRGBColorSpace ); - - if ( extension.specularColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, SRGBColorSpace ) ); - - } - - return Promise.all( pending ); - - } - -} - - -/** - * Materials bump Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/EXT_materials_bump - */ -class GLTFMaterialsBumpExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_MATERIALS_BUMP; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - materialParams.bumpScale = extension.bumpFactor !== undefined ? extension.bumpFactor : 1.0; - - if ( extension.bumpTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'bumpMap', extension.bumpTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * Materials anisotropy Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_anisotropy - */ -class GLTFMaterialsAnisotropyExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_MATERIALS_ANISOTROPY; - - } - - getMaterialType( materialIndex ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - - return MeshPhysicalMaterial; - - } - - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - - return Promise.resolve(); - - } - - const pending = []; - - const extension = materialDef.extensions[ this.name ]; - - if ( extension.anisotropyStrength !== undefined ) { - - materialParams.anisotropy = extension.anisotropyStrength; - - } - - if ( extension.anisotropyRotation !== undefined ) { - - materialParams.anisotropyRotation = extension.anisotropyRotation; - - } - - if ( extension.anisotropyTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'anisotropyMap', extension.anisotropyTexture ) ); - - } - - return Promise.all( pending ); - - } - -} - -/** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - */ -class GLTFTextureBasisUExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.KHR_TEXTURE_BASISU; - - } - - loadTexture( textureIndex ) { - - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ this.name ]; - const loader = parser.options.ktx2Loader; - - if ( ! loader ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - - } else { - - // Assumes that the extension is optional and that a fallback texture is present - return null; - - } - - } - - return parser.loadTextureImage( textureIndex, extension.source, loader ); - - } - -} - -/** - * WebP Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp - */ -class GLTFTextureWebPExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_TEXTURE_WEBP; - this.isSupported = null; - - } - - loadTexture( textureIndex ) { - - const name = this.name; - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; - - let loader = parser.textureLoader; - if ( source.uri ) { - - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.detectSupport().then( function ( isSupported ) { - - if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); - - } - - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); - - } ); - - } - - detectSupport() { - - if ( ! this.isSupported ) { - - this.isSupported = new Promise( function ( resolve ) { - - const image = new Image(); - - // Lossy test image. Support for lossy images doesn't guarantee support for all - // WebP images, unfortunately. - image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; - - image.onload = image.onerror = function () { - - resolve( image.height === 1 ); - - }; - - } ); - - } - - return this.isSupported; - - } - -} - -/** - * AVIF Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_avif - */ -class GLTFTextureAVIFExtension { - - constructor( parser ) { - - this.parser = parser; - this.name = EXTENSIONS.EXT_TEXTURE_AVIF; - this.isSupported = null; - - } - - loadTexture( textureIndex ) { - - const name = this.name; - const parser = this.parser; - const json = parser.json; - - const textureDef = json.textures[ textureIndex ]; - - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - - return null; - - } - - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; - - let loader = parser.textureLoader; - if ( source.uri ) { - - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.detectSupport().then( function ( isSupported ) { - - if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: AVIF required by asset but unsupported.' ); - - } - - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); - - } ); - - } - - detectSupport() { - - if ( ! this.isSupported ) { - - this.isSupported = new Promise( function ( resolve ) { - - const image = new Image(); - - // Lossy test image. - image.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkQAAAAB8dSLfI='; - image.onload = image.onerror = function () { - - resolve( image.height === 1 ); - - }; - - } ); - - } - - return this.isSupported; - - } - -} - -/** - * meshopt BufferView Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression - */ -class GLTFMeshoptCompression { - - constructor( parser ) { - - this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; - this.parser = parser; - - } - - loadBufferView( index ) { - - const json = this.parser.json; - const bufferView = json.bufferViews[ index ]; - - if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { - - const extensionDef = bufferView.extensions[ this.name ]; - - const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); - const decoder = this.parser.options.meshoptDecoder; - - if ( ! decoder || ! decoder.supported ) { - - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - - throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); - - } else { - - // Assumes that the extension is optional and that fallback buffer data is present - return null; - - } - - } - - return buffer.then( function ( res ) { - - const byteOffset = extensionDef.byteOffset || 0; - const byteLength = extensionDef.byteLength || 0; - - const count = extensionDef.count; - const stride = extensionDef.byteStride; - - const source = new Uint8Array( res, byteOffset, byteLength ); - - if ( decoder.decodeGltfBufferAsync ) { - - return decoder.decodeGltfBufferAsync( count, stride, source, extensionDef.mode, extensionDef.filter ).then( function ( res ) { - - return res.buffer; - - } ); - - } else { - - // Support for MeshoptDecoder 0.18 or earlier, without decodeGltfBufferAsync - return decoder.ready.then( function () { - - const result = new ArrayBuffer( count * stride ); - decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); - return result; - - } ); - - } - - } ); - - } else { - - return null; - - } - - } - -} - -/** - * GPU Instancing Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing - * - */ -class GLTFMeshGpuInstancing { - - constructor( parser ) { - - this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING; - this.parser = parser; - - } - - createNodeMesh( nodeIndex ) { - - const json = this.parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - - if ( ! nodeDef.extensions || ! nodeDef.extensions[ this.name ] || - nodeDef.mesh === undefined ) { - - return null; - - } - - const meshDef = json.meshes[ nodeDef.mesh ]; - - // No Points or Lines + Instancing support yet - - for ( const primitive of meshDef.primitives ) { - - if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES && - primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP && - primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN && - primitive.mode !== undefined ) { - - return null; - - } - - } - - const extensionDef = nodeDef.extensions[ this.name ]; - const attributesDef = extensionDef.attributes; - - // @TODO: Can we support InstancedMesh + SkinnedMesh? - - const pending = []; - const attributes = {}; - - for ( const key in attributesDef ) { - - pending.push( this.parser.getDependency( 'accessor', attributesDef[ key ] ).then( accessor => { - - attributes[ key ] = accessor; - return attributes[ key ]; - - } ) ); - - } - - if ( pending.length < 1 ) { - - return null; - - } - - pending.push( this.parser.createNodeMesh( nodeIndex ) ); - - return Promise.all( pending ).then( results => { - - const nodeObject = results.pop(); - const meshes = nodeObject.isGroup ? nodeObject.children : [ nodeObject ]; - const count = results[ 0 ].count; // All attribute counts should be same - const instancedMeshes = []; - - for ( const mesh of meshes ) { - - // Temporal variables - const m = new Matrix4(); - const p = new Vector3(); - const q = new Quaternion(); - const s = new Vector3( 1, 1, 1 ); - - const instancedMesh = new InstancedMesh( mesh.geometry, mesh.material, count ); - - for ( let i = 0; i < count; i ++ ) { - - if ( attributes.TRANSLATION ) { - - p.fromBufferAttribute( attributes.TRANSLATION, i ); - - } - - if ( attributes.ROTATION ) { - - q.fromBufferAttribute( attributes.ROTATION, i ); - - } - - if ( attributes.SCALE ) { - - s.fromBufferAttribute( attributes.SCALE, i ); - - } - - instancedMesh.setMatrixAt( i, m.compose( p, q, s ) ); - - } - - // Add instance attributes to the geometry, excluding TRS. - for ( const attributeName in attributes ) { - - if ( attributeName === '_COLOR_0' ) { - - const attr = attributes[ attributeName ]; - instancedMesh.instanceColor = new InstancedBufferAttribute( attr.array, attr.itemSize, attr.normalized ); - - } else if ( attributeName !== 'TRANSLATION' && - attributeName !== 'ROTATION' && - attributeName !== 'SCALE' ) { - - mesh.geometry.setAttribute( attributeName, attributes[ attributeName ] ); - - } - - } - - // Just in case - Object3D.prototype.copy.call( instancedMesh, mesh ); - - this.parser.assignFinalMaterial( instancedMesh ); - - instancedMeshes.push( instancedMesh ); - - } - - if ( nodeObject.isGroup ) { - - nodeObject.clear(); - - nodeObject.add( ... instancedMeshes ); - - return nodeObject; - - } - - return instancedMeshes[ 0 ]; - - } ); - - } - -} - -/* BINARY EXTENSION */ -const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; -const BINARY_EXTENSION_HEADER_LENGTH = 12; -const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - -class GLTFBinaryExtension { - - constructor( data ) { - - this.name = EXTENSIONS.KHR_BINARY_GLTF; - this.content = null; - this.body = null; - - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); - const textDecoder = new TextDecoder(); - - this.header = { - magic: textDecoder.decode( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; - - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - - } else if ( this.header.version < 2.0 ) { - - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - - } - - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); - let chunkIndex = 0; - - while ( chunkIndex < chunkContentsLength ) { - - const chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - const chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; - - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); - this.content = textDecoder.decode( contentArray ); - - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); - - } - - // Clients must ignore chunks with unknown types. - - chunkIndex += chunkLength; - - } - - if ( this.content === null ) { - - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); - - } - - } - -} - -/** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ -class GLTFDracoMeshCompressionExtension { - - constructor( json, dracoLoader ) { - - if ( ! dracoLoader ) { - - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); - - } - - this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); - - } - - decodePrimitive( primitive, parser ) { - - const json = this.json; - const dracoLoader = this.dracoLoader; - const bufferViewIndex = primitive.extensions[ this.name ].bufferView; - const gltfAttributeMap = primitive.extensions[ this.name ].attributes; - const threeAttributeMap = {}; - const attributeNormalizedMap = {}; - const attributeTypeMap = {}; - - for ( const attributeName in gltfAttributeMap ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; - - } - - for ( const attributeName in primitive.attributes ) { - - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - - if ( gltfAttributeMap[ attributeName ] !== undefined ) { - - const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - attributeTypeMap[ threeAttributeName ] = componentType.name; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; - - } - - } - - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { - - return new Promise( function ( resolve, reject ) { - - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { - - for ( const attributeName in geometry.attributes ) { - - const attribute = geometry.attributes[ attributeName ]; - const normalized = attributeNormalizedMap[ attributeName ]; - - if ( normalized !== undefined ) attribute.normalized = normalized; - - } - - resolve( geometry ); - - }, threeAttributeMap, attributeTypeMap, LinearSRGBColorSpace, reject ); - - } ); - - } ); - - } - -} - -/** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ -class GLTFTextureTransformExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; - - } - - extendTexture( texture, transform ) { - - if ( ( transform.texCoord === undefined || transform.texCoord === texture.channel ) - && transform.offset === undefined - && transform.rotation === undefined - && transform.scale === undefined ) { - - // See https://github.com/mrdoob/three.js/issues/21819. - return texture; - - } - - texture = texture.clone(); - - if ( transform.texCoord !== undefined ) { - - texture.channel = transform.texCoord; - - } - - if ( transform.offset !== undefined ) { - - texture.offset.fromArray( transform.offset ); - - } - - if ( transform.rotation !== undefined ) { - - texture.rotation = transform.rotation; - - } - - if ( transform.scale !== undefined ) { - - texture.repeat.fromArray( transform.scale ); - - } - - texture.needsUpdate = true; - - return texture; - - } - -} - -/** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ -class GLTFMeshQuantizationExtension { - - constructor() { - - this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; - - } - -} - -/*********************************/ -/********** INTERPOLATION ********/ -/*********************************/ - -// Spline Interpolation -// Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation -class GLTFCubicSplineInterpolant extends Interpolant { - - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - copySampleValue_( index ) { - - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. - - const result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; - - for ( let i = 0; i !== valueSize; i ++ ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - } - - interpolate_( i1, t0, t, t1 ) { - - const result = this.resultBuffer; - const values = this.sampleValues; - const stride = this.valueSize; - - const stride2 = stride * 2; - const stride3 = stride * 3; - - const td = t1 - t0; - - const p = ( t - t0 ) / td; - const pp = p * p; - const ppp = pp * p; - - const offset1 = i1 * stride3; - const offset0 = offset1 - stride3; - - const s2 = - 2 * ppp + 3 * pp; - const s3 = ppp - pp; - const s0 = 1 - s2; - const s1 = s3 - pp + p; - - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( let i = 0; i !== stride; i ++ ) { - - const p0 = values[ offset0 + i + stride ]; // splineVertex_k - const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) - - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; - - } - - return result; - - } - -} - -const _q = new Quaternion(); - -class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { - - interpolate_( i1, t0, t, t1 ) { - - const result = super.interpolate_( i1, t0, t, t1 ); - - _q.fromArray( result ).normalize().toArray( result ); - - return result; - - } - -} - - -/*********************************/ -/********** INTERNALS ************/ -/*********************************/ - -/* CONSTANTS */ - -const WEBGL_CONSTANTS = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 -}; - -const WEBGL_COMPONENT_TYPES = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array -}; - -const WEBGL_FILTERS = { - 9728: NearestFilter, - 9729: LinearFilter, - 9984: NearestMipmapNearestFilter, - 9985: LinearMipmapNearestFilter, - 9986: NearestMipmapLinearFilter, - 9987: LinearMipmapLinearFilter -}; - -const WEBGL_WRAPPINGS = { - 33071: ClampToEdgeWrapping, - 33648: MirroredRepeatWrapping, - 10497: RepeatWrapping -}; - -const WEBGL_TYPE_SIZES = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 -}; - -const ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv1', - TEXCOORD_2: 'uv2', - TEXCOORD_3: 'uv3', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', -}; - -const PATH_PROPERTIES = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' -}; - -const INTERPOLATION = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: InterpolateLinear, - STEP: InterpolateDiscrete -}; - -const ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' -}; - -/** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ -function createDefaultMaterial( cache ) { - - if ( cache[ 'DefaultMaterial' ] === undefined ) { - - cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: FrontSide - } ); - - } - - return cache[ 'DefaultMaterial' ]; - -} - -function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { - - // Add unknown glTF extensions to an object's userData. - - for ( const name in objectDef.extensions ) { - - if ( knownExtensions[ name ] === undefined ) { - - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; - - } - - } - -} - -/** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ -function assignExtrasToUserData( object, gltfDef ) { - - if ( gltfDef.extras !== undefined ) { - - if ( typeof gltfDef.extras === 'object' ) { - - Object.assign( object.userData, gltfDef.extras ); - - } - - } - -} - -/** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ -function addMorphTargets( geometry, targets, parser ) { - - let hasMorphPosition = false; - let hasMorphNormal = false; - let hasMorphColor = false; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; - if ( target.COLOR_0 !== undefined ) hasMorphColor = true; - - if ( hasMorphPosition && hasMorphNormal && hasMorphColor ) break; - - } - - if ( ! hasMorphPosition && ! hasMorphNormal && ! hasMorphColor ) return Promise.resolve( geometry ); - - const pendingPositionAccessors = []; - const pendingNormalAccessors = []; - const pendingColorAccessors = []; - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( hasMorphPosition ) { - - const pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; - - pendingPositionAccessors.push( pendingAccessor ); - - } - - if ( hasMorphNormal ) { - - const pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; - - pendingNormalAccessors.push( pendingAccessor ); - - } - - if ( hasMorphColor ) { - - const pendingAccessor = target.COLOR_0 !== undefined - ? parser.getDependency( 'accessor', target.COLOR_0 ) - : geometry.attributes.color; - - pendingColorAccessors.push( pendingAccessor ); - - } - - } - - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ), - Promise.all( pendingColorAccessors ) - ] ).then( function ( accessors ) { - - const morphPositions = accessors[ 0 ]; - const morphNormals = accessors[ 1 ]; - const morphColors = accessors[ 2 ]; - - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - if ( hasMorphColor ) geometry.morphAttributes.color = morphColors; - geometry.morphTargetsRelative = true; - - return geometry; - - } ); - -} - -/** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ -function updateMorphTargets( mesh, meshDef ) { - - mesh.updateMorphTargets(); - - if ( meshDef.weights !== undefined ) { - - for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { - - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; - - } - - } - - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { - - const targetNames = meshDef.extras.targetNames; - - if ( mesh.morphTargetInfluences.length === targetNames.length ) { - - mesh.morphTargetDictionary = {}; - - for ( let i = 0, il = targetNames.length; i < il; i ++ ) { - - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; - - } - - } - - } - -} - -function createPrimitiveKey( primitiveDef ) { - - let geometryKey; - - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; - - if ( dracoExtension ) { - - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); - - } else { - - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; - - } - - if ( primitiveDef.targets !== undefined ) { - - for ( let i = 0, il = primitiveDef.targets.length; i < il; i ++ ) { - - geometryKey += ':' + createAttributesKey( primitiveDef.targets[ i ] ); - - } - - } - - return geometryKey; - -} - -function createAttributesKey( attributes ) { - - let attributesKey = ''; - - const keys = Object.keys( attributes ).sort(); - - for ( let i = 0, il = keys.length; i < il; i ++ ) { - - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; - - } - - return attributesKey; - -} - -function getNormalizedComponentScale( constructor ) { - - // Reference: - // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data - - switch ( constructor ) { - - case Int8Array: - return 1 / 127; - - case Uint8Array: - return 1 / 255; - - case Int16Array: - return 1 / 32767; - - case Uint16Array: - return 1 / 65535; - - default: - throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); - - } - -} - -function getImageURIMimeType( uri ) { - - if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; - if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; - - return 'image/png'; - -} - -const _identityMatrix = new Matrix4(); - -/* GLTF PARSER */ - -class GLTFParser { - - constructor( json = {}, options = {} ) { - - this.json = json; - this.extensions = {}; - this.plugins = {}; - this.options = options; - - // loader object cache - this.cache = new GLTFRegistry(); - - // associations between Three.js objects and glTF elements - this.associations = new Map(); - - // BufferGeometry caching - this.primitiveCache = {}; - - // Node cache - this.nodeCache = {}; - - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; - - this.sourceCache = {}; - this.textureCache = {}; - - // Track node names, to ensure no duplicates - this.nodeNamesUsed = {}; - - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - - let isSafari = false; - let isFirefox = false; - let firefoxVersion = - 1; - - if ( typeof navigator !== 'undefined' ) { - - isSafari = /^((?!chrome|android).)*safari/i.test( navigator.userAgent ) === true; - isFirefox = navigator.userAgent.indexOf( 'Firefox' ) > - 1; - firefoxVersion = isFirefox ? navigator.userAgent.match( /Firefox\/([0-9]+)\./ )[ 1 ] : - 1; - - } - - if ( typeof createImageBitmap === 'undefined' || isSafari || ( isFirefox && firefoxVersion < 98 ) ) { - - this.textureLoader = new TextureLoader( this.options.manager ); - - } else { - - this.textureLoader = new ImageBitmapLoader( this.options.manager ); - - } - - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - this.textureLoader.setRequestHeader( this.options.requestHeader ); - - this.fileLoader = new FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); - - if ( this.options.crossOrigin === 'use-credentials' ) { - - this.fileLoader.setWithCredentials( true ); - - } - - } - - setExtensions( extensions ) { - - this.extensions = extensions; - - } - - setPlugins( plugins ) { - - this.plugins = plugins; - - } - - parse( onLoad, onError ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - // Clear the loader cache - this.cache.removeAll(); - this.nodeCache = {}; - - // Mark the special nodes/meshes in json for efficient parse - this._invokeAll( function ( ext ) { - - return ext._markDefs && ext._markDefs(); - - } ); - - Promise.all( this._invokeAll( function ( ext ) { - - return ext.beforeRoot && ext.beforeRoot(); - - } ) ).then( function () { - - return Promise.all( [ - - parser.getDependencies( 'scene' ), - parser.getDependencies( 'animation' ), - parser.getDependencies( 'camera' ), - - ] ); - - } ).then( function ( dependencies ) { - - const result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; - - addUnknownExtensionsToUserData( extensions, result, json ); - - assignExtrasToUserData( result, json ); - - return Promise.all( parser._invokeAll( function ( ext ) { - - return ext.afterRoot && ext.afterRoot( result ); - - } ) ).then( function () { - - onLoad( result ); - - } ); - - } ).catch( onError ); - - } - - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - _markDefs() { - - const nodeDefs = this.json.nodes || []; - const skinDefs = this.json.skins || []; - const meshDefs = this.json.meshes || []; - - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { - - const joints = skinDefs[ skinIndex ].joints; - - for ( let i = 0, il = joints.length; i < il; i ++ ) { - - nodeDefs[ joints[ i ] ].isBone = true; - - } - - } - - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - - const nodeDef = nodeDefs[ nodeIndex ]; - - if ( nodeDef.mesh !== undefined ) { - - this._addNodeRef( this.meshCache, nodeDef.mesh ); - - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { - - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; - - } - - } - - if ( nodeDef.camera !== undefined ) { - - this._addNodeRef( this.cameraCache, nodeDef.camera ); - - } - - } - - } - - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - _addNodeRef( cache, index ) { - - if ( index === undefined ) return; - - if ( cache.refs[ index ] === undefined ) { - - cache.refs[ index ] = cache.uses[ index ] = 0; - - } - - cache.refs[ index ] ++; - - } - - /** Returns a reference to a shared resource, cloning it if necessary. */ - _getNodeRef( cache, index, object ) { - - if ( cache.refs[ index ] <= 1 ) return object; - - const ref = object.clone(); - - // Propagates mappings to the cloned object, prevents mappings on the - // original object from being lost. - const updateMappings = ( original, clone ) => { - - const mappings = this.associations.get( original ); - if ( mappings != null ) { - - this.associations.set( clone, mappings ); - - } - - for ( const [ i, child ] of original.children.entries() ) { - - updateMappings( child, clone.children[ i ] ); - - } - - }; - - updateMappings( object, ref ); - - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); - - return ref; - - } - - _invokeOne( func ) { - - const extensions = Object.values( this.plugins ); - extensions.push( this ); - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) return result; - - } - - return null; - - } - - _invokeAll( func ) { - - const extensions = Object.values( this.plugins ); - extensions.unshift( this ); - - const pending = []; - - for ( let i = 0; i < extensions.length; i ++ ) { - - const result = func( extensions[ i ] ); - - if ( result ) pending.push( result ); - - } - - return pending; - - } - - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - getDependency( type, index ) { - - const cacheKey = type + ':' + index; - let dependency = this.cache.get( cacheKey ); - - if ( ! dependency ) { - - switch ( type ) { - - case 'scene': - dependency = this.loadScene( index ); - break; - - case 'node': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadNode && ext.loadNode( index ); - - } ); - break; - - case 'mesh': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMesh && ext.loadMesh( index ); - - } ); - break; - - case 'accessor': - dependency = this.loadAccessor( index ); - break; - - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadBufferView && ext.loadBufferView( index ); - - } ); - break; - - case 'buffer': - dependency = this.loadBuffer( index ); - break; - - case 'material': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadMaterial && ext.loadMaterial( index ); - - } ); - break; - - case 'texture': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadTexture && ext.loadTexture( index ); - - } ); - break; - - case 'skin': - dependency = this.loadSkin( index ); - break; - - case 'animation': - dependency = this._invokeOne( function ( ext ) { - - return ext.loadAnimation && ext.loadAnimation( index ); - - } ); - break; - - case 'camera': - dependency = this.loadCamera( index ); - break; - - default: - dependency = this._invokeOne( function ( ext ) { - - return ext != this && ext.getDependency && ext.getDependency( type, index ); - - } ); - - if ( ! dependency ) { - - throw new Error( 'Unknown type: ' + type ); - - } - - break; - - } - - this.cache.add( cacheKey, dependency ); - - } - - return dependency; - - } - - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - getDependencies( type ) { - - let dependencies = this.cache.get( type ); - - if ( ! dependencies ) { - - const parser = this; - const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; - - dependencies = Promise.all( defs.map( function ( def, index ) { - - return parser.getDependency( type, index ); - - } ) ); - - this.cache.add( type, dependencies ); - - } - - return dependencies; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - loadBuffer( bufferIndex ) { - - const bufferDef = this.json.buffers[ bufferIndex ]; - const loader = this.fileLoader; - - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { - - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); - - } - - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { - - return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); - - } - - const options = this.options; - - return new Promise( function ( resolve, reject ) { - - loader.load( LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { - - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); - - } ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - loadBufferView( bufferViewIndex ) { - - const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; - - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { - - const byteLength = bufferViewDef.byteLength || 0; - const byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - loadAccessor( accessorIndex ) { - - const parser = this; - const json = this.json; - - const accessorDef = this.json.accessors[ accessorIndex ]; - - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - - const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - const normalized = accessorDef.normalized === true; - - const array = new TypedArray( accessorDef.count * itemSize ); - return Promise.resolve( new BufferAttribute( array, itemSize, normalized ) ); - - } - - const pendingBufferViews = []; - - if ( accessorDef.bufferView !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); - - } else { - - pendingBufferViews.push( null ); - - } - - if ( accessorDef.sparse !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); - - } - - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { - - const bufferView = bufferViews[ 0 ]; - - const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; - - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - const elementBytes = TypedArray.BYTES_PER_ELEMENT; - const itemBytes = elementBytes * itemSize; - const byteOffset = accessorDef.byteOffset || 0; - const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - const normalized = accessorDef.normalized === true; - let array, bufferAttribute; - - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { - - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - const ibSlice = Math.floor( byteOffset / byteStride ); - const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - let ib = parser.cache.get( ibCacheKey ); - - if ( ! ib ) { - - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); - - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new InterleavedBuffer( array, byteStride / elementBytes ); - - parser.cache.add( ibCacheKey, ib ); - - } - - bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); - - } else { - - if ( bufferView === null ) { - - array = new TypedArray( accessorDef.count * itemSize ); - - } else { - - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); - - } - - bufferAttribute = new BufferAttribute( array, itemSize, normalized ); - - } - - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { - - const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; - - const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; - - const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); - - if ( bufferView !== null ) { - - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); - - } - - for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { - - const index = sparseIndices[ i ]; - - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); - - } - - } - - return bufferAttribute; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - loadTexture( textureIndex ) { - - const json = this.json; - const options = this.options; - const textureDef = json.textures[ textureIndex ]; - const sourceIndex = textureDef.source; - const sourceDef = json.images[ sourceIndex ]; - - let loader = this.textureLoader; - - if ( sourceDef.uri ) { - - const handler = options.manager.getHandler( sourceDef.uri ); - if ( handler !== null ) loader = handler; - - } - - return this.loadTextureImage( textureIndex, sourceIndex, loader ); - - } - - loadTextureImage( textureIndex, sourceIndex, loader ) { - - const parser = this; - const json = this.json; - - const textureDef = json.textures[ textureIndex ]; - const sourceDef = json.images[ sourceIndex ]; - - const cacheKey = ( sourceDef.uri || sourceDef.bufferView ) + ':' + textureDef.sampler; - - if ( this.textureCache[ cacheKey ] ) { - - // See https://github.com/mrdoob/three.js/issues/21559. - return this.textureCache[ cacheKey ]; - - } - - const promise = this.loadImageSource( sourceIndex, loader ).then( function ( texture ) { - - texture.flipY = false; - - texture.name = textureDef.name || sourceDef.name || ''; - - if ( texture.name === '' && typeof sourceDef.uri === 'string' && sourceDef.uri.startsWith( 'data:image/' ) === false ) { - - texture.name = sourceDef.uri; - - } - - const samplers = json.samplers || {}; - const sampler = samplers[ textureDef.sampler ] || {}; - - texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || LinearFilter; - texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || RepeatWrapping; - - parser.associations.set( texture, { textures: textureIndex } ); - - return texture; - - } ).catch( function () { - - return null; - - } ); - - this.textureCache[ cacheKey ] = promise; - - return promise; - - } - - loadImageSource( sourceIndex, loader ) { - - const parser = this; - const json = this.json; - const options = this.options; - - if ( this.sourceCache[ sourceIndex ] !== undefined ) { - - return this.sourceCache[ sourceIndex ].then( ( texture ) => texture.clone() ); - - } - - const sourceDef = json.images[ sourceIndex ]; - - const URL = self.URL || self.webkitURL; - - let sourceURI = sourceDef.uri || ''; - let isObjectURL = false; - - if ( sourceDef.bufferView !== undefined ) { - - // Load binary image data from bufferView, if provided. - - sourceURI = parser.getDependency( 'bufferView', sourceDef.bufferView ).then( function ( bufferView ) { - - isObjectURL = true; - const blob = new Blob( [ bufferView ], { type: sourceDef.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; - - } ); - - } else if ( sourceDef.uri === undefined ) { - - throw new Error( 'THREE.GLTFLoader: Image ' + sourceIndex + ' is missing URI and bufferView' ); - - } - - const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) { - - return new Promise( function ( resolve, reject ) { - - let onLoad = resolve; - - if ( loader.isImageBitmapLoader === true ) { - - onLoad = function ( imageBitmap ) { - - const texture = new Texture( imageBitmap ); - texture.needsUpdate = true; - - resolve( texture ); - - }; - - } - - loader.load( LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); - - } ); - - } ).then( function ( texture ) { - - // Clean up resources and configure Texture. - - if ( isObjectURL === true ) { - - URL.revokeObjectURL( sourceURI ); - - } - - texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); - - return texture; - - } ).catch( function ( error ) { - throw error; - - } ); - - this.sourceCache[ sourceIndex ] = promise; - return promise; - - } - - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - assignTexture( materialParams, mapName, mapDef, colorSpace ) { - - const parser = this; - - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { - - if ( ! texture ) return null; - - if ( mapDef.texCoord !== undefined && mapDef.texCoord > 0 ) { - - texture = texture.clone(); - texture.channel = mapDef.texCoord; - - } - - if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { - - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; - - if ( transform ) { - - const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); - - } - - } - - if ( colorSpace !== undefined ) { - - texture.colorSpace = colorSpace; - - } - - materialParams[ mapName ] = texture; - - return texture; - - } ); - - } - - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accommodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - assignFinalMaterial( mesh ) { - - const geometry = mesh.geometry; - let material = mesh.material; - - const useDerivativeTangents = geometry.attributes.tangent === undefined; - const useVertexColors = geometry.attributes.color !== undefined; - const useFlatShading = geometry.attributes.normal === undefined; - - if ( mesh.isPoints ) { - - const cacheKey = 'PointsMaterial:' + material.uuid; - - let pointsMaterial = this.cache.get( cacheKey ); - - if ( ! pointsMaterial ) { - - pointsMaterial = new PointsMaterial(); - Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px - - this.cache.add( cacheKey, pointsMaterial ); - - } - - material = pointsMaterial; - - } else if ( mesh.isLine ) { - - const cacheKey = 'LineBasicMaterial:' + material.uuid; - - let lineMaterial = this.cache.get( cacheKey ); - - if ( ! lineMaterial ) { - - lineMaterial = new LineBasicMaterial$1(); - Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); - lineMaterial.map = material.map; - - this.cache.add( cacheKey, lineMaterial ); - - } - - material = lineMaterial; - - } - - // Clone the material if it will be modified - if ( useDerivativeTangents || useVertexColors || useFlatShading ) { - - let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; - - if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - - let cachedMaterial = this.cache.get( cacheKey ); - - if ( ! cachedMaterial ) { - - cachedMaterial = material.clone(); - - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - - if ( useDerivativeTangents ) { - - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; - if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; - - } - - this.cache.add( cacheKey, cachedMaterial ); - - this.associations.set( cachedMaterial, this.associations.get( material ) ); - - } - - material = cachedMaterial; - - } - - mesh.material = material; - - } - - getMaterialType( /* materialIndex */ ) { - - return MeshStandardMaterial; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - loadMaterial( materialIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - const materialDef = json.materials[ materialIndex ]; - - let materialType; - const materialParams = {}; - const materialExtensions = materialDef.extensions || {}; - - const pending = []; - - if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { - - const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); - - } else { - - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - - const metallicRoughness = materialDef.pbrMetallicRoughness || {}; - - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; - - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - - const array = metallicRoughness.baseColorFactor; - - materialParams.color.setRGB( array[ 0 ], array[ 1 ], array[ 2 ], LinearSRGBColorSpace ); - materialParams.opacity = array[ 3 ]; - - } - - if ( metallicRoughness.baseColorTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, SRGBColorSpace ) ); - - } - - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; - - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); - - } - - materialType = this._invokeOne( function ( ext ) { - - return ext.getMaterialType && ext.getMaterialType( materialIndex ); - - } ); - - pending.push( Promise.all( this._invokeAll( function ( ext ) { - - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); - - } ) ) ); - - } - - if ( materialDef.doubleSided === true ) { - - materialParams.side = DoubleSide; - - } - - const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; - - if ( alphaMode === ALPHA_MODES.BLEND ) { - - materialParams.transparent = true; - - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; - - } else { - - materialParams.transparent = false; - - if ( alphaMode === ALPHA_MODES.MASK ) { - - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; - - } - - } - - if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); - - materialParams.normalScale = new Vector2( 1, 1 ); - - if ( materialDef.normalTexture.scale !== undefined ) { - - const scale = materialDef.normalTexture.scale; - - materialParams.normalScale.set( scale, scale ); - - } - - } - - if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); - - if ( materialDef.occlusionTexture.strength !== undefined ) { - - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; - - } - - } - - if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { - - const emissiveFactor = materialDef.emissiveFactor; - materialParams.emissive = new Color().setRGB( emissiveFactor[ 0 ], emissiveFactor[ 1 ], emissiveFactor[ 2 ], LinearSRGBColorSpace ); - - } - - if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { - - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, SRGBColorSpace ) ); - - } - - return Promise.all( pending ).then( function () { - - const material = new materialType( materialParams ); - - if ( materialDef.name ) material.name = materialDef.name; - - assignExtrasToUserData( material, materialDef ); - - parser.associations.set( material, { materials: materialIndex } ); - - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); - - return material; - - } ); - - } - - /** When Object3D instances are targeted by animation, they need unique names. */ - createUniqueName( originalName ) { - - const sanitizedName = PropertyBinding.sanitizeNodeName( originalName || '' ); - - if ( sanitizedName in this.nodeNamesUsed ) { - - return sanitizedName + '_' + ( ++ this.nodeNamesUsed[ sanitizedName ] ); - - } else { - - this.nodeNamesUsed[ sanitizedName ] = 0; - - return sanitizedName; - - } - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - loadGeometries( primitives ) { - - const parser = this; - const extensions = this.extensions; - const cache = this.primitiveCache; - - function createDracoPrimitive( primitive ) { - - return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { - - return addPrimitiveAttributes( geometry, primitive, parser ); - - } ); - - } - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey( primitive ); - - // See if we've already created this geometry - const cached = cache[ cacheKey ]; - - if ( cached ) { - - // Use the cached geometry if it exists - pending.push( cached.promise ); - - } else { - - let geometryPromise; - - if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { - - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); - - } else { - - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); - - } - - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; - - pending.push( geometryPromise ); - - } - - } - - return Promise.all( pending ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - loadMesh( meshIndex ) { - - const parser = this; - const json = this.json; - const extensions = this.extensions; - - const meshDef = json.meshes[ meshIndex ]; - const primitives = meshDef.primitives; - - const pending = []; - - for ( let i = 0, il = primitives.length; i < il; i ++ ) { - - const material = primitives[ i ].material === undefined - ? createDefaultMaterial( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); - - pending.push( material ); - - } - - pending.push( parser.loadGeometries( primitives ) ); - - return Promise.all( pending ).then( function ( results ) { - - const materials = results.slice( 0, results.length - 1 ); - const geometries = results[ results.length - 1 ]; - - const meshes = []; - - for ( let i = 0, il = geometries.length; i < il; i ++ ) { - - const geometry = geometries[ i ]; - const primitive = primitives[ i ]; - - // 1. create Mesh - - let mesh; - - const material = materials[ i ]; - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || - primitive.mode === undefined ) { - - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new SkinnedMesh( geometry, material ) - : new Mesh( geometry, material ); - - if ( mesh.isSkinnedMesh === true ) { - - // normalize skin weights to fix malformed assets (see #15319) - mesh.normalizeSkinWeights(); - - } - - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { - - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); - - } - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { - - mesh = new LineSegments( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { - - mesh = new Line( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { - - mesh = new LineLoop( geometry, material ); - - } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { - - mesh = new Points( geometry, material ); - - } else { - - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); - - } - - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { - - updateMorphTargets( mesh, meshDef ); - - } - - mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); - - assignExtrasToUserData( mesh, meshDef ); - - if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); - - parser.assignFinalMaterial( mesh ); - - meshes.push( mesh ); - - } - - for ( let i = 0, il = meshes.length; i < il; i ++ ) { - - parser.associations.set( meshes[ i ], { - meshes: meshIndex, - primitives: i - } ); - - } - - if ( meshes.length === 1 ) { - - if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, meshes[ 0 ], meshDef ); - - return meshes[ 0 ]; - - } - - const group = new Group$1(); - - if ( meshDef.extensions ) addUnknownExtensionsToUserData( extensions, group, meshDef ); - - parser.associations.set( group, { meshes: meshIndex } ); - - for ( let i = 0, il = meshes.length; i < il; i ++ ) { - - group.add( meshes[ i ] ); - - } - - return group; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - loadCamera( cameraIndex ) { - - let camera; - const cameraDef = this.json.cameras[ cameraIndex ]; - const params = cameraDef[ cameraDef.type ]; - - if ( ! params ) { - return; - - } - - if ( cameraDef.type === 'perspective' ) { - - camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); - - } else if ( cameraDef.type === 'orthographic' ) { - - camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); - - } - - if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); - - assignExtrasToUserData( camera, cameraDef ); - - return Promise.resolve( camera ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - loadSkin( skinIndex ) { - - const skinDef = this.json.skins[ skinIndex ]; - - const pending = []; - - for ( let i = 0, il = skinDef.joints.length; i < il; i ++ ) { - - pending.push( this._loadNodeShallow( skinDef.joints[ i ] ) ); - - } - - if ( skinDef.inverseBindMatrices !== undefined ) { - - pending.push( this.getDependency( 'accessor', skinDef.inverseBindMatrices ) ); - - } else { - - pending.push( null ); - - } - - return Promise.all( pending ).then( function ( results ) { - - const inverseBindMatrices = results.pop(); - const jointNodes = results; - - // Note that bones (joint nodes) may or may not be in the - // scene graph at this time. - - const bones = []; - const boneInverses = []; - - for ( let i = 0, il = jointNodes.length; i < il; i ++ ) { - - const jointNode = jointNodes[ i ]; - - if ( jointNode ) { - - bones.push( jointNode ); - - const mat = new Matrix4(); - - if ( inverseBindMatrices !== null ) { - - mat.fromArray( inverseBindMatrices.array, i * 16 ); - - } - - boneInverses.push( mat ); - - } - - } - - return new Skeleton( bones, boneInverses ); - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - loadAnimation( animationIndex ) { - - const json = this.json; - const parser = this; - - const animationDef = json.animations[ animationIndex ]; - const animationName = animationDef.name ? animationDef.name : 'animation_' + animationIndex; - - const pendingNodes = []; - const pendingInputAccessors = []; - const pendingOutputAccessors = []; - const pendingSamplers = []; - const pendingTargets = []; - - for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { - - const channel = animationDef.channels[ i ]; - const sampler = animationDef.samplers[ channel.sampler ]; - const target = channel.target; - const name = target.node; - const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; - - if ( target.node === undefined ) continue; - - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); - - } - - return Promise.all( [ - - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) - - ] ).then( function ( dependencies ) { - - const nodes = dependencies[ 0 ]; - const inputAccessors = dependencies[ 1 ]; - const outputAccessors = dependencies[ 2 ]; - const samplers = dependencies[ 3 ]; - const targets = dependencies[ 4 ]; - - const tracks = []; - - for ( let i = 0, il = nodes.length; i < il; i ++ ) { - - const node = nodes[ i ]; - const inputAccessor = inputAccessors[ i ]; - const outputAccessor = outputAccessors[ i ]; - const sampler = samplers[ i ]; - const target = targets[ i ]; - - if ( node === undefined ) continue; - - if ( node.updateMatrix ) { - - node.updateMatrix(); - - } - - const createdTracks = parser._createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target ); - - if ( createdTracks ) { - - for ( let k = 0; k < createdTracks.length; k ++ ) { - - tracks.push( createdTracks[ k ] ); - - } - - } - - } - - return new AnimationClip( animationName, undefined, tracks ); - - } ); - - } - - createNodeMesh( nodeIndex ) { - - const json = this.json; - const parser = this; - const nodeDef = json.nodes[ nodeIndex ]; - - if ( nodeDef.mesh === undefined ) return null; - - return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { - - const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); - - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { - - node.traverse( function ( o ) { - - if ( ! o.isMesh ) return; - - for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { - - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; - - } - - } ); - - } - - return node; - - } ); - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - loadNode( nodeIndex ) { - - const json = this.json; - const parser = this; - - const nodeDef = json.nodes[ nodeIndex ]; - - const nodePending = parser._loadNodeShallow( nodeIndex ); - - const childPending = []; - const childrenDef = nodeDef.children || []; - - for ( let i = 0, il = childrenDef.length; i < il; i ++ ) { - - childPending.push( parser.getDependency( 'node', childrenDef[ i ] ) ); - - } - - const skeletonPending = nodeDef.skin === undefined - ? Promise.resolve( null ) - : parser.getDependency( 'skin', nodeDef.skin ); - - return Promise.all( [ - nodePending, - Promise.all( childPending ), - skeletonPending - ] ).then( function ( results ) { - - const node = results[ 0 ]; - const children = results[ 1 ]; - const skeleton = results[ 2 ]; - - if ( skeleton !== null ) { - - // This full traverse should be fine because - // child glTF nodes have not been added to this node yet. - node.traverse( function ( mesh ) { - - if ( ! mesh.isSkinnedMesh ) return; - - mesh.bind( skeleton, _identityMatrix ); - - } ); - - } - - for ( let i = 0, il = children.length; i < il; i ++ ) { - - node.add( children[ i ] ); - - } - - return node; - - } ); - - } - - // ._loadNodeShallow() parses a single node. - // skin and child nodes are created and added in .loadNode() (no '_' prefix). - _loadNodeShallow( nodeIndex ) { - - const json = this.json; - const extensions = this.extensions; - const parser = this; - - // This method is called from .loadNode() and .loadSkin(). - // Cache a node to avoid duplication. - - if ( this.nodeCache[ nodeIndex ] !== undefined ) { - - return this.nodeCache[ nodeIndex ]; - - } - - const nodeDef = json.nodes[ nodeIndex ]; - - // reserve node's name before its dependencies, so the root has the intended name. - const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; - - const pending = []; - - const meshPromise = parser._invokeOne( function ( ext ) { - - return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); - - } ); - - if ( meshPromise ) { - - pending.push( meshPromise ); - - } - - if ( nodeDef.camera !== undefined ) { - - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { - - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); - - } ) ); - - } - - parser._invokeAll( function ( ext ) { - - return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); - - } ).forEach( function ( promise ) { - - pending.push( promise ); - - } ); - - this.nodeCache[ nodeIndex ] = Promise.all( pending ).then( function ( objects ) { - - let node; - - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { - - node = new Bone(); - - } else if ( objects.length > 1 ) { - - node = new Group$1(); - - } else if ( objects.length === 1 ) { - - node = objects[ 0 ]; - - } else { - - node = new Object3D(); - - } - - if ( node !== objects[ 0 ] ) { - - for ( let i = 0, il = objects.length; i < il; i ++ ) { - - node.add( objects[ i ] ); - - } - - } - - if ( nodeDef.name ) { - - node.userData.name = nodeDef.name; - node.name = nodeName; - - } - - assignExtrasToUserData( node, nodeDef ); - - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); - - if ( nodeDef.matrix !== undefined ) { - - const matrix = new Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); - - } else { - - if ( nodeDef.translation !== undefined ) { - - node.position.fromArray( nodeDef.translation ); - - } - - if ( nodeDef.rotation !== undefined ) { - - node.quaternion.fromArray( nodeDef.rotation ); - - } - - if ( nodeDef.scale !== undefined ) { - - node.scale.fromArray( nodeDef.scale ); - - } - - } - - if ( ! parser.associations.has( node ) ) { - - parser.associations.set( node, {} ); - - } - - parser.associations.get( node ).nodes = nodeIndex; - - return node; - - } ); - - return this.nodeCache[ nodeIndex ]; - - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - loadScene( sceneIndex ) { - - const extensions = this.extensions; - const sceneDef = this.json.scenes[ sceneIndex ]; - const parser = this; - - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - const scene = new Group$1(); - if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); - - assignExtrasToUserData( scene, sceneDef ); - - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); - - const nodeIds = sceneDef.nodes || []; - - const pending = []; - - for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { - - pending.push( parser.getDependency( 'node', nodeIds[ i ] ) ); - - } - - return Promise.all( pending ).then( function ( nodes ) { - - for ( let i = 0, il = nodes.length; i < il; i ++ ) { - - scene.add( nodes[ i ] ); - - } - - // Removes dangling associations, associations that reference a node that - // didn't make it into the scene. - const reduceAssociations = ( node ) => { - - const reducedAssociations = new Map(); - - for ( const [ key, value ] of parser.associations ) { - - if ( key instanceof Material || key instanceof Texture ) { - - reducedAssociations.set( key, value ); - - } - - } - - node.traverse( ( node ) => { - - const mappings = parser.associations.get( node ); - - if ( mappings != null ) { - - reducedAssociations.set( node, mappings ); - - } - - } ); - - return reducedAssociations; - - }; - - parser.associations = reduceAssociations( scene ); - - return scene; - - } ); - - } - - _createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target ) { - - const tracks = []; - - const targetName = node.name ? node.name : node.uuid; - const targetNames = []; - - if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { - - node.traverse( function ( object ) { - - if ( object.morphTargetInfluences ) { - - targetNames.push( object.name ? object.name : object.uuid ); - - } - - } ); - - } else { - - targetNames.push( targetName ); - - } - - let TypedKeyframeTrack; - - switch ( PATH_PROPERTIES[ target.path ] ) { - - case PATH_PROPERTIES.weights: - - TypedKeyframeTrack = NumberKeyframeTrack; - break; - - case PATH_PROPERTIES.rotation: - - TypedKeyframeTrack = QuaternionKeyframeTrack; - break; - - case PATH_PROPERTIES.position: - case PATH_PROPERTIES.scale: - - TypedKeyframeTrack = VectorKeyframeTrack; - break; - - default: - - switch ( outputAccessor.itemSize ) { - - case 1: - TypedKeyframeTrack = NumberKeyframeTrack; - break; - case 2: - case 3: - default: - TypedKeyframeTrack = VectorKeyframeTrack; - break; - - } - - break; - - } - - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear; - - - const outputArray = this._getArrayFromAccessor( outputAccessor ); - - for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { - - const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); - - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { - - this._createCubicSplineTrackInterpolant( track ); - - } - - tracks.push( track ); - - } - - return tracks; - - } - - _getArrayFromAccessor( accessor ) { - - let outputArray = accessor.array; - - if ( accessor.normalized ) { - - const scale = getNormalizedComponentScale( outputArray.constructor ); - const scaled = new Float32Array( outputArray.length ); - - for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { - - scaled[ j ] = outputArray[ j ] * scale; - - } - - outputArray = scaled; - - } - - return outputArray; - - } - - _createCubicSplineTrackInterpolant( track ) { - - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { - - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. - - const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; - - return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); - - }; - - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; - - } - -} - -/** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ -function computeBounds( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const box = new Box3(); - - if ( attributes.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ attributes.POSITION ]; - - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - box.set( - new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) - ); - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - box.min.multiplyScalar( boxScale ); - box.max.multiplyScalar( boxScale ); - - } - - } else { - - return; - - } - - } else { - - return; - - } - - const targets = primitiveDef.targets; - - if ( targets !== undefined ) { - - const maxDisplacement = new Vector3(); - const vector = new Vector3(); - - for ( let i = 0, il = targets.length; i < il; i ++ ) { - - const target = targets[ i ]; - - if ( target.POSITION !== undefined ) { - - const accessor = parser.json.accessors[ target.POSITION ]; - const min = accessor.min; - const max = accessor.max; - - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - - if ( min !== undefined && max !== undefined ) { - - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); - - - if ( accessor.normalized ) { - - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); - vector.multiplyScalar( boxScale ); - - } - - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); - - } - - } - - } - - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); - - } - - geometry.boundingBox = box; - - const sphere = new Sphere(); - - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; - - geometry.boundingSphere = sphere; - -} - -/** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} - */ -function addPrimitiveAttributes( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const pending = []; - - function assignAttributeAccessor( accessorIndex, attributeName ) { - - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { - - geometry.setAttribute( attributeName, accessor ); - - } ); - - } - - for ( const gltfAttributeName in attributes ) { - - const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); - - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; - - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); - - } - - if ( primitiveDef.indices !== undefined && ! geometry.index ) { - - const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { - - geometry.setIndex( accessor ); - - } ); - - pending.push( accessor ); - - } - - if ( ColorManagement.workingColorSpace !== LinearSRGBColorSpace && 'COLOR_0' in attributes ) ; - - assignExtrasToUserData( geometry, primitiveDef ); - - computeBounds( geometry, primitiveDef, parser ); - - return Promise.all( pending ).then( function () { - - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; - - } ); - -} - -const _taskCache$1 = new WeakMap(); - -class DRACOLoader extends Loader { - - constructor( manager ) { - - super( manager ); - - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; - - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; - - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' - }; - - } - - setDecoderPath( path ) { - - this.decoderPath = path; - - return this; - - } - - setDecoderConfig( config ) { - - this.decoderConfig = config; - - return this; - - } - - setWorkerLimit( workerLimit ) { - - this.workerLimit = workerLimit; - - return this; - - } - - load( url, onLoad, onProgress, onError ) { - - const loader = new FileLoader( this.manager ); - - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - - loader.load( url, ( buffer ) => { - - this.parse( buffer, onLoad, onError ); - - }, onProgress, onError ); - - } - - - parse( buffer, onLoad, onError = ()=>{} ) { - - this.decodeDracoFile( buffer, onLoad, null, null, SRGBColorSpace ).catch( onError ); - - } - - decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = LinearSRGBColorSpace, onError = () => {} ) { - - const taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs, - vertexColorSpace: vertexColorSpace, - }; - - return this.decodeGeometry( buffer, taskConfig ).then( callback ).catch( onError ); - - } - - decodeGeometry( buffer, taskConfig ) { - - const taskKey = JSON.stringify( taskConfig ); - - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( _taskCache$1.has( buffer ) ) { - - const cachedTask = _taskCache$1.get( buffer ); - - if ( cachedTask.key === taskKey ) { - - return cachedTask.promise; - - } else if ( buffer.byteLength === 0 ) { - - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( - - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' - - ); - - } - - } - - // - - let worker; - const taskID = this.workerNextTaskID ++; - const taskCost = buffer.byteLength; - - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - const geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { - - worker = _worker; - - return new Promise( ( resolve, reject ) => { - - worker._callbacks[ taskID ] = { resolve, reject }; - - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - - // this.debug(); - - } ); - - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); - - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { - - if ( worker && taskID ) { - - this._releaseTask( worker, taskID ); - - // this.debug(); - - } - - } ); - - // Cache the task result. - _taskCache$1.set( buffer, { - - key: taskKey, - promise: geometryPending - - } ); - - return geometryPending; - - } - - _createGeometry( geometryData ) { - - const geometry = new BufferGeometry(); - - if ( geometryData.index ) { - - geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); - - } - - for ( let i = 0; i < geometryData.attributes.length; i ++ ) { - - const result = geometryData.attributes[ i ]; - const name = result.name; - const array = result.array; - const itemSize = result.itemSize; - - const attribute = new BufferAttribute( array, itemSize ); - - if ( name === 'color' ) { - - this._assignVertexColorSpace( attribute, result.vertexColorSpace ); - - attribute.normalized = ( array instanceof Float32Array ) === false; - - } - - geometry.setAttribute( name, attribute ); - - } - - return geometry; - - } - - _assignVertexColorSpace( attribute, inputColorSpace ) { - - // While .drc files do not specify colorspace, the only 'official' tooling - // is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc - // file is passed into .load() or .parse(). GLTFLoader uses internal APIs - // to decode geometry, and vertex colors are already Linear-sRGB in there. - - if ( inputColorSpace !== SRGBColorSpace ) return; - - const _color = new Color(); - - for ( let i = 0, il = attribute.count; i < il; i ++ ) { - - _color.fromBufferAttribute( attribute, i ).convertSRGBToLinear(); - attribute.setXYZ( i, _color.r, _color.g, _color.b ); - - } - - } - - _loadLibrary( url, responseType ) { - - const loader = new FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - loader.setWithCredentials( this.withCredentials ); - - return new Promise( ( resolve, reject ) => { - - loader.load( url, resolve, undefined, reject ); - - } ); - - } - - preload() { - - this._initDecoder(); - - return this; - - } - - _initDecoder() { - - if ( this.decoderPending ) return this.decoderPending; - - const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - const librariesPending = []; - - if ( useJS ) { - - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - - } else { - - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); - - } - - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { - - const jsContent = libraries[ 0 ]; - - if ( ! useJS ) { - - this.decoderConfig.wasmBinary = libraries[ 1 ]; - - } - - const fn = DRACOWorker.toString(); - - const body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); - - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); - - } ); - - return this.decoderPending; - - } - - _getWorker( taskID, taskCost ) { - - return this._initDecoder().then( () => { - - if ( this.workerPool.length < this.workerLimit ) { - - const worker = new Worker( this.workerSourceURL ); - - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; - - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - - worker.onmessage = function ( e ) { - - const message = e.data; - - switch ( message.type ) { - - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; - - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; - - } - - }; - - this.workerPool.push( worker ); - - } else { - - this.workerPool.sort( function ( a, b ) { - - return a._taskLoad > b._taskLoad ? - 1 : 1; - - } ); - - } - - const worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; - - } ); - - } - - _releaseTask( worker, taskID ) { - - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; - - } - - debug() { - - } - - dispose() { - - for ( let i = 0; i < this.workerPool.length; ++ i ) { - - this.workerPool[ i ].terminate(); - - } - - this.workerPool.length = 0; - - if ( this.workerSourceURL !== '' ) { - - URL.revokeObjectURL( this.workerSourceURL ); - - } - - return this; - - } - -} - -/* WEB WORKER */ - -function DRACOWorker() { - - let decoderConfig; - let decoderPending; + varying vec3 vWorldPosition; + varying vec3 vSunDirection; + varying float vSunfade; + varying vec3 vBetaR; + varying vec3 vBetaM; + varying float vSunE; - onmessage = function ( e ) { + // constants for atmospheric scattering + const float e = 2.71828182845904523536028747135266249775724709369995957; + const float pi = 3.141592653589793238462643383279502884197169; - const message = e.data; + // wavelength of used primaries, according to preetham + const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); + // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: + // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) + const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); - switch ( message.type ) { + // mie stuff + // K coefficient for the primaries + const float v = 4.0; + const vec3 K = vec3( 0.686, 0.678, 0.666 ); + // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K + const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { + // earth shadow hack + // cutoffAngle = pi / 1.95; + const float cutoffAngle = 1.6110731556870734; + const float steepness = 1.5; + const float EE = 1000.0; - decoderConfig.onModuleLoaded = function ( draco ) { + float sunIntensity( float zenithAngleCos ) { + zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); + return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); + } - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); + vec3 totalMie( float T ) { + float c = ( 0.2 * T ) * 10E-18; + return 0.434 * c * MieConst; + } - }; + void main() { - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef + vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); + vWorldPosition = worldPosition.xyz; - } ); - break; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + gl_Position.z = gl_Position.w; // set z to camera.far - case 'decode': - const buffer = message.buffer; - const taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { + vSunDirection = normalize( sunPosition ); - const draco = module.draco; - const decoder = new draco.Decoder(); + vSunE = sunIntensity( dot( vSunDirection, up ) ); - try { + vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); - const geometry = decodeGeometry( draco, decoder, new Int8Array( buffer ), taskConfig ); + float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); - const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); + // extinction (absorbtion + out scattering) + // rayleigh coefficients + vBetaR = totalRayleigh * rayleighCoefficient; - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); + // mie coefficients + vBetaM = totalMie( turbidity ) * mieCoefficient; - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); + }`, - } catch ( error ) { + fragmentShader: /* glsl */` + varying vec3 vWorldPosition; + varying vec3 vSunDirection; + varying float vSunfade; + varying vec3 vBetaR; + varying vec3 vBetaM; + varying float vSunE; - self.postMessage( { type: 'error', id: message.id, error: error.message } ); + uniform float mieDirectionalG; + uniform vec3 up; - } finally { + // constants for atmospheric scattering + const float pi = 3.141592653589793238462643383279502884197169; - draco.destroy( decoder ); + const float n = 1.0003; // refractive index of air + const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius) - } + // optical length at zenith for molecules + const float rayleighZenithLength = 8.4E3; + const float mieZenithLength = 1.25E3; + // 66 arc seconds -> degrees, and the cosine of that + const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; - } ); - break; + // 3.0 / ( 16.0 * pi ) + const float THREE_OVER_SIXTEENPI = 0.05968310365946075; + // 1.0 / ( 4.0 * pi ) + const float ONE_OVER_FOURPI = 0.07957747154594767; + float rayleighPhase( float cosTheta ) { + return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); } - }; + float hgPhase( float cosTheta, float g ) { + float g2 = pow( g, 2.0 ); + float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); + return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse ); + } - function decodeGeometry( draco, decoder, array, taskConfig ) { + void main() { - const attributeIDs = taskConfig.attributeIDs; - const attributeTypes = taskConfig.attributeTypes; + vec3 direction = normalize( vWorldPosition - cameraPosition ); - let dracoGeometry; - let decodingStatus; + // optical length + // cutoff angle at 90 to avoid singularity in next formula. + float zenithAngle = acos( max( 0.0, dot( up, direction ) ) ); + float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); + float sR = rayleighZenithLength * inverse; + float sM = mieZenithLength * inverse; - const geometryType = decoder.GetEncodedGeometryType( array ); + // combined extinction factor + vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); - if ( geometryType === draco.TRIANGULAR_MESH ) { + // in scattering + float cosTheta = dot( direction, vSunDirection ); - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeArrayToMesh( array, array.byteLength, dracoGeometry ); + float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); + vec3 betaRTheta = vBetaR * rPhase; - } else if ( geometryType === draco.POINT_CLOUD ) { + float mPhase = hgPhase( cosTheta, mieDirectionalG ); + vec3 betaMTheta = vBetaM * mPhase; - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeArrayToPointCloud( array, array.byteLength, dracoGeometry ); + vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); + Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); - } else { + // nightsky + float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] + float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] + vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); + vec3 L0 = vec3( 0.1 ) * Fex; - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); + // composition + solar disc + float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); + L0 += ( vSunE * 19000.0 * Fex ) * sundisk; - } + vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { + vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); + gl_FragColor = vec4( retColor, 1.0 ); - } + #include + #include - const geometry = { index: null, attributes: [] }; + }` - // Gather all vertex attributes. - for ( const attributeName in attributeIDs ) { +}; - const attributeType = self[ attributeTypes[ attributeName ] ]; +// 多个canvas并没有id,只有父节点 - let attribute; - let attributeID; - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { +class Layer extends BasLayer{ + id; // 唯一标识 + layerContainer; // div#layer 容器 + zIndex=1;//默认为1 + opacity=1;//默认为1 + canvas;//canvas + dispose = false; + renderer;//canvas上下文 + scene;//场景 + visible=true;//是否可见 + mapView;//地图视图 + camera;//相机 + controls;//控件 + animateId;//动画事件id + base = false; // 是否为底图 + ambientLight; // 环境光 + directionalLight; // 方向光 + modelLayer = false; // 模型图层 + imageLayer = false; // 影像图层 + vectorLayer = false; // 矢量图层,如路网、行政区划,地名等图层 + waters = []; // 水面集合 + constructor(id, layerContainer, canvas, mapView, plane = true, camera = new PerspectiveCamera(80, 1, 0.1, 1e12)) { + super(); + this.id = id; + this.layerContainer = layerContainer; + this.canvas = canvas; + this.renderer = new WebGLRenderer({ + canvas: this.canvas, + antialias: true, + alpha: true, + logarithmicDepthBuffer: true, + precision: "highp", + }); + this.renderer.sortObjects = true; + this.renderer.setPixelRatio(window.devicePixelRatio); + this.renderer.setClearColor(0xFFFFFF, 0.0); + this.scene = new Scene(); + this.mapView = mapView; + this.camera = camera; + if(this.mapView){ + this.scene.add(this.mapView); + this.mapView.updateMatrixWorld(true); + } + if (plane){ + this.controls = new MapControls(this.camera, this.canvas); + this.controls.minDistance = 1e1; + this.controls.zoomSpeed = 2.0; + } else { + this.controls = new OrbitControls(this.camera, this.canvas); + this.controls.enablePan = false; + this.controls.minDistance = UnitsUtils.EARTH_RADIUS + 2; + this.controls.maxDistance = UnitsUtils.EARTH_RADIUS * 1e1; + } + this._raycaster = new Raycaster(); + if(Config.outLine.on){ + this.effectOutline = new EffectOutline(this.renderer, this.scene, this.camera, this.canvas.width, this.canvas.height); + } + if (Config.layer.map.ambientLight.add){ + this.scene.add(new AmbientLight(Config.layer.map.ambientLight.color, Config.layer.map.ambientLight.intensity)); + } + if (Config.layer.map.directionalLight.add){ + this.scene.add(new DirectionalLight(Config.layer.map.directionalLight.color, Config.layer.map.directionalLight.intensity)); + } + if (Config.layer.map.pointLight.add){ + let pointLight = new PointLight(Config.layer.map.pointLight.color, Config.layer.map.pointLight.intensity, Config.layer.map.pointLight.distance); + pointLight.position.set(...Config.layer.map.pointLight.position); + this.scene.add(pointLight); + } + } - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); + moveTo(lat, lon, height = 38472.48763833733){ + // var coords = UnitsUtils.datumsToSpherical(44.266119,90.139228); + var coords = UnitsUtils.datumsToSpherical(lat,lon); + this.camera.position.set(coords.x, height, -coords.y); + this.controls.target.set(this.camera.position.x, 0, this.camera.position.z); + } - } else { + moveToByCoords(coords){ + let offset = 50; + this.camera.position.set(coords.x, coords.y+offset, coords.z); + this.controls.target.set(coords.x, coords.y, coords.z); + } - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); + moveToByLL(lat, lon, distance = 384720){ + let dir = UnitsUtils.datumsToVector(lat, lon); + dir.multiplyScalar(UnitsUtils.EARTH_RADIUS + distance); + this.camera.position.copy(dir); + } - if ( attributeID === - 1 ) continue; - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); + on(eventName, callback){ + this.listener.on(eventName, callback); + } - } + setSceneBackground(color) { + this.scene.background = color; + } - const attributeResult = decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ); + clearSceneBackground() { + this.scene.background = null; + } - if ( attributeName === 'color' ) { + // 可用于添加灯光mesh等元素 + add(Object3D) { + if(Object3D ==null || Object3D ==undefined){ + return; + } + this.scene.add(Object3D); + } - attributeResult.vertexColorSpace = taskConfig.vertexColorSpace; - } + remove(Object3D) { + if(Object3D ==null || Object3D ==undefined){ + return; + } + this.scene.remove(Object3D); + } - geometry.attributes.push( attributeResult ); + openWaterConfig(){ + /** + * 打开渲染水系配置 + */ + // this.renderer.setPixelRatio( window.devicePixelRatio ); + this.renderer.toneMapping = ACESFilmicToneMapping; + this.renderer.toneMappingExposure = 0.5; + + // 添加天空 + this.sky = new Sky(); + this.sky.translateX = true; + this.sky.translateY = true; + this.sky.translateZ = true; + this.sky.rotateX = Math.PI / 2; + this.sky.scale.setScalar( Config.EARTH_RADIUS * 2 * Math.PI ); // 天空放大倍数 + this.scene.add( this.sky ); + const skyUniforms = this.sky.material.uniforms; + // 天空的配置 + skyUniforms[ 'turbidity' ].value = 10; + skyUniforms[ 'rayleigh' ].value = 2; + skyUniforms[ 'mieCoefficient' ].value = 0.005; + skyUniforms[ 'mieDirectionalG' ].value = 0.8; + // 旋转 设置为y朝上 + // sky.material.uniforms["up"].value = new THREE.Vector3(0, 1, 0); + + // 天空映射, 更新太阳位置 + this.pmremGenerator = new PMREMGenerator( this.renderer ); + this.sceneEnv = new Scene(); + this.renderTarget = null; + this.sun = new Vector3(); + this.updateSun(Config.SUNDEGREE, Config.SUNAZIMUTH); + } - } + updateSun(elevation, azimuth) { - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { + const phi = MathUtils.degToRad( 90 - elevation ); + const theta = MathUtils.degToRad( azimuth ); - geometry.index = decodeIndex( draco, decoder, dracoGeometry ); + this.sun.setFromSphericalCoords( 1, phi, theta ); - } + this.sky.material.uniforms[ 'sunPosition' ].value.copy( this.sun ); + for (let water of this.waters){ + water.material.uniforms[ 'sunDirection' ].value.copy( this.sun ).normalize(); + } + if ( this.renderTarget !== null ) this.renderTarget.dispose(); - draco.destroy( dracoGeometry ); + this.sceneEnv.add( this.sky ); + this.renderTarget = this.pmremGenerator.fromScene( this.sceneEnv ); + this.scene.add( this.sky ); - return geometry; + this.scene.environment = this.renderTarget.texture; - } - function decodeIndex( draco, decoder, dracoGeometry ) { + } - const numFaces = dracoGeometry.num_faces(); - const numIndices = numFaces * 3; - const byteLength = numIndices * 4; + /** + * 添加水系 + * @param {*} water + * @returns + */ + addWater(water) { + if(water ==null || water ==undefined){ + return; + } + this.scene.add(water); + this.waters.push(water); + } - const ptr = draco._malloc( byteLength ); - decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); - const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); - draco._free( ptr ); + removeWater(water) { + if(water ==null || water ==undefined){ + return; + } + this.scene.remove(water); + let index = this.waters.indexOf(water); + if (index > -1) { + this.waters.splice(index, 1); + } + } - return { array: index, itemSize: 1 }; + setVisible(visible) { + this.visible = this.visible; + this.layerContainer.style.display = visible ? 'block' : 'none'; + } + /** + * @deprecated 不建议用 + * @param {number} opacity + */ + setOpacity(opacity) { + this.opacity = opacity; + this.layerContainer.style.opacity = opacity; + } - } + /** + * 修改显示层级,默认越靠下的dom元素,显示上越靠上 + * @param {number} zIndex + */ + setZIndex(zIndex) { + this.zIndex = zIndex; + this.layerContainer.style.zIndex = this.zIndex; + } - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { + dispose() { + Element.removeLayer(id); + this.dispose = true; + this.animateId && cancelAnimationFrame(this.animateId); + this.mapView.root.dispose(); + this.mapView.dispose(); + } - const numComponents = attribute.num_components(); - const numPoints = dracoGeometry.num_points(); - const numValues = numPoints * numComponents; - const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; - const dataType = getDracoDataType( draco, attributeType ); + selectModel(insect){ + this.effectOutline.selectModel(insect); + } - const ptr = draco._malloc( byteLength ); - decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); - const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); - draco._free( ptr ); + resize(){ + var width = window.innerWidth; + var height = window.innerHeight; + this.renderer.setSize(width, height); + this.camera.aspect = width / height; + this.camera.updateProjectionMatrix(); + if(Config.outLine.on){ + this.effectOutline.resize(width, height); + } + } - return { - name: attributeName, - array: array, - itemSize: numComponents - }; + animate(){ + this.animateId = requestAnimationFrame(this.animate.bind(this)); + if(this.base){ + this.controls.update(); + } + if (this.base){ + update(); //目前只有在基础地图中添加相机移动的动画,所以暂且只在base地图中进行渲染,后期可改成每个图层都进行渲染,或者必要时进行渲染。 + } + for(let water of this.waters){ + water.material.uniforms[ 'time' ].value += 1.0 / 60.0; + } + if (Config.outLine.on){ + this.effectOutline.render(); // 合成器渲染 + } else { + this.renderer.autoClear = true; + } + this.renderer.render(this.scene, this.camera); + } - } + _raycast(meshes, recursive, faceExclude) { + const isects = this._raycaster.intersectObjects(meshes, recursive); + if (faceExclude) { + for (let i = 0; i < isects.length; i++) { + if (isects[i].face !== faceExclude) { + return isects[i]; + } + } + return null; + } + return isects.length > 0 ? isects[0] : null; + } - function getDracoDataType( draco, attributeType ) { + _raycastFromMouse(mx, my, width, height, cam, meshes, recursive=false) { + const mouse = new Vector2( // normalized (-1 to +1) + (mx / width) * 2 - 1, + - (my / height) * 2 + 1); + // https://threejs.org/docs/#api/core/Raycaster + // update the picking ray with the camera and mouse position + this._raycaster.setFromCamera(mouse, cam); + return this._raycast(meshes, recursive, null); + } - switch ( attributeType ) { + /** + * + * @param {*} mx 屏幕坐标x + * @param {*} my 屏幕坐标y + * @param {boolean} recursive 是否检查子节点,true 递归检查 + * @returns mesh + */ + raycastFromMouse(mx, my, recursive=false) { + //---- NG: 2x when starting with Chrome's inspector mobile + // const {width, height} = this.renderer.domElement; + // const {width, height} = this.canvas; + //---- OK + const {clientWidth, clientHeight} = this.canvas; - case Float32Array: return draco.DT_FLOAT32; - case Int8Array: return draco.DT_INT8; - case Int16Array: return draco.DT_INT16; - case Int32Array: return draco.DT_INT32; - case Uint8Array: return draco.DT_UINT8; - case Uint16Array: return draco.DT_UINT16; - case Uint32Array: return draco.DT_UINT32; + return this._raycastFromMouse( + mx, my, clientWidth, clientHeight, this.camera, + this.mapView.children, recursive); + } - } + insectALL(mx, my, recursive=false) { + const {clientWidth, clientHeight} = this.canvas; - } + return this._raycastFromMouse( + mx, my, clientWidth, clientHeight, this.camera, + this.scene.children, recursive); + } } @@ -41525,11 +36291,17 @@ class WegeoMap { this.baseMap.moveToByCoords(coords); } - moveToByLL(lat, lon){ + /** + * 跳转到指定位置,用于球形地图 + * @param {*} lat + * @param {*} lon + * @returns + */ + moveToByLL(lat, lon, distance = 384720){ if(!this.baseMap){ return; } - this.baseMap.moveToByLL(lat, lon); + this.baseMap.moveToByLL(lat, lon, distance); } // 鼠标点击获取模型 @@ -41893,12 +36665,12 @@ class Skybox { loadSkyBox(scale) { var aCubeMap = new CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); aCubeMap.format = RGBAFormat; @@ -41921,15 +36693,15 @@ class Skybox { } loadBox(){ var cube = new CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); return cube; } } -export { Animate, BingMapsProvider, CancelablePromise, CanvasUtils, Config, DebugProvider, Element, Geolocation, GeolocationUtils, GeoserverWMSProvider, GeoserverWMTSProvider, GoogleMapsProvider, HeightDebugProvider, HereMapsProvider, LODControl, LODFrustum, LODRadial, LODRaycast, LODSphere, Layer, MapBoxProvider, MapHeightNode, MapHeightNodeShader, MapNode, MapNodeGeometry, MapNodeHeightGeometry, MapPlaneNode, MapProvider, MapSphereNode, MapSphereNodeGeometry, MapTilerProvider, MapView, OpenMapTilesProvider, OpenStreetMapsProvider, QuadTreePosition, RoadImageProvider, Skybox, TextureUtils, TianDiTuHeightProvider, TianDiTuProvider, UnitsUtils, WegeoMap, XHRUtils }; +export { AngleUtils, Animate, BingMapsProvider, CancelablePromise, CanvasUtils, Config, DebugProvider, Element, Geolocation, GeolocationUtils, GeoserverWMSProvider, GeoserverWMTSProvider, GoogleMapsProvider, HeightDebugProvider, HereMapsProvider, LODControl, LODFrustum, LODRadial, LODRaycast, LODSphere, Layer, MapBoxProvider, MapHeightNode, MapHeightNodeShader, MapNode, MapNodeGeometry, MapNodeHeightGeometry, MapPlaneNode, MapProvider, MapSphereNode, MapSphereNodeGeometry, MapTilerProvider, MapView, OpenMapTilesProvider, OpenStreetMapsProvider, QuadTreePosition, RoadImageProvider, Skybox, TextureUtils, TianDiTuHeightProvider, TianDiTuProvider, UnitsUtils, WegeoMap, XHRUtils }; diff --git a/examples/js/THREEi_ONLY_SphereWithSomeHoles.js b/examples/js/THREEi_ONLY_SphereWithSomeHoles.js new file mode 100644 index 0000000..95a3f5c --- /dev/null +++ b/examples/js/THREEi_ONLY_SphereWithSomeHoles.js @@ -0,0 +1,1694 @@ +// THREEi_ONLY_SphereWithSomeHoles.js ( rev 109.0 ) + +// NOTE! This version contains only the older, simplified functions + +// * buildSphereWithHolesObj, buildSphereWithHoles( ), +// * this requires less effort in code and execution, +// but +// * does not check whether the current front overlaps, +// * in very many cases with few holes this is not a problem, +// * it can lead to errors in more complicated cases + +/** + * @author hofk / http://threejs.hofk.de/ +*/ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.THREEi = global.THREEi || {}))); +}(this, (function (exports) { + +'use strict'; + +var g; // THREE.BufferGeometry + +//################################################################################################# + +// Algorithm based on (simplified for sphere with holes) +// de: https://www2.mathematik.tu-darmstadt.de/~ehartmann/cdg0/cdg0n.pdf +// en: https://www2.mathematik.tu-darmstadt.de/~ehartmann/cdgen0104.pdf +// + +// ............................ Sphere with Holes (Triangulation) ................................. + +function createSphereWithHoles( arg1, arg2 ) { + + g = this; // THREE.BufferGeometry() - geometry object from three.js + + g.buildSphereWithHolesObj = buildSphereWithHolesObj; + g.buildSphereWithHoles = buildSphereWithHoles; + + if( typeof arg1 === 'number' ) { // variant detail, holes are optional, radius = 1 is fixed - use three.js .scale + // Variant with less effort in the algorithm! Other angle calculation too. + + g.detail = arg1; // count of triangles for half a great circle + g.holes = arg2 !== undefined ? arg2 : []; // optional + /* holes, example: + holes = [ + // circular hole, 3 elements: [ theta, phi, count ] + [ 2.44, 0.41, 12 ], + [ 0.72, 2.55, 19 ], + // points hole,: array of points theta, phi, ... (last point is connected to first) + [ 0,0, 0.5,-0.8, 0.25,-0.27, 0.4,0.3, 0.3,0.72 ] + ]; + */ + + g.buildSphereWithHoles( ); + + } else { // variant parameters object { d: div4: holes: } all elements optional + + g.d = arg1.d !== undefined ? arg1.d : 2 * Math.sin( Math.PI / 24 ); // to g.div4 default + g.div4 = arg1.div4 !== undefined ? arg1.div4 : 6; // 6 * 4 = 24 great circle divisions + g.holes = arg1.holes !== undefined ? arg1.holes : []; + + g.detail = g.div4 * 4; // division of the great circle + g.radius = g.d / Math.sin( Math.PI / g.detail ) / 2; // sphere radius, for external use as well + + /* holes, example: + holes: [ + // circular hole, 3 elements: [ theta, phi, div4Hole ], div4Hole <= div4 + [ 1.82, 0.41, 12 ], + // points hole,: array of points theta, phi, ... (last point is connected to first) + [ 0,0, 0.5,-0.8, 0.25,-0.27, 0.4,0.3, 0.3,0.72 ] + ] + */ + + g.buildSphereWithHolesObj( ); + + } + +} + +function buildSphereWithHolesObj( ) { + + const dd = g.d * g.d; + + const squareLength = ( x,y,z ) => ( x*x + y*y + z*z ); + const length = ( x, y, z ) => ( Math.sqrt( x * x + y * y + z * z ) ); + const prevFront = ( i ) => ( i !== 0 ? i - 1 : front.length - 1 ); + const nextFront = ( i ) => ( i !== front.length - 1 ? i + 1 : 0 ); + const determinant = ( xa,ya,za, xb,yb,zb, xc,yc,zc ) => ( xa*yb*zc + ya*zb*xc + za*xb*yc - za*yb*xc - xa*zb*yc - ya*xb*zc ); + + let m; // index of the current front point + let n; // number of new points + let nT; // number of new triangles + let nIns; // number of new points (after union or split) + let dAng; // partial angle + let len, d1, d2, d12; // lengths + let iSplit, jSplit; // split front indices + let iUnite, jUnite, fUnite; // unite front indices, front number (to unite) + + // points and vectors: + let x, y, z, xp, yp, zp; // coordinates point and actual point p + let x1, y1, z1, x2, y2, z2; // previous and next point to p in front + let xn, yn, zn; // normal, gradient (sphere: normalized point) + let xt1, yt1, zt1, xt2, yt2, zt2; // tangents + let xs1, ys1, xs2, ys2; // p in tangential system (only x, y required) + let xc, yc, zc; // actual point as center point for new points + + // preparation + + const faceCount = g.detail * g.detail * 8 ; + const posCount = g.detail * g.detail * 6 ; + + g.indices = new Uint32Array( faceCount * 3 ); + g.positions = new Float32Array( posCount * 3 ); + //g.normals = new Float32Array( posCount * 3 ); + + g.setIndex( new THREE.BufferAttribute( g.indices, 1 ) ); + g.addAttribute( 'position', new THREE.BufferAttribute( g.positions, 3 ) ); + + let posIdx = 0; + let indIdx = 0; + let frontPosIdx, unionIdxA, unionIdxB, splitIdx; + + let front = []; // active front // front[ i ]: object { idx: 0, ang: 0 } + let partFront = []; // separated part of the active front (to split) + let insertFront = []; // new front points to insert into active front + let fronts = []; // all fronts + let partBounds = []; // bounding box of partFront [ xmin, xmax, ymin, ymax, zmin, zmax ] + let boundings = []; // fronts bounding boxes + let smallAngles = []; // new angles < 1.5 + + let frontNo, frontStock; + let unite = false; + let split = false; + + frontNo = 0; // active front number + frontStock = 0; // number of fronts still to be processed + + // define holes fronts + + if ( g.holes.length === 0 ) { + + makeFirstTriangle( ); + + } else { + + g.circles = []; // array of arrays [ xc, yc, zc, rHole, div4Hole ], values for external use + + for ( let i = 0; i < g.holes.length; i ++ ) { + + if ( g.holes[ i ].length === 3 ) { + + makeCircularHole( i ); // [ theta, phi, div4Hole ] + + } else { + + makePointsHole( i ); // points: [ theta, phi, ... ] + + } + + } + + } + + frontNo = 0; + front = fronts[ frontNo ]; + + //////////////// DEBUG triangles ////////////////////////////////////// + // let stp = 0; + /////////////////////////////////////////////////////////////////////// + + // ------ triangulation cycle ------------- + + while ( frontStock > 0 ) { + + if ( !unite ) { // triangulation on the front + + smallAngles = []; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang === 0 ) calculateFrontAngle( i ); // is to be recalculated (angle was set to zero) + + } + + m = getMinimalAngleIndex( ); // front angle + makeNewTriangles( m ); + + if ( front.length > 9 && smallAngles.length === 0 ) { + + checkDistancesToUnite( m ); + + } + + if ( front.length === 3 ) { + + makeLastTriangle( ); // last triangle closes the front + chooseNextFront( ); // if aviable + + } + + } else { // unite the active front to another front + + uniteFront( m, iUnite, fUnite, jUnite ); + trianglesAtUnionPoints( ); + unite = false; + + } + + } + + // ..... detail functions ..... + + function makeFirstTriangle ( ) { + + fronts[ frontNo ] = []; + boundings[ frontNo ] = []; + + storePoint( 0, 0 ); // ( theta, phi ) + storePoint( Math.PI / 2 / g.div4, -Math.PI / 6 ); + storePoint( Math.PI / 2 / g.div4, Math.PI / 6 ); + + g.indices[ 0 ] = 0; + g.indices[ 1 ] = 1; + g.indices[ 2 ] = 2; + + indIdx += 3; + + fronts[ frontNo ].push( { idx: 0, ang: 0 }, { idx: 1, ang: 0 }, { idx: 2, ang: 0 } ); + + frontNo ++; + frontStock ++; + + } + + function makePointsHole( i ) { + + let theta, phi, count, xmin, ymin, zmin, xmax, ymax, zmax, xv2, yv2, zv2; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + fronts[ frontNo ] = []; + boundings[ frontNo ] = []; + + theta = g.holes[ i ][ 0 ]; + phi = g.holes[ i ][ 1 ]; + + x1 = g.radius * Math.sin( theta ) * Math.cos( phi ); + y1 = g.radius * Math.cos( theta ); + z1 = -g.radius * Math.sin( theta ) * Math.sin( phi ); + + for ( let j = 1; j < g.holes[ i ].length / 2 + 1; j ++ ) { + + g.positions[ posIdx ] = x1; + g.positions[ posIdx + 1 ] = y1; + g.positions[ posIdx + 2 ] = z1; + + fronts[ frontNo ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x1 < xmin ? x1 : xmin; + ymin = y1 < ymin ? y1 : ymin; + zmin = z1 < zmin ? z1 : zmin; + + xmax = x1 > xmax ? x1 : xmax; + ymax = y1 > ymax ? y1 : ymax; + zmax = z1 > zmax ? z1 : zmax; + + posIdx += 3; + + theta = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 : 0 ]; // 0 => connect to start + phi = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 + 1 : 1 ]; // 1 => connect to start + + x2 = g.radius * Math.sin( theta ) * Math.cos( phi ); + y2 = g.radius * Math.cos( theta ); + z2 = -g.radius * Math.sin( theta ) * Math.sin( phi ); + + xv2 = x2 - x1; + yv2 = y2 - y1; + zv2 = z2 - z1; + + len = length( xv2, yv2, zv2 ); + + if ( len > g.d ) { + + count = Math.ceil( len / g.d ); + + for ( let k = 1; k < count; k ++ ) { + + x = x1 + k * xv2 / count; + y = y1 + k * yv2 / count; + z = z1 + k * zv2 / count; + + len = length( x, y, z ); // to bring the point to the surface (g.radius * ..) + + g.positions[ posIdx ] = g.radius * x / len; + g.positions[ posIdx + 1 ] = g.radius * y / len; + g.positions[ posIdx + 2 ] = g.radius * z / len; + + fronts[ frontNo ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + } + + x1 = x2; + y1 = y2; + z1 = z2; + + } + + boundings[ frontNo ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + frontNo ++; + frontStock ++; + + } + + function makeCircularHole( i ) { + + let xa, ya, za, xb, yb; // for rotation around z, y + + const theta = g.holes[ i ][ 0 ]; + const phi = g.holes[ i ][ 1 ]; + const div4Hole = g.holes[ i ][ 2 ]; + const countH = div4Hole * 4; + + let xmin, ymin, zmin, xmax, ymax, zmax; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + const rHole = g.d / ( Math.sin( Math.PI / countH ) * 2 ); // radius cutting circle + const h = Math.sqrt( g.radius * g.radius - rHole * rHole ); // distance: sphere center to cutting circle + + // ... hole values for external use + xp = g.radius * Math.sin( theta ) * Math.cos( phi ); + yp = g.radius * Math.cos( theta ); + zp = -g.radius * -Math.sin( theta ) * Math.sin( phi ); + + xc = h / g.radius * xp; + yc = h / g.radius * yp; + zc = h / g.radius * zp; + + g.circles.push( [ xc, yc, zc, rHole, div4Hole ] ); // values for external use + + fronts[ frontNo ] = []; + boundings[ frontNo ] = []; + + ya = h; + + for ( let i = 0, alpha = 0; i < countH; i ++, alpha += 2 * Math.PI / countH ) { + + // cutting circle on top + xa = rHole * Math.cos( alpha ); + za = rHole * Math.sin( alpha ); + + // rotate around z axis + xb = xa * Math.cos( theta ) - ya * Math.sin( theta ); + yb = xa * Math.sin( theta ) + ya * Math.cos( theta ); + + // rotate around y axis + x = -xb * Math.cos( phi ) + za * Math.sin( phi ); + z = xb * Math.sin( phi ) + za * Math.cos( phi ); + + y = yb; // for storing and checking bounds + + g.positions[ posIdx ] = x; + g.positions[ posIdx + 1 ] = y; + g.positions[ posIdx + 2 ] = z; + + fronts[ frontNo ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + boundings[ frontNo ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + frontNo ++; + frontStock ++; + + } + + function checkDistancesToUnite( m ) { // for new active front points + + let idxJ, xChk, yChk, zChk, ddUnite; + let ddUniteMin = Infinity; + unite = false; + + for ( let i = 0; i < insertFront.length; i ++ ) { + + getPoint( m + i ); + + for ( let f = 0; f < fronts.length; f ++ ) { + + if ( f !== frontNo ) { + + xChk = ( xp > boundings[ f ][ 0 ] - g.d ) && ( xp < boundings[ f ][ 3 ] + g.d ); + yChk = ( yp > boundings[ f ][ 1 ] - g.d ) && ( yp < boundings[ f ][ 4 ] + g.d ); + zChk = ( zp > boundings[ f ][ 2 ] - g.d ) && ( zp < boundings[ f ][ 5 ] + g.d ); + + if ( xChk || yChk || zChk ) { + + for ( let j = 0; j < fronts[ f ].length; j ++ ) { + + idxJ = fronts[ f ][ j ].idx * 3; + + // Hint: here (2) is exceptionally point in other front! + x2 = g.positions[ idxJ ]; + y2 = g.positions[ idxJ + 1 ]; + z2 = g.positions[ idxJ + 2 ]; + + ddUnite = squareLength ( x2 - xp, y2 - yp, z2 - zp ); + + if ( ddUnite < dd && ddUnite < ddUniteMin ) { + + ddUniteMin = ddUnite; + iUnite = i; + jUnite = j; + fUnite = f; + unite = true; + + } + + } + + } + + } + + } + + } + + } + + function uniteFront( m, i, f, j ) { + + let tmp = []; + + tmp[ 0 ] = front.slice( 0, m + i + 1 ); + tmp[ 1 ] = fronts[ f ].slice( j , fronts[ f ].length ); + tmp[ 2 ] = fronts[ f ].slice( 0 , j + 1 ); + tmp[ 3 ] = front.slice( m + i, front.length ); + + unionIdxA = m + i; + unionIdxB = m + i + 1 + fronts[ f ].length + + front = []; + + for ( let t = 0; t < 4; t ++ ) { + + for ( let k = 0; k < tmp[ t ].length ; k ++ ) { + + front.push( tmp[ t ][ k ] ); + + } + + } + + fronts[ f ] = []; // empty united front + + frontStock -= 1; // front is eliminated + + } + + function trianglesAtUnionPoints( ) { + + nIns = 0; // count inserted points + + calculateFrontAngle( unionIdxA ); + calculateFrontAngle( unionIdxA + 1 ); + + if ( front[ unionIdxA ].ang < front[ unionIdxA + 1 ].ang ) { + + makeNewTriangles( unionIdxA ); + nIns += n - 1; + calculateFrontAngle( unionIdxA + 1 + nIns ); + makeNewTriangles( unionIdxA + 1 + nIns ); + nIns += n - 1; + + } else { + + makeNewTriangles( unionIdxA + 1 ); + nIns += n - 1; + calculateFrontAngle( unionIdxA ); + makeNewTriangles( unionIdxA ); + nIns += n - 1; + } + + calculateFrontAngle( unionIdxB + nIns ); + calculateFrontAngle( unionIdxB + 1 + nIns ); + + if ( front[ unionIdxB + nIns ].ang < front[ unionIdxB + 1 + nIns ].ang ) { + + makeNewTriangles( unionIdxB + nIns ); + nIns += n - 1; + calculateFrontAngle( unionIdxB + 1 + nIns ); + makeNewTriangles( unionIdxB + 1 + nIns ); + + } else { + + makeNewTriangles( unionIdxB + 1 + nIns ); + calculateFrontAngle( unionIdxB + nIns ); + makeNewTriangles( unionIdxB + nIns ); + + } + + } + + function getMinimalAngleIndex( ) { + + let angle = Infinity; + let m; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang < angle ) { + + angle = front[ i ].ang ; + m = i; + + } + + } + + return m; + + } + + function makeNewTriangles( m ) { + + // m: minimal angle (index) + + insertFront = []; // new front points + + nT = Math.floor( 3 * front[ m ].ang / Math.PI ) + 1; // number of new triangles + + dAng = front[ m ].ang / nT; + + getSystemAtPoint( m ); + getNextPoint( m ); + + d1 = length( x1 - xp, y1 - yp, z1 - zp ); + d2 = length( x2 - xp, y2 - yp, z2 - zp ); + d12 = length( x2 - x1, y2 - y1, z2 - z1 ); + + // correction of dAng, nT in extreme cases + + if ( dAng < 0.8 && nT > 1 ) { + + nT --; + dAng = front[ m ].ang / nT; + + } + + if ( dAng > 0.8 && nT === 1 && d12 > 1.25 * g.d ) { + + nT = 2; + dAng = front[ m ].ang / nT; + + } + + if ( d1 * d1 < 0.2 * dd || d2 * d2 < 0.2 * dd ) { + + nT = 1; + + } + + n = nT - 1; // n number of new points + + if ( n === 0 ) { // one triangle + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + front[ prevFront( m ) ].ang = 0; + front[ nextFront( m ) ].ang = 0; + + front.splice( m, 1 ); // delete point with index m from the front + + } else { // more then one triangle + + xc = xp; + yc = yp; + zc = zp; + + for ( let i = 0, phi = dAng; i < n; i ++, phi += dAng ) { + + xp = xc + Math.cos( phi ) * g.d * xt1 + Math.sin( phi ) * g.d * xt2; + yp = yc + Math.cos( phi ) * g.d * yt1 + Math.sin( phi ) * g.d * yt2; + zp = zc + Math.cos( phi ) * g.d * zt1 + Math.sin( phi ) * g.d * zt2; + + len = length( xp, yp, zp ); // to bring the point to the surface (g.radius * ..) + + g.positions[ posIdx ] = g.radius * xp / len; + g.positions[ posIdx + 1 ] = g.radius * yp / len; + g.positions[ posIdx + 2 ] = g.radius * zp / len; + + insertFront.push( { idx: posIdx / 3, ang: 0 } ); + + posIdx += 3; + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx + g.indices[ indIdx + 2 ] = insertFront[ 0 ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + front[ prevFront( m ) ].ang = 0; + + for ( let i = 0; i < n - 1; i ++ ) { + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ i ].idx; + g.indices[ indIdx + 2 ] = insertFront[ i + 1 ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ n - 1 ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + front[ nextFront( m ) ].ang = 0; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + replaceFront( m, insertFront ); // replaces front[ m ] with new points + + } + + } + + function makeLastTriangle( ) { + + g.indices[ indIdx ] = front[ 2 ].idx; + g.indices[ indIdx + 1 ] = front[ 1 ].idx + g.indices[ indIdx + 2 ] = front[ 0 ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + front = []; + + fronts[ frontNo ] = []; + + frontStock -= 1; // close front + + } + + function chooseNextFront( ) { + + if ( frontStock > 0 ) { + + for ( let i = 0; i < fronts.length; i ++ ) { + + if ( fronts[ i ].length > 0 ) { + + frontNo = i; + break; + + } + + } + + front = fronts[ frontNo ]; + + smallAngles = []; + + for ( let i = 0; i < front.length; i ++ ) { + + calculateFrontAngle( i ); // recalculate angles of next front + + } + + } + + } + + function atan2PI( x, y ) { + + let phi = Math.atan2( y, x ); + + if ( phi < 0 ) phi = phi + Math.PI * 2; + + return phi; + + } + + function coordTangentialSystem( ) { + + let det = determinant( xt1, yt1, zt1, xt2, yt2, zt2, xn, yn, zn ); + + xs1 = determinant( x1 - xp, y1 - yp, z1 - zp, xt2, yt2, zt2, xn, yn, zn ) / det; + ys1 = determinant( xt1, yt1, zt1, x1 - xp, y1 - yp, z1 - zp, xn, yn, zn ) / det; + //zs1 = determinant( xt1, yt1, zt1, xt2, yt2, zt2, x1 - xp, y1 - yp, z1 - zp ) / det; // not needed + + xs2 = determinant( x2 - xp, y2 - yp, z2 - zp, xt2, yt2, zt2, xn, yn, zn ) / det; + ys2 = determinant( xt1, yt1, zt1, x2 - xp, y2 - yp, z2 - zp, xn, yn, zn ) / det; + //zs2 = determinant( xt1, yt1, zt1, xt2, yt2, zt2, x2 - xp, y2 - yp, z2 - zp ) / det; // not needed + + } + + function calculateFrontAngle( i ) { + + let ang1, ang2; + + getSystemAtPoint( i ); + getNextPoint( i ); + + coordTangentialSystem( ); + + ang1 = atan2PI( xs1, ys1 ); + ang2 = atan2PI( xs2, ys2 ); + + if ( ang2 < ang1 ) ang2 += Math.PI * 2; + + front[ i ].ang = ang2 - ang1; + + if ( front[ i ].ang < 1.5 ) smallAngles.push( i ); + + } + + function partFrontBounds( ) { + + let idx, xmin, ymin, zmin, xmax, ymax, zmax; + + partBounds = []; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + for( let i = 0; i < partFront.length; i ++ ) { + + idx = partFront[ i ].idx * 3; + + x = g.positions[ idx ]; + y = g.positions[ idx + 1 ]; + z = g.positions[ idx + 2 ]; + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + } + + partBounds.push( xmin, ymin, zmin, xmax, ymax, zmax ); + + boundings.push( partBounds ); + + } + + function replaceFront( m, fNew ) { + + let rear = front.splice( m, front.length - m ); + + for ( let i = 0; i < fNew.length; i ++ ) { + + front.push( fNew[ i ] ); // new front points + + } + + for ( let i = 1; i < rear.length; i ++ ) { // i = 1: without old front point m + + front.push( rear[ i ] ); + + } + + } + + function getSystemAtPoint( i ) { + + getPrevPoint( i ); + getPoint( i ); + + len = length( xp, yp, zp ); // to normalize + + xn = xp / len; + yn = yp / len + zn = zp / len; + + // centerAngle = Math.acos( Math.abs( x1 * xp + y1 * yp + z1 * zp ) / ( g.radius * g.radius ) ); + const h = Math.abs( x1 * xp + y1 * yp + z1 * zp ) / g.radius; // distance: sphere center to cutting circle + + // center cutting circle (refers to previous point) + xc = h / g.radius * xp; + yc = h / g.radius * yp; + zc = h / g.radius * zp; + + // first tangent + xt1 = x1 - xc; + yt1 = y1 - yc; + zt1 = z1 - zc; + + len = length( xt1, yt1, zt1 ); // to normalize + + xt1 = xt1 / len; + yt1 = yt1 / len; + zt1 = zt1 / len; + + // cross, second tangent + + xt2 = yn * zt1 - zn * yt1; + yt2 = zn * xt1 - xn * zt1; + zt2 = xn * yt1 - yn * xt1; + + } + + function storePoint( theta, phi ) { + + g.positions[ posIdx ] = g.radius * Math.sin( theta ) * Math.cos( phi ); + g.positions[ posIdx + 1 ] = g.radius * Math.cos( theta ); + g.positions[ posIdx + 2 ] = -g.radius * Math.sin( theta ) * Math.sin( phi ); + + posIdx += 3; + + } + + function getPrevPoint( i ) { + + frontPosIdx = front[ prevFront( i ) ].idx * 3; + + x1 = g.positions[ frontPosIdx ]; + y1 = g.positions[ frontPosIdx + 1 ]; + z1 = g.positions[ frontPosIdx + 2 ]; + + } + + function getPoint( i ) { + + frontPosIdx = front[ i ].idx * 3; + + xp = g.positions[ frontPosIdx ]; + yp = g.positions[ frontPosIdx + 1 ]; + zp = g.positions[ frontPosIdx + 2 ]; + + } + + function getNextPoint( i ) { + + frontPosIdx = front[ nextFront( i ) ].idx * 3; + + x2 = g.positions[ frontPosIdx ]; + y2 = g.positions[ frontPosIdx + 1 ]; + z2 = g.positions[ frontPosIdx + 2 ]; + + } + +} + +function buildSphereWithHoles( ) { + + const length = ( x, y, z ) => ( Math.sqrt( x * x + y * y + z * z ) ); + const prevFront = ( i ) => ( i !== 0 ? i - 1 : front.length - 1 ); + const nextFront = ( i ) => ( i !== front.length - 1 ? i + 1 : 0 ); + + let d; // rough edge length of the triangles + let m; // index of the current front point + let n; // number of new points + let nT; // number of new triangles + let nUnion; // number of new points (after union) + let dAng; // partial angle + let len, d1, d2, d12, dd1, dd2, dd12; // lengths and their squares + let h; // distance center to circle + let acute, concave; // front angle properties + + // points and vectors: + let x, y, z, xp, yp, zp, xc, yc, zc, x1, y1, z1, x2, y2, z2, xt1, yt1, zt1, xt2, yt2, zt2, xv1, yv1, zv1, xv2, yv2, zv2; + + // preparation + + const faceCount = g.detail * g.detail * 4; + const posCount = g.detail * g.detail * 3; + + g.indices = new Uint32Array( faceCount * 3 ); + g.positions = new Float32Array( posCount * 3 ); + //g.normals = new Float32Array( posCount * 3 ); + + g.setIndex( new THREE.BufferAttribute( g.indices, 1 ) ); + g.addAttribute( 'position', new THREE.BufferAttribute( g.positions, 3 ) ); + + d = Math.PI / g.detail; // rough side length of the triangles + + let posIdx = 0; + let indIdx = 0; + let frontPosIdx, unionIdxA, unionIdxB; + + let front = []; // active front // front[ i ]: object { idx: 0, ang: 0 } + let partFront = []; // separated part of the active front + let insertFront = []; // new front points to insert into active front + let fronts = []; // all fronts + let partBounds = []; // bounding box of partFront [ xmin, xmax, ymin, ymax, zmin, zmax ] + let boundings = []; // fronts bounding boxes + let smallAngles = []; // new angles < 1.5 + + let start = true; + let united = false; + + // define holes + + let holeNumber; + + if ( g.holes.length === 0 ) { + + makeFirstTriangle( ); + + } else { + + g.circles = []; // [ center, r, count ] of holes for external use + + holeNumber = 0; + + for ( let i = 0; i < g.holes.length; i ++ ) { + + if ( g.holes[ i ].length === 3 ) { + + makeCircularHole( i ); // [ theta, phi, count ] + + } else { + + makePointsHole( i ); // points: [ theta, phi, ... ] + + } + + } + + } + + let activeFrontNo = 0; + front = fronts[ activeFrontNo ]; + + // ------ triangulation cycle ------------- + + while ( front.length > 3 || start ) { + + if ( start ) start = false; + + if ( front.length > 9 && smallAngles.length === 0 ) { + + // checkDistancesInFront( ); + checkDistancesToFronts( m ); + + } + + if ( united ) { + + trianglesAtUnionPoints( ); + + } else { + + calculateFrontAngles( ); + m = getMinimalAngleIndex( ); // front angle + newTriangles( m ); + + } + + } // end while + + makeLastTriangle( ); + + // ..... main detail functions ..... + + function checkDistancesToFronts( m ) { + + let idx, idxJ, xChk, yChk, zChk; + + for ( let i = 0; i < insertFront.length; i ++ ) { + + idx = front[ m + i ].idx * 3 + + xp = g.positions[ idx ]; + yp = g.positions[ idx + 1 ]; + zp = g.positions[ idx + 2 ]; + + for ( let f = 0; f < fronts.length; f ++ ) { + + if ( f !== activeFrontNo ) { + + xChk = ( xp > boundings[ f ][ 0 ] - d ) && ( xp < boundings[ f ][ 3 ] + d ); + yChk = ( yp > boundings[ f ][ 1 ] - d ) && ( yp < boundings[ f ][ 4 ] + d ); + zChk = ( zp > boundings[ f ][ 2 ] - d ) && ( zp < boundings[ f ][ 5 ] + d ); + + if ( xChk || yChk || zChk ) { + + for ( let j = 0; j < fronts[ f ].length; j ++ ) { + + idxJ = fronts[ f ][ j ].idx * 3; + x2 = g.positions[ idxJ ]; + y2 = g.positions[ idxJ + 1 ]; + z2 = g.positions[ idxJ + 2 ]; + + if ( length( x2 - xp, y2 - yp, z2 - zp ) < d ) { + + uniteFront( m, i, f, j ); + + } + + } + + } + + } + + } + + } + + } + + function calculateFrontAngles( ) { + + smallAngles = []; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang === 0 ) { + + frontAngle( i ); + + } + + } + + } + + function getMinimalAngleIndex( ) { + + let angle = Infinity; + let m; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang < angle ) { + + angle = front[ i ].ang ; + m = i; + + } + + } + + return m; + + } + + function newTriangles( m ) { + + // m: minimal angle (index) + + insertFront = []; + + nT = Math.floor( 3 * front[ m ].ang / Math.PI ) + 1; // number of new triangles + + dAng = front[ m ].ang / nT; + + getSystemAtPoint( m ); + getNextPoint( m ); + + d1 = length( x1 - xp, y1 - yp, z1 - zp ); + d2 = length( x2 - xp, y2 - yp, z2 - zp ); + d12 = length( x2 - x1, y2 - y1, z2 - z1 ); + + // correction of dAng, nT in extreme cases + + if ( dAng < 0.8 && nT > 1 ) { + + nT --; + dAng = front[ m ].ang / nT; + + } + + if ( dAng > 0.8 && nT === 1 && d12 > 1.25 * d ) { + + nT = 2; + dAng = front[ m ].ang / nT; + + } + + if ( d1 * d1 < 0.2 * d * d || d2 * d2 < 0.2 * d * d ) { + + nT = 1; + + } + + n = nT - 1; // n number of new points + + if ( n === 0 ) { // one triangle + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + indIdx += 3; + + front[ prevFront( m ) ].ang = 0; + front[ nextFront( m ) ].ang = 0; + + } else { // more then one triangle + + for ( let i = 0, phi = dAng; i < n; i ++, phi += dAng ) { + + xp = xc + Math.cos( phi ) * d * xt1 + Math.sin( phi ) * d * xt2; + yp = yc + Math.cos( phi ) * d * yt1 + Math.sin( phi ) * d * yt2; + zp = zc + Math.cos( phi ) * d * zt1 + Math.sin( phi ) * d * zt2; + + len = length( xp, yp, zp ); // to normalize + + g.positions[ posIdx ] = xp / len; + g.positions[ posIdx + 1 ] = yp / len; + g.positions[ posIdx + 2 ] = zp / len; + + insertFront.push( { idx: posIdx / 3, ang: 0 } ); + + posIdx += 3; + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx + g.indices[ indIdx + 2 ] = insertFront[ 0 ].idx; + + indIdx += 3; + + front[ prevFront( m ) ].ang = 0; + + for ( let i = 0; i < n - 1; i ++ ) { + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ i ].idx; + g.indices[ indIdx + 2 ] = insertFront[ i + 1 ].idx; + + indIdx += 3; + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ n - 1 ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + front[ nextFront( m ) ].ang = 0; + + indIdx += 3; + + } + + replaceFront( m, insertFront ); // replaces front[ m ] with new points + + } + + function trianglesAtUnionPoints( ) { + + nUnion = 0; // count inserted points + + frontAngle( unionIdxA ); + frontAngle( unionIdxA + 1 ); + + if ( front[ unionIdxA ].ang < front[ unionIdxA + 1 ].ang ) { + + newTriangles( unionIdxA ); + nUnion += n - 1; + frontAngle( unionIdxA + 1 + nUnion ); + newTriangles( unionIdxA + 1 + nUnion ); + nUnion += n - 1; + + } else { + + newTriangles( unionIdxA + 1 ); + nUnion += n - 1; + frontAngle( unionIdxA ); + newTriangles( unionIdxA ); + nUnion += n - 1; + } + + frontAngle( unionIdxB + nUnion ); + frontAngle( unionIdxB + 1 + nUnion ); + + if ( front[ unionIdxB + nUnion ].ang < front[ unionIdxB + 1 + nUnion ].ang ) { + + newTriangles( unionIdxB + nUnion ); + nUnion += n - 1; + frontAngle( unionIdxB + 1 + nUnion ); + newTriangles( unionIdxB + 1 + nUnion ); + + } else { + + newTriangles( unionIdxB + 1 + nUnion ); + frontAngle( unionIdxB + nUnion ); + newTriangles( unionIdxB + nUnion ); + + } + + united = false; + + } + + // ..... help functions ..... + + function frontAngle( i ) { + + getPrevPoint( i ); // (1) + getPoint( i ); + getNextPoint( i ); // (2) + + // centerAngle = Math.acos( Math.abs( x1 * xp + y1 * yp + z1 * zp ) ); + // r = Math.sin( centerAngle ); // radius circle + // h = Math.cos( centerAngle ); // distance center to circle + + h = Math.abs( x1 * xp + y1 * yp + z1 * zp ); + + // center cutting circle (refers to previous point) + xc = h * xp; + yc = h * yp; + zc = h * zp; + + xv1 = xc - x1; + yv1 = yc - y1; + zv1 = zc - z1; + + len = length( xv1, yv1, zv1 ); // to normalize + + xv1 = xv1 / len; + yv1 = yv1 / len; + zv1 = zv1 / len; + + xv2 = x2 - xc; + yv2 = y2 - yc; + zv2 = z2 - zc; + + len = length( xv2, yv2, zv2 ); // to normalize + + xv2 = xv2 / len; + yv2 = yv2 / len; + zv2 = zv2 / len; + + front[ i ].ang = Math.acos( Math.abs( xv1 * xv2 + yv1 * yv2 + zv1 * zv2 ) ); + + // cross, to detect curvature + x = yv1 * zv2 - zv1 * yv2; + y = zv1 * xv2 - xv1 * zv2; + z = xv1 * yv2 - yv1 * xv2; + + len = length( x, y, z ); // to normalize + + x = xp + x / len; + y = yp + y / len; + z = zp + z / len; + + concave = ( length( x, y, z ) < 1 ); + + d1 = length( x1 - xp, y1 - yp, z1 - zp ); + d2 = length( x2 - xp, y2 - yp, z2 - zp ); + d12 = length( x2 - x1, y2 - y1, z2 - z1 ); + + dd1 = d1 * d1; + dd2 = d2 * d2; + dd12 = d12 * d12; + + acute = ( dd12 < ( dd1 + dd2) ); + + // if ( concave && acute ) front[ i ].ang += 0; + if ( concave && !acute ) front[ i ].ang = Math.PI - front[ i ].ang ; + if ( !concave && acute ) front[ i ].ang = 2 * Math.PI - front[ i ].ang ; + if ( !concave && !acute ) front[ i ].ang = Math.PI + front[ i ].ang ; + + if ( front[ i ].ang < 1.5 ) smallAngles.push( i ); + + } + + function uniteFront( m, i, f, j ) { + + let tmp = []; + + tmp[ 0 ] = front.slice( 0, m + i + 1 ); + tmp[ 1 ] = fronts[ f ].slice( j , fronts[ f ].length ); + tmp[ 2 ] = fronts[ f ].slice( 0 , j + 1 ); + tmp[ 3 ] = front.slice( m + i, front.length ); + + unionIdxA = m + i; + unionIdxB = m + i + 1 + fronts[ f ].length + + front = []; + + for ( let t = 0; t < 4; t ++ ) { + + for ( let k = 0; k < tmp[ t ].length ; k ++ ) { + + front.push( tmp[ t ][ k ] ); + + } + + } + + fronts[ f ] = []; // empty united front + + united = true; + + } + + function partFrontBounds( ) { + + let idx, xmin, ymin, zmin, xmax, ymax, zmax; + + partBounds = []; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + for( let i = 0; i < partFront.length; i ++ ) { + + idx = partFront[ i ].idx * 3; + + x = g.positions[ idx ]; + y = g.positions[ idx + 1 ]; + z = g.positions[ idx + 2 ]; + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + } + + partBounds.push( xmin, ymin, zmin, xmax, ymax, zmax ); + + boundings.push( partBounds ); + + } + + function replaceFront( m, fNew ) { + + let rear = front.splice( m, front.length - m ) + + for ( let i = 0; i < fNew.length; i ++ ) { + + front.push( fNew[ i ] ); // new front points + + } + + for ( let i = 1; i < rear.length; i ++ ) { // 1: without old front point m + + front.push( rear[ i ] ); + + } + + } + + function storePoint( theta, phi ) { + + g.positions[ posIdx ] = Math.sin( theta ) * Math.cos( phi ); + g.positions[ posIdx + 1 ] = Math.cos( theta ); + g.positions[ posIdx + 2 ] = -Math.sin( theta ) * Math.sin( phi ); + + posIdx += 3; + + } + + function makeFirstTriangle ( ) { + + storePoint( 0, 0 ); // ( theta, phi ) + storePoint( d, -Math.PI / 6 ); + storePoint( d, Math.PI / 6 ); + + g.indices[ 0 ] = 0; + g.indices[ 1 ] = 1; + g.indices[ 2 ] = 2; + + indIdx += 3; + + front = []; + + front.push( { idx: 0, ang: 0 }, { idx: 1, ang: 0 }, { idx: 2, ang: 0 } ); + fronts.push( front ) + + } + + function makeLastTriangle( ) { + + g.indices[ indIdx ] = front[ 2 ].idx; + g.indices[ indIdx + 1 ] = front[ 1 ].idx + g.indices[ indIdx + 2 ] = front[ 0 ].idx; + + } + + function makePointsHole( i ) { + + let theta, phi, count, xmin, ymin, zmin, xmax, ymax, zmax; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + fronts[ holeNumber ] = []; + boundings[ holeNumber ] = []; + + theta = g.holes[ i ][ 0 ]; + phi = g.holes[ i ][ 1 ]; + + x1 = Math.sin( theta ) * Math.cos( phi ); + y1 = Math.cos( theta ); + z1 = -Math.sin( theta ) * Math.sin( phi ); + + for ( let j = 1; j < g.holes[ i ].length / 2 + 1; j ++ ) { + + g.positions[ posIdx ] = x1; + g.positions[ posIdx + 1 ] = y1; + g.positions[ posIdx + 2 ] = z1; + + fronts[ holeNumber ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x1 < xmin ? x1 : xmin; + ymin = y1 < ymin ? y1 : ymin; + zmin = z1 < zmin ? z1 : zmin; + + xmax = x1 > xmax ? x1 : xmax; + ymax = y1 > ymax ? y1 : ymax; + zmax = z1 > zmax ? z1 : zmax; + + posIdx += 3; + + theta = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 : 0 ]; // 0 => connect to start + phi = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 + 1 : 1 ]; // 1 => connect to start + + x2 = Math.sin( theta ) * Math.cos( phi ); + y2 = Math.cos( theta ); + z2 = -Math.sin( theta ) * Math.sin( phi ); + + xv2 = x2 - x1; + yv2 = y2 - y1; + zv2 = z2 - z1; + + len = length( xv2, yv2, zv2 ); + + if ( len > d ) { + + count = Math.ceil( len / d ); + + for ( let k = 1; k < count; k ++ ) { + + x = x1 + k * xv2 / count; + y = y1 + k * yv2 / count; + z = z1 + k * zv2 / count; + + len = length( x, y, z ); + + g.positions[ posIdx ] = x / len; + g.positions[ posIdx + 1 ] = y / len; + g.positions[ posIdx + 2 ] = z / len; + + fronts[ holeNumber ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + } + + x1 = x2; + y1 = y2; + z1 = z2; + + } + + boundings[ holeNumber ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + holeNumber ++; + + } + + function makeCircularHole( i ) { + + let theta = g.holes[ i ][ 0 ]; + let phi = g.holes[ i ][ 1 ]; + let count = g.holes[ i ][ 2 ]; + + let xmin, ymin, zmin, xmax, ymax, zmax; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + xp = Math.sin( theta ) * Math.cos( phi ); + yp = Math.cos( theta ); + zp = -Math.sin( theta ) * Math.sin( phi ); + + let r = count / detail / 2; // radius cutting circle + + h = Math.sqrt( 1 - r * r ); + + if ( !(xp === 0 && yp === 0 ) ) { + + xt1 = -yp; + yt1 = xp; + zt1 = 0; + + } else { + + xt1 = 0; + yt1 = 1; + zt1 = 0; + + } + + // cross + + xt2 = yp * zt1 - zp * yt1; + yt2 = zp * xt1 - xp * zt1; + zt2 = xp * yt1 - yp * xt1; + + len = length( xt1, yt1, zt1 ); // to normalize + + xt1 = xt1 / len; + yt1 = yt1 / len; + zt1 = zt1 / len; + + len = length( xt2, yt2, zt2 ); // to normalize + + xt2 = xt2 / len; + yt2 = yt2 / len; + zt2 = zt2 / len; + + xc = h * xp; + yc = h * yp; + zc = h * zp; + + g.circles.push( [ xc, yc, zc, r, count ] ); // for external use + + fronts[ holeNumber ] = []; + boundings[ holeNumber ] = []; + + for ( let i = 0, phi = 0; i < count; i ++, phi += 2 * Math.PI / count ) { + + x = xc + Math.cos( phi ) * r * xt1 + Math.sin( phi ) * r * xt2; + y = yc + Math.cos( phi ) * r * yt1 + Math.sin( phi ) * r * yt2; + z = zc + Math.cos( phi ) * r * zt1 + Math.sin( phi ) * r * zt2; + + g.positions[ posIdx ] = x; + g.positions[ posIdx + 1 ] = y; + g.positions[ posIdx + 2 ] = z; + + fronts[ holeNumber ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + boundings[ holeNumber ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + holeNumber ++; + + } + + function getSystemAtPoint( i ) { + + getPrevPoint( i ); + getPoint( i ); + + // centerAngle = Math.acos( Math.abs( x1 * xp + y1 * yp + z1 * zp ) ); + // r = Math.sin( centerAngle ); // radius cutting circle + // h = Math.cos( centerAngle ); // distance center to cutting circle + + h = Math.abs( x1 * xp + y1 * yp + z1 * zp ); + + // center cutting circle (refers to previous point) + xc = h * xp; + yc = h * yp; + zc = h * zp; + + // first tangent + xt1 = x1 - xc; + yt1 = y1 - yc; + zt1 = z1 - zc; + + len = length( xt1, yt1, zt1 ); // to normalize + + xt1 = xt1 / len; + yt1 = yt1 / len; + zt1 = zt1 / len; + + // cross, second tangent (sphere radius 1: p equals normal) + + xt2 = yp * zt1 - zp * yt1; + yt2 = zp * xt1 - xp * zt1; + zt2 = xp * yt1 - yp * xt1; + + } + + function getPrevPoint( i ) { + + frontPosIdx = front[ prevFront( i ) ].idx * 3 ; + x1 = g.positions[ frontPosIdx ]; + y1 = g.positions[ frontPosIdx + 1 ]; + z1 = g.positions[ frontPosIdx + 2 ]; + + } + + function getPoint( i ) { + + frontPosIdx = front[ i ].idx * 3; + xp = g.positions[ frontPosIdx ]; + yp = g.positions[ frontPosIdx + 1 ]; + zp = g.positions[ frontPosIdx + 2 ]; + + } + + function getNextPoint( i ) { + + frontPosIdx = front[ nextFront( i ) ].idx * 3; + x2 = g.positions[ frontPosIdx ]; + y2 = g.positions[ frontPosIdx + 1 ]; + z2 = g.positions[ frontPosIdx + 2 ]; + + } + +} + +exports.createSphereWithHoles = createSphereWithHoles; +exports.buildSphereWithHolesObj = buildSphereWithHolesObj; +exports.buildSphereWithHoles = buildSphereWithHoles; + +// ...................................... - .................................................. + +//################################################################################################# + +Object.defineProperty(exports, '__esModule', { value: true }); + +}))); \ No newline at end of file diff --git a/examples/js/createhole.js b/examples/js/createhole.js new file mode 100644 index 0000000..6d8a31d --- /dev/null +++ b/examples/js/createhole.js @@ -0,0 +1,1663 @@ +import * as THREE from 'three'; + +var g; // THREE.BufferGeometry + +//################################################################################################# + +// Algorithm based on (simplified for sphere with holes) +// de: https://www2.mathematik.tu-darmstadt.de/~ehartmann/cdg0/cdg0n.pdf +// en: https://www2.mathematik.tu-darmstadt.de/~ehartmann/cdgen0104.pdf +// + +// ............................ Sphere with Holes (Triangulation) ................................. + +function createSphereWithHoles( arg1, arg2 ) { + + g = this; // THREE.BufferGeometry() - geometry object from three.js + + g.buildSphereWithHolesObj = buildSphereWithHolesObj; + g.buildSphereWithHoles = buildSphereWithHoles; + + if( typeof arg1 === 'number' ) { // variant detail, holes are optional, radius = 1 is fixed - use three.js .scale + // Variant with less effort in the algorithm! Other angle calculation too. + + g.detail = arg1; // count of triangles for half a great circle + g.holes = arg2 !== undefined ? arg2 : []; // optional + /* holes, example: + holes = [ + // circular hole, 3 elements: [ theta, phi, count ] + [ 2.44, 0.41, 12 ], + [ 0.72, 2.55, 19 ], + // points hole,: array of points theta, phi, ... (last point is connected to first) + [ 0,0, 0.5,-0.8, 0.25,-0.27, 0.4,0.3, 0.3,0.72 ] + ]; + */ + + g.buildSphereWithHoles( ); + + } else { // variant parameters object { d: div4: holes: } all elements optional + + g.d = arg1.d !== undefined ? arg1.d : 2 * Math.sin( Math.PI / 24 ); // to g.div4 default + g.div4 = arg1.div4 !== undefined ? arg1.div4 : 6; // 6 * 4 = 24 great circle divisions + g.holes = arg1.holes !== undefined ? arg1.holes : []; + + g.detail = g.div4 * 4; // division of the great circle + g.radius = g.d / Math.sin( Math.PI / g.detail ) / 2; // sphere radius, for external use as well + + /* holes, example: + holes: [ + // circular hole, 3 elements: [ theta, phi, div4Hole ], div4Hole <= div4 + [ 1.82, 0.41, 12 ], + // points hole,: array of points theta, phi, ... (last point is connected to first) + [ 0,0, 0.5,-0.8, 0.25,-0.27, 0.4,0.3, 0.3,0.72 ] + ] + */ + + g.buildSphereWithHolesObj( ); + + } + +} + +function buildSphereWithHolesObj( ) { + + const dd = g.d * g.d; + + const squareLength = ( x,y,z ) => ( x*x + y*y + z*z ); + const length = ( x, y, z ) => ( Math.sqrt( x * x + y * y + z * z ) ); + const prevFront = ( i ) => ( i !== 0 ? i - 1 : front.length - 1 ); + const nextFront = ( i ) => ( i !== front.length - 1 ? i + 1 : 0 ); + const determinant = ( xa,ya,za, xb,yb,zb, xc,yc,zc ) => ( xa*yb*zc + ya*zb*xc + za*xb*yc - za*yb*xc - xa*zb*yc - ya*xb*zc ); + + let m; // index of the current front point + let n; // number of new points + let nT; // number of new triangles + let nIns; // number of new points (after union or split) + let dAng; // partial angle + let len, d1, d2, d12; // lengths + let iSplit, jSplit; // split front indices + let iUnite, jUnite, fUnite; // unite front indices, front number (to unite) + + // points and vectors: + let x, y, z, xp, yp, zp; // coordinates point and actual point p + let x1, y1, z1, x2, y2, z2; // previous and next point to p in front + let xn, yn, zn; // normal, gradient (sphere: normalized point) + let xt1, yt1, zt1, xt2, yt2, zt2; // tangents + let xs1, ys1, xs2, ys2; // p in tangential system (only x, y required) + let xc, yc, zc; // actual point as center point for new points + + // preparation + + const faceCount = g.detail * g.detail * 8 ; + const posCount = g.detail * g.detail * 6 ; + + g.indices = new Uint32Array( faceCount * 3 ); + g.positions = new Float32Array( posCount * 3 ); + //g.normals = new Float32Array( posCount * 3 ); + + g.setIndex( new THREE.BufferAttribute( g.indices, 1 ) ); + g.setAttribute( 'position', new THREE.BufferAttribute( g.positions, 3 ) ); + + let posIdx = 0; + let indIdx = 0; + let frontPosIdx, unionIdxA, unionIdxB, splitIdx; + + let front = []; // active front // front[ i ]: object { idx: 0, ang: 0 } + let partFront = []; // separated part of the active front (to split) + let insertFront = []; // new front points to insert into active front + let fronts = []; // all fronts + let partBounds = []; // bounding box of partFront [ xmin, xmax, ymin, ymax, zmin, zmax ] + let boundings = []; // fronts bounding boxes + let smallAngles = []; // new angles < 1.5 + + let frontNo, frontStock; + let unite = false; + let split = false; + + frontNo = 0; // active front number + frontStock = 0; // number of fronts still to be processed + + // define holes fronts + + if ( g.holes.length === 0 ) { + + makeFirstTriangle( ); + + } else { + + g.circles = []; // array of arrays [ xc, yc, zc, rHole, div4Hole ], values for external use + + for ( let i = 0; i < g.holes.length; i ++ ) { + + if ( g.holes[ i ].length === 3 ) { + + makeCircularHole( i ); // [ theta, phi, div4Hole ] + + } else { + + makePointsHole( i ); // points: [ theta, phi, ... ] + + } + + } + + } + + frontNo = 0; + front = fronts[ frontNo ]; + + //////////////// DEBUG triangles ////////////////////////////////////// + // let stp = 0; + /////////////////////////////////////////////////////////////////////// + + // ------ triangulation cycle ------------- + + while ( frontStock > 0 ) { + + if ( !unite ) { // triangulation on the front + + smallAngles = []; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang === 0 ) calculateFrontAngle( i ); // is to be recalculated (angle was set to zero) + + } + + m = getMinimalAngleIndex( ); // front angle + makeNewTriangles( m ); + + if ( front.length > 9 && smallAngles.length === 0 ) { + + checkDistancesToUnite( m ); + + } + + if ( front.length === 3 ) { + + makeLastTriangle( ); // last triangle closes the front + chooseNextFront( ); // if aviable + + } + + } else { // unite the active front to another front + + uniteFront( m, iUnite, fUnite, jUnite ); + trianglesAtUnionPoints( ); + unite = false; + + } + + } + + // ..... detail functions ..... + + function makeFirstTriangle ( ) { + + fronts[ frontNo ] = []; + boundings[ frontNo ] = []; + + storePoint( 0, 0 ); // ( theta, phi ) + storePoint( Math.PI / 2 / g.div4, -Math.PI / 6 ); + storePoint( Math.PI / 2 / g.div4, Math.PI / 6 ); + + g.indices[ 0 ] = 0; + g.indices[ 1 ] = 1; + g.indices[ 2 ] = 2; + + indIdx += 3; + + fronts[ frontNo ].push( { idx: 0, ang: 0 }, { idx: 1, ang: 0 }, { idx: 2, ang: 0 } ); + + frontNo ++; + frontStock ++; + + } + + function makePointsHole( i ) { + + let theta, phi, count, xmin, ymin, zmin, xmax, ymax, zmax, xv2, yv2, zv2; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + fronts[ frontNo ] = []; + boundings[ frontNo ] = []; + + theta = g.holes[ i ][ 0 ]; + phi = g.holes[ i ][ 1 ]; + + x1 = g.radius * Math.sin( theta ) * Math.cos( phi ); + y1 = g.radius * Math.cos( theta ); + z1 = -g.radius * Math.sin( theta ) * Math.sin( phi ); + + for ( let j = 1; j < g.holes[ i ].length / 2 + 1; j ++ ) { + + g.positions[ posIdx ] = x1; + g.positions[ posIdx + 1 ] = y1; + g.positions[ posIdx + 2 ] = z1; + + fronts[ frontNo ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x1 < xmin ? x1 : xmin; + ymin = y1 < ymin ? y1 : ymin; + zmin = z1 < zmin ? z1 : zmin; + + xmax = x1 > xmax ? x1 : xmax; + ymax = y1 > ymax ? y1 : ymax; + zmax = z1 > zmax ? z1 : zmax; + + posIdx += 3; + + theta = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 : 0 ]; // 0 => connect to start + phi = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 + 1 : 1 ]; // 1 => connect to start + + x2 = g.radius * Math.sin( theta ) * Math.cos( phi ); + y2 = g.radius * Math.cos( theta ); + z2 = -g.radius * Math.sin( theta ) * Math.sin( phi ); + + xv2 = x2 - x1; + yv2 = y2 - y1; + zv2 = z2 - z1; + + len = length( xv2, yv2, zv2 ); + + if ( len > g.d ) { + + count = Math.ceil( len / g.d ); + + for ( let k = 1; k < count; k ++ ) { + + x = x1 + k * xv2 / count; + y = y1 + k * yv2 / count; + z = z1 + k * zv2 / count; + + len = length( x, y, z ); // to bring the point to the surface (g.radius * ..) + + g.positions[ posIdx ] = g.radius * x / len; + g.positions[ posIdx + 1 ] = g.radius * y / len; + g.positions[ posIdx + 2 ] = g.radius * z / len; + + fronts[ frontNo ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + } + + x1 = x2; + y1 = y2; + z1 = z2; + + } + + boundings[ frontNo ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + frontNo ++; + frontStock ++; + + } + + function makeCircularHole( i ) { + + let xa, ya, za, xb, yb; // for rotation around z, y + + const theta = g.holes[ i ][ 0 ]; + const phi = g.holes[ i ][ 1 ]; + const div4Hole = g.holes[ i ][ 2 ]; + const countH = div4Hole * 4; + + let xmin, ymin, zmin, xmax, ymax, zmax; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + const rHole = g.d / ( Math.sin( Math.PI / countH ) * 2 ); // radius cutting circle + const h = Math.sqrt( g.radius * g.radius - rHole * rHole ); // distance: sphere center to cutting circle + + // ... hole values for external use + xp = g.radius * Math.sin( theta ) * Math.cos( phi ); + yp = g.radius * Math.cos( theta ); + zp = -g.radius * -Math.sin( theta ) * Math.sin( phi ); + + xc = h / g.radius * xp; + yc = h / g.radius * yp; + zc = h / g.radius * zp; + + g.circles.push( [ xc, yc, zc, rHole, div4Hole ] ); // values for external use + + fronts[ frontNo ] = []; + boundings[ frontNo ] = []; + + ya = h; + + for ( let i = 0, alpha = 0; i < countH; i ++, alpha += 2 * Math.PI / countH ) { + + // cutting circle on top + xa = rHole * Math.cos( alpha ); + za = rHole * Math.sin( alpha ); + + // rotate around z axis + xb = xa * Math.cos( theta ) - ya * Math.sin( theta ); + yb = xa * Math.sin( theta ) + ya * Math.cos( theta ); + + // rotate around y axis + x = -xb * Math.cos( phi ) + za * Math.sin( phi ); + z = xb * Math.sin( phi ) + za * Math.cos( phi ); + + y = yb; // for storing and checking bounds + + g.positions[ posIdx ] = x; + g.positions[ posIdx + 1 ] = y; + g.positions[ posIdx + 2 ] = z; + + fronts[ frontNo ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + boundings[ frontNo ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + frontNo ++; + frontStock ++; + + } + + function checkDistancesToUnite( m ) { // for new active front points + + let idxJ, xChk, yChk, zChk, ddUnite; + let ddUniteMin = Infinity; + unite = false; + + for ( let i = 0; i < insertFront.length; i ++ ) { + + getPoint( m + i ); + + for ( let f = 0; f < fronts.length; f ++ ) { + + if ( f !== frontNo ) { + + xChk = ( xp > boundings[ f ][ 0 ] - g.d ) && ( xp < boundings[ f ][ 3 ] + g.d ); + yChk = ( yp > boundings[ f ][ 1 ] - g.d ) && ( yp < boundings[ f ][ 4 ] + g.d ); + zChk = ( zp > boundings[ f ][ 2 ] - g.d ) && ( zp < boundings[ f ][ 5 ] + g.d ); + + if ( xChk || yChk || zChk ) { + + for ( let j = 0; j < fronts[ f ].length; j ++ ) { + + idxJ = fronts[ f ][ j ].idx * 3; + + // Hint: here (2) is exceptionally point in other front! + x2 = g.positions[ idxJ ]; + y2 = g.positions[ idxJ + 1 ]; + z2 = g.positions[ idxJ + 2 ]; + + ddUnite = squareLength ( x2 - xp, y2 - yp, z2 - zp ); + + if ( ddUnite < dd && ddUnite < ddUniteMin ) { + + ddUniteMin = ddUnite; + iUnite = i; + jUnite = j; + fUnite = f; + unite = true; + + } + + } + + } + + } + + } + + } + + } + + function uniteFront( m, i, f, j ) { + + let tmp = []; + + tmp[ 0 ] = front.slice( 0, m + i + 1 ); + tmp[ 1 ] = fronts[ f ].slice( j , fronts[ f ].length ); + tmp[ 2 ] = fronts[ f ].slice( 0 , j + 1 ); + tmp[ 3 ] = front.slice( m + i, front.length ); + + unionIdxA = m + i; + unionIdxB = m + i + 1 + fronts[ f ].length + + front = []; + + for ( let t = 0; t < 4; t ++ ) { + + for ( let k = 0; k < tmp[ t ].length ; k ++ ) { + + front.push( tmp[ t ][ k ] ); + + } + + } + + fronts[ f ] = []; // empty united front + + frontStock -= 1; // front is eliminated + + } + + function trianglesAtUnionPoints( ) { + + nIns = 0; // count inserted points + + calculateFrontAngle( unionIdxA ); + calculateFrontAngle( unionIdxA + 1 ); + + if ( front[ unionIdxA ].ang < front[ unionIdxA + 1 ].ang ) { + + makeNewTriangles( unionIdxA ); + nIns += n - 1; + calculateFrontAngle( unionIdxA + 1 + nIns ); + makeNewTriangles( unionIdxA + 1 + nIns ); + nIns += n - 1; + + } else { + + makeNewTriangles( unionIdxA + 1 ); + nIns += n - 1; + calculateFrontAngle( unionIdxA ); + makeNewTriangles( unionIdxA ); + nIns += n - 1; + } + + calculateFrontAngle( unionIdxB + nIns ); + calculateFrontAngle( unionIdxB + 1 + nIns ); + + if ( front[ unionIdxB + nIns ].ang < front[ unionIdxB + 1 + nIns ].ang ) { + + makeNewTriangles( unionIdxB + nIns ); + nIns += n - 1; + calculateFrontAngle( unionIdxB + 1 + nIns ); + makeNewTriangles( unionIdxB + 1 + nIns ); + + } else { + + makeNewTriangles( unionIdxB + 1 + nIns ); + calculateFrontAngle( unionIdxB + nIns ); + makeNewTriangles( unionIdxB + nIns ); + + } + + } + + function getMinimalAngleIndex( ) { + + let angle = Infinity; + let m; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang < angle ) { + + angle = front[ i ].ang ; + m = i; + + } + + } + + return m; + + } + + function makeNewTriangles( m ) { + + // m: minimal angle (index) + + insertFront = []; // new front points + + nT = Math.floor( 3 * front[ m ].ang / Math.PI ) + 1; // number of new triangles + + dAng = front[ m ].ang / nT; + + getSystemAtPoint( m ); + getNextPoint( m ); + + d1 = length( x1 - xp, y1 - yp, z1 - zp ); + d2 = length( x2 - xp, y2 - yp, z2 - zp ); + d12 = length( x2 - x1, y2 - y1, z2 - z1 ); + + // correction of dAng, nT in extreme cases + + if ( dAng < 0.8 && nT > 1 ) { + + nT --; + dAng = front[ m ].ang / nT; + + } + + if ( dAng > 0.8 && nT === 1 && d12 > 1.25 * g.d ) { + + nT = 2; + dAng = front[ m ].ang / nT; + + } + + if ( d1 * d1 < 0.2 * dd || d2 * d2 < 0.2 * dd ) { + + nT = 1; + + } + + n = nT - 1; // n number of new points + + if ( n === 0 ) { // one triangle + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + front[ prevFront( m ) ].ang = 0; + front[ nextFront( m ) ].ang = 0; + + front.splice( m, 1 ); // delete point with index m from the front + + } else { // more then one triangle + + xc = xp; + yc = yp; + zc = zp; + + for ( let i = 0, phi = dAng; i < n; i ++, phi += dAng ) { + + xp = xc + Math.cos( phi ) * g.d * xt1 + Math.sin( phi ) * g.d * xt2; + yp = yc + Math.cos( phi ) * g.d * yt1 + Math.sin( phi ) * g.d * yt2; + zp = zc + Math.cos( phi ) * g.d * zt1 + Math.sin( phi ) * g.d * zt2; + + len = length( xp, yp, zp ); // to bring the point to the surface (g.radius * ..) + + g.positions[ posIdx ] = g.radius * xp / len; + g.positions[ posIdx + 1 ] = g.radius * yp / len; + g.positions[ posIdx + 2 ] = g.radius * zp / len; + + insertFront.push( { idx: posIdx / 3, ang: 0 } ); + + posIdx += 3; + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx + g.indices[ indIdx + 2 ] = insertFront[ 0 ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + front[ prevFront( m ) ].ang = 0; + + for ( let i = 0; i < n - 1; i ++ ) { + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ i ].idx; + g.indices[ indIdx + 2 ] = insertFront[ i + 1 ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ n - 1 ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + front[ nextFront( m ) ].ang = 0; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + replaceFront( m, insertFront ); // replaces front[ m ] with new points + + } + + } + + function makeLastTriangle( ) { + + g.indices[ indIdx ] = front[ 2 ].idx; + g.indices[ indIdx + 1 ] = front[ 1 ].idx + g.indices[ indIdx + 2 ] = front[ 0 ].idx; + + indIdx += 3; + + /////////////// DEBUG triangles ////////////////////// + // stp ++; + //////////////////////////////////////////////////////// + + front = []; + + fronts[ frontNo ] = []; + + frontStock -= 1; // close front + + } + + function chooseNextFront( ) { + + if ( frontStock > 0 ) { + + for ( let i = 0; i < fronts.length; i ++ ) { + + if ( fronts[ i ].length > 0 ) { + + frontNo = i; + break; + + } + + } + + front = fronts[ frontNo ]; + + smallAngles = []; + + for ( let i = 0; i < front.length; i ++ ) { + + calculateFrontAngle( i ); // recalculate angles of next front + + } + + } + + } + + function atan2PI( x, y ) { + + let phi = Math.atan2( y, x ); + + if ( phi < 0 ) phi = phi + Math.PI * 2; + + return phi; + + } + + function coordTangentialSystem( ) { + + let det = determinant( xt1, yt1, zt1, xt2, yt2, zt2, xn, yn, zn ); + + xs1 = determinant( x1 - xp, y1 - yp, z1 - zp, xt2, yt2, zt2, xn, yn, zn ) / det; + ys1 = determinant( xt1, yt1, zt1, x1 - xp, y1 - yp, z1 - zp, xn, yn, zn ) / det; + //zs1 = determinant( xt1, yt1, zt1, xt2, yt2, zt2, x1 - xp, y1 - yp, z1 - zp ) / det; // not needed + + xs2 = determinant( x2 - xp, y2 - yp, z2 - zp, xt2, yt2, zt2, xn, yn, zn ) / det; + ys2 = determinant( xt1, yt1, zt1, x2 - xp, y2 - yp, z2 - zp, xn, yn, zn ) / det; + //zs2 = determinant( xt1, yt1, zt1, xt2, yt2, zt2, x2 - xp, y2 - yp, z2 - zp ) / det; // not needed + + } + + function calculateFrontAngle( i ) { + + let ang1, ang2; + + getSystemAtPoint( i ); + getNextPoint( i ); + + coordTangentialSystem( ); + + ang1 = atan2PI( xs1, ys1 ); + ang2 = atan2PI( xs2, ys2 ); + + if ( ang2 < ang1 ) ang2 += Math.PI * 2; + + front[ i ].ang = ang2 - ang1; + + if ( front[ i ].ang < 1.5 ) smallAngles.push( i ); + + } + + function partFrontBounds( ) { + + let idx, xmin, ymin, zmin, xmax, ymax, zmax; + + partBounds = []; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + for( let i = 0; i < partFront.length; i ++ ) { + + idx = partFront[ i ].idx * 3; + + x = g.positions[ idx ]; + y = g.positions[ idx + 1 ]; + z = g.positions[ idx + 2 ]; + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + } + + partBounds.push( xmin, ymin, zmin, xmax, ymax, zmax ); + + boundings.push( partBounds ); + + } + + function replaceFront( m, fNew ) { + + let rear = front.splice( m, front.length - m ); + + for ( let i = 0; i < fNew.length; i ++ ) { + + front.push( fNew[ i ] ); // new front points + + } + + for ( let i = 1; i < rear.length; i ++ ) { // i = 1: without old front point m + + front.push( rear[ i ] ); + + } + + } + + function getSystemAtPoint( i ) { + + getPrevPoint( i ); + getPoint( i ); + + len = length( xp, yp, zp ); // to normalize + + xn = xp / len; + yn = yp / len + zn = zp / len; + + // centerAngle = Math.acos( Math.abs( x1 * xp + y1 * yp + z1 * zp ) / ( g.radius * g.radius ) ); + const h = Math.abs( x1 * xp + y1 * yp + z1 * zp ) / g.radius; // distance: sphere center to cutting circle + + // center cutting circle (refers to previous point) + xc = h / g.radius * xp; + yc = h / g.radius * yp; + zc = h / g.radius * zp; + + // first tangent + xt1 = x1 - xc; + yt1 = y1 - yc; + zt1 = z1 - zc; + + len = length( xt1, yt1, zt1 ); // to normalize + + xt1 = xt1 / len; + yt1 = yt1 / len; + zt1 = zt1 / len; + + // cross, second tangent + + xt2 = yn * zt1 - zn * yt1; + yt2 = zn * xt1 - xn * zt1; + zt2 = xn * yt1 - yn * xt1; + + } + + function storePoint( theta, phi ) { + + g.positions[ posIdx ] = g.radius * Math.sin( theta ) * Math.cos( phi ); + g.positions[ posIdx + 1 ] = g.radius * Math.cos( theta ); + g.positions[ posIdx + 2 ] = -g.radius * Math.sin( theta ) * Math.sin( phi ); + + posIdx += 3; + + } + + function getPrevPoint( i ) { + + frontPosIdx = front[ prevFront( i ) ].idx * 3; + + x1 = g.positions[ frontPosIdx ]; + y1 = g.positions[ frontPosIdx + 1 ]; + z1 = g.positions[ frontPosIdx + 2 ]; + + } + + function getPoint( i ) { + + frontPosIdx = front[ i ].idx * 3; + + xp = g.positions[ frontPosIdx ]; + yp = g.positions[ frontPosIdx + 1 ]; + zp = g.positions[ frontPosIdx + 2 ]; + + } + + function getNextPoint( i ) { + + frontPosIdx = front[ nextFront( i ) ].idx * 3; + + x2 = g.positions[ frontPosIdx ]; + y2 = g.positions[ frontPosIdx + 1 ]; + z2 = g.positions[ frontPosIdx + 2 ]; + + } + +} + +function buildSphereWithHoles( ) { + + const length = ( x, y, z ) => ( Math.sqrt( x * x + y * y + z * z ) ); + const prevFront = ( i ) => ( i !== 0 ? i - 1 : front.length - 1 ); + const nextFront = ( i ) => ( i !== front.length - 1 ? i + 1 : 0 ); + + let d; // rough edge length of the triangles + let m; // index of the current front point + let n; // number of new points + let nT; // number of new triangles + let nUnion; // number of new points (after union) + let dAng; // partial angle + let len, d1, d2, d12, dd1, dd2, dd12; // lengths and their squares + let h; // distance center to circle + let acute, concave; // front angle properties + + // points and vectors: + let x, y, z, xp, yp, zp, xc, yc, zc, x1, y1, z1, x2, y2, z2, xt1, yt1, zt1, xt2, yt2, zt2, xv1, yv1, zv1, xv2, yv2, zv2; + + // preparation + + const faceCount = g.detail * g.detail * 4; + const posCount = g.detail * g.detail * 3; + + g.indices = new Uint32Array( faceCount * 3 ); + g.positions = new Float32Array( posCount * 3 ); + //g.normals = new Float32Array( posCount * 3 ); + + g.setIndex( new THREE.BufferAttribute( g.indices, 1 ) ); + g.addAttribute( 'position', new THREE.BufferAttribute( g.positions, 3 ) ); + + d = Math.PI / g.detail; // rough side length of the triangles + + let posIdx = 0; + let indIdx = 0; + let frontPosIdx, unionIdxA, unionIdxB; + + let front = []; // active front // front[ i ]: object { idx: 0, ang: 0 } + let partFront = []; // separated part of the active front + let insertFront = []; // new front points to insert into active front + let fronts = []; // all fronts + let partBounds = []; // bounding box of partFront [ xmin, xmax, ymin, ymax, zmin, zmax ] + let boundings = []; // fronts bounding boxes + let smallAngles = []; // new angles < 1.5 + + let start = true; + let united = false; + + // define holes + + let holeNumber; + + if ( g.holes.length === 0 ) { + + makeFirstTriangle( ); + + } else { + + g.circles = []; // [ center, r, count ] of holes for external use + + holeNumber = 0; + + for ( let i = 0; i < g.holes.length; i ++ ) { + + if ( g.holes[ i ].length === 3 ) { + + makeCircularHole( i ); // [ theta, phi, count ] + + } else { + + makePointsHole( i ); // points: [ theta, phi, ... ] + + } + + } + + } + + let activeFrontNo = 0; + front = fronts[ activeFrontNo ]; + + // ------ triangulation cycle ------------- + + while ( front.length > 3 || start ) { + + if ( start ) start = false; + + if ( front.length > 9 && smallAngles.length === 0 ) { + + // checkDistancesInFront( ); + checkDistancesToFronts( m ); + + } + + if ( united ) { + + trianglesAtUnionPoints( ); + + } else { + + calculateFrontAngles( ); + m = getMinimalAngleIndex( ); // front angle + newTriangles( m ); + + } + + } // end while + + makeLastTriangle( ); + + // ..... main detail functions ..... + + function checkDistancesToFronts( m ) { + + let idx, idxJ, xChk, yChk, zChk; + + for ( let i = 0; i < insertFront.length; i ++ ) { + + idx = front[ m + i ].idx * 3 + + xp = g.positions[ idx ]; + yp = g.positions[ idx + 1 ]; + zp = g.positions[ idx + 2 ]; + + for ( let f = 0; f < fronts.length; f ++ ) { + + if ( f !== activeFrontNo ) { + + xChk = ( xp > boundings[ f ][ 0 ] - d ) && ( xp < boundings[ f ][ 3 ] + d ); + yChk = ( yp > boundings[ f ][ 1 ] - d ) && ( yp < boundings[ f ][ 4 ] + d ); + zChk = ( zp > boundings[ f ][ 2 ] - d ) && ( zp < boundings[ f ][ 5 ] + d ); + + if ( xChk || yChk || zChk ) { + + for ( let j = 0; j < fronts[ f ].length; j ++ ) { + + idxJ = fronts[ f ][ j ].idx * 3; + x2 = g.positions[ idxJ ]; + y2 = g.positions[ idxJ + 1 ]; + z2 = g.positions[ idxJ + 2 ]; + + if ( length( x2 - xp, y2 - yp, z2 - zp ) < d ) { + + uniteFront( m, i, f, j ); + + } + + } + + } + + } + + } + + } + + } + + function calculateFrontAngles( ) { + + smallAngles = []; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang === 0 ) { + + frontAngle( i ); + + } + + } + + } + + function getMinimalAngleIndex( ) { + + let angle = Infinity; + let m; + + for ( let i = 0; i < front.length; i ++ ) { + + if( front[ i ].ang < angle ) { + + angle = front[ i ].ang ; + m = i; + + } + + } + + return m; + + } + + function newTriangles( m ) { + + // m: minimal angle (index) + + insertFront = []; + + nT = Math.floor( 3 * front[ m ].ang / Math.PI ) + 1; // number of new triangles + + dAng = front[ m ].ang / nT; + + getSystemAtPoint( m ); + getNextPoint( m ); + + d1 = length( x1 - xp, y1 - yp, z1 - zp ); + d2 = length( x2 - xp, y2 - yp, z2 - zp ); + d12 = length( x2 - x1, y2 - y1, z2 - z1 ); + + // correction of dAng, nT in extreme cases + + if ( dAng < 0.8 && nT > 1 ) { + + nT --; + dAng = front[ m ].ang / nT; + + } + + if ( dAng > 0.8 && nT === 1 && d12 > 1.25 * d ) { + + nT = 2; + dAng = front[ m ].ang / nT; + + } + + if ( d1 * d1 < 0.2 * d * d || d2 * d2 < 0.2 * d * d ) { + + nT = 1; + + } + + n = nT - 1; // n number of new points + + if ( n === 0 ) { // one triangle + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + indIdx += 3; + + front[ prevFront( m ) ].ang = 0; + front[ nextFront( m ) ].ang = 0; + + } else { // more then one triangle + + for ( let i = 0, phi = dAng; i < n; i ++, phi += dAng ) { + + xp = xc + Math.cos( phi ) * d * xt1 + Math.sin( phi ) * d * xt2; + yp = yc + Math.cos( phi ) * d * yt1 + Math.sin( phi ) * d * yt2; + zp = zc + Math.cos( phi ) * d * zt1 + Math.sin( phi ) * d * zt2; + + len = length( xp, yp, zp ); // to normalize + + g.positions[ posIdx ] = xp / len; + g.positions[ posIdx + 1 ] = yp / len; + g.positions[ posIdx + 2 ] = zp / len; + + insertFront.push( { idx: posIdx / 3, ang: 0 } ); + + posIdx += 3; + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = front[ prevFront( m ) ].idx + g.indices[ indIdx + 2 ] = insertFront[ 0 ].idx; + + indIdx += 3; + + front[ prevFront( m ) ].ang = 0; + + for ( let i = 0; i < n - 1; i ++ ) { + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ i ].idx; + g.indices[ indIdx + 2 ] = insertFront[ i + 1 ].idx; + + indIdx += 3; + + } + + g.indices[ indIdx ] = front[ m ].idx; + g.indices[ indIdx + 1 ] = insertFront[ n - 1 ].idx; + g.indices[ indIdx + 2 ] = front[ nextFront( m ) ].idx; + + front[ nextFront( m ) ].ang = 0; + + indIdx += 3; + + } + + replaceFront( m, insertFront ); // replaces front[ m ] with new points + + } + + function trianglesAtUnionPoints( ) { + + nUnion = 0; // count inserted points + + frontAngle( unionIdxA ); + frontAngle( unionIdxA + 1 ); + + if ( front[ unionIdxA ].ang < front[ unionIdxA + 1 ].ang ) { + + newTriangles( unionIdxA ); + nUnion += n - 1; + frontAngle( unionIdxA + 1 + nUnion ); + newTriangles( unionIdxA + 1 + nUnion ); + nUnion += n - 1; + + } else { + + newTriangles( unionIdxA + 1 ); + nUnion += n - 1; + frontAngle( unionIdxA ); + newTriangles( unionIdxA ); + nUnion += n - 1; + } + + frontAngle( unionIdxB + nUnion ); + frontAngle( unionIdxB + 1 + nUnion ); + + if ( front[ unionIdxB + nUnion ].ang < front[ unionIdxB + 1 + nUnion ].ang ) { + + newTriangles( unionIdxB + nUnion ); + nUnion += n - 1; + frontAngle( unionIdxB + 1 + nUnion ); + newTriangles( unionIdxB + 1 + nUnion ); + + } else { + + newTriangles( unionIdxB + 1 + nUnion ); + frontAngle( unionIdxB + nUnion ); + newTriangles( unionIdxB + nUnion ); + + } + + united = false; + + } + + // ..... help functions ..... + + function frontAngle( i ) { + + getPrevPoint( i ); // (1) + getPoint( i ); + getNextPoint( i ); // (2) + + // centerAngle = Math.acos( Math.abs( x1 * xp + y1 * yp + z1 * zp ) ); + // r = Math.sin( centerAngle ); // radius circle + // h = Math.cos( centerAngle ); // distance center to circle + + h = Math.abs( x1 * xp + y1 * yp + z1 * zp ); + + // center cutting circle (refers to previous point) + xc = h * xp; + yc = h * yp; + zc = h * zp; + + xv1 = xc - x1; + yv1 = yc - y1; + zv1 = zc - z1; + + len = length( xv1, yv1, zv1 ); // to normalize + + xv1 = xv1 / len; + yv1 = yv1 / len; + zv1 = zv1 / len; + + xv2 = x2 - xc; + yv2 = y2 - yc; + zv2 = z2 - zc; + + len = length( xv2, yv2, zv2 ); // to normalize + + xv2 = xv2 / len; + yv2 = yv2 / len; + zv2 = zv2 / len; + + front[ i ].ang = Math.acos( Math.abs( xv1 * xv2 + yv1 * yv2 + zv1 * zv2 ) ); + + // cross, to detect curvature + x = yv1 * zv2 - zv1 * yv2; + y = zv1 * xv2 - xv1 * zv2; + z = xv1 * yv2 - yv1 * xv2; + + len = length( x, y, z ); // to normalize + + x = xp + x / len; + y = yp + y / len; + z = zp + z / len; + + concave = ( length( x, y, z ) < 1 ); + + d1 = length( x1 - xp, y1 - yp, z1 - zp ); + d2 = length( x2 - xp, y2 - yp, z2 - zp ); + d12 = length( x2 - x1, y2 - y1, z2 - z1 ); + + dd1 = d1 * d1; + dd2 = d2 * d2; + dd12 = d12 * d12; + + acute = ( dd12 < ( dd1 + dd2) ); + + // if ( concave && acute ) front[ i ].ang += 0; + if ( concave && !acute ) front[ i ].ang = Math.PI - front[ i ].ang ; + if ( !concave && acute ) front[ i ].ang = 2 * Math.PI - front[ i ].ang ; + if ( !concave && !acute ) front[ i ].ang = Math.PI + front[ i ].ang ; + + if ( front[ i ].ang < 1.5 ) smallAngles.push( i ); + + } + + function uniteFront( m, i, f, j ) { + + let tmp = []; + + tmp[ 0 ] = front.slice( 0, m + i + 1 ); + tmp[ 1 ] = fronts[ f ].slice( j , fronts[ f ].length ); + tmp[ 2 ] = fronts[ f ].slice( 0 , j + 1 ); + tmp[ 3 ] = front.slice( m + i, front.length ); + + unionIdxA = m + i; + unionIdxB = m + i + 1 + fronts[ f ].length + + front = []; + + for ( let t = 0; t < 4; t ++ ) { + + for ( let k = 0; k < tmp[ t ].length ; k ++ ) { + + front.push( tmp[ t ][ k ] ); + + } + + } + + fronts[ f ] = []; // empty united front + + united = true; + + } + + function partFrontBounds( ) { + + let idx, xmin, ymin, zmin, xmax, ymax, zmax; + + partBounds = []; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + for( let i = 0; i < partFront.length; i ++ ) { + + idx = partFront[ i ].idx * 3; + + x = g.positions[ idx ]; + y = g.positions[ idx + 1 ]; + z = g.positions[ idx + 2 ]; + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + } + + partBounds.push( xmin, ymin, zmin, xmax, ymax, zmax ); + + boundings.push( partBounds ); + + } + + function replaceFront( m, fNew ) { + + let rear = front.splice( m, front.length - m ) + + for ( let i = 0; i < fNew.length; i ++ ) { + + front.push( fNew[ i ] ); // new front points + + } + + for ( let i = 1; i < rear.length; i ++ ) { // 1: without old front point m + + front.push( rear[ i ] ); + + } + + } + + function storePoint( theta, phi ) { + + g.positions[ posIdx ] = Math.sin( theta ) * Math.cos( phi ); + g.positions[ posIdx + 1 ] = Math.cos( theta ); + g.positions[ posIdx + 2 ] = -Math.sin( theta ) * Math.sin( phi ); + + posIdx += 3; + + } + + function makeFirstTriangle ( ) { + + storePoint( 0, 0 ); // ( theta, phi ) + storePoint( d, -Math.PI / 6 ); + storePoint( d, Math.PI / 6 ); + + g.indices[ 0 ] = 0; + g.indices[ 1 ] = 1; + g.indices[ 2 ] = 2; + + indIdx += 3; + + front = []; + + front.push( { idx: 0, ang: 0 }, { idx: 1, ang: 0 }, { idx: 2, ang: 0 } ); + fronts.push( front ) + + } + + function makeLastTriangle( ) { + + g.indices[ indIdx ] = front[ 2 ].idx; + g.indices[ indIdx + 1 ] = front[ 1 ].idx + g.indices[ indIdx + 2 ] = front[ 0 ].idx; + + } + + function makePointsHole( i ) { + + let theta, phi, count, xmin, ymin, zmin, xmax, ymax, zmax; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + fronts[ holeNumber ] = []; + boundings[ holeNumber ] = []; + + theta = g.holes[ i ][ 0 ]; + phi = g.holes[ i ][ 1 ]; + + x1 = Math.sin( theta ) * Math.cos( phi ); + y1 = Math.cos( theta ); + z1 = -Math.sin( theta ) * Math.sin( phi ); + + for ( let j = 1; j < g.holes[ i ].length / 2 + 1; j ++ ) { + + g.positions[ posIdx ] = x1; + g.positions[ posIdx + 1 ] = y1; + g.positions[ posIdx + 2 ] = z1; + + fronts[ holeNumber ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x1 < xmin ? x1 : xmin; + ymin = y1 < ymin ? y1 : ymin; + zmin = z1 < zmin ? z1 : zmin; + + xmax = x1 > xmax ? x1 : xmax; + ymax = y1 > ymax ? y1 : ymax; + zmax = z1 > zmax ? z1 : zmax; + + posIdx += 3; + + theta = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 : 0 ]; // 0 => connect to start + phi = g.holes[ i ][ j < g.holes[ i ].length / 2 ? j * 2 + 1 : 1 ]; // 1 => connect to start + + x2 = Math.sin( theta ) * Math.cos( phi ); + y2 = Math.cos( theta ); + z2 = -Math.sin( theta ) * Math.sin( phi ); + + xv2 = x2 - x1; + yv2 = y2 - y1; + zv2 = z2 - z1; + + len = length( xv2, yv2, zv2 ); + + if ( len > d ) { + + count = Math.ceil( len / d ); + + for ( let k = 1; k < count; k ++ ) { + + x = x1 + k * xv2 / count; + y = y1 + k * yv2 / count; + z = z1 + k * zv2 / count; + + len = length( x, y, z ); + + g.positions[ posIdx ] = x / len; + g.positions[ posIdx + 1 ] = y / len; + g.positions[ posIdx + 2 ] = z / len; + + fronts[ holeNumber ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + } + + x1 = x2; + y1 = y2; + z1 = z2; + + } + + boundings[ holeNumber ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + holeNumber ++; + + } + + function makeCircularHole( i ) { + + let theta = g.holes[ i ][ 0 ]; + let phi = g.holes[ i ][ 1 ]; + let count = g.holes[ i ][ 2 ]; + + let xmin, ymin, zmin, xmax, ymax, zmax; + + xmin = ymin = zmin = Infinity; + xmax = ymax = zmax = -Infinity; + + xp = Math.sin( theta ) * Math.cos( phi ); + yp = Math.cos( theta ); + zp = -Math.sin( theta ) * Math.sin( phi ); + + let r = count / detail / 2; // radius cutting circle + + h = Math.sqrt( 1 - r * r ); + + if ( !(xp === 0 && yp === 0 ) ) { + + xt1 = -yp; + yt1 = xp; + zt1 = 0; + + } else { + + xt1 = 0; + yt1 = 1; + zt1 = 0; + + } + + // cross + + xt2 = yp * zt1 - zp * yt1; + yt2 = zp * xt1 - xp * zt1; + zt2 = xp * yt1 - yp * xt1; + + len = length( xt1, yt1, zt1 ); // to normalize + + xt1 = xt1 / len; + yt1 = yt1 / len; + zt1 = zt1 / len; + + len = length( xt2, yt2, zt2 ); // to normalize + + xt2 = xt2 / len; + yt2 = yt2 / len; + zt2 = zt2 / len; + + xc = h * xp; + yc = h * yp; + zc = h * zp; + + g.circles.push( [ xc, yc, zc, r, count ] ); // for external use + + fronts[ holeNumber ] = []; + boundings[ holeNumber ] = []; + + for ( let i = 0, phi = 0; i < count; i ++, phi += 2 * Math.PI / count ) { + + x = xc + Math.cos( phi ) * r * xt1 + Math.sin( phi ) * r * xt2; + y = yc + Math.cos( phi ) * r * yt1 + Math.sin( phi ) * r * yt2; + z = zc + Math.cos( phi ) * r * zt1 + Math.sin( phi ) * r * zt2; + + g.positions[ posIdx ] = x; + g.positions[ posIdx + 1 ] = y; + g.positions[ posIdx + 2 ] = z; + + fronts[ holeNumber ].push( { idx: posIdx / 3, ang: 0 } ); + + xmin = x < xmin ? x : xmin; + ymin = y < ymin ? y : ymin; + zmin = z < zmin ? z : zmin; + + xmax = x > xmax ? x : xmax; + ymax = y > ymax ? y : ymax; + zmax = z > zmax ? z : zmax; + + posIdx += 3; + + } + + boundings[ holeNumber ].push( xmin, xmax, ymin, ymax, zmin, zmax ); + + holeNumber ++; + + } + + function getSystemAtPoint( i ) { + + getPrevPoint( i ); + getPoint( i ); + + // centerAngle = Math.acos( Math.abs( x1 * xp + y1 * yp + z1 * zp ) ); + // r = Math.sin( centerAngle ); // radius cutting circle + // h = Math.cos( centerAngle ); // distance center to cutting circle + + h = Math.abs( x1 * xp + y1 * yp + z1 * zp ); + + // center cutting circle (refers to previous point) + xc = h * xp; + yc = h * yp; + zc = h * zp; + + // first tangent + xt1 = x1 - xc; + yt1 = y1 - yc; + zt1 = z1 - zc; + + len = length( xt1, yt1, zt1 ); // to normalize + + xt1 = xt1 / len; + yt1 = yt1 / len; + zt1 = zt1 / len; + + // cross, second tangent (sphere radius 1: p equals normal) + + xt2 = yp * zt1 - zp * yt1; + yt2 = zp * xt1 - xp * zt1; + zt2 = xp * yt1 - yp * xt1; + + } + + function getPrevPoint( i ) { + + frontPosIdx = front[ prevFront( i ) ].idx * 3 ; + x1 = g.positions[ frontPosIdx ]; + y1 = g.positions[ frontPosIdx + 1 ]; + z1 = g.positions[ frontPosIdx + 2 ]; + + } + + function getPoint( i ) { + + frontPosIdx = front[ i ].idx * 3; + xp = g.positions[ frontPosIdx ]; + yp = g.positions[ frontPosIdx + 1 ]; + zp = g.positions[ frontPosIdx + 2 ]; + + } + + function getNextPoint( i ) { + + frontPosIdx = front[ nextFront( i ) ].idx * 3; + x2 = g.positions[ frontPosIdx ]; + y2 = g.positions[ frontPosIdx + 1 ]; + z2 = g.positions[ frontPosIdx + 2 ]; + + } + +} + +export {createSphereWithHoles, buildSphereWithHolesObj, buildSphereWithHoles}; \ No newline at end of file diff --git a/examples/main.html b/examples/main.html index 231d51c..c489799 100644 --- a/examples/main.html +++ b/examples/main.html @@ -61,6 +61,14 @@

wegeo基于threejs

特效/边框线
+ diff --git a/examples/screenshots/mask.png b/examples/screenshots/mask.png new file mode 100644 index 0000000..4ab9192 Binary files /dev/null and b/examples/screenshots/mask.png differ diff --git a/examples/sphereEarth/mask/mask.html b/examples/sphereEarth/mask/mask.html new file mode 100644 index 0000000..02e5445 --- /dev/null +++ b/examples/sphereEarth/mask/mask.html @@ -0,0 +1,141 @@ + + + + + + + +
+ 掩膜的设计主要是参考了https://github.com/hofk/THREEi.js这个代码,我将代码改成es module的形式了。 +

其他在几何体添加孔洞的方法:

+

https://github.com/manthrax/THREE-CSGMesh 用于物体求交、减、加。

+

https://threejs.org/examples/#webgl_geometry_convex 和这个示例。

+

思路:通过境界经纬度构造ConvexGeometry几何体,然后再使用csg对球体和ConvexGeometry求减。

+

cesium也有相应的构造掩膜的方法,但我不知道他们怎么做的,哎!汗颜~~~!

+

如果有其他优雅的方式欢迎各位大佬讨论交流,或者提交pr。

+

境界服务的数据来自阿里的datav,如果境界有误请及时联系我进行修改删除,谢谢!

+
+
+ +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/wegeo.html b/examples/wegeo.html index 4e8be91..f875ac9 100644 --- a/examples/wegeo.html +++ b/examples/wegeo.html @@ -27,40 +27,103 @@ - + diff --git a/src/WegeoMap.js b/src/WegeoMap.js index 81e116b..8833b68 100644 --- a/src/WegeoMap.js +++ b/src/WegeoMap.js @@ -5,7 +5,7 @@ import { Layer } from './layers/Layer'; import {D3TilesLayer} from './layers/3DTilesLayer'; import { Element } from './utils/Element'; import {GUI} from 'three/examples/jsm/libs/lil-gui.module.min.js' -import { OrbitControls } from './jsm/controls/OrbitControls'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; import {BingMapsProvider} from './providers/BingMapsProvider'; import { Listener } from './listener/listener'; import { RaycasterUtils } from './raycaster/utils'; @@ -184,11 +184,17 @@ export class WegeoMap { this.baseMap.moveToByCoords(coords); } - moveToByLL(lat, lon){ + /** + * 跳转到指定位置,用于球形地图 + * @param {*} lat + * @param {*} lon + * @returns + */ + moveToByLL(lat, lon, distance = 384720){ if(!this.baseMap){ return; } - this.baseMap.moveToByLL(lat, lon); + this.baseMap.moveToByLL(lat, lon, distance); } // 鼠标点击获取模型 diff --git a/src/animation/Animate.js b/src/animation/Animate.js index 29e4ff4..8813503 100644 --- a/src/animation/Animate.js +++ b/src/animation/Animate.js @@ -1,4 +1,4 @@ -import * as TWEEN from '../jsm/libs/tween.module.js'; +import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'; // import TWEEN from '@tweenjs/tween.js'; /** * 相机移动控制 diff --git a/src/effect/outline.js b/src/effect/outline.js index bde57a9..8169358 100644 --- a/src/effect/outline.js +++ b/src/effect/outline.js @@ -1,9 +1,9 @@ -import { EffectComposer } from '../jsm/postprocessing/EffectComposer.js'; -import { RenderPass } from '../jsm/postprocessing/RenderPass.js'; -import { ShaderPass } from '../jsm/postprocessing/ShaderPass.js'; -import { OutlinePass } from '../jsm/postprocessing/OutlinePass.js'; -import { OutputPass } from '../jsm/postprocessing/OutputPass.js'; -import { FXAAShader } from '../jsm/shaders/FXAAShader.js'; +import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'; +import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'; +import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'; +import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'; +import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'; import { Vector2 } from 'three'; import { Config} from '../environment/config.js'; // 上述几个包是做outline效果必须的几个包 diff --git a/src/examples/3dtiles.js b/src/examples/3dtiles.js index 1d7a542..b7d15d1 100644 --- a/src/examples/3dtiles.js +++ b/src/examples/3dtiles.js @@ -12,12 +12,12 @@ import { MeshBasicMaterial } from 'three'; - import { OrbitControls } from '../jsm/controls/OrbitControls'; + import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; import { Loader3DTiles } from 'three-loader-3dtiles'; import { TilesRenderer } from '3d-tiles-renderer'; - import { GLTFLoader } from '../jsm/loaders/GLTFLoader.js'; - import Stats from '../jsm/libs/stats.module.js'; + import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; + import Stats from 'three/examples/jsm/libs/stats.module.js'; const queryParams = new URLSearchParams(document.location.search); diff --git a/src/examples/basic.js b/src/examples/basic.js index 2492933..640df58 100644 --- a/src/examples/basic.js +++ b/src/examples/basic.js @@ -1,7 +1,7 @@ // @ts-nocheck import {WebGLRenderer, Scene, Color, AmbientLight, PerspectiveCamera, LinearSRGBColorSpace} from 'three'; -import {MapControls} from '../jsm/controls/MapControls.js'; +import {MapControls} from 'three/examples/jsm/controls/MapControls.js'; import {MapView, BingMapsProvider, UnitsUtils} from '../main'; var canvas = document.getElementById('canvas'); diff --git a/src/layers/3DTilesLayer.js b/src/layers/3DTilesLayer.js index 5c4b2e0..0acaec5 100644 --- a/src/layers/3DTilesLayer.js +++ b/src/layers/3DTilesLayer.js @@ -1,9 +1,9 @@ // 多个canvas并没有id,只有父节点 import { Element } from "../utils/Element"; -import {MapControls} from '../jsm/controls/MapControls.js'; +import {MapControls} from 'three/examples/jsm/controls/MapControls.js'; import {UnitsUtils} from '../utils/UnitsUtils.js'; import { PerspectiveCamera, WebGLRenderer, Scene, Color, Raycaster, Vector3, Vector2, Clock } from 'three'; -import * as TWEEN from '../jsm/libs/tween.module.js'; +import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'; import {EffectOutline} from '../effect/outline'; import {Config} from '../environment/config'; import { Loader3DTiles } from 'three-loader-3dtiles'; diff --git a/src/layers/Layer.js b/src/layers/Layer.js index ea6ce47..f29f2a2 100644 --- a/src/layers/Layer.js +++ b/src/layers/Layer.js @@ -1,16 +1,16 @@ // 多个canvas并没有id,只有父节点 import { Element } from "../utils/Element"; -import {MapControls} from '../jsm/controls/MapControls.js'; -import { OrbitControls } from '../jsm/controls/OrbitControls'; +import {MapControls} from 'three/examples/jsm/controls/MapControls.js'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; import {UnitsUtils} from '../utils/UnitsUtils.js'; import { PerspectiveCamera, WebGLRenderer, Scene, Color, Raycaster, Vector3, Vector2, ACESFilmicToneMapping, BoxGeometry, MeshBasicMaterial , Mesh, TextureLoader, PMREMGenerator, MathUtils, AmbientLight, DirectionalLight, PointLight, MOUSE } from 'three'; -import * as TWEEN from '../jsm/libs/tween.module.js'; +import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'; import {EffectOutline} from '../effect/outline'; import {Config} from '../environment/config' import BasLayer from "./basLayer"; -import { Sky } from '../jsm/objects/Sky.js'; +import { Sky } from 'three/examples/jsm/objects/Sky.js'; export class Layer extends BasLayer{ diff --git a/src/loader/ModelLoader.js b/src/loader/ModelLoader.js index 151b476..a79b247 100644 --- a/src/loader/ModelLoader.js +++ b/src/loader/ModelLoader.js @@ -1,7 +1,7 @@ -import { GLTFLoader } from '../jsm/loaders/GLTFLoader.js'; -import { DRACOLoader } from '../jsm/loaders/DRACOLoader.js'; -import { OBJLoader } from '../jsm/loaders/OBJLoader.js'; -import { SVGLoader } from '../jsm/loaders/SVGLoader.js'; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'; +import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; +import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js'; import { Config } from '../environment/config'; import { ObjectLoader } from 'three'; diff --git a/src/loader/Water1.js b/src/loader/Water1.js index 00541aa..66a4d98 100644 --- a/src/loader/Water1.js +++ b/src/loader/Water1.js @@ -1,4 +1,4 @@ -import {Water} from '../jsm/objects/Water.js'; +import {Water} from 'three/examples/jsm/objects/Water.js'; import { TextureLoader, Vector3 , RepeatWrapping, DoubleSide, FrontSide, BackSide} from 'three'; export class Water1 { diff --git a/src/loader/Water2.js b/src/loader/Water2.js index a84768c..52d25b4 100644 --- a/src/loader/Water2.js +++ b/src/loader/Water2.js @@ -1,5 +1,5 @@ -import {Water} from '../jsm/objects/Water2'; +import {Water} from 'three/examples/jsm/objects/Water2'; import { Colors } from '../utils/Colors'; import { Vector2, TextureLoader } from 'three'; diff --git a/src/main.js b/src/main.js index 87306c7..8122eaa 100644 --- a/src/main.js +++ b/src/main.js @@ -40,6 +40,7 @@ export {CancelablePromise} from './utils/CancelablePromise'; export {XHRUtils} from './utils/XHRUtils'; export {TextureUtils} from './utils/TextureUtils'; export {Element} from './utils/Element'; +export {AngleUtils} from './utils/AngleUtils'; export {Layer} from './layers/Layer'; export {WegeoMap} from './WegeoMap'; diff --git a/src/sky/Skybox.js b/src/sky/Skybox.js index d7be7be..2749ac7 100644 --- a/src/sky/Skybox.js +++ b/src/sky/Skybox.js @@ -4,12 +4,12 @@ export class Skybox { loadSkyBox(scale) { var aCubeMap = new CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); aCubeMap.format = RGBAFormat; @@ -33,12 +33,12 @@ export class Skybox { } loadBox(){ var cube = new CubeTextureLoader().load([ - 'png/sky/px.jpg', - 'png/sky/nx.jpg', - 'png/sky/py.jpg', - 'png/sky/ny.jpg', - 'png/sky/pz.jpg', - 'png/sky/nz.jpg' + '/examples/png/sky/px.jpg', + '/examples/png/sky/nx.jpg', + '/examples/png/sky/py.jpg', + '/examples/png/sky/ny.jpg', + '/examples/png/sky/pz.jpg', + '/examples/png/sky/nz.jpg' ]); return cube; } diff --git a/src/utils/AngleUtils.js b/src/utils/AngleUtils.js new file mode 100644 index 0000000..fd68263 --- /dev/null +++ b/src/utils/AngleUtils.js @@ -0,0 +1,20 @@ + +export class AngleUtils { + /** + * 弧度转角度 + * @param {*} rad + * @returns + */ + static radToDeg(rad) { + return rad * (180 / Math.PI); + } + /** + * 角度转弧度 + * @param {*} deg + * @returns + */ + static degToRad(deg) { + return deg * (Math.PI / 180); + } + +} \ No newline at end of file