Jump to content

Help with two axis rotation problem before I go insane


stripeyzebra
 Share

You are about to reply to a thread that has been inactive for 1525 days.

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

Recommended Posts

I have a simple linked objeexample_001.png.ef56b4a581da2dd53fc8e2057beb1562.pngct (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;
    llSetLinkPrimitiveParamsFast (id_of_cyl, [PRIM_ROT_LOCAL, new_rot]);
}

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;                       
    llSetLinkPrimitiveParamsFast (LINK_SET, [PRIM_LINK_TARGET, id_of_cyl, PRIM_ROT_LOCAL, new_cyl_rot,
                           					 PRIM_LINK_TARGET, id_of_box1, PRIM_ROT_LOCAL, new_box_rot,
                           					 PRIM_LINK_TARGET, id_of_box2, PRIM_ROT_LOCAL, new_box_rot]);
}

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?

Link to comment
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.

 

Link to comment
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)example_002.thumb.png.f370ae16868c77d40dc340a7808ade1d.png

Link to comment
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;

}

 

Link to comment
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.

Link to comment
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.

Link to comment
Share on other sites

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
        llSetLinkPrimitiveParamsFast (LINK_SET, [PRIM_LINK_TARGET, id_of_cyl, PRIM_ROT_LOCAL, initial_rot,
                                                 PRIM_LINK_TARGET, id_of_box1, PRIM_ROT_LOCAL, initial_rot,
                                                 PRIM_LINK_TARGET, id_of_box2, PRIM_ROT_LOCAL, initial_rot]);

        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;                       
            llSetLinkPrimitiveParamsFast (LINK_SET, [PRIM_LINK_TARGET, id_of_cyl, PRIM_ROT_LOCAL, new_cyl_rot,
                                                     PRIM_LINK_TARGET, id_of_box1, PRIM_ROT_LOCAL, new_box_rot,
                                                     PRIM_LINK_TARGET, id_of_box2, PRIM_ROT_LOCAL, new_box_rot]);
        }
    }
}

 

  • Like 2
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1525 days.

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...