Difference between revisions of "User talk:Aleric Inglewood"

From Second Life Wiki
Jump to navigation Jump to search
(Replaced content with "This was moved elsewhere...")
 
(111 intermediate revisions by the same user not shown)
Line 1: Line 1:
== About Coordinate Systems and Rotations ==
This was moved elsewhere...
 
=== The four coordinate systems ===
 
There are four coordinate systems that are related to LSL programming:
# World coordinates
# Region coordinates
# Object coordinates (root prim coordinates)
# Child prim coordinates (coordinates relative to the prim that the script is in, or refers to by link number)
 
The World coordinates refer to the map, and allow to include the sim
in the coordinates, or refer to void water.
 
Region coordinates are relative to a given sim.
The origin is in the South/West corner at height 0.
The North/East corner then is 256, 256 and a Z coordinate
for the height up to 4096 meter (on opensim you can go even
higher).
 
Object coordinates are relative to the root prim. Hence, if the object
is moved or rotated then the orientation of a child prim, when given
in object coordinates, doesn't change. In LSL "local position" and
"local rotation" refer to this coordinate system. "local" means
relative to the root prim.
 
Prim Coordinates are relative to a given prim. If this prim is the root
prim then the Prim Coordinates are the same as the Object Coordinates.
For example, if a child prim is a cube with a size of 1,1,1 and one
red surface where the center of that surface is at 1,0,0 then it will
still be at 1,0,0 no matter how you move or rotate that child prim
(relative to the other linked prims).
 
=== Positions ===
 
A different position of the origin of a coordinate system is easy to understand:
You can think of positions as vectors that start in the origin of the
coordinate system that they are given in and end in the point that they
refer to. While the length of the vector is independent of the rotation of
the coordinate system, the three coordinates are not; but a mental picture
of an arrow doesn't have little numbers for three coordinates, so that
picture works independent of the rotation too.
 
Since the rotation of the World Coordinate system and the Region Coordinate
system is the same (X, Y and Z axis are parallel of both), and since
World Coordinates aren't used in many LSL functions to begin with, we
will ignore World Coordinates for now and only refer to Region Coordinates,
or say "global" when we mean Region Coordinates.
 
=== Rotations ===
 
An LSL rotation internally stores a vector that is the axis around which
to rotate and the angle of the rotation around that axis.
Let '''V''' = <u, v, w> be the normalized vector (that is, with length 1) around
which we rotate and let a be the angle around which we have to rotate.
Then the LSL rotation is a quaternion stored as r = <x, y, z, s> = < '''V''' * sin(a/2), cos(a/2) >.
Thus, r.x = x * sin(a/2), and r.s = cos(a/2) etc. Note that the quaternion
is also normalized.
Also note that there is a duality here because inverting '''V''' (making it point the
opposite way) and inverting the angle gives the same rotation; r and -r have
different values but are the same rotation.
Also note that if you don't rotate at all (a == 0) then it doesn't matter
what axis '''V''' you pick, which is apparent because '''V''' drops out since sin(0) = 0.
The quaternion <0, 0, 0, 1> is the ZERO_ROTATION quaternion.
 
The point of this technical story is to show that for an LSL rotation to
make sense in terms of orientation, you need to be able to express a vector
in three coordinates (u, v, w above): the axis around which we rotate is
expressed relative to the X-, Y- and Z-axes of the coordinate system. Hence,
it is the orientation of the X-, Y- and Z-axes that defines the meaning
of a rotation in LSL.
 
In terms of a mental picture the origin with the (orientation of the) three axis,
the red X-axis, the green Y-axis and the blue Z-axis is all the reference
we need, combined with a vector for position or a quaternion for rotation.
 
When you edit an object, the viewer shows either 'World' or 'Local' axes, but
really the 'World' axes show the wrong origin, shifted to an averaged center
of the object, because if the origin was drawn at (0, 0, 0) you'd most likely
not see it. The 'Local' ruler shows the correct coordinate system for the
selected prim as its Prim Coordinate System. Selecting the root prim with
'Local' ruler on then shows the Object Coordinate System.
 
