Jump to content
Sign in to follow this  
Susie Chaffe

llSetKeyframedMotion - turning a corner

Recommended Posts

All my previous vehicles have used physical motion, but with the arrival of llSetKeyframedMotion I thought I would experiment a little.
Linear motion is good, and region crossings present no unusual problems.

Turns are ugly...the strength/damping effect of llRotLookAt on non-physical objects is relatively ineffective.

I have tried to smooth the turns by creating a keyframe list from interpolated points on an arc.

This is in the form of a function that will eventually be called from a command in a route notecard.
The gTurnLR is simply a hack to get it working for testing

The script works...but there is a certain amount of drift by the time the object reaches its final point.
I had ascribed this to rounding errors, but now I am not so sure. There is quite a variance in results.

I plan most turns to be 90 degrees..but this script will make an object complete a circle.

If you want to test, you will need a mesh enabled viewer so you can set a normal prim to a convex hull, and access to a region running Magnum.

I struggle with rotations...so I am open to any suggestions on refining this script, or taking a completely different approach to the problem.

 

integer gTurnLR;      //-1 for clockwise (right turn) 1 for anticlockwise (left turn)
list glstFrames;       //list of keyframes
 
makeKeyFrames(vector vStepPos, rotation qStepRot)
{
    glstFrames = [];
    float fRadius  = 10.0;  //radius of turn
    integer iSteps = 24;    //variable depending on radius of turn - experiment!
    float fTime = 1.0;      //KeyFrame step time interval
    rotation vRotArc       = llEuler2Rot( <0.0, 0.0, (TWO_PI/iSteps)*gTurnLR>); // step angle -radians
    vector   vPosOffset     = <0.0, fRadius*gTurnLR, 0.0>;                      // radius of turn
 
    integer i;
    for(i=0;i< iSteps ;i++)  // iSteps/4 for a 90 degree turn
    {
        vector vKeyFrame = vStepPos;
        // http://wiki.secondlife.com/wiki/Rotation
        vStepPos = vStepPos + (vPosOffset - vPosOffset * vRotArc) * qStepRot;
        qStepRot = vRotArc * qStepRot;
        vKeyFrame = vStepPos - vKeyFrame;       //relative to last frame
        glstFrames += [vKeyFrame, vRotArc, fTime];
    }
}
 
 
default
{
    touch_start(integer total_number)
    {
        gTurnLR = -1;       //clockwise turn - actual value from route notecard
        makeKeyFrames(llGetPos(),llGetRot());
        llSetKeyframedMotion(glstFrames, [KFM_MODE, KFM_FORWARD]);
    }
}

 

Grr...trying to edit a post is "challenging"

Share this post


Link to post
Share on other sites

First, no, you do not need a mesh-aware viewer to script llSetKeyframedMotion(). Just insert the following line in the state_entry():

llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);

 Any way...Despite the late hour, I managed to scribble something that works...

