Rotation School Lesson 1: Order Matters

Sun 03 May 2009 | -- (permalink)

Lots of LSL scripters, even intermediate to advanced ones, have trouble with LSL rotations. I did too for quite a while, until I forced myself to do a series of projects that helped me figure them out. This post is the first in a series to help people understand how LSL rotations work.

In LSL, you use the same operator to combine rotations ("*") as you use to perform multiplication. Since people starting with the language are probably familiar with multiplication already, they sensibly assume that combining rotations must work similarly, since it's done with the same character.

In arithmetic, for example, "2 * 3" is exactly the same as "3 * 2". So scripters assume that "rot1 * rot2" must be the same as "rot2 * rot1". This assumption, however, is completely incorrect, and creates no end of frustration for LSL scripters.

When combining rotations, order matters. The goal of this post is to show you why.

I'll start with a boat, which I've picked because it's easy to tell up from down, forward from back, etc. You can get a copy of the boat here.

rotboat_0011

When rezzed at zero rotation, the boat's nose will be pointing in the positive direction along the x axis.

This example will also use four tiny scripts. Create these 4 scripts in your inventory, not in the object. We'll drop them in one by one as we go.

~~~~ {lang="lsl"} //script 1 - rotate 90 degrees around the x axis default { state_entry() { llSetRot(llGetRot() * llEuler2Rot(<90, 0, 0> * DEG_TO_RAD)); llRemoveInventory(llGetScriptName()); } } //script 2 - rotate 90 degrees around the y axis default { state_entry() { llSetRot(llGetRot() * llEuler2Rot(<0, 90, 0> * DEG_TO_RAD)); llRemoveInventory(llGetScriptName()); } } //script 3 - rotate 90 degrees around the z axis default { state_entry() { llSetRot(llGetRot() * llEuler2Rot(<0, 0, 90> * DEG_TO_RAD)); llRemoveInventory(llGetScriptName()); } } //script 4, set to ZERO_ROTATION default { state_entry() { llSetRot(ZERO_ROTATION); llRemoveInventory(llGetScriptName()); } } ~~~~

Now to show why order matters when combining rotations, we'll drop our scripts into the boat object, one at a time.

Drag the "rotate around x" script from your inventory and onto the boat object. After rotating the boat 90 degrees around the x axis, it should be on its side like this:

rotboat_002

Now drag the "rotate around y" script into the object. The result should look like this, with the nose pointing down and the sails pointing to the right:

rotboat_006

Finally, drag the "rotate around z" script into the boat:

rotboat_007

If you're standing behind the object like I was when taking these pictures, you should now be staring at the bottom of the boat, with the nose pointed straight down.

If we were going to do all of those rotations in one script, it would look like this:

~~~~ {lang="lsl"} //rotate 90 degrees around x, then around y, then around z default { state_entry() { rotation rot1 = llGetRot() * llEuler2Rot(<90, 0, 0> * DEG_TO_RAD); rotation rot2 = rot1 * llEuler2Rot(<0, 90, 0> * DEG_TO_RAD); rotation rot3 = rot2 * llEuler2Rot(<0, 0, 90> * DEG_TO_RAD); llSetRot(rot3); llRemoveInventory(llGetScriptName()); } } ~~~~

So far so good. Now let's prove that order matters when combining rotations. Drop the "zero rotation" script into the boat so we can start with a clean slate.

rotboat_0011

Now this time we'll use the same three rotation scripts, but in reverse order by rotating around z, then around y, then around x. First drop in the "rotate around z" script. The boat should turn to point to the left like this:

rotboat_003

Now drop in the "rotate around y" script. The boat will turn on its side like this:

rotboat_004

Finally, drop in the "rotate around x" script. The boat will turn with its nose pointing straight up in the air.

rotboat_005

Hopefully by this point, you can see why the order in which you combine rotations will affect the final rotation of the object. This:

~~~~ {lang="lsl"} llSetRot(rot1 * rot2 * rot3); ~~~~

...is not the same as this:

~~~~ {lang="lsl"} llSetRot(rot3 * rot2 * rot1); ~~~~

Bottom line: If you're frustrated because you can't seem to script the rotation you're picturing in your head, break it down into steps and make sure you're doing them in the right order.