|
@@ -38,6 +38,7 @@ THREE.FBXLoader = ( function () {
|
|
|
crossOrigin: 'anonymous',
|
|
|
|
|
|
load: function ( url, onLoad, onProgress, onError ) {
|
|
|
+
|
|
|
var self = this;
|
|
|
|
|
|
var resourceDirectory = THREE.LoaderUtils.extractUrlBase( url );
|
|
@@ -64,7 +65,8 @@ THREE.FBXLoader = ( function () {
|
|
|
}
|
|
|
|
|
|
}, onProgress, onError );
|
|
|
- },
|
|
|
+
|
|
|
+},
|
|
|
|
|
|
setCrossOrigin: function ( value ) {
|
|
|
|
|
@@ -99,7 +101,7 @@ THREE.FBXLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
- // console.log( fbxTree );
|
|
|
+ console.log( fbxTree );
|
|
|
|
|
|
var textureLoader = new THREE.TextureLoader( this.manager ).setPath( resourceDirectory ).setCrossOrigin( this.crossOrigin );
|
|
|
|
|
@@ -825,6 +827,25 @@ THREE.FBXLoader = ( function () {
|
|
|
|
|
|
sceneGraph.animations = animations;
|
|
|
|
|
|
+ sceneGraph.traverse( function ( node ) {
|
|
|
+
|
|
|
+ if ( node.userData.transformData ) {
|
|
|
+
|
|
|
+ if ( node.parent ) node.userData.transformData.parentMatrixWorld = node.parent.matrix;
|
|
|
+
|
|
|
+ var transform = generateTransform( node.userData.transformData );
|
|
|
+
|
|
|
+ // if ( node.parent ) transform = node.parent.matrix.getInverse( node.parent.matrix ).multiply( transform );
|
|
|
+
|
|
|
+ node.applyMatrix( transform );
|
|
|
+
|
|
|
+ // if ( node.parent )
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
},
|
|
|
|
|
|
// parse nodes in FBXTree.Objects.Model
|
|
@@ -870,7 +891,7 @@ THREE.FBXLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
- this.setModelTransforms( model, node );
|
|
|
+ this.getTransformData( model, node );
|
|
|
modelMap.set( id, model );
|
|
|
|
|
|
}
|
|
@@ -1108,9 +1129,9 @@ THREE.FBXLoader = ( function () {
|
|
|
var penumbra = 0;
|
|
|
if ( lightAttribute.OuterAngle !== undefined ) {
|
|
|
|
|
|
- // TODO: this is not correct - FBX calculates outer and inner angle in degrees
|
|
|
- // with OuterAngle > InnerAngle && OuterAngle <= Math.PI
|
|
|
- // while three.js uses a penumbra between (0, 1) to attenuate the inner angle
|
|
|
+ // TODO: this is not correct - FBX calculates outer and inner angle in degrees
|
|
|
+ // with OuterAngle > InnerAngle && OuterAngle <= Math.PI
|
|
|
+ // while three.js uses a penumbra between (0, 1) to attenuate the inner angle
|
|
|
penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value );
|
|
|
penumbra = Math.max( penumbra, 1 );
|
|
|
|
|
@@ -1223,22 +1244,43 @@ THREE.FBXLoader = ( function () {
|
|
|
|
|
|
},
|
|
|
|
|
|
- // parse the model node for transform details and apply them to the model
|
|
|
- setModelTransforms: function ( model, modelNode ) {
|
|
|
+ // parse the model node for transform data
|
|
|
+ getTransformData: function ( model, modelNode ) {
|
|
|
|
|
|
var transformData = {};
|
|
|
|
|
|
+ // 0 = RrSs, 1 = RSrs, 2 = Rrs
|
|
|
+ // RrSs: Scaling of parent is applied in the child world after the local child rotation
|
|
|
+ // RrSs:= GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * LocalRM(x) * [GlobalSHM(P(x)) * GlobalSM(P(x))] * LocalSM(x)
|
|
|
+ // RSrs: Scaling of parent is applied in the parent world
|
|
|
+ // RSrs:= GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * [GlobalSHM(P(x)) * GlobalSM(P(x))] * LocalRM(x) * LocalSM(x)
|
|
|
+ // Rrs: Scaling of parent does not affect the scaling of children
|
|
|
+ // Rrs:= GlobalRM(x) * (GlobalSHM(x) * GlobalSM(x)) = GlobalRM(P(x)) * LocalRM(x) * LocalSM(x)
|
|
|
+ if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value );
|
|
|
+
|
|
|
+ // rotation order
|
|
|
if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value );
|
|
|
+
|
|
|
+ // translation
|
|
|
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;
|
|
|
+
|
|
|
+ // rotation
|
|
|
if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value;
|
|
|
+ if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value;
|
|
|
if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value;
|
|
|
+
|
|
|
+ // scaling
|
|
|
if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value;
|
|
|
|
|
|
- var transform = generateTransform( transformData );
|
|
|
|
|
|
- model.applyMatrix( transform );
|
|
|
+ // offset and pivot
|
|
|
+ if ( 'ScalingOffset' in modelNode ) transformData.scalingOffset = modelNode.ScalingOffset.value;
|
|
|
+ if ( 'ScalingPivot' in modelNode ) transformData.scalingPivot = modelNode.ScalingPivot.value;
|
|
|
+
|
|
|
+ if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value;
|
|
|
+ if ( 'RotationPivot' in modelNode ) transformData.rotationPivot = modelNode.RotationPivot.value;
|
|
|
+
|
|
|
+ model.userData.transformData = transformData;
|
|
|
|
|
|
},
|
|
|
|
|
@@ -1493,16 +1535,15 @@ THREE.FBXLoader = ( function () {
|
|
|
|
|
|
}, null );
|
|
|
|
|
|
- // 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 ) { }
|
|
|
-
|
|
|
- // For now just assume one model and get the preRotations from that
|
|
|
+ // Assume one model and get the preRotations from that
|
|
|
+ // if there is more than one model associated with the geometry this may cause problems
|
|
|
var modelNode = modelNodes[ 0 ];
|
|
|
|
|
|
var transformData = {};
|
|
|
|
|
|
if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = modelNode.RotationOrder.value;
|
|
|
+ if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.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;
|
|
@@ -2496,18 +2537,42 @@ THREE.FBXLoader = ( function () {
|
|
|
|
|
|
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;
|
|
|
+
|
|
|
+ if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value );
|
|
|
+
|
|
|
+ // rotation order
|
|
|
if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = parseInt( modelNode.RotationOrder.value );
|
|
|
|
|
|
+ // translation
|
|
|
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;
|
|
|
+ // rotation
|
|
|
if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value;
|
|
|
-
|
|
|
+ if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value;
|
|
|
if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value;
|
|
|
|
|
|
+ // scaling
|
|
|
if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value;
|
|
|
|
|
|
+
|
|
|
+ // offset and pivot
|
|
|
+ if ( 'ScalingOffset' in modelNode ) transformData.scalingOffset = modelNode.ScalingOffset.value;
|
|
|
+ if ( 'ScalingPivot' in modelNode ) transformData.scalingPivot = modelNode.ScalingPivot.value;
|
|
|
+
|
|
|
+ if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value;
|
|
|
+ if ( 'RotationPivot' in modelNode ) transformData.rotationPivot = modelNode.RotationPivot.value;
|
|
|
+
|
|
|
return generateTransform( transformData );
|
|
|
|
|
|
},
|
|
@@ -2954,7 +3019,7 @@ THREE.FBXLoader = ( function () {
|
|
|
// if the subnode already exists, append it
|
|
|
if ( nodeName in currentNode ) {
|
|
|
|
|
|
- // special case Pose needs PoseNodes as an array
|
|
|
+ // special case Pose needs PoseNodes as an array
|
|
|
if ( nodeName === 'PoseNode' ) {
|
|
|
|
|
|
currentNode.PoseNode.push( node );
|
|
@@ -3860,47 +3925,48 @@ 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
|
|
|
+ // ref: http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=cpp_ref/_transformations_2main_8cxx-example.html,topicNumber=cpp_ref__transformations_2main_8cxx_example_htmlfc10a1e1-b18d-4e72-9dc0-70d0f1959f5e
|
|
|
function generateTransform( transformData ) {
|
|
|
|
|
|
- var transform = new THREE.Matrix4();
|
|
|
- translation.set( 0, 0, 0 );
|
|
|
- rotation.identity();
|
|
|
+ // console.log( 'transformData', transformData );
|
|
|
|
|
|
- var order = ( transformData.eulerOrder ) ? getEulerOrder( transformData.eulerOrder ) : getEulerOrder( 0 );
|
|
|
+ var lTranslationM = new THREE.Matrix4();
|
|
|
+ var lPreRotationM = new THREE.Matrix4();
|
|
|
+ var lRotationM = new THREE.Matrix4();
|
|
|
+ var lPostRotationM = new THREE.Matrix4();
|
|
|
+ var lTransform = new THREE.Matrix4();
|
|
|
|
|
|
- if ( transformData.translation ) translation.fromArray( transformData.translation );
|
|
|
- if ( transformData.rotationOffset ) translation.add( tempVec.fromArray( transformData.rotationOffset ) );
|
|
|
+ var lScalingM = new THREE.Matrix4();
|
|
|
+ var lScalingPivotM = new THREE.Matrix4();
|
|
|
+ var lScalingOffsetM = new THREE.Matrix4();
|
|
|
+ var lRotationOffsetM = new THREE.Matrix4();
|
|
|
+ var lRotationPivotM = new THREE.Matrix4();
|
|
|
|
|
|
- if ( transformData.rotation ) {
|
|
|
+ var lParentGX = new THREE.Matrix4();
|
|
|
+ var lGlobalT = new THREE.Matrix4();
|
|
|
+ var lGlobalRS = new THREE.Matrix4();
|
|
|
|
|
|
- var array = transformData.rotation.map( THREE.Math.degToRad );
|
|
|
- array.push( order );
|
|
|
- rotation.makeRotationFromEuler( tempEuler.fromArray( array ) );
|
|
|
+ var order = ( transformData.eulerOrder ) ? getEulerOrder( transformData.eulerOrder ) : getEulerOrder( 0 );
|
|
|
+ var inheritType = ( transformData.inheritType ) ? transformData.inheritType : 0;
|
|
|
|
|
|
- }
|
|
|
+ if ( transformData.translation ) lTranslationM.setPosition( tempVec.fromArray( transformData.translation ) );
|
|
|
|
|
|
if ( transformData.preRotation ) {
|
|
|
|
|
|
var array = transformData.preRotation.map( THREE.Math.degToRad );
|
|
|
array.push( order );
|
|
|
- tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) );
|
|
|
+ lPreRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- rotation.premultiply( tempMat );
|
|
|
+ if ( transformData.rotation ) {
|
|
|
+
|
|
|
+ var array = transformData.rotation.map( THREE.Math.degToRad );
|
|
|
+ array.push( order );
|
|
|
+ lRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -3908,20 +3974,69 @@ THREE.FBXLoader = ( function () {
|
|
|
|
|
|
var array = transformData.postRotation.map( THREE.Math.degToRad );
|
|
|
array.push( order );
|
|
|
- tempMat.makeRotationFromEuler( tempEuler.fromArray( array ) );
|
|
|
+ lPostRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) );
|
|
|
|
|
|
- tempMat.getInverse( tempMat );
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( transformData.scale ) lScalingM.scale( tempVec.fromArray( transformData.scale ) );
|
|
|
+
|
|
|
+ // Pivots and offsets
|
|
|
+ if ( transformData.scalingOffset ) lScalingOffsetM.setPosition( tempVec.fromArray( transformData.scalingOffset ) );
|
|
|
+ if ( transformData.scalingPivot ) lScalingPivotM.setPosition( tempVec.fromArray( transformData.scalingPivot ) );
|
|
|
+ if ( transformData.rotationOffset ) lRotationOffsetM.setPosition( tempVec.fromArray( transformData.rotationOffset ) );
|
|
|
+ if ( transformData.rotationPivot ) lRotationPivotM.setPosition( tempVec.fromArray( transformData.rotationPivot ) );
|
|
|
+
|
|
|
+ // parent transform
|
|
|
+ if ( transformData.parentMatrixWorld ) lParentGX = transformData.parentMatrixWorld;
|
|
|
+
|
|
|
+ // Global Rotation
|
|
|
+ var lLRM = lPreRotationM.multiply( lRotationM ).multiply( lPostRotationM );
|
|
|
+ var lParentGRM = new THREE.Matrix4();
|
|
|
+ lParentGX.extractRotation( lParentGRM );
|
|
|
+
|
|
|
+ // Global Shear*Scaling
|
|
|
+ var lLSM = new THREE.Matrix4();
|
|
|
+ var lParentGSM = new THREE.Matrix4();
|
|
|
+ var lParentGRSM = new THREE.Matrix4();
|
|
|
+ var lParentTM = new THREE.Matrix4();
|
|
|
+
|
|
|
+ lParentTM.copyPosition( lParentGX );
|
|
|
+ lParentGRSM = lParentTM.getInverse( lParentTM ).multiply( lParentGX );
|
|
|
+ lParentGSM = lParentGRM.getInverse( lParentGRM ).multiply( lParentGRSM );
|
|
|
+ lLSM = lScalingM;
|
|
|
+
|
|
|
+ if ( inheritType === 0 ) {
|
|
|
+
|
|
|
+ lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM ).multiply( lLSM );
|
|
|
|
|
|
- rotation.multiply( tempMat );
|
|
|
+ } else if ( inheritType === 1 ) {
|
|
|
+
|
|
|
+ lGlobalRS = lParentGRM.multiply( lParentGSM ).multiply( lLRM ).multiply( lLSM );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ var lParentLSM = new THREE.Matrix4().copy( lScalingM );
|
|
|
+
|
|
|
+ var lParentGSM_noLocal = lParentGSM.multiply( lParentLSM.getInverse( lParentLSM ) );
|
|
|
+
|
|
|
+ lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( transformData.scale ) transform.scale( tempVec.fromArray( transformData.scale ) );
|
|
|
|
|
|
- transform.setPosition( translation );
|
|
|
- transform.multiply( rotation );
|
|
|
+ // Calculate the local transform matrix
|
|
|
+ lTransform = lTranslationM.multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM.getInverse( lRotationPivotM ) ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM.getInverse( lScalingPivotM ) );
|
|
|
+
|
|
|
+ var lLocalTWithAllPivotAndOffsetInfo = new THREE.Matrix4().copyPosition( lTransform );
|
|
|
+
|
|
|
+ var lGlobalTranslation = lParentGX.multiply( lLocalTWithAllPivotAndOffsetInfo );
|
|
|
+ lGlobalT.copyPosition( lGlobalTranslation );
|
|
|
+
|
|
|
+ lTransform = lGlobalT.multiply( lGlobalRS );
|
|
|
+
|
|
|
+ return lTransform;
|
|
|
|
|
|
- return transform;
|
|
|
+ // = new THREE.Matrix4();
|
|
|
|
|
|
}
|
|
|
|
|
@@ -3936,7 +4051,7 @@ THREE.FBXLoader = ( function () {
|
|
|
'ZXY', // -> YXZ extrinsic
|
|
|
'YXZ', // -> ZXY extrinsic
|
|
|
'XYZ', // -> ZYX extrinsic
|
|
|
- //'SphericXYZ', // not possible to support
|
|
|
+ //'SphericXYZ', // not possible to support
|
|
|
];
|
|
|
|
|
|
if ( order === 6 ) {
|