Reputation: 3135
I am having problems doing the calculation for a bone to "look at" an object. First off, the lookAt function is not working for me. I can kind of understand this because the bone's matrix is an identity matrix, in local space so it wont work out of the box. (doing lookAt produces strange results).
Here is what I have managed to far. It rotates the head left to right, but up and down I haven't calculated yet. I should add, it doesn't work very well at the moment :(
//local looking forawrd vector
var v1 = new THREE.Vector3(0, 0, 1);
//vector represting 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();
//seem to need this to 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;
//var headUD = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), 0.0);
headBone.quaternion.setFromAxisAngle(new THREE.Vector3(0,0,-1), fAng);
//headBone.quaternion.multiply(headUD);
Suggestions on how to do this properly would be GREATLY appeciated as I can't figure out the math at the moment. In the mean time I will continue to do what feels like hacking away...
If it helps, below is the worldMatrix of the headBone at rest:
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
I have updated my code. It does work when I rotate on 1 axis (x axis to move head up and down towards camera or z axis to move it left or right towards camera) but when I attempt to combine those 2 rotations things go haywire when I am at the sides of the character the head starts to spin.
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 can separate out the quaternions like so:
//head left and right to match camera
var Q1 = new THREE.Quaternion().setFromEuler( new THREE.Euler(0,0,-fAng), false );
//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);
But this still does not track the camera properly. Note that applying one or the other does work.
Upvotes: 0
Views: 1075
Reputation: 3135
I have reached an acceptable answer. I found a way to make the bone look at an arbitrary point. Perhaps this could be expanded on and included in THREE.js.
This still suffers from problems and does not seem 100% accurate. For example I placed this on my head bone, and it goes upside down when I am pointing behind the character. Does anyone know how it can be improved? Please comment!
Also note that I am pointing the bone's Z axis THREE.Vector3(0,0,1);
. It could be moved to a parameter.
function boneLookAt(bone, position) {
var target = new THREE.Vector3(
position.x - bone.matrixWorld.elements[12],
position.y - bone.matrixWorld.elements[13],
position.z - bone.matrixWorld.elements[14]
).normalize();
var v = new THREE.Vector3(0,0,1);
var q = new THREE.Quaternion().setFromUnitVectors( v, target );
var tmp = q.z;
q.z = -q.y;
q.y = tmp;
bone.quaternion.copy(q);
}
Upvotes: 3