Closed vorg closed 8 months ago
I thought lookAt does also translation? What does it mean for a quaternion? Just the rotation part? Is it different from fromTo
?
Usually you specify a target point and it gives the quaternion rotation to face that point
@simonharrisco any suggestions on the name? What other engines are using? (unity, three, babylonjs)
three, unity and babylon all use lookAt.
however when you use lookAt in three and unity it rotates the object; it doesnt return the quat required to do the rotation.
http://glmatrix.net/docs/module-mat4.html
Search for lookAt
and targetTo
Historical reasons: https://github.com/toji/gl-matrix/issues/225
This issue is mostly for pex-renderer where we have to orient things (directional lights, camera, meshes themselves). So right now i see 3 options:
var position = [2, 2, 2]
var target = [0, 0, 0]
directionalLightEntity.transform.set({ position: position })
//1. currently
var lightDir = vec3.normalize(vec3.sub(vec3.copy(target), position))
var rotation = quat.fromTo(q.create(), [0, 0, 1], lightDir)
directionalLightEntity.transform.set({ rotation: rotation })
//2. quat.lookAt
var rotation = quat.lookAt(q.create(), position, target)
directionalLightEntity.transform.set({ rotation: rotation })
//3. transform.lookAt
directionalLightEntity.transform.lookAt(target)
2 feels more consistent with the rest of pex math
I have a quaternion function that does a LookAt type thing that I ported from a C# example on pastebin. Original Example : https://pastebin.com/ubATCxJY
` static lookRotation(vDir, vUp, out = null){ var zAxis = new Vec3(vDir), //Forward up = new Vec3(vUp), xAxis = new Vec3(), //Right yAxis = new Vec3();
zAxis.normalize();
Vec3.cross(up,zAxis,xAxis);
xAxis.normalize();
Vec3.cross(zAxis,xAxis,yAxis); //new up
//fromAxis - Mat3 to Quaternion
var m00 = xAxis.x, m01 = xAxis.y, m02 = xAxis.z,
m10 = yAxis.x, m11 = yAxis.y, m12 = yAxis.z,
m20 = zAxis.x, m21 = zAxis.y, m22 = zAxis.z,
t = m00 + m11 + m22,
x, y, z, w, s;
if(t > 0.0){
s = Math.sqrt(t + 1.0);
w = s * 0.5 ; // |w| >= 0.5
s = 0.5 / s;
x = (m12 - m21) * s;
y = (m20 - m02) * s;
z = (m01 - m10) * s;
}else if((m00 >= m11) && (m00 >= m22)){
s = Math.sqrt(1.0 + m00 - m11 - m22);
x = 0.5 * s;// |x| >= 0.5
s = 0.5 / s;
y = (m01 + m10) * s;
z = (m02 + m20) * s;
w = (m12 - m21) * s;
}else if(m11 > m22){
s = Math.sqrt(1.0 + m11 - m00 - m22);
y = 0.5 * s; // |y| >= 0.5
s = 0.5 / s;
x = (m10 + m01) * s;
z = (m21 + m12) * s;
w = (m20 - m02) * s;
}else{
s = Math.sqrt(1.0 + m22 - m00 - m11);
z = 0.5 * s; // |z| >= 0.5
s = 0.5 / s;
x = (m20 + m02) * s;
y = (m21 + m12) * s;
w = (m01 - m10) * s;
}
out = out || new Quaternion();
out[0] = x;
out[1] = y;
out[2] = z;
out[3] = w;
return out;
}`
That's an interesting point from @dmnsgn. TLDR: in glMatrix
It means that transform.lookAt
makes sense if we interpret it as three.js does "Rotates the object to face a point in world space." https://threejs.org/docs/index.html#api/en/core/Object3D.lookAt
But quat.lookAt
is misleading and would be better to be named quat.targetTo
like glMatrix http://glmatrix.net/docs/mat4.js.html#line1484. The documentation is missing the definition of where front is though? "Generates a matrix that makes something look at something else.". I would assume it's -Z in OpenGL coordinate system.
if we assume something's "face" is on Z+ then targetTo
can be implemented as
quat.targetTo = (q, position, target) => {
quat.fromTo(q, [0, 0, 1], vec3.normalize(vec3.sub([...target], position)))
}
@dmnsgn bump! I'm using above ^^ function in pex-renderer examples and lib-renderer directional light node already
FromToRotation(Vector3 fromDirection, Vector3 toDirection);
: Creates a rotation which rotates from fromDirection to toDirectionRotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta);
: Rotates a rotation from towards togl-matrix quat.rotationTo === pex-math quat.fromTo
gl-matrix has mat4.targetTo(out, eye, target, up)
: Generates a matrix that makes something look at something else.
But we're trying to avoid using a matrix
So the best option would be
mat4.targetTo = (out, eye, target, up = [0, 1, 0]) => {
let eyex = eye[0];
let eyey = eye[1];
let eyez = eye[2];
let upx = up[0];
let upy = up[1];
let upz = up[2];
let z0 = eyex - target[0];
let z1 = eyey - target[1];
let z2 = eyez - target[2];
let len = z0 * z0 + z1 * z1 + z2 * z2;
if (len > 0) {
len = 1 / Math.sqrt(len);
z0 *= len;
z1 *= len;
z2 *= len;
}
let x0 = upy * z2 - upz * z1;
let x1 = upz * z0 - upx * z2;
let x2 = upx * z1 - upy * z0;
len = x0 * x0 + x1 * x1 + x2 * x2;
if (len > 0) {
len = 1 / Math.sqrt(len);
x0 *= len;
x1 *= len;
x2 *= len;
}
out[0] = x0;
out[1] = x1;
out[2] = x2;
out[3] = 0;
out[4] = z1 * x2 - z2 * x1;
out[5] = z2 * x0 - z0 * x2;
out[6] = z0 * x1 - z1 * x0;
out[7] = 0;
out[8] = z0;
out[9] = z1;
out[10] = z2;
out[11] = 0;
out[12] = eyex;
out[13] = eyey;
out[14] = eyez;
out[15] = 1;
return out;
};
and then
const mat4tmp = mat4.create();
const quat.targetTo = function (q, eye, target, up=[0, 1, 0]) {
return quat.fromMat4(q, mat4TargetTo(mat4tmp, eye, target, upTmp));
};
Added in v4.0.0-alpha.3.
So this version of quat.targetTo
basically does mat4.looktAt
but without translation to eye position. While still useful for orienting camera at a given position it's not suitable at rotating objects to face towards something as the opposite rotation is needed. Since we moved to using rotation for directional lights I would love to use the same function as for rotating objects. That assumes that directional light is facing Z+ by default. Which is opposite to e.g. ThreeJS where they use the same lookAt
for both cameras and lights. From my understanding Unity's lights is also facing Z+.
quat.fromTo
is insufficient as it's missing 'up' vector.
ThreeJS handles the naming problem by making it function of the object and reversing eye/target for meshes.
BabylonJS has
public static LookDirectionRHToRef<T extends Matrix>(
forward: DeepImmutable<Vector3>,
up: DeepImmutable<Vector3>,
result: T): T {
const right = MathTmp.Vector3[2];
Vector3.CrossToRef(up, forward, right);
// Generate the rotation matrix.
Matrix.FromValuesToRef(
right._x, right._y, right._z, 0.0,
up._x, up._y, up._z, 0.0,
forward._x, forward._y, forward._z,
0.0, 0, 0, 0, 1.0,
result);
return result;
};
Which translates to
const tempVec1 = vec3.create();
const tempVec2 = vec3.create();
const tempMat41 = mat4.create();
function quatFromPointToPointFacingZPos(q, eye, target, up) {
const forward = tempVec1;
const right = tempVec2;
vec3.normalize(vec3.sub(vec3.set(forward, target), eye));
vec3.cross(vec3.set(right, up), forward);
const m = tempMat41;
m[0] = right[0];
m[1] = right[1];
m[2] = right[2];
m[3] = 0.0;
m[4] = up[0];
m[5] = up[1];
m[6] = up[2];
m[7] = 0.0;
m[8] = forward[0];
m[9] = forward[1];
m[10] = forward[2];
m[11] = 0.0;
m[12] = 0.0;
m[13] = 0.0;
m[14] = 0.0;
m[15] = 1.0;
return quat.fromMat4(q, m);
}
And optimised and cleaned up for readability
function quatFromPointToPointFacingZPos(q, eye, target, up) {
let forwardX = target[0] - eye[0];
let forwardY = target[1] - eye[1];
let forwardZ = target[2] - eye[2];
let upX = up[0];
let upY = up[1];
let upZ = up[2];
let len = forwardX * forwardX + forwardY * forwardY + forwardZ * forwardZ;
if (len > 0) {
len = 1 / Math.sqrt(len);
forwardX *= len;
forwardY *= len;
forwardZ *= len;
}
let rightX = upY * forwardZ - upZ * forwardY;
let rightY = upZ * forwardX - upX * forwardZ;
let rightZ = upX * forwardY - upY * forwardX;
const m = TEMP_MAT4;
m[0] = rightX;
m[1] = rightY;
m[2] = rightZ;
m[3] = 0.0;
m[4] = upX;
m[5] = upY;
m[6] = upZ;
m[7] = 0.0;
m[8] = forwardX;
m[9] = forwardY;
m[10] = forwardZ;
m[11] = 0.0;
m[12] = 0.0;
m[13] = 0.0;
m[14] = 0.0;
m[15] = 1.0;
return quat.fromMat4(q, m);
}
So how should we name this? fromPointToPointFacingZPos is a bit of a mouthful. Is mat/quat.lookDirection not descriptive enough? Do we need to explicit that the rotation is Z forward and that Z is following Right Handed system.
Also Unity has Quaternion.LookRotation
The point of targetTo is hassle free call where i just give two points (position, target) and the subtraction, normalisation, conversion happens inside the library. quat.fromPointToPoint
is good enough for me.
quat.lookDirection is setting new quaternion from a right, up, forward coordinate system. I would love to have it separately mat4/quat.lookDirection for making quaterions from TBN (Tangent, Bitangent, Normal or Right Up Forward) vector triples which i found useful few times this week. We could then remove quat.fromTo
or add missing up vector param.
Btw one thing to watch up from quat.lookDirection is that calling it lazy "lazy up" quat.lookDirection(AminusB, [0, 1, 0]
might generate invalid rotations as basis vectors require to be orthonormal to each other. I had such bug in lib-renderer/DirectionallLight. Of course we could always double checking by calculating right vector internally and then recomputing up again but that's hidden cost although i don't wanna do that in the userspace either... (in which case i should probably use fromPointToPoint anyway.
This is too useful to not have it. Especially with the new pex-renderer where everything depends on transform (lights, camera, geometries) and it's hard to rotate meshes with Euler angles.