From fde0d3494b257bfefacd42ea05b78a9f1237cc3c Mon Sep 17 00:00:00 2001 From: Lewy Blue Date: Thu, 26 Jul 2018 13:31:05 +0100 Subject: [PATCH] setup for merge dev --- examples/js/loaders/FBXLoader.js | 255 +++++++++++++++++++------------ 1 file changed, 161 insertions(+), 94 deletions(-) diff --git a/examples/js/loaders/FBXLoader.js b/examples/js/loaders/FBXLoader.js index abd7b4013b..d1c6776faf 100644 --- a/examples/js/loaders/FBXLoader.js +++ b/examples/js/loaders/FBXLoader.js @@ -9,9 +9,6 @@ * * Needs Support: * Morph normals / blend shape normals - * Animation tracks for morph targets - * - * Euler rotation order * * FBX format references: * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure @@ -839,8 +836,6 @@ THREE.FBXLoader = ( function () { }, null ); - var preTransform = new THREE.Matrix4(); - // TODO: if there is more than one model associated with the geometry, AND the models have // different geometric transforms, then this will cause problems // if ( modelNodes.length > 1 ) { } @@ -848,28 +843,16 @@ THREE.FBXLoader = ( function () { // For now just assume one model and get the preRotations from that var modelNode = modelNodes[ 0 ]; - if ( 'GeometricRotation' in modelNode ) { - - var array = modelNode.GeometricRotation.value.map( THREE.Math.degToRad ); - array[ 3 ] = 'ZYX'; - - preTransform.makeRotationFromEuler( new THREE.Euler().fromArray( array ) ); - - } - - if ( 'GeometricTranslation' in modelNode ) { - - preTransform.setPosition( new THREE.Vector3().fromArray( modelNode.GeometricTranslation.value ) ); - - } - - if ( 'GeometricScaling' in modelNode ) { + var transformData = {}; - preTransform.scale( new THREE.Vector3().fromArray( modelNode.GeometricScaling.value ) ); + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = modelNode.RotationOrder.value; + if ( 'GeometricTranslation' in modelNode ) transformData.translation = modelNode.GeometricTranslation.value; + if ( 'GeometricRotation' in modelNode ) transformData.rotation = modelNode.GeometricRotation.value; + if ( 'GeometricScaling' in modelNode ) transformData.scale = modelNode.GeometricScaling.value; - } + var transform = generateTransform( transformData ); - return this.genGeometry( geoNode, skeleton, morphTarget, preTransform ); + return this.genGeometry( geoNode, skeleton, morphTarget, transform ); }, @@ -1637,6 +1620,7 @@ THREE.FBXLoader = ( function () { this.bindSkeleton( deformers.skeletons, geometryMap, modelMap ); this.addAnimations( sceneGraph ); + this.createAmbientLight( sceneGraph ); this.setupMorphMaterials( sceneGraph ); @@ -2054,67 +2038,19 @@ THREE.FBXLoader = ( function () { // parse the model node for transform details and apply them to the model setModelTransforms: function ( model, modelNode ) { - // http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html - if ( 'RotationOrder' in modelNode ) { - - var enums = [ - 'XYZ', // default - 'XZY', - 'YZX', - 'ZXY', - 'YXZ', - 'ZYX', - 'SphericXYZ', - ]; - - var value = parseInt( modelNode.RotationOrder.value, 10 ); - - if ( value > 0 && value < 6 ) { - - // model.rotation.order = enums[ value ]; - - // Note: Euler order other than XYZ is currently not supported, so just display a warning for now - console.warn( 'THREE.FBXLoader: unsupported Euler Order: %s. Currently only XYZ order is supported. Animations and rotations may be incorrect.', enums[ value ] ); + var transformData = {}; - } else if ( value === 6 ) { - - console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); - - } - - } + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value ); + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; - if ( 'Lcl_Translation' in modelNode ) { + var transform = generateTransform( transformData ); - model.position.fromArray( modelNode.Lcl_Translation.value ); - - } - - if ( 'Lcl_Rotation' in modelNode ) { - - var rotation = modelNode.Lcl_Rotation.value.map( THREE.Math.degToRad ); - rotation.push( 'ZYX' ); - model.quaternion.setFromEuler( new THREE.Euler().fromArray( rotation ) ); - - } - - if ( 'Lcl_Scaling' in modelNode ) { - - model.scale.fromArray( modelNode.Lcl_Scaling.value ); - - } - - if ( 'PreRotation' in modelNode ) { - - var array = modelNode.PreRotation.value.map( THREE.Math.degToRad ); - array[ 3 ] = 'ZYX'; - - var preRotations = new THREE.Euler().fromArray( array ); - - preRotations = new THREE.Quaternion().setFromEuler( preRotations ); - model.quaternion.premultiply( preRotations ); - - } + model.applyMatrix( transform ); }, @@ -2263,6 +2199,7 @@ THREE.FBXLoader = ( function () { var curveNodesMap = this.parseAnimationCurveNodes(); this.parseAnimationCurves( curveNodesMap ); + var layersMap = this.parseAnimationLayers( curveNodesMap ); var rawClips = this.parseAnimStacks( layersMap ); @@ -2346,7 +2283,7 @@ THREE.FBXLoader = ( function () { curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve; - } else if ( animationCurveRelationship.match( /d|DeformPercent/ ) ) { + } else if ( animationCurveRelationship.match( /d|DeformPercent/ ) && curveNodesMap.has( animationCurveID ) ) { curveNodesMap.get( animationCurveID ).curves[ 'morph' ] = animationCurve; @@ -2406,18 +2343,14 @@ THREE.FBXLoader = ( function () { initialPosition: [ 0, 0, 0 ], initialRotation: [ 0, 0, 0 ], initialScale: [ 1, 1, 1 ], + transform: this.getModelAnimTransform( rawModel ), }; - if ( 'Lcl_Translation' in rawModel ) node.initialPosition = rawModel.Lcl_Translation.value; - - if ( 'Lcl_Rotation' in rawModel ) node.initialRotation = rawModel.Lcl_Rotation.value; - - if ( 'Lcl_Scaling' in rawModel ) node.initialScale = rawModel.Lcl_Scaling.value; - // if the animated model is pre rotated, we'll have to apply the pre rotations to every // animation value as well if ( 'PreRotation' in rawModel ) node.preRotations = rawModel.PreRotation.value; + if ( 'PostRotation' in rawModel ) node.postRotations = rawModel.PostRotation.value; layerCurveNodes[ i ] = node; @@ -2474,6 +2407,26 @@ THREE.FBXLoader = ( function () { }, + getModelAnimTransform: function ( modelNode ) { + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value ); + + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + return generateTransform( transformData ); + + }, + // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation // hierarchy. Each Stack node will be used to create a THREE.AnimationClip parseAnimStacks: function ( layersMap ) { @@ -2529,23 +2482,33 @@ THREE.FBXLoader = ( function () { var tracks = []; + var initialPosition = new THREE.Vector3(); + var initialRotation = new THREE.Quaternion(); + var initialScale = new THREE.Vector3(); + + if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale ); + + initialPosition = initialPosition.toArray(); + initialRotation = new THREE.Euler().setFromQuaternion( initialRotation ).toArray(); // todo: euler order + initialScale = initialScale.toArray(); + if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) { - var positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, rawTracks.initialPosition, 'position' ); + var positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position' ); if ( positionTrack !== undefined ) tracks.push( positionTrack ); } if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) { - var rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, rawTracks.initialRotation, rawTracks.preRotations ); + var rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotations, rawTracks.postRotations ); if ( rotationTrack !== undefined ) tracks.push( rotationTrack ); } if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) { - var scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, rawTracks.initialScale, 'scale' ); + var scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale' ); if ( scaleTrack !== undefined ) tracks.push( scaleTrack ); } @@ -2570,7 +2533,7 @@ THREE.FBXLoader = ( function () { }, - generateRotationTrack: function ( modelName, curves, initialValue, preRotations ) { + generateRotationTrack: function ( modelName, curves, initialValue, preRotations, postRotations ) { if ( curves.x !== undefined ) { @@ -2604,6 +2567,16 @@ THREE.FBXLoader = ( function () { } + if ( postRotations !== undefined ) { + + postRotations = postRotations.map( THREE.Math.degToRad ); + postRotations.push( 'ZYX' ); + + postRotations = new THREE.Euler().fromArray( postRotations ); + postRotations = new THREE.Quaternion().setFromEuler( postRotations ).inverse(); + + } + var quaternion = new THREE.Quaternion(); var euler = new THREE.Euler(); @@ -2615,7 +2588,8 @@ THREE.FBXLoader = ( function () { quaternion.setFromEuler( euler ); - if ( preRotations !== undefined )quaternion.premultiply( preRotations ); + if ( preRotations !== undefined ) quaternion.premultiply( preRotations ); + if ( postRotations !== undefined ) quaternion.multiply( postRotations ); quaternion.toArray( quaternionValues, ( i / 3 ) * 4 ); @@ -3853,6 +3827,99 @@ THREE.FBXLoader = ( function () { } + var tempMat = new THREE.Matrix4(); + var tempEuler = new THREE.Euler(); + var tempVec = new THREE.Vector3(); + var translation = new THREE.Vector3(); + var rotation = new THREE.Matrix4(); + + // generate transformation from FBX transform data + // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm + // transformData = { + // eulerOrder: int, + // translation: [], + // rotationOffset: [], + // preRotation + // rotation + // postRotation + // scale + // } + // all entries are optional + function generateTransform( transformData ) { + + var transform = new THREE.Matrix4(); + translation.set( 0, 0, 0 ); + rotation.identity(); + + var order = ( transformData.eulerOrder ) ? getEulerOrder( transformData.eulerOrder ) : getEulerOrder( 0 ); + + if ( transformData.translation ) translation.fromArray( transformData.translation ); + if ( transformData.rotationOffset ) translation.add( tempVec.fromArray( transformData.rotationOffset ) ); + + if ( transformData.rotation ) { + + var array = transformData.rotation.map( THREE.Math.degToRad ); + array.push( order ); + rotation.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.preRotation ) { + + var array = transformData.preRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + rotation.premultiply( tempMat ); + + } + + if ( transformData.postRotation ) { + + var array = transformData.postRotation.map( THREE.Math.degToRad ); + array.push( order ); + tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + tempMat.getInverse( tempMat ); + + rotation.multiply( tempMat ); + + } + + if ( transformData.scale ) transform.scale( tempVec.fromArray( transformData.scale ) ); + + transform.setPosition( translation ); + transform.multiply( rotation ); + + return transform; + + } + + // Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order + // ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html + function getEulerOrder( order ) { + + var enums = [ + 'ZYX', // -> XYZ extrinsic + 'YZX', // -> XZY extrinsic + 'XZY', // -> YZX extrinsic + 'ZXY', // -> YXZ extrinsic + 'YXZ', // -> ZXY extrinsic + 'XYZ', // -> ZYX extrinsic + //'SphericXYZ', // not possible to support + ]; + + if ( order === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + return enums[ 0 ]; + + } + + return enums[ order ]; + + } + // Parses comma separated list of numbers and returns them an array. // Used internally by the TextParser function parseNumberArray( value ) { -- GitLab