list FrameList;uuMakeCurve(rotation rot, integer dir, float angle, float radius, float steps, float time){    // dir: 1 = left, -1 = right    // angle: in degrees    // time = total time     //    vector offset = <-radius, 0.0, 0.0> * (llEuler2Rot(<0.0, 0.0, dir * PI_BY_TWO>) * rot);    // Offset from relative center of the arc of circle    time /= steps; // Time for each step    angle = (angle * DEG_TO_RAD * dir) / steps; // Arc of circle for each step    rot = llEuler2Rot(<0.0, 0.0, angle>); // Relative rotation for each step    FrameList = [];    integer i = 1; // Don't re-do step 0!    for (; i <= (integer)steps; ++i)    {        vector move = (offset * llEuler2Rot(<0.0, 0.0, angle * (float)i>))                     - (offset * llEuler2Rot(<0.0, 0.0, angle * (float)(i - 1)>));        // Relative movement from previous pos to current one.        FrameList += [move, rot, time];    }}default{    state_entry()    {        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);    }    touch_end(integer num)    {        uuMakeCurve(llGetRot(), 1, 90.0, 10.0, 24.0, 12.0);        llSetKeyframedMotion(FrameList, [KFM_MODE, KFM_FORWARD]);    }}

 BEWARE! My left is 1 and my right is -1. And I realized that the function does not need the pos. Everything is relative in llSetKeyframedMotion()... including the rotation.

It works, I said, but the drift is awful. Doing 4 quarters of circles does not bring you back to your starting position. I will see if cosinuses and sinuses are more precise than vectors * rotations... tomorrow... Later this morning already!

btw, did anybody notice that all these relative positions and rotations are driving you insane? :matte-motes-confused: I want absolute positions!

 

 

Share this post


Link to post
Share on other sites

It s not a rotation .

It s just a polygon you are drawing .

 

I am stupefied to see there are not curves interpolation .

I can t believe that Havok 7 has no function to deal with this . And i don t understand why Linden have not given this feature .

Why should it be  the scripter to compute ( and compute badly )  the movements .....

 

failed ...

Share this post


Link to post
Share on other sites

Yes its a polygon - that is currently the only way to approximate a curve with the current toolset.

failed ............yes and no

The primary objective was to produce a visually smooth turn rather than a sharp right angle.....it does.

The assumed result that it would finish where it was intended to... is a fail.

The magnitude of error is not massive and can be corrected by reference to a fixed point following the turn.


I had the opportunity to bring this question up at Simon Linden's office hour yesterday.
It was mentioned that "drift" had been noticed even in simple lateral motion...though this is difficult to repro.
There was some suggestion that region time dilation might be affecting the movement during a given frame.

Like many things in SL...half the fun is working out how to get around its shortcomings


As for the bad computation....thats why I asked for help :matte-motes-smile:

Share this post


Link to post
Share on other sites

I felt slightly insulted by this "and compute badly"... My calculations are accurate!

I replaced llSetKeyframedMotion() with something more direct in the uuMakeCurve() function:

llSetLinkPrimitiveParamsFast(LINK_THIS,                                 [PRIM_POSITION, llGetPos() + move,                                 PRIM_ROTATION, rot * llGetRot()]);llSleep(time); 

And I also made the object say its position when you click it. Here are the results of 2 full circles on llSLPPF:

[02:24]  llSLPPF: pos=<200.00000, 94.00000, 92.00000> rot=<0.00000, 0.00000, 0.00000>[02:24]  llSLPPF: pos=<210.00000, 104.00000, 92.00000> rot=<0.00000, 0.00000, 90.00011>[02:24]  llSLPPF: pos=<200.00000, 114.00000, 92.00000> rot=<0.00000, 0.00000, -180.00000>[02:24]  llSLPPF: pos=<190.00000, 104.00000, 92.00000> rot=<0.00000, 0.00000, -90.00008>[02:25]  llSLPPF: pos=<200.00000, 93.99999, 92.00000> rot=<0.00000, 0.00000, -0.00001>[02:25]  llSLPPF: pos=<210.00000, 104.00000, 92.00000> rot=<0.00000, 0.00000, 90.00005>[02:25]  llSLPPF: pos=<200.00000, 114.00000, 92.00000> rot=<0.00000, 0.00000, 180.00000>[02:25]  llSLPPF: pos=<190.00000, 104.00000, 92.00000> rot=<0.00000, 0.00000, -90.00003>

 Final postion:

[02:26]  Kaluura: <200., 93.999992, 92.> <0., 0., 0.>

Almost perfect!

I made only 1 turn with llSKfM:

[02:35]  llSKfM: pos=<200.00000, 94.00000, 92.00000> rot=<0.00000, 0.00000, 0.00000>[02:35]  llSKfM: pos=<209.77640, 103.77720, 92.00000> rot=<0.00000, 0.00000, 87.99987>[02:35]  llSKfM: pos=<200.34710, 113.88950, 92.00000> rot=<0.00000, 0.00000, 175.99560>[02:35]  llSKfM: pos=<189.91070, 104.81920, 92.00000> rot=<0.00000, 0.00000, -96.00464>

 Final position:

[02:36]  Kaluura: <198.611908, 94.073349, 92.> <0., 0., 352.>

 I think I'll keep on using the old lag-inducing methods until further notice. I can do much better than Havok... by hand!

 

 

Share this post


Link to post
Share on other sites

Ouch!

Thank you Kaluura - those are very telling results. 

They clearly show what would happen if you tried to run a looped circuit (think monorail or tram).

For any degree of accuracy the movement will have to be recalculated for every step using a fixed point as reference.

 

Share this post


Link to post
Share on other sites

Hey folks,

 

I have a hunch that the cause of the drift is related to the simulator's fixed time step and the fact that llSetKeyframedMotion is implemented in terms of frames and not real time. This decision was made in order to produce more deterministic results. Before giving up on this method of animation, can you try changing your delta times to be integer multiples of 1/45s? Let me know if this resolves the issue. If not, please file a jira.

 

Falcon

Share this post


Link to post
Share on other sites

I tried with fixed times of 10/45, 22/45, 45/45... The drift is greatly reduced.

With a time of 10/45:

[08:43]  llSKfM: pos=<200.00000, 94.00000, 92.00000> rot=<0.00000, 0.00000, 0.00000>[08:43]  llSKfM: pos=<209.99920, 103.99940, 92.00000> rot=<0.00000, 0.00000, 89.99792>[08:43]  llSKfM: pos=<200.00020, 113.99890, 92.00000> rot=<0.00000, 0.00000, 179.99610>[08:44]  llSKfM: pos=<189.99970, 104.00050, 92.00000> rot=<0.00000, 0.00000, -90.00515>

 Final position:

[08:44]  Kaluura: <199.998169, 94.000458, 92.> <0., 0., 0.>

 The drift stays below 0.01 for both the position and the rotation.

Interesting information to write on the wiki page...

 

Share this post


Link to post
Share on other sites

Hi Kaluura,

would you have better precision in replacing your function by this one :

I ve changed the loop

 

 

uuMakeCurve(rotation rot, integer dir, float angle, float radius, float steps, float time){    // dir: 1 = left, -1 = right    // angle: in degrees    // time = total time     //    vector offset = <-radius, 0.0, 0.0> * (llEuler2Rot(<0.0, 0.0, dir * PI_BY_TWO>) * rot);    // Offset from relative center of the arc of circle    time = 10.0/45.0; // Time for each step    //angle = (angle * DEG_TO_RAD * dir) / steps; // Arc of circle for each step     angle = (angle * DEG_TO_RAD ) * (float)dir / steps; // Arc of circle for each step        rot = llEuler2Rot(<0.0, 0.0, angle>); // Relative rotation for each step    FrameList = [];    integer i = 1; // Don't re-do step 0!    for (; i <= (integer)steps; ++i)    {       // the offset is now defined as the precedent rotated vector        vector move = offset ;        offset = offset * llEuler2Rot(<0.0, 0.0, angle >) ;        move = offset - move;      // Original Code      //  vector move = (offset * llEuler2Rot(<0.0, 0.0, angle * (float)i>))       //              - (offset * llEuler2Rot(<0.0, 0.0, angle * (float)(i - 1)>));        // Relative movement from previous pos to current one.        FrameList += [move, rot, time];    } }

 

This one avoids some compute of rotations . It  keeps the old vector rotated , compute the new vector rotated and does the difference

Share this post


Link to post
Share on other sites

I built my own circle flyer and saw the same kind of drift. However, we need to recognize that we're dealing with floating point numbers, and they are never exact. My next step will be to numerically integrate the circle and see how much of the drift is just due to floating point rounding. 

It's certainly also at least possible that the motion implementation itself is flawed. I'm not sure how we could determine that. If it uses some kind of "close enough" or "within range X" method to move ... or if it just sets a direction and moves for the time interval ... it could easily be triggering the next segment before getting to the actual target position.

Does anyone know much about the actual implementation?

Anyway, I'll report back the results of numerically integrating my circle, to see if that explains the drift.

Share this post


Link to post
Share on other sites

In case anyone is reading this thread looking for driftless motion/rotation, the better method, not mentioned here so far, is to request a motion/rotation from the current, actual position/rotation, instead of from the currently-hoped-for position/rotation.

Old method:

  • Break a complete motion into a list of steps.
  • Ask object to move from one pre-calculated step to the next, expecting it to have landed exactly on each step perfectly
  • Suffer from accumulating drift.

Windy's method:

  • Break a complete motion into a list of steps.
  • Ask object to move from current position/rotation to the next step in a given time.
  • After waiting that given time, repeat until motion complete, allowing for small errors at each step but then ignoring them to determine change to next step.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...