Look at behavior + Z.Vec3 and Z.Mat documentation

Hey,

Any possibility of providing some docs (or hints) for functions in Z.Vec3 and Z.Mat? I’m especially interested in Z.Mat.createRotationMatrix() and Z.Mat.anglesFromRotationMatrix().

I’m trying to build up a ‘look at’ function for rotating objects towards a specified point. I’ve made a heading vector from the objects current Euler angles and another one pointing towards my target point. If I’m not entirely mistaken, I can use atan2 to get the required rotation angle and define the axis to rotate around with a cross product. My problem lies in applying this angle and rotation-axis to the Euler angles of my zappar object.

I was wondering if the Z.Mat functions could help me with this issue. Math certainly isn’t my strong suit but I still feel like I might be fairly close with this. Any help would be appreciated!

Hey Ero,

Good catch, look like we missed them when documenting.

While we work on doing so the platform team provided some descriptions below which will hopefully help:

// reference: http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToMatrix/index.htm
matrixFromAxisAngle(axis, angle) {
	var c = Math.cos(angle);
    var s = Math.sin(angle);
    var t = 1.0 - c;

    var m00 = c + axis[0]*axis[0]*t;
    var m11 = c + axis[1]*axis[1]*t;
    var m22 = c + axis[2]*axis[2]*t;


    var tmp1 = axis[0]*axis[1]*t;
    var tmp2 = axis[2]*s;
    var m10 = tmp1 + tmp2;
    var m01 = tmp1 - tmp2;
    tmp1 = axis[0]*axis[2]*t;
    tmp2 = axis[1]*s;
    var m20 = tmp1 - tmp2;
    var m02 = tmp1 + tmp2;    
    tmp1 = axis[1]*axis[2]*t;
    tmp2 = axis[0]*s;
    var m21 = tmp1 + tmp2;
    var m12 = tmp1 - tmp2;

    return [m00, m01, m02,
            m10, m11, m12,
            m20, m21, m22];
}

normalize(vec) {
	return Z.Vec3.scalarMultiply(forwardVec, 1.0 / Z.Vec3.magnitude(forwardVec));
}

cross(vecA, vecB) {
	var x = vecA[1] * vecB[2] - vecB[1] * vecA[2];
	var y = vecA[2] * vecB[0] - vecB[2] * vecA[0];
	var z = vecA[0] * vecB[1] - vecB[0] * vecA[1];

	return [x, y, z]
}

lookAt(forwardVec, targetVec) {
	// normalize
	var forward = normalize(forwardVec);
	var target = normalize(targetVec);

	// compute the rotation axis
	var axis = cross(forward, target);

	// compute the angle
	var cosTheta = Z.Vec3.dot(forward, target);
	var angle = Math.acos(cosTheta);

	// compute the rotation matrix
	var rotM = matrixFromAxisAngle(axis, angle);

	// get the Euler angels
	return Z.Mat.anglesFromRotationMatrix(rotM);
}

// let's say, the object's position is objPos with the defined forward vector as objForward, and the look at target's postion is targetPos, then call
var angles = lookAt(objForward, Z.Vec3.subtract(targetPos, objPos));

Cheers,
Mark

Hey Mark,

Thank you! That helps a lot.

I managed to implement this behavior with simpler math, but with separate transform groups for pitch and yaw. I think what you posted makes things cleaner though so I’ll probably switch over to that.

I used this behavior to implement hacky line drawing between points and it’s working quite well. Probably not the best performance-wise, but it works for now. Now this made me think of more feature requests. :wink:

Thanks again!

This function blows up and freezes the whole app.

Thanks for the report, it does indeed look to be broken (the implementation tries to reference Z which is actually a closure variable only available in script nodes - it’s possible this wasn’t the case when the code was written and no one has noticed before, our set of regression tests don’t exercise this function).

The simplest approach is just to avoid Z.Mat.anglesFromRotationMatrix() and just include the function locally instead:

function anglesFromRotationMatrix(mat, compare?) {
    if (!compare) { compare = [0,0,0]; };

    var x1 = 0;
    var y1 = 0;
    var z1 = 0;
    var x2 = 0;
    var y2 = 0;
    var z2 = 0;

    var m00 = mat[0];
    var m01 = mat[1];
    var m02 = mat[2];
    var m12 = mat[5];
    var m22 = mat[8];

    var m10 = mat[3];
    var m20 = mat[6];

    if (Math.abs(m02) < 0.99999 ) {
        y1 = Math.asin(m02);
        y2 = Math.PI - y1;

        x1 = Math.atan2( -m12 / Math.cos(y1), m22 / Math.cos(y1) );
        x2 = Math.atan2( -m12 / Math.cos(y2), m22 / Math.cos(y2) );

        z1 = Math.atan2( -m01 / Math.cos(y1), m00 / Math.cos(y1) );
        z2 = Math.atan2( -m01 / Math.cos(y2), m00 / Math.cos(y2) );
    } else if (m02<0) {
        // m10 =  cx * sz - sx * cz = sin(z - x);
        // m20 =  sx * sz + cx * cz = cos(z - x);
        y1 = y2 = -Math.PI / 2;
        x1 = x2 = compare[0];
        z1 = z2 = x1 + Math.atan2(m10, m20);
    } else {
        // m10 =  cx * sz + sx * cz = sin(x + z);
        // m20 =  sx * sz - cx * cz = -cos(x + z);
        y1 = y2 = Math.PI / 2;
        x1 = x2 = compare[0];
        z1 = z2 = Math.atan2(m10, -m20) - x1;
    }

    var v1 = [x1, y1, z1];
    var v2 = [x2, y2, z2];

    v1[0] += (2 * Math.PI) * Math.round((compare[0] - v1[0]) / (2 * Math.PI));
    v1[1] += (2 * Math.PI) * Math.round((compare[1] - v1[1]) / (2 * Math.PI));
    v1[2] += (2 * Math.PI) * Math.round((compare[2] - v1[2]) / (2 * Math.PI));

    v2[0] += (2 * Math.PI) * Math.round((compare[0] - v2[0]) / (2 * Math.PI));
    v2[1] += (2 * Math.PI) * Math.round((compare[1] - v2[1]) / (2 * Math.PI));
    v2[2] += (2 * Math.PI) * Math.round((compare[2] - v2[2]) / (2 * Math.PI));

    var distV1 = Z.Vec3.magnitude( Z.Vec3.subtract(v1, compare) );
    var distV2 = Z.Vec3.magnitude( Z.Vec3.subtract(v2, compare) );

    if (distV1 > distV2) {
        return v2;
    }
    return v1;
}

I think the idea of “compare” is you can pass in the current rotation angles and it should give you the minimum rotation, so if you’re already at [0, 0, 350] and the result needs to be [0, 0, 10] it will give you [0, 0, 370] instead.

Leaving this here for anyone who needs to do a “look at” within a scene. For Instant Tracking / World Tracking placement though there is an easier way - will reply to the other thread on that.