User:Pedro Oval/Calculate rotation for pointing in a direction

From Second Life Wiki
Jump to: navigation, search

Here are two functions that, given a vector to point at, return the rotation that corresponds to that vector.

One of the functions, PointAt2Rot, makes the forward (X) vector point exactly in that direction, while keeping the up (Z) vector pointing "as up as possible" and the left (Y) vector completely horizontal. It is similar to what llLookAt does, except that the vector that points to the target is X (forward) instead of Z (up), and the result is a rotation, not a continuously updating state of pointing in the given direction. A similar result can be obtained by combining these functions with llRotLookAt.

The other function, PointAtHoriz2Rot, points to the horizontal that is closest to the given vector, while the up vector points exactly up. It is useful if the final resulting rotation must be horizontal.

// Written by Pedro Oval, 2011-01-11
 
rotation PointAt2Rot(vector target)
{
    vector left = llVecNorm(<0,0,1> % target);
    target = llVecNorm(target);
    return llAxes2Rot(target, left, target % left);
}
 
rotation PointAtHoriz2Rot(vector target)
{
    // Find the Z angle and halve it for the quaternion:
    target.z = llAtan2(target.y, target.x) * 0.5;
    // Make a quaternion out of it on z:
    return <0, 0, llSin(target.z), llCos(target.z)>;
}

The following are versions of PointAt2Rot and PointAtHoriz2Rot that allow a viewup vector (the vector that determines the tilting) expressed in the same coordinate system as the target vector:

// Written by Pedro Oval, 2011-01-08
 
rotation PointAtViewup2Rot(vector target, vector viewup)
{
    // Reuse the variables to avoid declaring locals, which is expensive
    viewup = llVecNorm(viewup % target);
    target = llVecNorm(target);
    return llAxes2Rot(target, viewup, target % viewup);
}
 
rotation PointAtHorizViewup2Rot(vector target, vector viewup)
{
    // Reuse the variables to avoid declaring locals, which is expensive
    target = llVecNorm(viewup % target);
    viewup = llVecNorm(viewup);
    return llAxes2Rot(target % viewup, target, viewup);
}

When these two functions are passed a viewup of <0, 0, 1>, they return the same result as the former two (except for possible precision issues).

Examples

The following are incomplete examples, as they need the above functions included, which have been stripped for brevity and ease of maintenance.

This example uses a 0.5 second sensor to detect a nearby prim called Target and makes the current prim's X axis point to it (assuming it's in a prim alone, or in the root of a linked set):

default
{
    state_entry()
    {
        llSensorRepeat("Target", "", ACTIVE | PASSIVE, 30., PI, 0.5);
    }
 
    sensor(integer num)
    {
        llSetRot(PointAt2Rot(llDetectedPos(0) - llGetPos()));
    }
}

Changing PointAt2Rot to PointAtHoriz2Rot in the above will make the prim be always horizontal but with its X face facing the target as much as possible while keeping the horizontal.

The following example shows how to use PointAtViewup2Rot to orient the prim's top in world coordinates, when the script is used within a linked prim. It is assumed to be in a prim with a link number greater than 2. It detects the position of the prim with link number 2 using a timer, and makes this prim's X axis point towards it, and the Z axis as close to the world's up vector as possible:

default
{
    state_entry()
    {
        llSetTimerEvent(0.5);
    }
 
    timer()
    {
        rotation RootRot = llGetRootRotation();
 
        // Get the target prim's position in world coordinates.
        vector pos = llList2Vector(llGetLinkPrimitiveParams(2, [PRIM_POSITION]), 0);
 
        // Use the world's <0, 0, 1> vector as viewup, after converting it
        // to local coordinates, and the vector from our position to the target
        // as the PointAt, also in local coordinates:
        llSetLocalRot(PointAtViewup2Rot((pos - llGetPos()) / RootRot, <0, 0, 1> / RootRot));
    }
}