Jump to content

uRot2Euler() ?


Quistess Alpha
 Share

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

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

Recommended Posts

Inspired by a recent thread I've been working on a few examples of how to use quaternions and such for rotations. as a part of that it would be useful to have a function that is the inverse of llEuler2Rot, i.e. that takes a rotation and gives the SL-euler angle. I got a version working for ZYX intrinsic == XYZ extrinsic rotations (which is much more intuitive than SL's Euler angle implementation IMO), but not for XYZi/ZYXe rotations (the convention llEuler2Rot uses).

vector uRot2Euler(rotation rot) // borked :( only works on single-axis rotations.
{
    vector ret;
    vector temp = <0,1,0>*rot;
    ret.x = llAtan2(temp.z,temp.y);
    rot = rot/llEuler2Rot(<ret.x,0,0>);
    temp = <1,0,0>*rot;
    ret.y = -llAtan2(temp.z,temp.x);
    rot = rot/llEuler2Rot(<0,ret.y,0>);
    temp = <1,0,0>*rot;
    ret.z = llAtan2(temp.y,temp.x);
    return ret;
}

vector uRot2Euler2(rotation rot)
{
    vector ret;
    vector temp = <1,0,0>*rot;
    ret.z = llAtan2(temp.y,temp.x);
    rot = rot/llEuler2Rot(<0,0,ret.z>);
    temp = <1,0,0>*rot;
    ret.y = -llAtan2(temp.z,temp.x);
    rot = rot/llEuler2Rot(<0,ret.y,0>);
    temp = <0,1,0>*rot;
    ret.x = llAtan2(temp.z,temp.y);
    return ret;
}
rotation uEuler2Rot2(vector v)
{   return //ZYX intrinsic = XYZ extrinsic
        <llSin(v.x/2),0,0,llCos(v.x/2)> *
        <0,llSin(v.y/2),0,llCos(v.y/2)> *
        <0,0,llSin(v.z/2),llCos(v.z/2)> ;
}
rotation uEuler2Rot(vector v) // duplicates llEuler2Rot()
{   return //XYZ intrinsic = ZYX extrinsic
        <0,0,llSin(v.z/2),llCos(v.z/2)> *
        <0,llSin(v.y/2),0,llCos(v.y/2)> *
        <llSin(v.x/2),0,0,llCos(v.x/2)> ;
}
default
{
    state_entry()
    {
        rotation rot =
            //<0,0,0,1>;
            //< 0.5, -0.5, 0.5, 0.5 >; //** breaks test 1.
            //< 0.5,  0.5,-0.5, 0.5 >; //** breaks test 1.
            //< 0.5,  0.5, 0.5,0.5>;// ** double pass
            llEuler2Rot(<00,00,90>*DEG_TO_RAD);
        llOwnerSay("---test 1---");
        llOwnerSay((string)rot);
        llOwnerSay((string)(uRot2Euler(rot)*RAD_TO_DEG));
        llOwnerSay((string)llEuler2Rot(uRot2Euler(rot)));
        
        llOwnerSay("---test 2---");
        llOwnerSay((string)rot);
        llOwnerSay((string)uRot2Euler2(rot));
        llOwnerSay((string)uEuler2Rot2(uRot2Euler2(rot)));
        
        /*llOwnerSay("---test 3---");
        vector vec = <15,-45,35>*DEG_TO_RAD;
        llOwnerSay((string)uEuler2Rot(vec));
        llOwnerSay((string)llEuler2Rot(vec));*/
        
    }
}

Does anyone perhaps have a working version of uRot2Euler() in their back pocket?

Edited by Quistessa
  • Like 1
Link to comment
Share on other sites

the issue with this is that a lot of eulers translate to the same quaternion. example:

<-180,-180,-180>
<-180,-180, 180>
<-180, 180,-180>
< 180,-180,-180>
< 180,-180, 180>
< 180, 180,-180>
< 180, 180, 180>
<   0,   0,   0>

all translate to <0, 0, 0, 1>

< 180, 180,   0>
<   0,   0, 180>

both translate to <0, 0, 1, 0>

<   0, 180, 180>
< 180,   0,   0>

to <1, 0, 0, 0>


and when we go to convert it back, we have no way of knowing what the degrees typed in were

a thing is that eulers derived from quaternions are not quite the same thing as degrees. A way to help to get our heads round this is with a degrees to euler translator. Example:

