I am encountering issues with calculating the orientation of a bone to "look at" an object. The lookAt function is not functioning as expected for me. This could be due to the fact that the bone's matrix is an identity matrix in local space, so the default behavior does not align correctly (resulting in strange outcomes).
So far, I have managed to rotate the head from left to right, but I have not yet figured out the calculation for up and down movement. Unfortunately, the current implementation is not working well at the moment :(
// Define local forward vector
var v1 = new THREE.Vector3(0, 0, 1);
// Vector representing the camera in local space
var v2 = new THREE.Vector3(
camera.position.x - headBone.matrixWorld.elements[12],
0,
camera.position.z - headBone.matrixWorld.elements[14]
);
v2.normalize();
// Determine whether to rotate left or right
var mult = 1.0;
if(camera.position.x - headBone.matrixWorld.elements[12] < 0.0000) {
mult = -1.0;
}
var fAng = v1.angleTo(v2) * mult;
// Rotate the head bone left or right
headBone.quaternion.setFromAxisAngle(new THREE.Vector3(0,0,-1), fAng);
I am open to any suggestions on how to achieve this calculation correctly since I am currently struggling with the math. In the meantime, I will continue troubleshooting...
If it helps, here is the worldMatrix of the headBone in its original position:
0.9950730800628662, -0.02474924363195896, 0.09600517153739929, 0
-0.09914379566907883, -0.2472541183233261, 0.9638656973838806, 0
-0.00011727288801921532, -0.9686352014541626, -0.24848966300487518, 0
0.5000047087669373, 1.5461946725845337, -0.01913299970328808, 1
UPDATE
After updating my code, I have made progress in rotating the head bone on different axes (x and z) to align with the camera. However, when I try to combine these rotations, the orientation goes haywire especially when at the sides of the character, causing the head to spin uncontrollably.
function lookAt() {
var v1 = new THREE.Vector3(0, 0, 1);
var v2 = new THREE.Vector3(
camera.position.x - headBone.matrixWorld.elements[12],
0,
camera.position.z - headBone.matrixWorld.elements[14]
).normalize();
var mult = 1.0;
if(camera.position.x - headBone.matrixWorld.elements[12] < 0.0000) {
mult = -1.0;
}
var fAng = v2.angleTo(v1) * mult;
v2 = new THREE.Vector3(
0,
camera.position.y - headBone.matrixWorld.elements[13],
camera.position.z - headBone.matrixWorld.elements[14]
);
mult = 1.0;
if(camera.position.y - headBone.matrixWorld.elements[13] > 0.0000) {
mult = -1.0;
}
fAng2 = v2.angleTo(v1) * mult;
headBone.rotation.set(
Math.max(-0.5, Math.min(fAng2, 0.1)),
0,
-Math.max(-1.0, Math.min(fAng, 1.0))
);
}
I have attempted to use separate quaternions for individual rotations:
// Rotate head left and right to match camera
var Q1 = new THREE.Quaternion().setFromEuler( new THREE.Euler(0,0,-fAng), false );
// Rotate head up and down to match camera
var Q2 = new THREE.Quaternion().setFromEuler( new THREE.Euler(fAng2,0,0), false );
Q2.multiply(Q1);
headBone.quaternion.copy(Q2);
However, this solution still does not accurately track the camera. It is worth noting that applying either rotation individually works fine.