Browse Source

FBXLoader: Add skinning fixes. (#33316)

Michael Herzog 2 weeks ago
parent
commit
13a06fc663

+ 40 - 5
examples/jsm/loaders/FBXLoader.js

@@ -824,8 +824,6 @@ class FBXTreeParser {
 				indices: [],
 				weights: [],
 				transformLink: new Matrix4().fromArray( boneNode.TransformLink.a ),
-				// transform: new Matrix4().fromArray( boneNode.Transform.a ),
-				// linkMode: boneNode.Mode,
 
 			};
 
@@ -918,8 +916,6 @@ class FBXTreeParser {
 
 		} );
 
-		this.bindSkeleton( deformers.skeletons, geometryMap, modelMap );
-
 		this.addGlobalSceneSettings();
 
 		sceneGraph.traverse( function ( node ) {
@@ -942,6 +938,13 @@ class FBXTreeParser {
 
 		} );
 
+		// Bind skeletons after transforms are applied so that bind matrices
+		// are computed from the final scene state. This ensures the rest pose
+		// is correct even when the FBX file's Cluster TransformLink matrices
+		// differ from the reconstructed bone transforms (common in files
+		// without a BindPose section).
+		this.bindSkeleton( deformers.skeletons, geometryMap, modelMap );
+
 		const animations = new AnimationParser().parse();
 
 		// if all the models where already combined in a single group, just return that
@@ -1466,6 +1469,26 @@ class FBXTreeParser {
 
 			const skeleton = skeletons[ ID ];
 
+			// Compute bone inverses from TransformLink rather than from the
+			// bones' current matrixWorld. The TransformLink matrices represent
+			// each bone's global transform at the time the skin weights were
+			// painted, which may differ from the scene-reconstructed transforms.
+			const boneInverses = [];
+
+			for ( let i = 0, l = skeleton.bones.length; i < l; i ++ ) {
+
+				const inverse = new Matrix4();
+
+				if ( skeleton.bones[ i ] && skeleton.rawBones[ i ] ) {
+
+					inverse.copy( skeleton.rawBones[ i ].transformLink ).invert();
+
+				}
+
+				boneInverses.push( inverse );
+
+			}
+
 			const parents = connections.get( parseInt( skeleton.ID ) ).parents;
 
 			parents.forEach( function ( parent ) {
@@ -1481,7 +1504,19 @@ class FBXTreeParser {
 
 							const model = modelMap.get( geoConnParent.ID );
 
-							model.bind( new Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] );
+							// Always provide a bind matrix to prevent bind() from
+							// calling calculateInverses() which would overwrite the
+							// TransformLink-based bone inverses computed above.
+							let bindMatrix = bindMatrices[ geoConnParent.ID ];
+
+							if ( bindMatrix === undefined ) {
+
+								model.updateMatrixWorld( true );
+								bindMatrix = model.matrixWorld;
+
+							}
+
+							model.bind( new Skeleton( skeleton.bones, boneInverses ), bindMatrix );
 
 						}
 

BIN
examples/models/fbx/archer/ArcherRi01.FBX


BIN
examples/models/fbx/archer/ArcherRi01.png


BIN
examples/models/fbx/archer/ArcherRi01_W.PNG


BIN
examples/models/fbx/warrior/100820_kl_npc_d_512.png


BIN
examples/models/fbx/warrior/Warrior.fbx


+ 11 - 0
examples/webgl_loader_fbx.html

@@ -50,8 +50,16 @@
 				'monkey',
 				'monkey_embedded_texture',
 				'vCube',
+				'archer/ArcherRi01',
+				'warrior/Warrior',
+				'stanford-bunny',
+				'mixamo',
 			];
 
+			const scales = new Map();
+			scales.set( 'warrior/Warrior', 100 );
+			scales.set( 'archer/ArcherRi01', 100 );
+			scales.set( 'stanford-bunny', 0.001 );
 
 			init();
 
@@ -162,6 +170,9 @@
 
 					object = group;
 
+					const scale = scales.get( asset );
+					object.scale.setScalar( scale || 1 );
+
 					if ( object.animations && object.animations.length ) {
 
 						mixer = new THREE.AnimationMixer( object );

粤ICP备19079148号