|
|
| (45 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 α (alpha) 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(α/2), cos(α/2) >.
| |
| Thus, r.x = x * sin(α/2), and r.s = cos(α/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 (α == 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 ===
| |
| | |
| Applying rotations and translations (moving) is very much the same as coordinate system transformations described above.
| |
| To show this consider the following: imagine you have a prim 'pa' with its own coordinate system A.
| |
| Then it trivially has position ZERO_VECTOR and rotation ZERO_ROTATION relative to A, per definition.
| |
| | |
| vector pa_pos_A = ZERO_VECTOR;
| |
| rotation pa_rot_A = ZERO_ROTATION;
| |
| | |
| Now consider a prim 'pb' that has some relative position pb_pos_A and rotation pb_rot_A given in A coordinates.
| |
| For example,
| |
| | |
| vector pb_pos_A = <0.5, 0.6, 0.7>;
| |
| rotation pb_rot_A = <0.38, 0.26, 0.34, 0.82>;
| |
| | |
| Note that 0.38² + 0.26² + 0.34² + 0.82² = 1, as it must be since rotations are normalized.
| |
| | |
| As seen before, the orientation of pb relative to pa is the same as the orientation of pb relative to A,
| |
| and the same as the orientation of B relative to A. pa and A are interchangeable as long as pa has
| |
| ZERO_VECTOR and ZERO_ROTATION relative to A. Thus, with B being the coordinate system where pb currently
| |
| is, we also have:
| |
| | |
| vector B_pos_A = pb_pos_A; // The orientation of coordinate system B, in coordinates of A.
| |
| rotation B_rot_A = pb_rot_A;
| |
| | |
| Now suppose we want move the prim pa (without moving the coordinate system A),
| |
| by applying the translation 'B_pos_A', after all a translation is stored in a vector
| |
| and B_pos_A is a vector. Likewise, we want to rotate pa (again without influencing A)
| |
| by applying the rotation B_rot_A. Then the most logical order to this in is by
| |
| ''first'' applying the rotation and ''then'' applying the translation, because then
| |
| the meaning of the translation is preserved and will match the meaning of B_pos_A
| |
| in that pa will end up where pb is now. If you'd first apply the translation so that
| |
| pa ends up where pb is, and then apply the rotation (around A!) then pa would swoop
| |
| around the origin of A and move away from its desired position.
| |
| | |
| Doing this correctly should leave us with pa_pos_A == pb_pos_A and pa_rot_A == pb_rot_A,
| |
| so that pa ended up precisely where pb is now. We can't just assign those values
| |
| however, because we're looking for a formula that works in general; a way to apply
| |
| B_rot_A and then B_pos_A to any prim with -say- orientation pc_pos_A, pc_rot_A.
| |
| | |
| Thus, first we apply the rotation:
| |
| | |
| pc_rot_A *= B_rot_A;
| |
| pc_pos_A *= B_rot_A;
| |
| | |
| And then apply the translation:
| |
| | |
| pc_pos_A += B_pos_A;
| |
| | |
| Now lets check if indeed this would cause pa to end up where pb is.
| |
| The effect on pa_*_A would be:
| |
| | |
| pa_rot_A = pa_rot_A * B_rot_A = ZERO_ROTATION * B_rot_A = B_rot_A = pb_rot_A;
| |
| pa_pos_A = pa_pos_A * B_rot_A = ZERO_VECTOR * B_rot_A = ZERO_VECTOR;
| |
|
| |
| and then the translation
| |
|
| |
| pa_pos_A = pa_pos_A + B_pos_A = ZERO_VECTOR + B_pos_A = B_pos_A = pb_pos_A;
| |
| | |
| So yes, pa_*_A ends up as pb_*_A!
| |
| | |
| The real test however would be if we first applied some transformation to pa
| |
| to bring it to some arbitrary orientation (aka, pc), then apply the transformation
| |
| B_rot_A,B_pos_A and then reverse the first transformation but now relative to B,
| |
| and then find that we indeed end up at pb.
| |
| | |
| Let us first define a 'transformation', which exists of a vector for the
| |
| translation and a rotation for the rotation. We will have two transformation
| |
| during this test: t1: pa -> pc, and t2: pa -> pb.
| |
| | |
| As we just saw (or we hope this is going to be the case), t2 can be defined as:
| |
| | |
| vector t2_translation = B_pos_A;
| |
| rotation t2_rotation = B_rot_A;
| |
| | |
| where as t1 is arbitrary, just some values t1_translation and t1_rotation.
| |
| | |
| Applying t1 to pa, we get (this is thus the same as above, but now with the 't1' notation):
| |
| | |
| pa_rot_A *= t1_rotation;
| |
| pa_pos_A *= t1_rotation;
| |
| pa_pos_A += t1_translation;
| |
| | |
| Next we apply translation t2 to that:
| |
| | |
| pa_rot_A *= t2_rotation;
| |
| pa_pos_A *= t2_rotation;
| |
| pa_pos_A += t2_translation;
| |
| | |
| Finally we want to reverse t1, but in the coordinate system of B.
| |
| Therefore, we first convert our coordinates to that of B:
| |
| | |
| pa_pos_B = pa_pos_A * A_rot_B + A_pos_B;
| |
| pa_rot_B = pa_rot_A * A_rot_B;
| |
| | |
| where
| |
| | |
| A_pos_B = - B_pos_A / B_rot_A;
| |
| A_rot_B = ZERO_ROTATION / B_rot_A;
| |
| | |
| and then reverse t1. Note now we have to do the translation first!
| |
| | |
| pa_pos_B -= t1_translation;
| |
| pa_pos_B /= t1_rotation;
| |
| pa_rot_B /= t1_rotation;
| |
| | |
| And if everything worked out then pa now should be at ZERO_VECTOR, ZERO_ROTATION relative to B!
| |
| | |
| As pa_pos_A,pa_rot_A started as ZERO_VECTOR,ZERO_ROTATION, the first step
| |
| of applying t1 resulted in pa_rot_A = t1_rotation and pa_pos_A = t1_translation.
| |
| | |
| After applying t2 we get pa_rot_A = t1_rotation * t2_rotation, and pa_pos_A = t1_translation * t2_rotation + t2_translation.
| |
| | |
| Converting that to B gives
| |
| | |
| pa_pos_B = (t1_translation * t2_rotation + t2_translation) * A_rot_B + A_pos_B
| |
| pa_rot_B = (t1_rotation * t2_rotation) * A_rot_B
| |
| | |
| where, remembering that t2_translation = B_pos_A and t2_rotation = B_rot_A,
| |
| | |
| A_pos_B = - B_pos_A / B_rot_A = - t2_translation / t2_rotation
| |
| A_rot_B = ZERO_ROTATION / B_rot_A = ZERO_ROTATION / t2_rotation
| |
| | |
| so that we get
| |
| | |
| pa_pos_B = (t1_translation * t2_rotation + t2_translation) / t2_rotation - t2_translation / t2_rotation = t1_translation
| |
| pa_rot_B = (t1_rotation * t2_rotation) / t2_rotation = t1_rotation
| |
| | |
| and finally reversing t1 gives:
| |
| | |
| pa_pos_B = (t1_translation - t1_translation) / t1_rotation = ZERO_VECTOR
| |
| pa_rot_B = t1_rotation / t1_rotation = ZERO_ROTATION
| |
| | |
| So that we can conclude that if you have two prims pa and pb, each representing their own coordinate system A and B respectively,
| |
| and you express the orientation of B relative to A as the pair (B_pos_A, B_rot_A) — then the following holds.
| |
| | |
| '''The transformation pair (t_translation, t_rotation) equals (B_pos_A, B_rot_A) as long as you first apply the rotation and then the translation while working in the coordinate system of A'''.
| |
| | |
| The latter is also very practical, because you normally won't know
| |
| anything about the target coordinate system B until you actually did the transformation.
| |
| | |
| Now remember that we found that in order to convert variables from being relative to a coordinate system B to a coordinate system A,
| |
| we did:
| |
| | |
| pos_A = pos_B * B_rot_A + B_pos_A
| |
| rot_A = rot_B * B_rot_A
| |
| | |
| which is thus exactly the same as first applying the rotation t1_rotation and then the translation t1_translation
| |
| on the pair (pos_B, rot_B) where that pair is relative to A! That isn't too weird because if you consider a prim
| |
| in the origin of A with no rotation, aka (ZERO_VECTOR, ZERO_ROTATION) relative to A and then apply the transformation
| |
| then you expect to end up in the origin of B with no rotation relative to B. Aka pos_B = ZERO_VECTOR and rot_B = ZERO_ROTATION.
| |
| So now work backwards: assume you already have the prim in B like that, then what are its coordinates in A?
| |
| That would be the above conversion thus, with pos_B = ZERO_VECTOR and rot_B = ZERO_ROTATION.
| |
| | |
| Hence, doing the conversion with ZERO as input gives the same results as doing the t transformation with ZERO as input!
| |
| And the final formula for a transformation just looks exactly the same:
| |
| | |
| '''target_pos_A = source_pos_A * t_rotation + t_translation'''
| |
| '''target_rot_A = source_rot_A * t_rotation'''
| |
| | |
| === LSL functions ===
| |
| | |
| What all of the above taught you is mainly a coding style: use prefixes and postfixes for your rotations (and translations)!
| |
| It's all in the '''''names''''' of your variables. A 'rotation' type can be the rotation part of a transformation
| |
| (not tight to a particular coordinate system, but rather rotating around whatever coordinate system you apply it to),
| |
| it can be the representation of a unit vector, it can be the rotation part of the orientation of a prim relative to a
| |
| given coordinate system, or it can represent the rotation part of a coordinate system conversion.
| |
| If you don't use a consistent way to reflect all that in your variable names then you ''will'' get confused.
| |
| | |
| The LSL functions mostly deal with two types: orientations (position + rotation) of prims relative to some coordinate system,
| |
| and simply transformations (rotation and/or translation).
| |
| | |
| The table below shows the coordinate systems involved.
| |
| | |
| {| rules="all" style="margin:2em 1em 2em 1em; border:solid 2px #000000; background-color:#F9F9F9; font-size:120%; empty-cells:show;"
| |
| |- {{Hl2}}
| |
| ! style="padding: 5px 10px 5px 10px; color: blue; background-color: #eeeeb0" | Coordinate System
| |
| ! style="padding: 5px 10px 5px 10px; color: green; background-color: lightgray" | Abbreviation
| |
| ! style="padding: 5px 10px 5px 10px" | script in root prim
| |
| ! style="padding: 5px 10px 5px 10px" | script in child prim
| |
| ! style="padding: 5px 10px 5px 10px" | [[llGetLinkPrimitiveParams]](link, ...)
| |
| |-
| |
| | style="padding: 5px 10px 5px 10px; color: blue; background-color: #ffffa0" | World/Map Coordinates
| |
| | style="padding: 5px 10px 5px 10px" | '''wc'''
| |
| | style="padding: 5px 10px 5px 10px; text-align:center" colspan=3 | Add [[llGetRegionCorner]]() to Region Coordinates
| |
| |-
| |
| | style="padding: 5px 10px 5px 10px; color: blue; background-color: #ffffa0" | Region Coordinates
| |
| | style="padding: 5px 10px 5px 10px" | '''rc'''
| |
| | style="padding: 5px 10px 5px 10px; text-align:center" colspan=2 | [[llGetPos]]()
| |
| | style="padding: 5px 10px 5px 10px" | element3
| |
| |-
| |
| | style="padding: 5px 10px 5px 10px; color: blue; background-color: #ffffa0" | Object/Root Coordinates
| |
| | style="padding: 5px 10px 5px 10px" | '''oc'''
| |
| | style="padding: 5px 10px 5px 10px" | e1
| |
| | style="padding: 5px 10px 5px 10px" | e2
| |
| | style="padding: 5px 10px 5px 10px" | e3
| |
| |-
| |
| | style="padding: 5px 10px 5px 10px; color: blue; background-color: #ffffa0" | Prim/Self Coordinates
| |
| | style="padding: 5px 10px 5px 10px" | '''pc'''
| |
| | style="padding: 5px 10px 5px 10px" |
| |
| | style="padding: 5px 10px 5px 10px" |
| |
| | style="padding: 5px 10px 5px 10px" |
| |
| |}
| |
| | |
| New paragraph.
| |
| | |
| === New title ===
| |