vector Deg2Euler(vector degrees)
{
   return llRot2Euler(llEuler2Rot(degrees * DEG_TO_RAD)) * RAD_TO_DEG;
}

// examples:

vector degrees = <180, 180, 180>;
vector euler = Deg2Euler(degrees);
// euler = <0,0,0>

degrees = <180, -180, 180>;
euler = Deg2Euler(degrees);
// euler = <0,0,0>

 

then after a time we learn to do this translation in our heads

  • Like 1
Link to comment
Share on other sites

14 minutes ago, Mollymews said:

the issue with this is that a lot of eulers translate to the same quaternion.

I'm aware of that, it makes the problem hard, but it shouldn't make it intractable. I'm just looking for a function such that

llEuler2Rot(uRot2Euler(rot))

is roughly the same as rot. (floating point errors and normalization make exact matching unlikely)

There are multiple eulers for a given orientation, but only two distinct quaternions.

  • Like 1
Link to comment
Share on other sites

translation shlanslation:

default
{
    touch_start(integer total_number)
    {
        rotation rot_zero = llGetRot();
        rotation rot = llGetRot();
        vector ret;
        vector temp;

        temp = <0,0,1>*rot;
        ret.x = llAtan2(temp.z,temp.y)-PI/2;
        rot = rot/llEuler2Rot(<ret.x,0,0>);
        llSetRot(rot);
        llSetText((string)(ret*RAD_TO_DEG),<0,1,1>,1.0);
        llSleep(1.0);
        
        temp = <0,0,1>*rot;
        ret.y = -llAtan2(temp.z,temp.x)+PI/2;
        rot = rot/llEuler2Rot(<0,ret.y,0>);
        llSetRot(rot);
        llSetText((string)(ret*RAD_TO_DEG),<1,0,1>,1.0);
        llSleep(1.0);
        
        temp = <1,0,0>*rot;
        ret.z = llAtan2(temp.y,temp.x);
        rot = rot/llEuler2Rot(<0,0,ret.z>);
        llSetRot(rot);
        llSetText((string)(ret*RAD_TO_DEG),<1,1,0>,1.0);
        llSleep(3.0);
        
        llSetRot(rot_zero);
    }
}

Nothing a little stepping through 1 step at a time can't solve.

Put that in a prim and watch it magically work out its rotation. Sure slightly weird things will happen if it's /exactly/ on an edge case *cough*<0,90,0>*cough* but it's good enough for what I'm going to use it for. . .

Edited by Quistessa
  • Like 1
Link to comment
Share on other sites

1 hour ago, Quistessa said:

translation shlanslation:

your code can work for a subset of the matrix yes

it won't work with all elements of the matrix

example using your code:    
 

rotation rot = <1,0,0,0>; // rotated on the x axis by PI (3.14..) radians
         
vector ret;
vector temp;
temp = <0,0,1>*rot;
ret.x = llAtan2(temp.z,temp.y)-PI/2;
rot = rot/llEuler2Rot(<ret.x,0,0>);
// llSetRot(rot);
// llSetText((string)(ret*RAD_TO_DEG),<0,1,1>,1.0);
// llSleep(1.0);
temp = <0,0,1>*rot;
ret.y = -llAtan2(temp.z,temp.x)+PI/2;
rot = rot/llEuler2Rot(<0,ret.y,0>);
// llSetRot(rot);
        

... at this point rot = <0,0,0,1> ...

 

  • Like 1
Link to comment
Share on other sites

7 hours ago, Quistessa said:

Inspired by a recent thread

I must have missed that.

As a result of my recent experiences I've realised that there is a definite need for something like this. For many of us the wiki page and various examples lead us down a monkey-see-monkey-do path: we use the functions with an imperfect understanding of what is actually happening. For most of the time that's fine.

Every now and then somebody turns up who isn't happy with the "stop asking why, just do the calculations" dogma, rather like Quantum Physics with the Copenhagen Interpretation approach, "never mind that it doesn't make sense, just get on with using the formulae, they're proven". Such people need to understand in their heads what is going on when they use rotations.

