From 5dd809afb7001bf4d7845bbd8de040f39ab287e7 Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Mon, 7 Aug 2017 20:38:00 -0700 Subject: [PATCH] [gltf] Refactor loadMeshes. Includes changes necessary to support the upcoming Draco extension, and more glTF primitive types. --- examples/js/loaders/GLTF2Loader.js | 430 +++++++++++++++-------------- 1 file changed, 217 insertions(+), 213 deletions(-) diff --git a/examples/js/loaders/GLTF2Loader.js b/examples/js/loaders/GLTF2Loader.js index 0f373bbda1..39de9acacc 100644 --- a/examples/js/loaders/GLTF2Loader.js +++ b/examples/js/loaders/GLTF2Loader.js @@ -1579,6 +1579,125 @@ THREE.GLTF2Loader = ( function () { } + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets + * @param {THREE.Mesh} mesh + * @param {GLTF.Mesh} meshDef + * @param {GLTF.Primitive} primitiveDef + * @param {Object} dependencies + */ + function addMorphTargets ( mesh, meshDef, primitiveDef, dependencies ) { + + var geometry = mesh.geometry; + var material = mesh.material; + + var targets = primitiveDef.targets; + var morphAttributes = geometry.morphAttributes; + + morphAttributes.position = []; + morphAttributes.normal = []; + + material.morphTargets = true; + + for ( var i = 0, il = targets.length; i < il; i ++ ) { + + var target = targets[ i ]; + var attributeName = 'morphTarget' + i; + + var positionAttribute, normalAttribute; + + if ( target.POSITION !== undefined ) { + + // Three.js morph formula is + // position + // + weight0 * ( morphTarget0 - position ) + // + weight1 * ( morphTarget1 - position ) + // ... + // while the glTF one is + // position + // + weight0 * morphTarget0 + // + weight1 * morphTarget1 + // ... + // then adding position to morphTarget. + // So morphTarget value will depend on mesh's position, then cloning attribute + // for the case if attribute is shared among two or more meshes. + + positionAttribute = dependencies.accessors[ target.POSITION ].clone(); + var position = geometry.attributes.position; + + for ( var j = 0, jl = positionAttribute.count; j < jl; j ++ ) { + + positionAttribute.setXYZ( + j, + positionAttribute.getX( j ) + position.getX( j ), + positionAttribute.getY( j ) + position.getY( j ), + positionAttribute.getZ( j ) + position.getZ( j ) + ); + + } + + } else { + + // Copying the original position not to affect the final position. + // See the formula above. + positionAttribute = geometry.attributes.position.clone(); + + } + + if ( target.NORMAL !== undefined ) { + + material.morphNormals = true; + + // see target.POSITION's comment + + normalAttribute = dependencies.accessors[ target.NORMAL ].clone(); + var normal = geometry.attributes.normal; + + for ( var j = 0, jl = normalAttribute.count; j < jl; j ++ ) { + + normalAttribute.setXYZ( + j, + normalAttribute.getX( j ) + normal.getX( j ), + normalAttribute.getY( j ) + normal.getY( j ), + normalAttribute.getZ( j ) + normal.getZ( j ) + ); + + } + + } else { + + normalAttribute = geometry.attributes.normal.clone(); + + } + + if ( target.TANGENT !== undefined ) { + + // TODO: implement + + } + + positionAttribute.name = attributeName; + normalAttribute.name = attributeName; + + morphAttributes.position.push( positionAttribute ); + morphAttributes.normal.push( normalAttribute ); + + } + + mesh.updateMorphTargets(); + + if ( meshDef.weights !== undefined ) { + + for ( var i = 0, il = meshDef.weights.length; i < il; i ++ ) { + + mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; + + } + + } + + } + // Deferred constructor for RawShaderMaterial types function DeferredShaderMaterial( params ) { @@ -2116,102 +2235,126 @@ THREE.GLTF2Loader = ( function () { }; - GLTFParser.prototype.loadMeshes = function () { + GLTFParser.prototype.loadGeometries = function ( primitives ) { - var json = this.json; + var extensions = this.extensions; return this._withDependencies( [ 'accessors', - 'materials' + 'bufferViews', ] ).then( function ( dependencies ) { - return _each( json.meshes, function ( mesh ) { + return _each( primitives, function ( primitive ) { - var group = new THREE.Group(); - if ( mesh.name !== undefined ) group.name = mesh.name; + var geometry = new THREE.BufferGeometry(); - if ( mesh.extras ) group.userData = mesh.extras; + var attributes = primitive.attributes; - var primitives = mesh.primitives || []; + for ( var attributeId in attributes ) { - for ( var name in primitives ) { + var attributeEntry = attributes[ attributeId ]; - var primitive = primitives[ name ]; + if ( attributeEntry === undefined ) return; - var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + var bufferAttribute = dependencies.accessors[ attributeEntry ]; - var geometry; + switch ( attributeId ) { - var meshNode; + case 'POSITION': - if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + geometry.addAttribute( 'position', bufferAttribute ); + break; - geometry = new THREE.BufferGeometry(); + case 'NORMAL': - var attributes = primitive.attributes; + geometry.addAttribute( 'normal', bufferAttribute ); + break; - for ( var attributeId in attributes ) { + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': - var attributeEntry = attributes[ attributeId ]; + geometry.addAttribute( 'uv', bufferAttribute ); + break; - if ( attributeEntry === undefined ) return; + case 'TEXCOORD_1': - var bufferAttribute = dependencies.accessors[ attributeEntry ]; + geometry.addAttribute( 'uv2', bufferAttribute ); + break; - switch ( attributeId ) { + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': - case 'POSITION': + geometry.addAttribute( 'color', bufferAttribute ); + break; - geometry.addAttribute( 'position', bufferAttribute ); - break; + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. - case 'NORMAL': + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; - geometry.addAttribute( 'normal', bufferAttribute ); - break; + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. - case 'TEXCOORD_0': - case 'TEXCOORD0': - case 'TEXCOORD': + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; - geometry.addAttribute( 'uv', bufferAttribute ); - break; + } - case 'TEXCOORD_1': + } - geometry.addAttribute( 'uv2', bufferAttribute ); - break; + if ( primitive.indices !== undefined ) { - case 'COLOR_0': - case 'COLOR0': - case 'COLOR': + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); - geometry.addAttribute( 'color', bufferAttribute ); - break; + } - case 'WEIGHTS_0': - case 'WEIGHT': // WEIGHT semantic deprecated. + return geometry; - geometry.addAttribute( 'skinWeight', bufferAttribute ); - break; + } ); - case 'JOINTS_0': - case 'JOINT': // JOINT semantic deprecated. + } ); - geometry.addAttribute( 'skinIndex', bufferAttribute ); - break; + }; - } + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes + */ + GLTFParser.prototype.loadMeshes = function () { - } + var scope = this; + var json = this.json; + + return this._withDependencies( [ - if ( primitive.indices !== undefined ) { + 'accessors', + 'materials' - geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + ] ).then( function ( dependencies ) { - } + return _each( json.meshes, function ( meshDef ) { + + var group = new THREE.Group(); + + if ( meshDef.name !== undefined ) group.name = meshDef.name; + if ( meshDef.extras ) group.userData = meshDef.extras; + + var primitives = meshDef.primitives || []; + + return scope.loadGeometries( primitives ).then( function ( geometries ) { + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + var geometry = geometries[ name ]; + + var material = primitive.material === undefined + ? createDefaultMaterial() + : dependencies.materials[ primitive.material ]; if ( material.aoMap && geometry.attributes.uv2 === undefined @@ -2222,6 +2365,13 @@ THREE.GLTF2Loader = ( function () { } + if ( geometry.attributes.color !== undefined ) { + + material.vertexColors = THREE.VertexColors; + material.needsUpdate = true; + + } + if ( geometry.attributes.normal === undefined ) { if ( material.flatShading !== undefined ) { @@ -2237,185 +2387,39 @@ THREE.GLTF2Loader = ( function () { } - meshNode = new THREE.Mesh( geometry, material ); - meshNode.castShadow = true; - - if ( primitive.targets !== undefined ) { - - var targets = primitive.targets; - var morphAttributes = geometry.morphAttributes; - - morphAttributes.position = []; - morphAttributes.normal = []; - - material.morphTargets = true; - - for ( var i = 0, il = targets.length; i < il; i ++ ) { - - var target = targets[ i ]; - var attributeName = 'morphTarget' + i; - - var positionAttribute, normalAttribute; - - if ( target.POSITION !== undefined ) { - - // Three.js morph formula is - // position - // + weight0 * ( morphTarget0 - position ) - // + weight1 * ( morphTarget1 - position ) - // ... - // while the glTF one is - // position - // + weight0 * morphTarget0 - // + weight1 * morphTarget1 - // ... - // then adding position to morphTarget. - // So morphTarget value will depend on mesh's position, then cloning attribute - // for the case if attribute is shared among two or more meshes. - - positionAttribute = dependencies.accessors[ target.POSITION ].clone(); - var position = geometry.attributes.position; + var mesh; - for ( var j = 0, jl = positionAttribute.count; j < jl; j ++ ) { + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { - positionAttribute.setXYZ( - j, - positionAttribute.getX( j ) + position.getX( j ), - positionAttribute.getY( j ) + position.getY( j ), - positionAttribute.getZ( j ) + position.getZ( j ) - ); - - } - - } else if ( geometry.attributes.position ) { - - // Copying the original position not to affect the final position. - // See the formula above. - positionAttribute = geometry.attributes.position.clone(); - - } - - if ( target.NORMAL !== undefined ) { - - material.morphNormals = true; - - // see target.POSITION's comment - - normalAttribute = dependencies.accessors[ target.NORMAL ].clone(); - var normal = geometry.attributes.normal; - - for ( var j = 0, jl = normalAttribute.count; j < jl; j ++ ) { - - normalAttribute.setXYZ( - j, - normalAttribute.getX( j ) + normal.getX( j ), - normalAttribute.getY( j ) + normal.getY( j ), - normalAttribute.getZ( j ) + normal.getZ( j ) - ); - - } - - } else if ( geometry.attributes.normal ) { - - normalAttribute = geometry.attributes.normal.clone(); - - } - - // TODO: implement - if ( target.TANGENT !== undefined ) { - - } + mesh = new THREE.Mesh( geometry, material ); - if ( positionAttribute ) { + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { - positionAttribute.name = attributeName; - morphAttributes.position.push( positionAttribute ); + mesh = new THREE.LineSegments( geometry, material ); - } - - if ( normalAttribute ) { - - normalAttribute.name = attributeName; - morphAttributes.normal.push( normalAttribute ); - - } - - } - - meshNode.updateMorphTargets(); - - if ( mesh.weights !== undefined ) { - - for ( var i = 0, il = mesh.weights.length; i < il; i ++ ) { - - meshNode.morphTargetInfluences[ i ] = mesh.weights[ i ]; + } else { - } - - } + throw new Error( 'THREE.GLTF2Loader: Only TRIANGLE and LINE primitives are supported.' ); } - } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { - - geometry = new THREE.BufferGeometry(); - - var attributes = primitive.attributes; - - for ( var attributeId in attributes ) { - - var attributeEntry = attributes[ attributeId ]; - - if ( ! attributeEntry ) return; - - var bufferAttribute = dependencies.accessors[ attributeEntry ]; + mesh.name = group.name + '_' + name; - switch ( attributeId ) { - - case 'POSITION': - geometry.addAttribute( 'position', bufferAttribute ); - break; - - case 'COLOR_0': - case 'COLOR0': - case 'COLOR': - geometry.addAttribute( 'color', bufferAttribute ); - break; - - } - - } - - if ( primitive.indices !== undefined ) { + if ( primitive.targets !== undefined ) { - geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + addMorphTargets( mesh, meshDef, primitive, dependencies ); } - meshNode = new THREE.LineSegments( geometry, material ); + if ( primitive.extras ) mesh.userData = primitive.extras; - } else { - - throw new Error( 'THREE.GLTF2Loader: Only triangular and line primitives are supported.' ); - - } - - if ( geometry.attributes.color !== undefined ) { - - material.vertexColors = THREE.VertexColors; - material.needsUpdate = true; + group.add( mesh ); } - meshNode.name = group.name + '_' + name; + return group; - if ( primitive.extras ) meshNode.userData = primitive.extras; - - group.add( meshNode ); - - } - - return group; + } ); } ); -- GitLab