User talk:Aleric Inglewood
About Coordinate Systems and Rotations
The four coordinate systems
There are four coordinate systems that are related to LSL programming:
- World coordinates
- Region coordinates
- Object coordinates (root prim coordinates)
- Prim coordinates
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 spherical coordinate system and express the unit vectors with the two the angular coordinates φ and θ, 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 oc_pos, and its orientation with the rotation oc_rot, both relative to the Object Coordinates System.
A script inside that prim can easily obtain those values:
vector oc_pos = llGetLocalPos(); rotation oc_rot = llGetLocalRot();
The prefix '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 oc_pos, and its orientation with the rotation oc_rot, 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 rc_pos = llGetPos(); rotation rc_rot = llGetRot();
Both sets, (oc_pos, oc_rot) and (rc_pos, rc_rot) 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 rc_ocs_pos = llGetRootPosition(); rotation rc_ocs_rot = llGetRootRotation();
As indicated by the 'rc_' prefix, these are again in Region Coordinates.
Having the relative orientation of the ocs 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 A_pos = B_pos * A_B_rot + A_B_pos; rotation A_rot = B_rot * A_B_rot;
The order of the multiplications is important; swapping the A_B_rot to the front will not work!
Note that the A_B_* are the position and rotation of the coordinate system B expressed in terms of the coordinate system A.
A complete example script follows: <lsl> default {
touch(integer num_detected) { llSay(0, "---------");
// Get the pos and rot in Object Coordinates. vector oc_pos = llGetLocalPos(); rotation oc_rot = llGetLocalRot(); // Fix these values when this script is in the root prim! if (llGetLinkNumber() == 0) { oc_pos = ZERO_VECTOR; oc_rot = ZERO_ROTATION; } // Get the pos and rot in Region Coordinates. vector rc_pos = llGetPos(); rotation rc_rot = llGetRot(); // Get the pos and rot of the Object Coordinate System in Region Coordinates. vector rc_ocs_pos = llGetRootPosition(); rotation rc_ocs_rot = llGetRootRotation(); // Print the values of the positions (the rotations are too hard to understand). llSay(0, "rc_ocs_pos = " + (string)rc_ocs_pos); llSay(0, "oc_pos = " + (string)oc_pos); llSay(0, "rc_pos = " + (string)rc_pos); // Calculate rc_pos and rc_rot from oc_pos and oc_rot : converting between oc_ and rc_. vector rc_pos2 = oc_pos * rc_ocs_rot + rc_ocs_pos; rotation rc_rot2 = oc_rot * rc_ocs_rot; // Print the result for the position, and show the difference with what llGetPos() and llGetRot() returned. llSay(0, "rc_pos2 = " + (string)rc_pos2); // These values should be ZERO_VECTOR and ZERO_ROTATION (or -ZERO_ROTATION). llSay(0, "rc_pos - rc_pos2 = " + (string)(rc_pos - rc_pos2)); llSay(0, "rc_rot / rc_rot2 = " + (string)(rc_rot / rc_rot2)); }
} </lsl>
Obviously there is not preference for one coordinate system over the other for this conversion; one can equally as well convert orientations in A into B:
vector B_pos = A_pos * B_A_rot + B_A_pos; rotation B_rot = A_rot * B_A_rot;
The question arises then how to obtain the B_A_* values. For example, there is no LSL function that returns directly the position or rotation of the region relative to a prim (aka, oc_rcs_*).
Intuitively one would say that it has to be possible to express B_A_* in A_B_* on grounds of symmetry, and that is indeed the case.
To show how this works, lets just take the formula for A_pos and A_rot (see above) and rework them to get B_pos and B_rot on the left-hand side. This results first in
B_pos * A_B_rot = A_pos - A_B_pos B_rot * A_B_rot = A_rot
Then divide both sides by A_B_rot to get:
B_pos = A_pos / A_B_rot - A_B_pos / A_B_rot B_rot = A_rot / A_B_rot
from which we can conclude that
B_A_pos = - A_B_pos / A_B_rot B_A_rot = ZERO_ROTATION / A_B_rot
And for fun, note that if we do this inversion again we get:
A_B_pos = - B_A_pos / B_A_rot = - (- A_B_pos / A_B_rot) / (ZERO_ROTATION / A_B_rot) = A_B_pos / A_B_rot * A_B_rot = A_B_pos A_B_rot = ZERO_ROTATION / B_A_rot = ZERO_ROTATION / (ZERO_ROTATION / A_B_rot) = A_B_rot
as expected.