I've got a few insights into Quarternions as a result of trying to find a way to demonstrate to somebody that vector*rotation is doing for you what calculating three separate triangles does. I can explain it quite well by showing that the x,y,z components of a rotation equate pretty well to multiples of PI, but the fourth component, explained elsewhere as the total amount of rotation on the object, I have no real explanation for at the moment. As Molly has shown and as I have scratched my head over, various combinations of X,Y,Z come back to the same value in the W component.

I am especially intrigued by the state so far, in that it seems impossible to precisely reverse rotations to Eulers in all cases?

 

Edited by Profaitchikenz Haiku
  • Like 1
Link to comment
Share on other sites

to help with understanding the relationship between quaternions and eulers I paste here a SL port of the standard algorithms to convert between the two

the standard algorithms are explained here: https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles

i haven't tested it much, but am reasonably sure llEuler2Rot and llRot2Euler use the same algorithms
 

rotation Vec2Quat(vector v)
{
    float cz = llCos(v.z * 0.5);
    float sz = llSin(v.z * 0.5);
    float cy = llCos(v.y * 0.5);
    float sy = llSin(v.y * 0.5);
    float cx = llCos(v.x * 0.5);
    float sx = llSin(v.x * 0.5);

    rotation q;
    q.x = sx * cy * cz - cx * sy * sz;
    q.y = cx * sy * cz + sx * cy * sz;
    q.z = cx * cy * sz - sx * sy * cz;
    q.s = cx * cy * cz + sx * sy * sz;
    return q;
}

vector Quat2Vec(rotation q)
{
    vector v;
    // x-axis
    float s = 2 * (q.s * q.x + q.y * q.z);
    float c = 1 - 2 * (q.x * q.x + q.y * q.y);
    v.x = llAtan2(s, c);
    // y-axis
    s = 2 * (q.s * q.y - q.z * q.x);
    if (llFabs(s) >= 1)
        v.y = (-1 | (s < 0)) * PI_BY_TWO;
    else
        v.y = llAsin(s);
    // z-axis
    s = 2 * (q.s * q.z + q.x * q.y);
    c = 1 - 2 * (q.y * q.y + q.z * q.z);
    v.z = llAtan2(s, c);
    return v;
}
   
default
{
    state_entry()
    {
        vector v = <PI_BY_TWO, 0, 0>;
        
        rotation q = Vec2Quat(v);
        vector w = Quat2Vec(q);
        vector u = llRot2Euler(q);
       
        llOwnerSay(llDumpList2String([v, q, w, u], " "));
    }
}

 

 


 

 

  • Like 2
Link to comment
Share on other sites

7 hours ago, Mollymews said:

your code can work for a subset of the matrix yes

it won't work with all elements of the matrix

example using your code:    

Quote


... at this point rot = <0,0,0,1> ...

That's the point. for all inputs of rot, after my algorithm rot is rotated to <0,0,0,1> and ret is mapped to the euler vector for the original rotation.

Your algorithm seems to use the standard pitch-yaw-roll convention. SL doesn't.

vector uRot2Euler(rotation rot)
{
        vector ret;
        vector temp;

        //x-axis
        temp = <0,0,1>*rot;
        ret.x = llAtan2(temp.z,temp.y)-PI/2;
        rot = rot/llEuler2Rot(<ret.x,0,0>);
        //y-axis
        temp = <0,0,1>*rot;
        ret.y = -llAtan2(temp.z,temp.x)+PI/2;
        rot = rot/llEuler2Rot(<0,ret.y,0>);
        //z-axis
        temp = <1,0,0>*rot;
        ret.z = llAtan2(temp.y,temp.x);
        return ret;
}

vector uRot2Euler2(rotation rot)
{
    vector ret;
    vector temp = <1,0,0>*rot;
    ret.z = llAtan2(temp.y,temp.x);
    rot = rot/llEuler2Rot(<0,0,ret.z>);
    temp = <1,0,0>*rot;
    ret.y = -llAtan2(temp.z,temp.x);
    rot = rot/llEuler2Rot(<0,ret.y,0>);
    temp = <0,1,0>*rot;
    ret.x = llAtan2(temp.z,temp.y);
    return ret;
}
vector Quat2Vec(rotation q)
{
    vector v;
    // x-axis
    float s = 2 * (q.s * q.x + q.y * q.z);
    float c = 1 - 2 * (q.x * q.x + q.y * q.y);
    v.x = llAtan2(s, c);
    // y-axis
    s = 2 * (q.s * q.y - q.z * q.x);
    if (llFabs(s) >= 1)
        v.y = (-1 | (s < 0)) * PI_BY_TWO;
    else
        v.y = llAsin(s);
    // z-axis
    s = 2 * (q.s * q.z + q.x * q.y);
    c = 1 - 2 * (q.y * q.y + q.z * q.z);
    v.z = llAtan2(s, c);
    return v;
}
   
