# Help with two axis rotation problem before I go insane

Please take a moment to consider if this thread is worth bumping.

## Recommended Posts

I have a simple linked object (see picture) with the colors representing the positive axes.  The cylinder is in the same orientation as the colored boxes with +Z at the top face.  The two boxes and the cylinder are all rotated at <0, 30, 0> with respect to the root (the white prim) and share the same center of rotation.  The root is at zero rotation.

The cylinder must rotate in increments on its local X axis.

Now, imagine that the cubes are children of the cylinder (how I wish we had proper parenting in SL!).  When the cylinder rotates in X, the cubes will follow.  Also easy to implement and is simply a matter of doing the same rotation on the cubes in the SLPPF call.

```integer id_of_cyl = 2;
rotation Rx = llEuler2Rot (<1.0, 0.0, 0.0> * DEG_TO_RAD);
rotation O;
rotation new_rot;
integer count;

for (count = 0; count < 10; count++) {
O = llList2Rot (llGetLinkPrimitiveParams (id_of_cyl, [PRIM_ROT_LOCAL]), 0);
new_rot = Rx * O;
}```

In addition to rotating with the cylinder, the cubes must also rotate on their local Z axis.  Again, this is easy and is the same solution as to what I've already written.  The issue is that both rotations should happen at the same time.  Limiting movement to one axis works for both axes.  Rotating the cubes on both axes at once produces unexpected rotation around the Y axis.  I (obviously naively) thought that it was a matter of composing the rotations like so:

```integer id_of_cyl = 2;
integer id_of_box1 = 3;
integer id_of_box2 = 4;
rotation Rx = llEuler2Rot (<1.0, 0.0, 0.0> * DEG_TO_RAD);
rotation Rz = llEuler2Rot (<0.0, 0.0, 1.0> * DEG_TO_RAD);
rotation O_cyl;
rotation O_box;
rotation new_cyl_rot;
rotation new_box_rot;
integer count;

for (count = 0; count < 10; count++) {
O_cyl = llList2Rot (llGetLinkPrimitiveParams (id_of_cyl, [PRIM_ROT_LOCAL]), 0);
O_box = llList2Rot (llGetLinkPrimitiveParams (id_od_box1, [PRIM_ROT_LOCAL]), 0);
new_cyl_rot = Rx * O_cyl;
new_box_rot = Rz * Rx * O_box;
}```

Based on research I've done, the error is due to a problem with the order applying the rotations.  However, with a orientation and two rotations, there can only be 12 ways to combine them, and I've tried them all, and all exhibit some degree of error.  As a sanity check, here are the orders I've tried.  O = current local rotation...

• Rz * Rx * O
• Rx * Rz * O
• Rz * O * Rx
• Rx * O * Rz
• O * Rx * Rz
• O * Rz * Rx
• (Rz * Rx) * O
• (Rx * Rz) * O
• (Rz * O) * Rx
• (Rx * O) * Rz
• (O * Rx) * Rz
• (O * Rz) * Rx

I must be missing something (well I'm sure I'm missing something!)  Could someone point out my error and save my sanity please?

##### Share on other sites

You are dealing with Gimbal Lock,   You'll find some excellent tutorials with a Google search. As long as you use llEuler2Rot, you will continue to run into the problem.

##### Share on other sites

I'm going from zero degrees to 10 degrees, not 90 degrees, how can I be dealing with gimbal lock?  And even if I am, what are the alternatives to make this actually work?

Edited by stripeyzebra
##### Share on other sites

Sorry.  That's what happens if I type something in the middle of the night.  Gimbal lock is a typical worry, but I think the problem here is llEuler2Rot itself.  If you look at the function in the LSL wiki, you'll see that the llEuler2Rot rotation is calculated from

```v/=2;
rotation k = <0.0, 0.0, llSin(v.z), llCos(v.z)> * <0.0, llSin(v.y), 0.0, llCos(v.y)> * <llSin(v.x), 0.0, 0.0, llCos(v.x)>;```

I think you're dealing with noise due to roundoff errors when the function calculates k from the sine or cosine of a small angle like 1.0 degrees.  When you make small rotations of a degree at a time, the errors accumulate.  You might try just doing a final cleanup adjustment after you have finished doing the incremental rotations.

##### Share on other sites

Firstly, thanks so much for your input so far.  Based on your suggestion that the increments were so small that they were producing roundoff error (even though the same increments work perfectly in one axis), I increased the increment to 10 degrees.  Here's a picture of the object rotated 40 degrees on X and 20 degrees on Z.  This is the same error produced with my original increment (which is actually significantly smaller that 1 degree.  1 degree was easier to talk about that SPEED/ FRAMERATE)

##### Share on other sites

Incrementing Z by 1.0 degree each time, and just do Rz * new_cyl_rot on the boxes might be an option?

Like:

```float gfRzInc;

loop
{
gfRzInc += 1.0;
Rz = llEuler2Rot (<0.0, 0.0, gfRzInc> * DEG_TO_RAD);
new_cyl_rot = Rx * O_cyl;
new_box_rot = Rz * new_cyl_rot;

}```

##### Share on other sites

2 hours ago, arton Rotaru said:

Incrementing Z by 1.0 degree each time, and just do Rz * new_cyl_rot on the boxes might be an option?

Like:

```
float gfRzInc;

loop
{
gfRzInc += 1.0;
Rz = llEuler2Rot (<0.0, 0.0, gfRzInc> * DEG_TO_RAD);
new_cyl_rot = Rx * O_cyl;
new_box_rot = Rz * new_cyl_rot;

}```

How is your rotation composition different from

`new_box_rot = Rz * Rx * O_cyl;`

except the composition order (which, believe me, I've tried)?  I've actually tried the "save the start rotation of the objects and add increments to them" approach, and I'd like to get back to that as it optimizes the increment loop.  Unfortunately you still have to do Rz * Rx * O_cyl, no matter if the increment is always the same and O_cyl is the current position, or the increment increases and O_cyl is the "start" position.

##### Share on other sites

12 hours ago, stripeyzebra said:

How is your rotation composition different from

```
new_box_rot = Rz * Rx * O_cyl;```

Having a fixed 1 degree rotation around z will rotate the boxes just that initial 1 degree obviously, and that's it. Because the cylinder doesn't rotate around local Z.

##### Share on other sites

I thought only insane people understand this stuff.

# Solved!

```default {
state_entry () {
integer id_of_cyl = 2;
integer id_of_box1 = 3;
integer id_of_box2 = 4;
rotation initial_rot = llEuler2Rot (<0.0, 30.0, 0.0> * DEG_TO_RAD);
rotation Rx = llEuler2Rot (<1.0, 0.0, 0.0> * DEG_TO_RAD);
rotation Rz = llEuler2Rot (<0.0, 0.0, 1.0> * DEG_TO_RAD);
rotation O_cyl;
rotation O_box;
rotation relative_rot;
rotation new_cyl_rot;
rotation new_box_rot;
integer count;

// Set initial orientation

for (count = 0; count < 10; count++) {
O_cyl = llList2Rot (llGetLinkPrimitiveParams (id_of_cyl, [PRIM_ROT_LOCAL]), 0);
O_box = llList2Rot (llGetLinkPrimitiveParams (id_of_box1, [PRIM_ROT_LOCAL]), 0);
relative_rot = O_box / O_cyl;
new_cyl_rot = Rx * O_cyl;
new_box_rot = Rz * relative_rot * new_cyl_rot;
}
}
}```

• 2
##### Share on other sites

Thanks for posting what worked for you~! Much appreciated. ^-^

##### Share on other sites

Please take a moment to consider if this thread is worth bumping.