|
@@ -218,23 +218,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
} );
|
|
|
|
|
|
- parser.parse( function ( scene, scenes, cameras, animations, json ) {
|
|
|
-
|
|
|
- var glTF = {
|
|
|
- scene: scene,
|
|
|
- scenes: scenes,
|
|
|
- cameras: cameras,
|
|
|
- animations: animations,
|
|
|
- asset: json.asset,
|
|
|
- parser: parser,
|
|
|
- userData: {}
|
|
|
- };
|
|
|
-
|
|
|
- addUnknownExtensionsToUserData( extensions, glTF, json );
|
|
|
-
|
|
|
- onLoad( glTF );
|
|
|
-
|
|
|
- }, onError );
|
|
|
+ parser.parse( onLoad, onError );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -528,21 +512,23 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
for ( var attributeName in gltfAttributeMap ) {
|
|
|
|
|
|
- if ( ! ( attributeName in ATTRIBUTES ) ) continue;
|
|
|
+ var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
|
|
|
|
|
|
- threeAttributeMap[ ATTRIBUTES[ attributeName ] ] = gltfAttributeMap[ attributeName ];
|
|
|
+ threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ];
|
|
|
|
|
|
}
|
|
|
|
|
|
for ( attributeName in primitive.attributes ) {
|
|
|
|
|
|
- if ( ATTRIBUTES[ attributeName ] !== undefined && gltfAttributeMap[ attributeName ] !== undefined ) {
|
|
|
+ var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
|
|
|
+
|
|
|
+ if ( gltfAttributeMap[ attributeName ] !== undefined ) {
|
|
|
|
|
|
var accessorDef = json.accessors[ primitive.attributes[ attributeName ] ];
|
|
|
var componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
|
|
|
|
|
|
- attributeTypeMap[ ATTRIBUTES[ attributeName ] ] = componentType;
|
|
|
- attributeNormalizedMap[ ATTRIBUTES[ attributeName ] ] = accessorDef.normalized === true;
|
|
|
+ attributeTypeMap[ threeAttributeName ] = componentType;
|
|
|
+ attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -1218,10 +1204,8 @@ THREE.GLTFLoader = ( function () {
|
|
|
};
|
|
|
|
|
|
var INTERPOLATION = {
|
|
|
- CUBICSPLINE: THREE.InterpolateSmooth, // We use custom interpolation GLTFCubicSplineInterpolation for CUBICSPLINE.
|
|
|
- // KeyframeTrack.optimize() can't handle glTF Cubic Spline output values layout,
|
|
|
- // using THREE.InterpolateSmooth for KeyframeTrack instantiation to prevent optimization.
|
|
|
- // See KeyframeTrack.optimize() for the detail.
|
|
|
+ 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
|
|
|
};
|
|
@@ -1267,12 +1251,14 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ var defaultMaterial;
|
|
|
+
|
|
|
/**
|
|
|
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
|
|
|
*/
|
|
|
function createDefaultMaterial() {
|
|
|
|
|
|
- return new THREE.MeshStandardMaterial( {
|
|
|
+ defaultMaterial = defaultMaterial || new THREE.MeshStandardMaterial( {
|
|
|
color: 0xFFFFFF,
|
|
|
emissive: 0x000000,
|
|
|
metalness: 1,
|
|
@@ -1282,6 +1268,8 @@ THREE.GLTFLoader = ( function () {
|
|
|
side: THREE.FrontSide
|
|
|
} );
|
|
|
|
|
|
+ return defaultMaterial;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) {
|
|
@@ -1358,30 +1346,21 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
if ( hasMorphPosition ) {
|
|
|
|
|
|
- // TODO: Error-prone use of a callback inside a loop.
|
|
|
- var accessor = target.POSITION !== undefined
|
|
|
+ var pendingAccessor = target.POSITION !== undefined
|
|
|
? parser.getDependency( 'accessor', target.POSITION )
|
|
|
- .then( function ( accessor ) {
|
|
|
- // Cloning not to pollute original accessor below
|
|
|
- return cloneBufferAttribute( accessor );
|
|
|
- } )
|
|
|
: geometry.attributes.position;
|
|
|
|
|
|
- pendingPositionAccessors.push( accessor );
|
|
|
+ pendingPositionAccessors.push( pendingAccessor );
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( hasMorphNormal ) {
|
|
|
|
|
|
- // TODO: Error-prone use of a callback inside a loop.
|
|
|
- var accessor = target.NORMAL !== undefined
|
|
|
+ var pendingAccessor = target.NORMAL !== undefined
|
|
|
? parser.getDependency( 'accessor', target.NORMAL )
|
|
|
- .then( function ( accessor ) {
|
|
|
- return cloneBufferAttribute( accessor );
|
|
|
- } )
|
|
|
: geometry.attributes.normal;
|
|
|
|
|
|
- pendingNormalAccessors.push( accessor );
|
|
|
+ pendingNormalAccessors.push( pendingAccessor );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -1395,6 +1374,24 @@ THREE.GLTFLoader = ( function () {
|
|
|
var morphPositions = accessors[ 0 ];
|
|
|
var morphNormals = accessors[ 1 ];
|
|
|
|
|
|
+ // Clone morph target accessors before modifying them.
|
|
|
+
|
|
|
+ for ( var i = 0, il = morphPositions.length; i < il; i ++ ) {
|
|
|
+
|
|
|
+ if ( geometry.attributes.position === morphPositions[ i ] ) continue;
|
|
|
+
|
|
|
+ morphPositions[ i ] = cloneBufferAttribute( morphPositions[ i ] );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( var i = 0, il = morphNormals.length; i < il; i ++ ) {
|
|
|
+
|
|
|
+ if ( geometry.attributes.normal === morphNormals[ i ] ) continue;
|
|
|
+
|
|
|
+ morphNormals[ i ] = cloneBufferAttribute( morphNormals[ i ] );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
for ( var i = 0, il = targets.length; i < il; i ++ ) {
|
|
|
|
|
|
var target = targets[ i ];
|
|
@@ -1515,30 +1512,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
}
|
|
|
|
|
|
}
|
|
|
-
|
|
|
- function isPrimitiveEqual( a, b ) {
|
|
|
-
|
|
|
- var dracoExtA = a.extensions ? a.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] : undefined;
|
|
|
- var dracoExtB = b.extensions ? b.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] : undefined;
|
|
|
-
|
|
|
- if ( dracoExtA && dracoExtB ) {
|
|
|
-
|
|
|
- if ( dracoExtA.bufferView !== dracoExtB.bufferView ) return false;
|
|
|
-
|
|
|
- return isObjectEqual( dracoExtA.attributes, dracoExtB.attributes );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( a.indices !== b.indices ) {
|
|
|
-
|
|
|
- return false;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return isObjectEqual( a.attributes, b.attributes );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
function isObjectEqual( a, b ) {
|
|
|
|
|
|
if ( Object.keys( a ).length !== Object.keys( b ).length ) return false;
|
|
@@ -1553,59 +1526,40 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
- function isArrayEqual( a, b ) {
|
|
|
+ function createPrimitiveKey( primitiveDef ) {
|
|
|
|
|
|
- if ( a.length !== b.length ) return false;
|
|
|
+ var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ];
|
|
|
+ var geometryKey;
|
|
|
|
|
|
- for ( var i = 0, il = a.length; i < il; i ++ ) {
|
|
|
+ if ( dracoExtension ) {
|
|
|
|
|
|
- if ( a[ i ] !== b[ i ] ) return false;
|
|
|
+ geometryKey = 'draco:' + dracoExtension.bufferView
|
|
|
+ + ':' + dracoExtension.indices
|
|
|
+ + ':' + createAttributesKey( dracoExtension.attributes );
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- function getCachedGeometry( cache, newPrimitive ) {
|
|
|
-
|
|
|
- for ( var i = 0, il = cache.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- var cached = cache[ i ];
|
|
|
+ } else {
|
|
|
|
|
|
- if ( isPrimitiveEqual( cached.primitive, newPrimitive ) ) return cached.promise;
|
|
|
+ geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode;
|
|
|
|
|
|
}
|
|
|
|
|
|
- return null;
|
|
|
+ return geometryKey;
|
|
|
|
|
|
}
|
|
|
|
|
|
- function getCachedCombinedGeometry( cache, geometries ) {
|
|
|
-
|
|
|
- for ( var i = 0, il = cache.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- var cached = cache[ i ];
|
|
|
-
|
|
|
- if ( isArrayEqual( geometries, cached.baseGeometries ) ) return cached.geometry;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
-
|
|
|
- }
|
|
|
+ function createAttributesKey( attributes ) {
|
|
|
|
|
|
- function getCachedMultiPassGeometry( cache, geometry, primitives ) {
|
|
|
+ var attributesKey = '';
|
|
|
|
|
|
- for ( var i = 0, il = cache.length; i < il; i ++ ) {
|
|
|
+ var keys = Object.keys( attributes ).sort();
|
|
|
|
|
|
- var cached = cache[ i ];
|
|
|
+ for ( var i = 0, il = keys.length; i < il; i ++ ) {
|
|
|
|
|
|
- if ( geometry === cached.baseGeometry && isArrayEqual( primitives, cached.primitives ) ) return cached.geometry;
|
|
|
+ attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';';
|
|
|
|
|
|
}
|
|
|
|
|
|
- return null;
|
|
|
+ return attributesKey;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -1634,48 +1588,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Checks if we can build a single Mesh with MultiMaterial from multiple primitives.
|
|
|
- * Returns true if all primitives use the same attributes/morphAttributes/mode
|
|
|
- * and also have index. Otherwise returns false.
|
|
|
- *
|
|
|
- * @param {Array<GLTF.Primitive>} primitives
|
|
|
- * @return {Boolean}
|
|
|
- */
|
|
|
- function isMultiPassGeometry( primitives ) {
|
|
|
-
|
|
|
- if ( primitives.length < 2 ) return false;
|
|
|
-
|
|
|
- var primitive0 = primitives[ 0 ];
|
|
|
- var targets0 = primitive0.targets || [];
|
|
|
-
|
|
|
- if ( primitive0.indices === undefined ) return false;
|
|
|
-
|
|
|
- for ( var i = 1, il = primitives.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- var primitive = primitives[ i ];
|
|
|
-
|
|
|
- if ( primitive0.mode !== primitive.mode ) return false;
|
|
|
- if ( primitive.indices === undefined ) return false;
|
|
|
- if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) return false;
|
|
|
- if ( ! isObjectEqual( primitive0.attributes, primitive.attributes ) ) return false;
|
|
|
-
|
|
|
- var targets = primitive.targets || [];
|
|
|
-
|
|
|
- if ( targets0.length !== targets.length ) return false;
|
|
|
-
|
|
|
- for ( var j = 0, jl = targets0.length; j < jl; j ++ ) {
|
|
|
-
|
|
|
- if ( ! isObjectEqual( targets0[ j ], targets[ j ] ) ) return false;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
/* GLTF PARSER */
|
|
|
|
|
|
function GLTFParser( json, extensions, options ) {
|
|
@@ -1688,9 +1600,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
this.cache = new GLTFRegistry();
|
|
|
|
|
|
// BufferGeometry caching
|
|
|
- this.primitiveCache = [];
|
|
|
- this.multiplePrimitivesCache = [];
|
|
|
- this.multiPassGeometryCache = [];
|
|
|
+ this.primitiveCache = {};
|
|
|
|
|
|
this.textureLoader = new THREE.TextureLoader( this.options.manager );
|
|
|
this.textureLoader.setCrossOrigin( this.options.crossOrigin );
|
|
@@ -1702,7 +1612,9 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
GLTFParser.prototype.parse = function ( onLoad, onError ) {
|
|
|
|
|
|
+ var parser = this;
|
|
|
var json = this.json;
|
|
|
+ var extensions = this.extensions;
|
|
|
|
|
|
// Clear the loader cache
|
|
|
this.cache.removeAll();
|
|
@@ -1710,21 +1622,27 @@ THREE.GLTFLoader = ( function () {
|
|
|
// Mark the special nodes/meshes in json for efficient parse
|
|
|
this.markDefs();
|
|
|
|
|
|
- // Fire the callback on complete
|
|
|
- this.getMultiDependencies( [
|
|
|
+ Promise.all( [
|
|
|
|
|
|
- 'scene',
|
|
|
- 'animation',
|
|
|
- 'camera'
|
|
|
+ this.getDependencies( 'scene' ),
|
|
|
+ this.getDependencies( 'animation' ),
|
|
|
+ this.getDependencies( 'camera' ),
|
|
|
|
|
|
] ).then( function ( dependencies ) {
|
|
|
|
|
|
- var scenes = dependencies.scenes || [];
|
|
|
- var scene = scenes[ json.scene || 0 ];
|
|
|
- var animations = dependencies.animations || [];
|
|
|
- var cameras = dependencies.cameras || [];
|
|
|
+ var 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 );
|
|
|
|
|
|
- onLoad( scene, scenes, cameras, animations, json );
|
|
|
+ onLoad( result );
|
|
|
|
|
|
} ).catch( onError );
|
|
|
|
|
@@ -1897,40 +1815,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * Requests all multiple dependencies of the specified types asynchronously, with caching.
|
|
|
- * @param {Array<string>} types
|
|
|
- * @return {Promise<Object<Array<Object>>>}
|
|
|
- */
|
|
|
- GLTFParser.prototype.getMultiDependencies = function ( types ) {
|
|
|
-
|
|
|
- var results = {};
|
|
|
- var pending = [];
|
|
|
-
|
|
|
- for ( var i = 0, il = types.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- var type = types[ i ];
|
|
|
- var value = this.getDependencies( type );
|
|
|
-
|
|
|
- // TODO: Error-prone use of a callback inside a loop.
|
|
|
- value = value.then( function ( key, value ) {
|
|
|
-
|
|
|
- results[ key ] = value;
|
|
|
-
|
|
|
- }.bind( this, type + ( type === 'mesh' ? 'es' : 's' ) ) );
|
|
|
-
|
|
|
- pending.push( value );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return Promise.all( pending ).then( function () {
|
|
|
-
|
|
|
- return results;
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
/**
|
|
|
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
|
|
|
* @param {number} bufferIndex
|
|
@@ -2263,6 +2147,124 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
+ /**
|
|
|
+ * 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 accomodate different primitive types, defines, etc. New materials will
|
|
|
+ * be created if necessary, and reused from a cache.
|
|
|
+ * @param {THREE.Object3D} mesh Mesh, Line, or Points instance.
|
|
|
+ */
|
|
|
+ GLTFParser.prototype.assignFinalMaterial = function ( mesh ) {
|
|
|
+
|
|
|
+ var geometry = mesh.geometry;
|
|
|
+ var material = mesh.material;
|
|
|
+ var extensions = this.extensions;
|
|
|
+
|
|
|
+ var useVertexTangents = geometry.attributes.tangent !== undefined;
|
|
|
+ var useVertexColors = geometry.attributes.color !== undefined;
|
|
|
+ var useFlatShading = geometry.attributes.normal === undefined;
|
|
|
+ var useSkinning = mesh.isSkinnedMesh === true;
|
|
|
+ var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0;
|
|
|
+ var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined;
|
|
|
+
|
|
|
+ if ( mesh.isPoints ) {
|
|
|
+
|
|
|
+ var cacheKey = 'PointsMaterial:' + material.uuid;
|
|
|
+
|
|
|
+ var 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.lights = false; // PointsMaterial doesn't support lights yet
|
|
|
+
|
|
|
+ this.cache.add( cacheKey, pointsMaterial );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ material = pointsMaterial;
|
|
|
+
|
|
|
+ } else if ( mesh.isLine ) {
|
|
|
+
|
|
|
+ var cacheKey = 'LineBasicMaterial:' + material.uuid;
|
|
|
+
|
|
|
+ var lineMaterial = this.cache.get( cacheKey );
|
|
|
+
|
|
|
+ if ( ! lineMaterial ) {
|
|
|
+
|
|
|
+ lineMaterial = new THREE.LineBasicMaterial();
|
|
|
+ THREE.Material.prototype.copy.call( lineMaterial, material );
|
|
|
+ lineMaterial.color.copy( material.color );
|
|
|
+ lineMaterial.lights = false; // LineBasicMaterial doesn't support lights yet
|
|
|
+
|
|
|
+ this.cache.add( cacheKey, lineMaterial );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ material = lineMaterial;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Clone the material if it will be modified
|
|
|
+ if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
|
|
|
+
|
|
|
+ var cacheKey = 'ClonedMaterial:' + material.uuid + ':';
|
|
|
+
|
|
|
+ if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
|
|
|
+ if ( useSkinning ) cacheKey += 'skinning:';
|
|
|
+ if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
|
|
|
+ if ( useVertexColors ) cacheKey += 'vertex-colors:';
|
|
|
+ if ( useFlatShading ) cacheKey += 'flat-shading:';
|
|
|
+ if ( useMorphTargets ) cacheKey += 'morph-targets:';
|
|
|
+ if ( useMorphNormals ) cacheKey += 'morph-normals:';
|
|
|
+
|
|
|
+ var cachedMaterial = this.cache.get( cacheKey );
|
|
|
+
|
|
|
+ if ( ! cachedMaterial ) {
|
|
|
+
|
|
|
+ cachedMaterial = material.isGLTFSpecularGlossinessMaterial
|
|
|
+ ? extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].cloneMaterial( material )
|
|
|
+ : material.clone();
|
|
|
+
|
|
|
+ if ( useSkinning ) cachedMaterial.skinning = true;
|
|
|
+ if ( useVertexTangents ) cachedMaterial.vertexTangents = true;
|
|
|
+ if ( useVertexColors ) cachedMaterial.vertexColors = THREE.VertexColors;
|
|
|
+ if ( useFlatShading ) cachedMaterial.flatShading = true;
|
|
|
+ if ( useMorphTargets ) cachedMaterial.morphTargets = true;
|
|
|
+ if ( useMorphNormals ) cachedMaterial.morphNormals = true;
|
|
|
+
|
|
|
+ this.cache.add( cacheKey, cachedMaterial );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ material = cachedMaterial;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // workarounds for mesh and geometry
|
|
|
+
|
|
|
+ if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
|
|
|
+
|
|
|
+ console.log( 'THREE.GLTFLoader: Duplicating UVs to support aoMap.' );
|
|
|
+ geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( material.isGLTFSpecularGlossinessMaterial ) {
|
|
|
+
|
|
|
+ // for GLTFSpecularGlossinessMaterial(ShaderMaterial) uniforms runtime update
|
|
|
+ mesh.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ mesh.material = material;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
/**
|
|
|
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
|
|
|
* @param {number} materialIndex
|
|
@@ -2450,9 +2452,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
for ( var gltfAttributeName in attributes ) {
|
|
|
|
|
|
- var threeAttributeName = ATTRIBUTES[ gltfAttributeName ];
|
|
|
-
|
|
|
- if ( ! threeAttributeName ) continue;
|
|
|
+ var threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase();
|
|
|
|
|
|
// Skip attributes already provided by e.g. Draco extension.
|
|
|
if ( threeAttributeName in geometry.attributes ) continue;
|
|
@@ -2489,8 +2489,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
|
|
|
*
|
|
|
* Creates BufferGeometries from primitives.
|
|
|
- * If we can build a single BufferGeometry with .groups from multiple primitives, returns one BufferGeometry.
|
|
|
- * Otherwise, returns BufferGeometries without .groups as many as primitives.
|
|
|
*
|
|
|
* @param {Array<GLTF.Primitive>} primitives
|
|
|
* @return {Promise<Array<THREE.BufferGeometry>>}
|
|
@@ -2501,22 +2499,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
var extensions = this.extensions;
|
|
|
var cache = this.primitiveCache;
|
|
|
|
|
|
- var isMultiPass = isMultiPassGeometry( primitives );
|
|
|
- var originalPrimitives;
|
|
|
-
|
|
|
- if ( isMultiPass ) {
|
|
|
-
|
|
|
- originalPrimitives = primitives; // save original primitives and use later
|
|
|
-
|
|
|
- // We build a single BufferGeometry with .groups from multiple primitives
|
|
|
- // because all primitives share the same attributes/morph/mode and have indices.
|
|
|
-
|
|
|
- primitives = [ primitives[ 0 ] ];
|
|
|
-
|
|
|
- // Sets .groups and combined indices to a geometry later in this method.
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
function createDracoPrimitive( primitive ) {
|
|
|
|
|
|
return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]
|
|
@@ -2534,14 +2516,15 @@ THREE.GLTFLoader = ( function () {
|
|
|
for ( var i = 0, il = primitives.length; i < il; i ++ ) {
|
|
|
|
|
|
var primitive = primitives[ i ];
|
|
|
+ var cacheKey = createPrimitiveKey( primitive );
|
|
|
|
|
|
// See if we've already created this geometry
|
|
|
- var cached = getCachedGeometry( cache, primitive );
|
|
|
+ var cached = cache[ cacheKey ];
|
|
|
|
|
|
if ( cached ) {
|
|
|
|
|
|
// Use the cached geometry if it exists
|
|
|
- pending.push( cached );
|
|
|
+ pending.push( cached.promise );
|
|
|
|
|
|
} else {
|
|
|
|
|
@@ -2560,7 +2543,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
}
|
|
|
|
|
|
// Cache this geometry
|
|
|
- cache.push( { primitive: primitive, promise: geometryPromise } );
|
|
|
+ cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise };
|
|
|
|
|
|
pending.push( geometryPromise );
|
|
|
|
|
@@ -2568,95 +2551,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
- return Promise.all( pending ).then( function ( geometries ) {
|
|
|
-
|
|
|
- if ( isMultiPass ) {
|
|
|
-
|
|
|
- var baseGeometry = geometries[ 0 ];
|
|
|
-
|
|
|
- // See if we've already created this combined geometry
|
|
|
- var cache = parser.multiPassGeometryCache;
|
|
|
- var cached = getCachedMultiPassGeometry( cache, baseGeometry, originalPrimitives );
|
|
|
-
|
|
|
- if ( cached !== null ) return [ cached.geometry ];
|
|
|
-
|
|
|
- // Cloning geometry because of index override.
|
|
|
- // Attributes can be reused so cloning by myself here.
|
|
|
- var geometry = new THREE.BufferGeometry();
|
|
|
-
|
|
|
- geometry.name = baseGeometry.name;
|
|
|
- geometry.userData = baseGeometry.userData;
|
|
|
-
|
|
|
- for ( var key in baseGeometry.attributes ) geometry.addAttribute( key, baseGeometry.attributes[ key ] );
|
|
|
- for ( var key in baseGeometry.morphAttributes ) geometry.morphAttributes[ key ] = baseGeometry.morphAttributes[ key ];
|
|
|
-
|
|
|
- var pendingIndices = [];
|
|
|
-
|
|
|
- for ( var i = 0, il = originalPrimitives.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- pendingIndices.push( parser.getDependency( 'accessor', originalPrimitives[ i ].indices ) );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return Promise.all( pendingIndices ).then( function ( accessors ) {
|
|
|
-
|
|
|
- var indices = [];
|
|
|
- var offset = 0;
|
|
|
-
|
|
|
- for ( var i = 0, il = originalPrimitives.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- var accessor = accessors[ i ];
|
|
|
-
|
|
|
- for ( var j = 0, jl = accessor.count; j < jl; j ++ ) indices.push( accessor.array[ j ] );
|
|
|
-
|
|
|
- geometry.addGroup( offset, accessor.count, i );
|
|
|
-
|
|
|
- offset += accessor.count;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- geometry.setIndex( indices );
|
|
|
-
|
|
|
- cache.push( { geometry: geometry, baseGeometry: baseGeometry, primitives: originalPrimitives } );
|
|
|
-
|
|
|
- return [ geometry ];
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- } else if ( geometries.length > 1 && THREE.BufferGeometryUtils !== undefined ) {
|
|
|
-
|
|
|
- // Tries to merge geometries with BufferGeometryUtils if possible
|
|
|
-
|
|
|
- for ( var i = 1, il = primitives.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- // can't merge if draw mode is different
|
|
|
- if ( primitives[ 0 ].mode !== primitives[ i ].mode ) return geometries;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // See if we've already created this combined geometry
|
|
|
- var cache = parser.multiplePrimitivesCache;
|
|
|
- var cached = getCachedCombinedGeometry( cache, geometries );
|
|
|
-
|
|
|
- if ( cached ) {
|
|
|
-
|
|
|
- if ( cached.geometry !== null ) return [ cached.geometry ];
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- var geometry = THREE.BufferGeometryUtils.mergeBufferGeometries( geometries, true );
|
|
|
-
|
|
|
- cache.push( { geometry: geometry, baseGeometries: geometries } );
|
|
|
-
|
|
|
- if ( geometry !== null ) return [ geometry ];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return geometries;
|
|
|
-
|
|
|
- } );
|
|
|
+ return Promise.all( pending );
|
|
|
|
|
|
};
|
|
|
|
|
@@ -2690,8 +2585,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
return parser.loadGeometries( primitives ).then( function ( geometries ) {
|
|
|
|
|
|
- var isMultiMaterial = geometries.length === 1 && geometries[ 0 ].groups.length > 0;
|
|
|
-
|
|
|
var meshes = [];
|
|
|
|
|
|
for ( var i = 0, il = geometries.length; i < il; i ++ ) {
|
|
@@ -2703,7 +2596,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
var mesh;
|
|
|
|
|
|
- var material = isMultiMaterial ? originalMaterials : originalMaterials[ i ];
|
|
|
+ var material = originalMaterials[ i ];
|
|
|
|
|
|
if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
|
|
|
primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
|
|
@@ -2761,121 +2654,9 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
assignExtrasToUserData( mesh, meshDef );
|
|
|
|
|
|
- meshes.push( mesh );
|
|
|
-
|
|
|
- // 2. update Material depending on Mesh and BufferGeometry
|
|
|
-
|
|
|
- var materials = isMultiMaterial ? mesh.material : [ mesh.material ];
|
|
|
-
|
|
|
- var useVertexTangents = geometry.attributes.tangent !== undefined;
|
|
|
- var useVertexColors = geometry.attributes.color !== undefined;
|
|
|
- var useFlatShading = geometry.attributes.normal === undefined;
|
|
|
- var useSkinning = mesh.isSkinnedMesh === true;
|
|
|
- var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0;
|
|
|
- var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined;
|
|
|
-
|
|
|
- for ( var j = 0, jl = materials.length; j < jl; j ++ ) {
|
|
|
-
|
|
|
- var material = materials[ j ];
|
|
|
+ parser.assignFinalMaterial( mesh );
|
|
|
|
|
|
- if ( mesh.isPoints ) {
|
|
|
-
|
|
|
- var cacheKey = 'PointsMaterial:' + material.uuid;
|
|
|
-
|
|
|
- var pointsMaterial = parser.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.lights = false; // PointsMaterial doesn't support lights yet
|
|
|
-
|
|
|
- parser.cache.add( cacheKey, pointsMaterial );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- material = pointsMaterial;
|
|
|
-
|
|
|
- } else if ( mesh.isLine ) {
|
|
|
-
|
|
|
- var cacheKey = 'LineBasicMaterial:' + material.uuid;
|
|
|
-
|
|
|
- var lineMaterial = parser.cache.get( cacheKey );
|
|
|
-
|
|
|
- if ( ! lineMaterial ) {
|
|
|
-
|
|
|
- lineMaterial = new THREE.LineBasicMaterial();
|
|
|
- THREE.Material.prototype.copy.call( lineMaterial, material );
|
|
|
- lineMaterial.color.copy( material.color );
|
|
|
- lineMaterial.lights = false; // LineBasicMaterial doesn't support lights yet
|
|
|
-
|
|
|
- parser.cache.add( cacheKey, lineMaterial );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- material = lineMaterial;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // Clone the material if it will be modified
|
|
|
- if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
|
|
|
-
|
|
|
- var cacheKey = 'ClonedMaterial:' + material.uuid + ':';
|
|
|
-
|
|
|
- if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
|
|
|
- if ( useSkinning ) cacheKey += 'skinning:';
|
|
|
- if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
|
|
|
- if ( useVertexColors ) cacheKey += 'vertex-colors:';
|
|
|
- if ( useFlatShading ) cacheKey += 'flat-shading:';
|
|
|
- if ( useMorphTargets ) cacheKey += 'morph-targets:';
|
|
|
- if ( useMorphNormals ) cacheKey += 'morph-normals:';
|
|
|
-
|
|
|
- var cachedMaterial = parser.cache.get( cacheKey );
|
|
|
-
|
|
|
- if ( ! cachedMaterial ) {
|
|
|
-
|
|
|
- cachedMaterial = material.isGLTFSpecularGlossinessMaterial
|
|
|
- ? extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].cloneMaterial( material )
|
|
|
- : material.clone();
|
|
|
-
|
|
|
- if ( useSkinning ) cachedMaterial.skinning = true;
|
|
|
- if ( useVertexTangents ) cachedMaterial.vertexTangents = true;
|
|
|
- if ( useVertexColors ) cachedMaterial.vertexColors = THREE.VertexColors;
|
|
|
- if ( useFlatShading ) cachedMaterial.flatShading = true;
|
|
|
- if ( useMorphTargets ) cachedMaterial.morphTargets = true;
|
|
|
- if ( useMorphNormals ) cachedMaterial.morphNormals = true;
|
|
|
-
|
|
|
- parser.cache.add( cacheKey, cachedMaterial );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- material = cachedMaterial;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- materials[ j ] = material;
|
|
|
-
|
|
|
- // workarounds for mesh and geometry
|
|
|
-
|
|
|
- if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
|
|
|
-
|
|
|
- console.log( 'THREE.GLTFLoader: Duplicating UVs to support aoMap.' );
|
|
|
- geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( material.isGLTFSpecularGlossinessMaterial ) {
|
|
|
-
|
|
|
- // for GLTFSpecularGlossinessMaterial(ShaderMaterial) uniforms runtime update
|
|
|
- mesh.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- mesh.material = isMultiMaterial ? materials : materials[ 0 ];
|
|
|
+ meshes.push( mesh );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -3060,10 +2841,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {
|
|
|
|
|
|
- // node can be THREE.Group here but
|
|
|
- // PATH_PROPERTIES.weights(morphTargetInfluences) should be
|
|
|
- // the property of a mesh object under group.
|
|
|
-
|
|
|
+ // Node may be a THREE.Group (glTF mesh with several primitives) or a THREE.Mesh.
|
|
|
node.traverse( function ( object ) {
|
|
|
|
|
|
if ( object.isMesh === true && object.morphTargetInfluences ) {
|
|
@@ -3080,20 +2858,16 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
- // KeyframeTrack.optimize() will modify given 'times' and 'values'
|
|
|
- // buffers before creating a truncated copy to keep. Because buffers may
|
|
|
- // be reused by other tracks, make copies here.
|
|
|
for ( var j = 0, jl = targetNames.length; j < jl; j ++ ) {
|
|
|
|
|
|
var track = new TypedKeyframeTrack(
|
|
|
targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ],
|
|
|
- THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ),
|
|
|
- THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ),
|
|
|
+ inputAccessor.array,
|
|
|
+ outputAccessor.array,
|
|
|
interpolation
|
|
|
);
|
|
|
|
|
|
- // Here is the trick to enable custom interpolation.
|
|
|
- // Overrides .createInterpolant in a factory method which creates custom interpolation.
|
|
|
+ // Override interpolation with custom factory method.
|
|
|
if ( sampler.interpolation === 'CUBICSPLINE' ) {
|
|
|
|
|
|
track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) {
|
|
@@ -3106,8 +2880,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
- // Workaround, provide an alternate way to know if the interpolant type is cubis spline to track.
|
|
|
- // track.getInterpolation() doesn't return valid value for custom interpolant.
|
|
|
+ // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants.
|
|
|
track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true;
|
|
|
|
|
|
}
|
|
@@ -3335,7 +3108,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
mesh.bind( new THREE.Skeleton( bones, boneInverses ), mesh.matrixWorld );
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
return node;
|
|
|
|