|
|
| (8 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>
| |
| | |
| More in general, if you want to convert from B to A, but you only have A_*_B instead of B_*_A, then (combining the above)
| |
| you can do:
| |
| | |
| '''vector pos_A = (pos_B - A_pos_B) / A_rot_B;'''
| |
| '''rotation rot_A = rot_B / A_rot_B;'''
| |
| | |
| === 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'''
| |
| | |
| === Scaling coordinate systems ===
| |
| | |
| Apart from rotation and translation, there is one more linear operator that can be used to convert
| |
| between coordinate systems: scaling.
| |
| | |
| Let A_pos_B be the position of the origin of A in the coordinates of B. Let A_rot_B be the orientation of A within B,
| |
| and let the vector A_B_scale be the scaling factors for each axes. Scaling has no influence on the origin of
| |
| a coordinate system, but it matters for the rotation when the scaling factors per axis are different.
| |
| | |
| I think that a good choice is scale first and rotate afterwards. Because that leaves the values
| |
| of the coordinates of A_rot_B alone: it remains the rotation as defined in B, which makes sense
| |
| since it is written in the coordinates of B. However, it's just a matter of choice really, which
| |
| in this case will deform B_rot_A instead of A_rot_B. I can imagine that the choice might depend
| |
| on the application.
| |
| | |
| Chosing that we first scale, a point at pos_A is mapped onto the point pos_B as follows:
| |
| | |
| pos_B = (S * pos_A) * A_rot_B + A_pos_B
| |
| | |
| where S is a 3x3 matrix with as diagonal the vector A_B_scale (and the rest zeroes).
| |
| Note that when each element of A_B_scale is the same then you can just use a scalar for S, equal to the scale of each axis.
| |
| | |
| Whether or not S is a matrix or a scalar, lets just replace it with 'A_B_scale' here. Just keep in
| |
| mind that if it isn't a scalar then this won't work in LSL directly: then you'll have to split
| |
| it up in three different code lines because LSL doesn't support scaling or matrices.
| |
| | |
| pos_B = pos_A;
| |
| pos_B.x *= A_B_scale.x; // Scale
| |
| pos_B.y *= A_B_scale.y;
| |
| pos_B.z *= A_B_scale.z;
| |
| pos_B *= A_rot_B; // Rotate
| |
| pos_B += A_pos_B; // Translate
| |
| | |
| Note that the inverse of S, 1/S, is a 3x3 matrix with as diagonal the inverse of each element of A_B_scale.
| |
| We will either write 'B_A_scale', or divide by 'A_B_scale' in our notation.
| |
| | |
| Therefore our notation becomes:
| |
| | |
| pos_B = ('A_B_scale' * pos_A) * A_rot_B + A_pos_B
| |
| | |
| Since A and B are arbitrary, we would expect the same formula to hold when swapping A and B:
| |
| | |
| pos_A = ('B_A_scale' * pos_B) * B_rot_A + B_pos_A
| |
| | |
| Solving pos_B from that by subtracting B_pos_A from both sides, then dividing both sides by B_rot_A
| |
| and finally left multiplying both sides with the inverse of B_A_scale, gives:
| |
| | |
| pos_B = 'A_B_scale' * ((pos_A - B_pos_A) / B_rot_A) = 'A_B_scale' * (pos_A / B_rot_A) - 'A_B_scale' * (B_pos_A / B_rot_A)
| |
| | |
| from which we can conclude (by setting pos_A to ZERO_VECTOR) that
| |
| | |
| A_pos_B = -'A_B_scale' * (B_pos_A / B_rot_A)
| |
| | |
| Solving B_pos_A from that by left multiplying both sides with -B_A_scale and then right multiplying both sides with B_rot_A, gives:
| |
| | |
| B_pos_A = - ('B_A_scale' * A_pos_B) * B_rot_A
| |
| | |
| By setting A_pos_B to ZERO_VECTOR, and this B_pos_A too, we can also conclude that
| |
| | |
| ('A_B_scale' * pos_A) * A_rot_B = 'A_B_scale' * (pos_A / B_rot_A)
| |
| | |
| If 'A_B_scale' is a scalar, so that the order of multiplication doesn't matter,
| |
| then the scale just cancels on both sides and we find the conversion that we
| |
| found before, that B_rot_A is the inverse of A_rot_B. However, if A_B_scale
| |
| is a diagonal matrix then the order matters and the brackets are significant.
| |
| | |
| There is no way to let pos_A drop out: we cannot write B_rot_A as function
| |
| of A_B_scale and A_rot_B.
| |
| | |
| The conclusion is that a scale is asymmetrical: if you choose that a scale A_B_scale
| |
| means that you first apply the scale on a vector in A and then rotate, then
| |
| that automatically implies that when going from B to A you first have to rotate
| |
| and ''then'' apply the scale.
| |
| | |
| To make the choice clear in the name, I suggest to put an 'r' in front of the
| |
| coordinate system that need rotation to be applied first.
| |
| | |
| In summary, if A_rB_scale is defined as
| |
| | |
| '''pos_B = ('A_rB_scale' * pos_A) * A_rot_B + A_pos_B'''
| |
| | |
| then that implies that rB_A_scale has to be applied after rotation.
| |
| Note this means braces, NOT changing the left- or right- side of
| |
| multiplications! Matrices (scales) are always on the left side
| |
| of a vector, while rotations are always on the right side. Thus we get:
| |
| | |
| '''pos_A = 'rB_A_scale' * (pos_B * B_rot_A) + B_pos_A'''
| |
| | |
| If now we solve pos_B from the latter, we get:
| |
| | |
| pos_B = ('A_rB_scale' * (pos_A - B_pos_A)) / B_rot_A = ('A_rB_scale' * pos_A) / B_rot_A - ('A_rB_scale' * B_pos_A) / B_rot_A
| |
| | |
| Hence
| |
| | |
| '''B_pos_A = - 'rB_A_scale' * (A_pos_B / A_rot_B)'''
| |
| '''B_rot_A = ZERO_ROTATION / A_rot_B'''
| |
| | |
| and
| |
| | |
| '''A_pos_B = - ('A_rB_scale' * B_posA) / B_rot_A'''
| |
| '''A_rot_B = ZERO_ROTATION / B_rot_A'''
| |
| | |
| Note how these two sets condense into the same thing when the scale is a scalar,
| |
| but also when A_rot_B and B_rot_A are ZERO_ROTATION. In fact, when the rotation
| |
| involved is of the special case of one or more rotations of 90 degrees around
| |
| any of the axes, so that the rotation merely swaps axes, one could define
| |
| the inverse of A_B_scale by applying that rotation to that vector (swapping
| |
| the scaling factors), next to inverting each factor individually.
| |
| That is a different way of how we defined B_A_scale, but it would collapse
| |
| the two sets into one for that special set of rotations. I'm using that trick
| |
| in the example script below.
| |
| | |
| === 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:none 2px #000000; background-color:#F9F9F9; font-size:120%; empty-cells:hide;"
| |
| |-
| |
| | colspan=2 |
| |
| | style="padding: 5px 10px 5px 10px; background-color: #ffdddd; text-align: center" colspan=2 | Coordinates of the prim containing the script ('''sc_''')
| |
| |- {{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 child prim
| |
| ! style="padding: 5px 10px 5px 10px" | script in root prim
| |
| |-
| |
| | 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" colspan=2 | <span style="font-family: mono; font-size: 90%">sc_pos_'''wc''' = sc_pos_rc + [[llGetRegionCorner]]()<br>sc_rot_'''wc''' = sc_rot_rc</span>
| |
| |-
| |
| | 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" colspan=2 | <span style="font-family: mono; font-size: 90%">sc_pos_'''rc''' = [[llGetPos]]()<br>sc_rot_'''rc''' = [[llGetRot]]()</span>
| |
| |-
| |
| | 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" | <span style="font-family: mono; font-size: 90%">sc_pos_'''oc''' = [[llGetLocalPos]]()<br>sc_rot_'''oc''' = [[llGetLocalRot]]()</span>
| |
| | style="padding: 5px 10px 5px 10px" | <span style="font-family: mono; font-size: 90%">sc_pos_'''oc''' = [[ZERO_VECTOR]]<br>sc_rot_'''oc''' = [[ZERO_ROTATION]]</span>
| |
| |-
| |
| | style="padding: 5px 10px 5px 10px; color: blue; background-color: #ffffa0" | Self Coordinates
| |
| | style="padding: 5px 10px 5px 10px" | '''sc'''
| |
| | style="padding: 5px 10px 5px 10px" colspan=2 | <span style="font-family: mono; font-size: 90%">sc_pos_'''sc''' = [[ZERO_VECTOR]]<br>sc_rot_'''sc''' = [[ZERO_ROTATION]]</span>
| |
| |}
| |
| | |
| Where 'in the root prim' can be detect by testing that [[llGetLinkNumber]]() returns a value <= 1.
| |
| | |
| Alternatively, you can set link = [[llGetLinkNumber]]() and use the following table which
| |
| shows how to obtain the coordinates of a prim with link number 'link' from a script anywhere in the same object.
| |
| Note that this doesn't work when the object exist of a single prim: then [[llGetLinkNumber]]() returns 0,
| |
| while 'link' must be larger than 0 here. If you need to get Object Coordinates (_oc) then do not use the
| |
| negative value [[LINK_THIS]]. You can use [[LINK_THIS]] for the other coordinate systems, provided
| |
| the object exists of at least two prims.
| |
| | |
| {| rules="all" style="margin:2em 1em 2em 1em; border:none 2px #000000; background-color:#F9F9F9; font-size:120%; empty-cells:hide;"
| |
| |-
| |
| | colspan=2 |
| |
| | style="padding: 5px 10px 5px 10px; background-color: #ffdddd; text-align: center" colspan=2 | Coordinates of a linked prim ('''link_''')
| |
| |- {{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" | link > 1
| |
| ! style="padding: 5px 10px 5px 10px" | link = 1
| |
| |-
| |
| | 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" colspan=2 | <span style="font-family: mono; font-size: 90%">link_pos_'''wc''' = link_pos_rc + [[llGetRegionCorner]]()<br>link_rot_'''wc''' = link_rot_rc</span>
| |
| |-
| |
| | 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" colspan=2 | <span style="font-family: mono; font-size: 90%">link_pos_'''rc''' = [[llList2Vector]]([[llGetLinkPrimitiveParams]]('''link''', [<span></span>[[PRIM_POSITION]]]), 0)<br>link_rot_'''rc''' = [[llList2Rot]]([[llGetLinkPrimitiveParams]]('''link''', [<span></span>[[PRIM_ROTATION]]]), 0)</span>
| |
| |-
| |
| | 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" | <span style="font-family: mono; font-size: 90%">link_pos_'''oc''' = [[llList2Vector]]([[llGetLinkPrimitiveParams]]('''link''', [<span></span>[[PRIM_POS_LOCAL]]]), 0)<br>link_rot_'''oc''' = [[llList2Rot]]([[llGetLinkPrimitiveParams]]('''link''', [<span></span>[[PRIM_ROT_LOCAL]]]), 0)</span>
| |
| | style="padding: 5px 10px 5px 10px" | <span style="font-family: mono; font-size: 90%">link_pos_'''oc''' = [[ZERO_VECTOR]]<br>link_rot_'''oc''' = [[ZERO_ROTATION]]</span>
| |
| |-
| |
| | style="padding: 5px 10px 5px 10px; color: blue; background-color: #ffffa0" | Self Coordinates
| |
| | style="padding: 5px 10px 5px 10px" | '''sc'''
| |
| | style="padding: 5px 10px 5px 10px" colspan=2 | <span style="font-family: mono; font-size: 90%">link_pos_'''sc''' = (link_pos_oc - sc_pos_oc) / sc_rot_oc<br>link_rot_'''sc''' = link_rot_oc / sc_rot_oc</span>
| |
| |}
| |
| | |
| While the above table works for link = [[LINK_ROOT]], it is probably more convenient to use the following table:
| |
| | |
| {| rules="all" style="margin:2em 1em 2em 1em; border:none 2px #000000; background-color:#F9F9F9; font-size:120%; empty-cells:hide;"
| |
| |-
| |
| | colspan=2 |
| |
| | style="padding: 5px 10px 5px 10px; background-color: #ffdddd; text-align: center" colspan=2 | Coordinates of the root prim ('''oc_''')
| |
| |- {{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 any prim
| |
| |-
| |
| | 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" | <span style="font-family: mono; font-size: 90%">oc_pos_'''wc''' = oc_pos_rc + [[llGetRegionCorner]]()<br>oc_rot_'''wc''' = oc_rot_rc</span>
| |
| |-
| |
| | 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" | <span style="font-family: mono; font-size: 90%">oc_pos_'''rc''' = [[llGetRootPosition]]()<br>oc_rot_'''rc''' = [[llGetRootRotation]]()</span>
| |
| |-
| |
| | 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" | <span style="font-family: mono; font-size: 90%">oc_pos_'''oc''' = [[ZERO_VECTOR]]<br>oc_rot_'''oc''' = [[ZERO_ROTATION]]</span>
| |
| |-
| |
| | style="padding: 5px 10px 5px 10px; color: blue; background-color: #ffffa0" | Self Coordinates
| |
| | style="padding: 5px 10px 5px 10px" | '''sc'''
| |
| | style="padding: 5px 10px 5px 10px" | <span style="font-family: mono; font-size: 90%">oc_pos_'''sc''' = - sc_pos_oc / sc_rot_oc<br>oc_rot_'''sc''' = [[ZERO_ROTATION]] / sc_rot_oc</span>
| |
| |}
| |
| | |
| As shows from the above table, LSL does not directly support '''_sc''' coordinates,
| |
| and I used conversions to fill in the tables. All in all, we listed four coordinate systems (wc, rc, oc and sc; not including the 'link_' stuff,
| |
| which isn't really a separate coordinate system)
| |
| and thus could write out four times four is sixteen ways to convert one into the other. Four of those would be trivial, and of the remaining
| |
| twelve we can express six as the inverse of the other six.
| |
| | |
| However, lets ignore the World Coordinates for now, which are just the same as the Region Coordinates with an offset for the position.
| |
| Then we have three coordinate systems (rc, oc and sc), leading to nine conversion, three of which are trivial, and of the remaining
| |
| six three can be expressed as the inverse of the other three. Those three are: oc_*_rc, sc_*_rc and sc_*_oc, which are all listed above.
| |
| The trivial ones, rc_*_rc, oc_*_oc and sc_*_sc, are all ZERO_* (the latter two also are listed in the tables above).
| |
| Finally, the inverse values, rc_*_oc, rc_*_sc and oc_*_sc can be expressed with the inversion formula given in [[#Converting between coordinate systems]].
| |
| So this is really all there is.
| |
| | |
| === The fourth dimension ===
| |
| | |
| Picture a coordinate system as three perpendicular lines, like an object in-world perhaps existing of
| |
| three really long prims in the colors red, green and blue. You can toss them into the region
| |
| where they randomly bounce around like dice – a whole bunch of them!
| |
| And then you freeze time - make a snapshot.
| |
| | |
| With the knowledge so far you can now express the position and rotation of an object or prim relative
| |
| to any of those coordinate systems – and convert between them at will.
| |
| Apply rotations and translations relative to any chosen system.
| |
| All of the coordinate systems are virtually the same! But in practice isn't the case, why is that?
| |
| | |
| The reason is that in practice coordinate systems change in time, in a way that you cannot predict.
| |
| It's like they are still tumbling and bouncing around and you have no idea where they are.
| |
| | |
| This noise comes mainly from one thing: objects are being moved. Taken into repositories and rezzed back,
| |
| rotated and possibly even scaled! In fact, not just objects, but also prims inside objects can be changed
| |
| at will at any moment and a good script must take that into account.
| |
| | |
| The art of programming therefore becomes ''design''. You must choose which coordinate systems you
| |
| want to use; what your global variables store: relative to which coordinate system!
| |
| | |
| Unfortunately, a script can ''not'' detect when this might have happened and only perform extra computations,
| |
| or re-computation, of otherwise constant variables when a prim or object has actually been changed.
| |
| There is an LSL event [[moving_start]] but that only works half on SL and not at all on opensim.
| |
| For rotation detection there is nothing.
| |
| | |
| As such, it is necessary to call the LSL functions listed in the tables above every time you
| |
| want to do anything that depends on them; which hopefully won't be too often.
| |
| | |
| The following example script can be put in the root prim of an object of any number of cubes
| |
| and allows one to sit on each cube, using [[llSitTarget]], even if you move, rotate or rescale
| |
| child prims.
| |
| | |
| <lsl>
| |
| | |
| </lsl>
| |