default
{
    touch_start(integer total_number)
    {
        //rotation q = <0.5,-0.5,-0.5,0.5>;
        //rotation q = <1,0,0,0>;
        //rotation q = <0.5,-0.5,0.5,0.5>;
        //rotation q = llGetRot();
        rotation q =<0.306186, 0.176777, 0.306186, 0.883884>;
        vector a = llRot2Euler(q)*RAD_TO_DEG;
        vector b = uRot2Euler(q)*RAD_TO_DEG;
        vector c = Quat2Vec(q)*RAD_TO_DEG;
        vector d = uRot2Euler2(q)*RAD_TO_DEG;
        string text =  llDumpList2String([q,a,b,c,d],"\n");
        //llOwnerSay(text);
        llSetText(text,<0,1,0>,1.0);
    }
}

output:

<30.000000, 30.000000, 30.000000> (llRot2Euler)
<30.000000, 30.000000, 30.000000> (uRot2Euler) (my implementation)
<40.893390, 7.180757, 40.893390> (molly's implementation)
<40.893390, 7.180757, 40.893390> (my first working implementation)

Edited by Quistessa
added output.
  • Like 1
Link to comment
Share on other sites

9 hours ago, Quistessa said:

<30.000000, 30.000000, 30.000000> (llRot2Euler)

<30.000000, 30.000000, 30.000000> (uRot2Euler) (my implementation)

<40.893390, 7.180757, 40.893390> (molly's implementation)

when we mix up our matrixes then we get different results as you show

when we use the method that applies to the matrix we are using then

     

        vector v = <30.0, 30.0, 30.0>;
        
        rotation a = llEuler2Rot(v * DEG_TO_RAD);
        vector b = llRot2Euler(a) * RAD_TO_DEG;
        
        rotation c = Vec2Quat(v * DEG_TO_RAD);
        vector d = Quat2Vec(c) * RAD_TO_DEG;
        
        rotation e = llEuler2Rot(v * DEG_TO_RAD);
        vector f = uRot2Euler(e) * RAD_TO_DEG;
   
       
        llOwnerSay(llDumpList2String(["LSL", v, a, b], " "));
        llOwnerSay(llDumpList2String(["Mol", v, c, d], " "));
        llOwnerSay(llDumpList2String(["Qui", v, e, f], " "));
      
        // output is      
        // LSL <30.000000, 30.000000, 30.000000> <0.306186, 0.176777, 0.306186, 0.883884> <30.000000, 30.000000, 30.000000>
        // Mol <30.000000, 30.000000, 30.000000> <0.176777, 0.306186, 0.176777, 0.918559> <30.000000, 30.000000, 30.000000>
        // Qui <30.000000, 30.000000, 30.000000> <0.306186, 0.176777, 0.306186, 0.883884> <30.000000, 30.000010, 30.000000>

 

 

  • Like 2
Link to comment
Share on other sites

i looked up in the SL viewer source code for how Linden does this:

rot to euler (file: llquaternion)

// SJB: This code is correct for a logicly stored (non-transposed) matrix;
// Our matrices are stored transposed, OpenGL style, so this generates the
// INVERSE matrix, or the CORRECT matrix form an INVERSE quaternion.
// Because we use similar logic in LLMatrix3::quaternion(),
// we are internally consistant so everything works OK :)

LLMatrix3 LLQuaternion::getMatrix3(void) const
{
   LLMatrix3 mat;
   F32 xx, xy, xz, xw, yy, yz, yw, zz, zw;

   xx = mQ[VX] * mQ[VX];
   xy = mQ[VX] * mQ[VY];
   xz = mQ[VX] * mQ[VZ];
   xw = mQ[VX] * mQ[VW];

   yy = mQ[VY] * mQ[VY];
   yz = mQ[VY] * mQ[VZ];
   yw = mQ[VY] * mQ[VW];

   zz = mQ[VZ] * mQ[VZ];
   zw = mQ[VZ] * mQ[VW];

   mat.mMatrix[0][0]  = 1.f - 2.f * ( yy + zz );
   mat.mMatrix[0][1]  = 2.f * ( xy + zw );
   mat.mMatrix[0][2]  = 2.f * ( xz - yw );

   mat.mMatrix[1][0]  = 2.f * ( xy - zw );
   mat.mMatrix[1][1]  = 1.f - 2.f * ( xx + zz );
   mat.mMatrix[1][2]  = 2.f * ( yz + xw );

   mat.mMatrix[2][0]  = 2.f * ( xz + yw );
   mat.mMatrix[2][1]  = 2.f * ( yz - xw );
   mat.mMatrix[2][2]  = 1.f - 2.f * ( xx + yy );

   return mat;
}


euler to rot (file: m3math)

// SJB: This code is correct for a logicly stored (non-transposed) matrix;
// Our matrices are stored transposed, OpenGL style, so this generates the
// INVERSE quaternion (-x, -y, -z, w)!
// Because we use similar logic in LLQuaternion::getMatrix3,
// we are internally consistant so everything works OK :)

LLQuaternion LLMatrix3::quaternion() const
{
   LLQuaternion quat;
   F32 tr, s, q[4];
   U32 i, j, k;
   U32 nxt[3] = {1, 2, 0};

   tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2];

   // check the diagonal
   if (tr > 0.f)
   {
      s = (F32)sqrt (tr + 1.f);
      quat.mQ[VS] = s / 2.f;
      s = 0.5f / s;
      quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s;
      quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s;
      quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s;
   }
   else
   {        
      // diagonal is negative
      i = 0;
      if (mMatrix[1][1] > mMatrix[0][0])
         i = 1;
      if (mMatrix[2][2] > mMatrix[i][i])
     i = 2;

      j = nxt[i];
      k = nxt[j];

      s = (F32)sqrt((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f);

      q[i] = s * 0.5f;

      if (s != 0.f)
         s = 0.5f / s;

      q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s;
      q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s;
      q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s;

      quat.setQuat(q);
   }
   return quat;
}

 

would be possible to port this code to LSL. Be a little bit messy tho

  • Like 1
Link to comment
Share on other sites

37 minutes ago, Innula Zenovka said:

Sorry, I don't understand, but what's the difference between these userfunctions and llRot2Euler?

SOme of them are identical to the ll functions, some use a different (more intuitive IMO) convention:

group 1: llRot2Euler() uRot2Euler() (not the version in my first post, read down) are the inverse of llEuler2Rot()

should be roughly identical to the linden-versions.

group 2 : Vec2Quat() uRot2Euler2() are the inverse of Quat2Vec()

Do the same things but the actual output is different. if you (for some reason) find some pitch-yaw-roll angles 'in the wild' they will more probably work with group 2 than group 1.

according to Wikipedia there are no less than 12 different conventions for "euler angles". https://en.wikipedia.org/wiki/Euler_angles

Edit: fun factoid: 'Group 2' are called 'nautical angles' or 'Cardan angles' . Group one doesn't have a fancy name, therefore group 2 beats group 1 :P.

also, part of the point of the thread as wolfie says, I didn't realize llRot2Euler existed.

Edited by Quistessa
I read Wikipedia.
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

I'm slightly worried by something Molly has been illustrating, it suggests there is non-commutability between Eulers and quaternions (3-to-1 mapping between some eulers and a single rotation for example). However, these only seem to be occurring at distinct positions such as right-angles and reversals?

As far as I can see, the main point of having the two conversions is that very very few of us can visualise rotations as quarternions, and very few can even visualise things as radians when we're building. If i were the norm, or even achievable with a little education, the build floater for orientation could be in radians or even quarternions instead of degrees.

 

  • Like 1
Link to comment
Share on other sites

6 hours ago, Wulfie Reanimator said:

I was gonna ask that when the thread was first posted, but I assumed the whole point was to see how the calculations are done.

the conversation goes back to a question previous asked. Which was how to be able to use vector degree so that Rot2Deg(llGetRot()) will return the same degree value that was input

to begin to do this then we have to understand how quaternions and vectors are translated/converted, so the code examples posted

so begin...

we input llSetRot(<0, 91, 0> * DEG_TO_RAD) and llRot2Euler(llGetRot()) * RAD_TO_DEG returns <180,  89, 180>

and we think we will write our function Rot2Deg() which returns <0,91,0>, the same vector degree we input

but..

suppose we input <180, 89, 180> then our Rot2Deg is going to return <0, 91, 0> and not what we entered <180, 89, 180>

this is the issue with trying to do this, we have to code up a translation matrix for all degree values that we want to work with. and then restrict the input to that matrix. I.e if we try to input <180, 89, 180> then the matrix raises an error: Value out of bounds

or..

we can go: hmm! that's going to be a pain. Maybe I spend a bit of time looking at the values returned by llRot2Euler() * RAD_TO_DEG, and I will learn what they are and just input them. So that same in is same out

 

i just add on here. I had a bit more dig into llQuaternion and ported the following code, which should output the same as llEuler2Rot(). Which can be used as the companion to Quitessa's uRot2Euler, for exploring these things further. In a close enough is good enough way

rotation uEuler2Rot(vector v)
{
    v.x *= 0.5;
    v.y *= 0.5;
    v.z *= 0.5;
    float sinX = llSin(v.x);
    float cosX = llCos(v.x);
    float sinY = llSin(v.y);
    float cosY = llCos(v.y);
    float sinZ = llSin(v.z);
    float cosZ = llCos(v.z);
    rotation r;
    r.x = sinX * cosY * cosZ + cosX * sinY * sinZ;
    r.y = cosX * sinY * cosZ - sinX * cosY * sinZ;
    r.z = cosX * cosY * sinZ + sinX * sinY * cosZ;
    r.s = cosX * cosY * cosZ - sinX * sinY * sinZ;
    return r;
}

 

 

 

 

Edited by Mollymews
+
  • Like 2
  • Thanks 1
Link to comment
Share on other sites

For didactic purposes, I'll provide a mathematically equivalent version of uEuler2Rot:

rotation uEuler2Rot(vector v) // equivalent to llEuler2Rot
{   return //XYZ intrinsic = ZYX extrinsic
        <0,0,llSin(v.z/2),llCos(v.z/2)> *
        <0,llSin(v.y/2),0,llCos(v.y/2)> *
        <llSin(v.x/2),0,0,llCos(v.x/2)> ;
}
rotation uEuler2Rot2(vector v) //Yaw Pitch Roll
{   return //ZYX intrinsic = XYZ extrinsic
        <llSin(v.x/2),0,0,llCos(v.x/2)> *
        <0,llSin(v.y/2),0,llCos(v.y/2)> *
        <0,0,llSin(v.z/2),llCos(v.z/2)> ;
}

It's just doing global rotations on the 3 axes. one nice way of understanding what a Quaternion is is to convert it to 'axis-angle' form:

list uRot2AxisAngle(rotation q)
{   if(q.s==1.0) return [<1,0,0>,0.0];

    float s = llSin(llAcos(q.s));
    llOwnerSay((string)s+" "+(string)q.s); // debug
    return [<q.x/s,q.y/s,q.z/s>,llAcos(q.s)*2];
}
rotation uAxisAngle2Rot(vector v, float th)
{   float s = llSin(th/2);
    return <s*v.x,s*v.y,s*v.z,llCos(th/2)>;
}

default
{
    touch_start(integer total_number)
    {   list aa = uRot2AxisAngle(llGetRot());
        
        llSetText(
            (string)(llList2Vector(aa,0))+
            (string)(llList2Float (aa,1)*RAD_TO_DEG),
            <0,1,0>,1.0);
    }
}

something to try yourself IRL: take a roughly-spherical (as you get better at this you can try with less and less spherical) small object and place it between your thumb and forefinger, then rotate the object. if the line between your fingers passes through the center of the object, the axis between forefinger and thumb is the axis of rotation. Beieve it or not you can move the object to any orientation with only one of these operations. (assuming you can actually hold on to the object on any axis).

Edited by Quistessa
  • Like 2
Link to comment
Share on other sites

7 hours ago, Quistessa said:

For didactic purposes, I'll provide a mathematically equivalent version of uEuler2Rot:

for a tiny speed increase multiply by 0.5 rather than divide by 2

example: llCos(v.z * 0.5)

other than that I like it

  • Like 2
Link to comment
Share on other sites

just a fyi

looking into llquaternion it turns out that Linden use the inverse of the standard algorithm. The comments say that this better suits Linden for OpenGL purposes. Something I didn't know til I had a look

this is the standard:

r.x = sinX * cosY * cosZ - cosX * sinY * sinZ;
r.y = cosX * sinY * cosZ + sinX * cosY * sinZ;
r.z = cosX * cosY * sinZ - sinX * sinY * cosZ;
r.s = cosX * cosY * cosZ + sinX * sinY * sinZ;

    
and this is the Linden inverse

r.x = sinX * cosY * cosZ + cosX * sinY * sinZ;
r.y = cosX * sinY * cosZ - sinX * cosY * sinZ;
r.z = cosX * cosY * sinZ + sinX * sinY * cosZ;
r.s = cosX * cosY * cosZ - sinX * sinY * sinZ;

note that + and - are inverted

 

  • Like 2
Link to comment
Share on other sites

25 minutes ago, Mollymews said:

looking into llquaternion it turns out that Linden use the inverse of the standard algorithm.

(I believe I already mentioned that. . .)

26 minutes ago, Mollymews said:

The comments say that this better suits Linden for OpenGL purposes. Something I didn't know til I had a look

Huh, nice to know they have some reason for making it that way. A shame that it makes it harder to use for anything other than single-axis rotations.

  • Like 2
Link to comment
Share on other sites

1 hour ago, Mollymews said:

i think it would be quite good if somebody with wiki edit capabilities could add Quistessa's uEuler2Rot and uRot2Euler to  the respective llEuler2Rot and llRot2Euler pages, as LSL coded examples of how what these functions do. Similar to the LSL coded examples of other rotational functions on the wiki

edit add. looks like somebody already did for llEuler2Rot. If they could do the same for llRot2Euler that would be good

I can do that if @Quistessa wants it. Which version of the function(s) would I add exactly?

Also, does it matter that uRot2Euler seems to be going in XYZ order despite the wiki page saying "The Euler angle vector (in radians) is converted from a rotation by doing the rotations around the 3 axes in Z, Y, X order."? Could it be edited for clarity?

Edited by Wulfie Reanimator
Link to comment
Share on other sites

39 minutes ago, Wulfie Reanimator said:

Also, does it matter that uRot2Euler seems to be going in XYZ order despite the wiki page saying "The Euler angle vector (in radians) is converted from a rotation by doing the rotations around the 3 axes in Z, Y, X order."? Could it be edited for clarity?

XYZ order rotations about global/extrinsic axes are the same as ZYX order rotations about local/intrinsic axes. so it is technically correct to say that llEuler2Rot rotates the object about the X, then Y, then Z, axes of the object. IMO it would be better if it said that llEuler2Rot rotates in ZYX order about the global axes.

Quote

The Euler angle vector (in radians) is converted to a rotation by doing the rotations around the 3 axes in Z, Y, X order. So llEuler2Rot(<1.0, 2.0, 3.0> * DEG_TO_RAD) generates a rotation by first rotating 3 degrees around the global Z axis, then rotating the result around the global Y axis, and finally rotating that 1 degree around the global X axis.

Seems to be what the wiki says now and that's accurate.

Might be nice to add something to the wiki saying Euler2rot/Rot2Euler are /not/ nautical angles/roll-pitch-yaw.

p.s. feel free to add/not add any of my examples or derivatives thereof to the wiki.

Link to comment
Share on other sites

I just realized that LL also implements quaternions themselves opposite form standard. i*j=-k . (or equivalently you could imagine that ll flips every quaternion expression before evaluating it. . .)

llOwnerSay((string)(<1,0,0,0>*<0,1,0,0>));
// output <0,0,-1,0> // would be <0,0,1,0> by standard non-SL convention.

Would explain why @Wulfie Reanimator  was a tad confused by the implementation.

Personally, I think LL convention is more sensible for practical use. a*b*c is 'a then b then c' global rotations. c(b(a(x))) or c∘b∘a∘x is nice for pure math though.

Edited by Quistessa
clarifying comment.
Link to comment
Share on other sites

33 minutes ago, Quistessa said:

Would explain why @Wulfie Reanimator  was a tad confused by the implementation.

I'm just speaking in general, I haven't looked at the implementation too closely since doing the calculations by hand (never mind dealing with matrices) makes my head spin real fast.

I've gone ahead and added your uRot2Euler to the wiki page, though.

Link to comment
Share on other sites

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

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

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...