=== The dimension of rotations ===
 
As said before, given some coordinate system, any point in space can
be represented with a vector. Obviously space is three dimensional,
and thus vectors exists of three real values:
one needs three distinct floating point numbers, the x coordinate,
the y coordinate and the z coordinate to uniquely
identify a position in a given coordinate system.
 
However, if one limits oneself to only normalized vectors, vectors
with a length of one, then those represent all points on the surface
of a sphere with radius 1.
A surface is obviously two dimensional, so it should be possible to
uniquely identify any point on the surface of such a sphere with
only two floating point numbers.
 
One might think that selecting just two coordinates of the three of
the vector will suffice because the third is fixed by the length requirement,
but that only works for half spheres; for example, if x and y are known
then z can still be either plus the square root of x squared plus y squared,
''or'' minus that value.
 
Instead, a better choice would be the [http://mathworld.wolfram.com/SphericalCoordinates.html spherical coordinate system]
and express the unit vectors with the two the angular coordinates φ (phi) and θ (theta),
where φ is the angle between the vector and the positive Z axis, and
θ the angle that the projection of the vector on to the X,Y plane makes
with the positive X axis. In other words, starting with a unit vector
along the positive Z-axis, one can obtain the required point on
the surface of the sphere by first rotating this vector around the
Y axis (towards the positive X axis) by an angle of φ and then rotating
the result around the Z-axis by an angle of θ. Note that in both cases
the rotations are counter-clockwise when one looks at it from the positive
side of the axis that one rotates around (towards the origin).
 
What we just did was expressing a unit vector in terms of two rotations:
one around the Z axis and one around the Y axis; and indeed every rotation
can be expressed as two rotations around those two axis (or really, any
two arbitrary axes, as long as they are independent (not parallel)).
 
It is therefore possible to represent unit vectors (aka, a ''direction'')
with a rotation, as both are two dimensional. The mental picture here is
that any unit vector can be expressed as some fixed unit vector (for example
the one pointing along the positive Z axis, but any other would do as well)
and the rotation needed to turn that 'starting vector' into the unit vector
that one wants to represent. The other way around almost works as well, with the
exception of the starting vector itself, as that can be expressed by any arbitrary
rotation around itself, so that information was lost and it is not possible
to know which of those rotations was used. In fact, any vector
close to the starting vector would give inaccurate results in the light
of floating point round off errors and is not a good way to represent
a rotation. Of course, the two dimensional vector (θ, φ) would be excellent
to represent both: the unit vector, as well as any rotation; but LSL stores
vectors in x, y, z coordinates, not in spherical coordinates.
 
=== Converting between coordinate systems ===
 
Consider a child prim of an object that is not the root prim and express
its position with the vector pos_oc, and its orientation with the rotation rot_oc,
both relative to the Object Coordinates System.
 
A script inside that prim can easily obtain those values:
 
vector pos_oc = [[llGetLocalPos]]();
rotation rot_oc = [[llGetLocalRot]]();
 
The postfix '_oc' stands for Object Coordinates and is used to make clear
relative to which coordinate system the x, y and z values of both
variables are. Remember that also the rotation contains a vector
(the axis around which we rotate), so from now on we'll talk about
'coordinates' relative to a coordinate system for both, the position
as well as the rotation, where the coordinates refer to the x, y and z
components of the position and the rotation. For example, the first
sentence of this paragraph will read "...express its position with the
vector pos_oc, and its orientation with the rotation rot_oc, ''both in Object Coordinates''."
 
'''Note that''' one has to be careful here, because a script in the
root prim of the object will '''''not''''' obtain
Object Coordinates but instead Region Coordinates!
The reason is that the 'Local' functions return coordinates relative to
the 'parent' coordinate system, where the parent of a non-root prim is the root prim,
but the parent of the root prim is the region (or avatar in case
of an attachment). The position and rotation of the root prim in
Object Coordinates is trivial (ZERO_VECTOR and ZERO_ROTATION respectively).
 
Back to the script in the child prim. It can also easily obtain its position
and rotation in Region Coordinates:
 
vector pos_rc = [[llGetPos]]();
rotation rot_rc = [[llGetRot]]();
 
Both sets, (pos_oc, rot_oc) and (pos_rc, rot_rc) refer to the same thing,
so it should be possible to convert them into each other.
 
One can do this by looking at the orientation (position and rotation) of
one coordinate system relative to the other and expressed
in the coordinates of that other. The position and rotation
of the Object Coordinate System relative to the Region Coordinate
System, and in Region Coordinates, could be obtained as follows:
 
vector oc_pos_rc = [[llGetRootPosition]]();
rotation oc_rot_rc = [[llGetRootRotation]]();
 
As indicated by the '_rc' postfix, these are again in Region Coordinates.
 
Having the relative orientation of the Object Coordinate System in Region Coordinates, it is
possible to convert Object Coordinates into Region Coordinates. More
abstractly put, if you have two coordinate systems A and B, and you have
their relative orientation expressed in A then you can convert orientations
expressed in B into A.
 
The formula is as follows:
 
'''vector  pos_A = pos_B * B_rot_A + B_pos_A;'''
'''rotation rot_A = rot_B * B_rot_A;'''
 
The order of the multiplications is important; swapping the B_rot_A to the front will not work!
 
Note that the B_*_A are the position and rotation of the coordinate system B expressed
in terms of the coordinate system A. The reason that I use the prefix and postfix in
the order is directly related to the order enforced for rotation multiplications.
Written this way, the left-hand postfix of a multiplication must match the right-hand prefix
and conviently short-circuits the notation:
 
something_A = something_B * B_rot_A
 
the two B's 'connect' and drop out (along with the '_rot_') to give something_ ... A.
This ''only'' works when the postfix refers to the coordinate system that the variable
is in, and the prefix is whatever is described by the variable, possibly another
coordinate system.
 
A complete example script follows:
<lsl>
default
{
    touch(integer num_detected)
    {
        llSay(0, "---------");
 
        // Get the pos and rot in Object Coordinates.
        vector pos_oc = llGetLocalPos();
        rotation rot_oc = llGetLocalRot();
       
        // Fix these values when this script is in the root prim!
        if (llGetLinkNumber() == 0)
        {
            pos_oc = ZERO_VECTOR;
            rot_oc = ZERO_ROTATION;
        }
       
        // Get the pos and rot in Region Coordinates.
        vector pos_rc = llGetPos();
        rotation rot_rc = llGetRot();
       
        // Get the pos and rot of the Object Coordinate System in Region Coordinates.
        vector oc_pos_rc = llGetRootPosition();
        rotation oc_rot_rc = llGetRootRotation();
       
        // Print the values of the positions (the rotations are too hard to understand).
        llSay(0, "oc_pos_rc = " + (string)oc_pos_rc);
        llSay(0, "pos_os = " + (string)pos_os);
        llSay(0, "pos_rc = " + (string)pos_rc);
       
        // Calculate pos_rc and rot_rc from pos_oc and rot_oc : converting between _oc and _rc.
        vector  pos2_rc = pos_oc * oc_rot_rc + oc_pos_rc;
        rotation rot2_rc = rot_oc * oc_rot_rc;
       
        // Print the result for the position, and show the difference with what llGetPos() and llGetRot() returned.
        llSay(0, "pos2_rc = " + (string)pos2_rc);
        // These values should be ZERO_VECTOR and ZERO_ROTATION (or -ZERO_ROTATION).
        llSay(0, "pos_rc - pos2_rc = " + (string)(pos_rc - pos2_rc));
        llSay(0, "rot_rc / rot2_rc = " + (string)(rot_rc / rot2_rc));
    }
}
</lsl>
 
Obviously there is no preference for one coordinate system over the other for this conversion;
one can equally as well convert orientations in A into B:
 
vector  pos_B = pos_A * A_rot_B + A_pos_B;
rotation rot_B = rot_A * A_rot_B;
 
The question arises then how to obtain the A_*_B values. For example, there is no LSL function that returns directly
the position or rotation of the region relative to a prim (aka, rc_*_oc).
 
Intuitively one would say that it has to be possible to express A_*_B in B_*_A on grounds of symmetry, and that
is indeed the case.
 
To show how this works, lets just take the formula for pos_A and rot_A (see above) and rework
them to get pos_B and rot_B on the left-hand side. This results first in
 
pos_B * B_rot_A = pos_A - B_pos_A
rot_B * B_rot_A = rot_A
 
Then divide both sides by B_rot_A to get:
 
pos_B = pos_A / B_rot_A - B_pos_A / B_rot_A
rot_B = rot_A / B_rot_A
 
from which we can conclude that
 
'''A_pos_B = - B_pos_A / B_rot_A'''
'''A_rot_B = ZERO_ROTATION / B_rot_A'''
 
Note how A_rot_B is the inverse of B_rot_A: A_rot_B * B_rot_A = ZERO_ROTATION.
As per our notation convention you'd expect A_rot_B * B_rot_A to be A_rot_A, and that is
actually the case as A_rot_A means the relative orientation of coordinate system A
expressed in A's coordinates, which is the trivial ZERO_VECTOR and ZERO_ROTATION as
we saw before for the root prim orientation expressed in Object Coordinates.
 
And for fun, note that if we do this inversion again we get:
 
B_pos_A = - A_pos_B / A_rot_B = - (- B_pos_A / B_rot_A) / (ZERO_ROTATION / B_rot_A) = B_pos_A / B_rot_A * B_rot_A = B_pos_A
B_rot_A = ZERO_ROTATION / A_rot_B = ZERO_ROTATION / (ZERO_ROTATION / B_rot_A) = B_rot_A
 
as expected.
 
We can now also convert Region Coordinates to Object Coordinates.
 
One could first calculate,
 
vector rc_pos_oc = - llGetRootPosition() / llGetRootRotation();
rotation rc_rot_oc = ZERO_ROTATION / llGetRootRotation();
 
and then convert as usual:
 
vector  pos_oc = pos_rc * rc_rot_oc + rc_pos_oc;
rotation rot_oc = rot_rc * rc_rot_oc;
 
or if you don't want to calculate the intermediate rc_*_oc values, you could do immediately:
 
vector  pos_oc = (pos_rc - llGetRootPosition()) / llGetRootRotation();
rotation rot_oc = rot_rc / llGetRootRotation();
 
To verify this, you could extend the above script with
<lsl>
        // Calculate pos_oc and rot_oc from pos_rc and rot_rc.
        vector  pos2_oc = (pos_rc - llGetRootPosition()) / llGetRootRotation();
        rotation rot2_oc = rot_rc / llGetRootRotation();
       
        llSay(0, "pos2_oc = " + (string)pos2_oc);
        // These values should again be ZERO_VECTOR and +/- ZERO_ROTATION.
        llSay(0, "pos_oc - pos2_oc = " + (string)(pos_oc - pos2_oc));
        llSay(0, "rot_oc / rot2_oc = " + (string)(rot_oc / rot2_oc));
</lsl>
 
=== Cascading coordinate systems ===
 
Imagine you have a castle object where the floor is the root prim. Everything is linked because you
want it to be easy to move the castle around. One of the child prims is a sculpty representing the hinges
around which you want to rotate a large gate. The gate has a rotating wheel on it.
 
You want that the object (castle) to keep working when it is moved and/or rotated, but also when it is edited
and the hinges are moved and/or rotated. This means that the state of the gate prims
must be ''stored'' relative to the hinges, which is by far the easiest thing to do to begin with,
after all, the gate rotates around the hinges and has no relation with the rest of the castle.
 
Hence, we have the Region Coordinate System, the Object Coordinate System (the castle) and the Hinges Coordinate System.
The latter is our first example of a Prim Coordinates System, but I'll use the postfix '_hc' for "Hinges Coordinates"
instead of '_pc'. Finally we have the Spill Coordinate System (or Gate Coordinates) where the spill is a prim
around which we want to rotate the wheel, and I'll used '_sc' as postfix for that.
 
The variables used then would be an orientation for the wheel in *_sc coordinates, the orientation of the spill,
and other gate prims, in *_hc coordinates and the orientation of the hinges in *_oc coordinates. Finally we could
have variables for the orientation of the castle (root prim) in *_rc coordinates of course.
 
vector  castle_pos_rc; // The position of the castle in Region Coordinates.
rotation castle_rot_rc; // The rotation of the castle in Region Coordinates.
vector  hinges_pos_oc; // The position of the hinges in Object Coordinates.
rotation hinges_rot_oc; // The rotation of the hinges in Object Coordinates.
vector  spill_pos_hc;  // The position of the spill in Hinges Coordinates.
rotation spill_rot_hc;  // The rotation of the spill in Hinges Coordinates.
vector  wheel_pos_sc;  // The position of the wheel in Spill Coordinates.
rotation wheel_rot_sc;  // The rotation of the wheel in Spill Coordinates.
 
Now because we used existing prims orientations as coordinate system for the next prim, the position
and rotation of those prims ''are'' the position and rotation of that coordinate system.
Just like before we saw that llGetPos/llGetRot returns the global orientation of the root prim
when used in a script in the root prim, while llGetRootPos/llGetRootRot gave us the relative
orientation of the Object Coordinate System in Region Coordinates, but is in fact the same as
what llGetPos/llGetRot in the root prim returns: the position/rotation of the root prim '''is'''
the orientation of the Object Coordinate System relative to the Region.
 
Likewise, in this case, castle_*_rc is the relative orientation of the Object Coordinate System
in Region Coordinates as well as the orientation of the root prim in Region Coordinates; they
are the same thing.
 
The same then applies to the *_oc variables, which we defined as the orientation of
the hinges in Object Coordinates, but at the same time are the relative orientation
of the Hinges Coordinate System in Object Coordinates. The *_hc variables are the
orientation of spill in Hinges Coordinates, but also the relative orientation of
the Spill Coordinates in Hinges Coordinates.
 
Lets write it out using the same notation convention as used above, then
we have:
 
vector  oc_pos_rc = castle_pos_rc; // The Object Coordinate System orientation in Region Coordinates.
rotation oc_rot_rc = castle_rot_rc;
vector  hc_pos_oc = hinges_pos_oc; // The Hinges Coordinate System orientation in Object Coordinates.
rotation hc_rot_oc = hinges_rot_oc;
 
vector  sc_pos_hc = spill_pos_hc;  // The Spill Coordinate System orientation in Hinges Coordinates.
rotation sc_rot_hc = spill_rot_hc;
 
It will therefore probably not surprise you that
 
vector  hc_pos_rc = hc_pos_oc * oc_rot_rc + oc_pos_rc; // The Hinges Coordinate System orientation in Region Coordinates.
rotation hc_rot_rc = hc_rot_oc * oc_rot_rc;
vector  sc_pos_rc = sc_pos_hc * hc_rot_rc + hc_pos_rc; // The Spill Coordinate System orientation in Region Coordinates.
rotation sc_rot_rc = sc_rot_hc * hc_rot_rc;
 
And finally, using the latter, we can express the wheel orientation in region coordinates:
 
vector  wheel_pos_rc = wheel_pos_sc * sc_rot_rc + sc_pos_rc;
rotation wheel_rot_rc = wheel_rot_sc * sc_rot_rc;
 
Although this was nothing else but applying the coordinate system conversion formula three times,
it might give some insight to get rid of the intermediate variables and express the wheel
orientation in region coordinates without them, giving:
 
wheel_pos_rc = ((wheel_pos_sc * sc_rot_hc + sc_pos_hc) * hc_rot_oc + hc_pos_oc) * oc_rot_rc + oc_pos_rc
wheel_rot_rc = wheel_rot_sc * sc_rot_hc * hc_rot_oc * oc_rot_rc
 
=== Applying rotations and translations ===

Latest revision as of 17:02, 27 March 2014

This was moved elsewhere...