Closed mmmovania closed 2 months ago
Some more updates: I notice that there is issue is also there in vegetta model. So it seems that my code is wrong in some part.
function align_dir( Lc, Rc, arm, up )
{
let wp = new THREE.Quaternion(), // World Parent
wc = new THREE.Quaternion(), // World Child
lc = new THREE.Quaternion(), // Local Child
lft = new THREE.Vector3(),
fwd = new THREE.Vector3(),
bb = arm.bones,
i = Lc[0],
d, b;
//App.node.getWorldRot( bb[ i ], wp, false ); // Get Chain's parent world space rotation
bb[i].getWorldQuaternion( wp );
for( i=0; i < Lc.length; i++ )
{
b = bb[ Lc[i] ]; // Get Bone we're working on
//wc.from_mul( wp, b.local.rot ); // Get Child's WS Rotation
//wc.multiplyQuaternions(wp, b.quaternion );
b.getWorldQuaternion( wc )
// Define new rot axis with up pointing in the direction
// we need it to point While trying to keep the bone's
// forward direction in the same general direction
//fwd.copy( Vec3.FORWARD ).transform_quat( wc ); // Bone's Forward Direction
fwd.copy(Vec3_FORWARD).applyQuaternion(wc);
//lft.from_cross( up, fwd ).normalize(); // Calc Left Direction
lft.crossVectors(up, fwd).normalize();
//fwd.from_cross( lft, up ).normalize(); // Realign Forward
fwd.crossVectors(lft, up).normalize();
//wc.from_axis( lft, up, fwd ); // Final Aligned World Space Rotation
wc = from_axis(lft, up, fwd);
//lc.from_mul( wp.invert(), wc ); // Convert to bone's Local Space
lc.multiplyQuaternions(wp.invert(), wc);
//d = Quat.dot( lc, b.local.rot ); // Check for inverted Axis that can cause issues.
d = lc.dot(b.quaternion);
//if( d < 0 ) lc.negate();
if( d < 0 )
lc = negate(lc);
//b.setRot( lc ); // Save Local to Bone
b.quaternion.copy(lc);
//bb[ Rc[i] ].Node.setRot( lc.mirror_x() ); // Mirror and save to other bone
bb[ Rc[i] ].quaternion.copy(mirror_x( lc ) );
//wp.copy( wc ); // World child becomes World Parent for next bone.
wp.copy( wc.clone() );
}
bb[i].getWorldQuaternion( wp );
console.log(wp);
}
So it seems something is wrong in my code.
OK finally I got it. The issue was the following call before the loop
bb[i].getWorldQuaternion( wp );
This is supposed to return the rotation of the first bone passed to align_dir function in world space. But compaing the results with the original code, this produces wrong output. So then I manually wrote my own function getWorldRot based on the framework of this tutorial and it produces correct output. Here are the final functions and outputs. It works fine for all the models I tested this on.
function getWorldRot( e, out = null, incChild=true ){
out = out || new THREE.Quaternion();
if( !e.parent )
return out.copy( e.quaternion );
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get the heirarchy nodes
let n = e,
tree = [ ];
if( incChild ) tree.push( n ); // Incase we do not what to add the requested entity to the world transform.
while( n.parent != null ){
tree.push( n.parent );
n = n.parent;
}
// Nothing
if( tree.length == 0 ) return out.reset();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let i = tree.length - 1;
out.copy( tree[ i ].quaternion ); // Copy in the Root Parent
for( i--; i > -1; i-- )
out.multiply( tree[ i ].quaternion ); // Add Up All Transforms from root to child.
return out;
}
function align_dir( Lc, Rc, skeleton, up )
{
let wp = new THREE.Quaternion(), // World Parent
wc = new THREE.Quaternion(), // World Child
lc = new THREE.Quaternion(), // Local Child
lft = new THREE.Vector3(),
fwd = new THREE.Vector3(),
bb = skeleton.bones,
i = Lc[0],
d, b;
//App.node.getWorldRot( bb[ i ], wp, false ); // Get Chain's parent world space rotation
getWorldRot(bb[i], wp, false);
for( i=0; i < Lc.length; i++ )
{
b = bb[ Lc[i] ]; // Get Bone we're working on
//wc.from_mul( wp, b.local.rot ); // Get Child's WS Rotation
wc.multiplyQuaternions(wp, b.quaternion );
// Define new rot axis with up pointing in the direction
// we need it to point While trying to keep the bone's
// forward direction in the same general direction
//fwd.copy( Vec3.FORWARD ).transform_quat( wc ); // Bone's Forward Direction
fwd.copy(Vec3_FORWARD).applyQuaternion(wc );
//lft.from_cross( up, fwd ).normalize(); // Calc Left Direction
lft.crossVectors(up, fwd).normalize();
//fwd.from_cross( lft, up ).normalize(); // Realign Forward
fwd.crossVectors(lft, up).normalize();
//wc.from_axis( lft, up, fwd ); // Final Aligned World Space Rotation
wc = from_axis(lft, up, fwd);
//lc.from_mul( wp.invert(), wc ); // Convert to bone's Local Space
lc.multiplyQuaternions(wp.invert(), wc );
//d = Quat.dot( lc, b.local.rot ); // Check for inverted Axis that can cause issues.
d = lc.dot(b.quaternion);
//if( d < 0 ) lc.negate();
if( d < 0 )
lc = negate(lc);
//b.setRot( lc ); // Save Local to Bone
b.quaternion.copy(lc);
//bb[ Rc[i] ].Node.setRot( lc.mirror_x() ); // Mirror and save to other bone
bb[ Rc[i] ].quaternion.copy(mirror_x( lc ) );
wp.copy( wc ); // World child becomes World Parent for next bone.
}
//arm.isModified = true;
}
First, I would like to thank you for sharing your immense knowledge in the area of skeleton retargeting. I am trying to do it it Three.js for my own mixamo model that i just downloaded. The model is in A-pose with the left limb X local axes flipped to back as shown below.![image](https://github.com/sketchpunk/FunWithWebGL2/assets/1354859/30f5a173-6a17-491b-98b5-91ea57bd739f)
When I try to apply your align_dir function, it gives the following result where the arms are slighly higher. The same goes for the legs and my avatar looks more in a ballet dancer pose :P.![image](https://github.com/sketchpunk/FunWithWebGL2/assets/1354859/92a498c2-82f1-4a6a-a974-ce27fe8ec973)
Please share if you know how to fix this issue?