LSL Wiki Mirror 10-5-2006: LibraryRotationFunctions

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings ::

Experimental Rotation Math Functions


While the default math library functions are good, they are in no way perfect and are subject to change (and fail due to changes). All math functions will lose precision especially interpolation for quaternions. Drift will occur with many conversions between Euler and quaternion. These functions will return different results from the base LSL functions, this is due to the fact that the LSL functions work differently.

EulerToRotFast is accurate for about 7 decimal places as compaired to llEuler2Rot.
EulerToRot needs some work; it is supposed to give the more accurate answer of llEuler2Rot and EulerToRotFast
RotToEuler is currently a bit more accurate then llRot2Euler and more sensitive to drift.

WARNING about EulerToRotFast AND EulerToRot

have issues with gimble lock when y=|90| degress and |x+z|>180

Converts an Euler to a Rotation. Should be equivalent to llEuler2Rot.

rotation EulerToRotFast(vector v)
{
    v/=2;
    float ax=llSin(v.x);
    float aw=llCos(v.x);
    float by=llSin(v.y);
    float bw=llCos(v.y);
    float cz=llSin(v.z);
    float cw=llCos(v.z);
    return <aw*by*cz + ax*bw*cw,
            aw*by*cw - ax*bw*cz,
            aw*bw*cz + ax*by*cw,
            aw*bw*cw - ax*by*cz>;
}

Converts an Euler to a Rotation. Error checks llEuler2Rot and returns the less bugged answer.

rotation EulerToRot(vector v)
{
    float err=0.00001;
    v/=2;
    rotation a=<0,0,llSin(v.z),llCos(v.z)>*<0,llSin(v.y),0,llCos(v.y)>*<llSin(v.x),0,0,llCos(v.x)>;
    float ax=llSin(v.x);
    float aw=llCos(v.x);
    float by=llSin(v.y);
    float bw=llCos(v.y);
    float cz=llSin(v.z);
    float cw=llCos(v.z);
    rotation b = <ax*bw*cw + aw*by*cz,
                  aw*by*cw - ax*bw*cz,
                  aw*bw*cz + ax*by*cw,
                  aw*bw*cw - ax*by*cz>;
    rotation c=a+b;
    rotation d=a-b;
    if( (llFabs(c.x)>err && llFabs(d.x)>err) ||
        (llFabs(c.y)>err && llFabs(d.y)>err) ||
        (llFabs(c.z)>err && llFabs(d.z)>err) ||
        (llFabs(c.s)>err && llFabs(d.s)>err) )
        return b;
    return a;
}

Converts a Rotation to a Euler. Should be equivalent to llRot2Euler.

vector RotToEuler(rotation r)
{
    rotation t=<r.x*r.x,r.y*r.y,r.z*r.z,r.s*r.s>;
    float m=(t.x + t.y + t.z + t.s);
    if(m==0) return <0,0,0>;
    float n=2*(r.y*r.s + r.x*r.z);
    float p=m*m-n*n;
    if(p>0)
        return < llAtan2(2.0 * (r.x*r.s - r.y*r.z),(-t.x - t.y + t.z + t.s)),
                 llAtan2(n,llSqrt(p)), llAtan2(2.0 * (r.z*r.s - r.x*r.y),( t.x - t.y - t.z + t.s))>;
    else if(n>0)
        return < 0, PI_BY_TWO, llAtan2((r.z*r.s + r.x*r.y), 0.5 - t.x - t.z)>;
    else
        return < 0, -PI_BY_TWO, llAtan2((r.z*r.s + r.x*r.y), 0.5 - t.x - t.z)>;
}

While converting from Euler to Rotation this is mathematicly accurate this suffers from gimble lock (set y to PI_BY_TWO) expanding it and simplifying it removes the gimble lock. EulerToRotFast is the expanded and simplified form. Ironicly the expanded simplifed form isn't as accurate. EulerToRot compairs the answer from both.

Multiplying Quaternions (rotations)
rotation MultiplyRot(rotation a, rotation b)
{
float x = a.s*b.x + a.x*b.s + a.y*b.z - a.z*b.y;
float y = a.s*b.y + a.y*b.s + a.z*b.x - a.x*b.z;
float z = a.s*b.z + a.z*b.s + a.x*b.y - a.y*b.x;
float w = a.s*b.s - a.x*b.x - a.y*b.y - a.z*b.z;
return <x,y,z,w>;
}



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

can be worked out to be EulerToRotFast by using quaternions multiplication.



//Taken from http://forums.secondlife.com/showthread.php?p=536622
rotation slerp( rotation a, rotation b, float f ) {
    return a*llAxisAngle2Rot(llRot2Axis(b/a)*a, llAngleBetween(a, b)*f);
}

//Taken from http://forums.secondlife.com/showthread.php?p=536622
rotation slerp( rotation a, rotation b, float f ) {
    float angleBetween = llAngleBetween(a, b);
    
    if ( angleBetween > PI )
        angleBetween = angleBetween - TWO_PI;
    return a*llAxisAngle2Rot(llRot2Axis(b/a)*a, angleBetween*f);
}

Copyright status of functions:
All functions on this page are released into public domain and may have been based off other public domain works.

Disclamer:
I do not have a degree in higher mathematics. Please use this as reference, and do plenty of testing. - BW


rotation torot(vector raw)
{
    raw *= DEG_TO_RAD; //convert to radians
    rotation quat = llEuler2Rot(raw); //convert to quaternion
    return(quat);//return rotation
}

raw = Vector to be converted;

This simply returns the rotation value of raw. - Andrew Montagne

Not efficiant, better to just inline llEuler2Rot(raw * DEG_TO_RAD) - BW


ScriptLibrary
There is no comment on this page. [Display comments/form]