Difference between revisions of "User:Pedro Oval/Calculate rotation for pointing in a direction"

From Second Life Wiki
Jump to navigation Jump to search
(I should have run the benchmarks before... For some reason the llAxes2Rot version outperforms llAxisAngle2Rot, and the quaternion in the Horiz version is simple to build (and fastest))
m (Explain in comment that the angle is halved for the quaternion)
Line 17: Line 17:
rotation PointAtHoriz2Rot(vector target)
rotation PointAtHoriz2Rot(vector target)
{
{
     // Find the Z angle:
     // Find the Z angle and halve it for the quaternion:
     target.z = llAtan2(target.y, target.x) * 0.5;
     target.z = llAtan2(target.y, target.x) * 0.5;
     // Make a quaternion out of it on z:
     // Make a quaternion out of it on z:

Revision as of 14:56, 8 November 2011

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". 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.

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.

<lsl> // 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)>;

} </lsl>

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

<lsl> // 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);

} </lsl>

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): <lsl> default {

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

} </lsl>

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 horizontality.

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: <lsl> default {

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

} </lsl>