Jump to content

Ghosts of KFM past


Qie Niangao
 Share

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

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

Recommended Posts

The objective is to stop KFM, move the object around a bit, then start KFM again, but I'm finding that calling llSetKeyframedMotion with empty list arguments doesn't really reset the motion, and in fact whatever I've tried, when KFM restarts the object is apt to return to wherever it was when KFM was previously active. Here's a sample script:

float PERIOD = 4.0;
float AMP = 0.1; 
integer KFM_STEPS = 25;
list kfmCommands;
vector savePos;

default
{
    state_entry()
    {
        // set up KFM command list to bounce up and back
        float interval = (integer)((PERIOD / KFM_STEPS) * 45.0) / 45.0;
        float lastZ = llSin(0.0);
        integer step = 1;
        for (step = 1; step < KFM_STEPS; ++step)
        {
            float x = (TWO_PI * step) / KFM_STEPS;
            float newZ = AMP * llSin(x);
            kfmCommands += [ <0, 0, newZ - lastZ>, interval];
            lastZ = newZ;
        }
        savePos = llGetPos();
    }
    touch_start(integer num_detected)
    {
        // llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);   // Doesn't help
        llSetKeyframedMotion([], []);
        vector atPos = llGetPos();
        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POSITION, savePos]);  // Restore position
        vector setPos = llGetPos();
        llOwnerSay((string)atPos.z+" --> "+(string)setPos.z);
        llSetKeyframedMotion(kfmCommands, [KFM_DATA, KFM_TRANSLATION, KFM_MODE, KFM_LOOP]);
    }
}

To see the problem, drop the script in a box and touch it. Then touch it again when it's near the apex of its motion. Do that a few times in succession and note that although it kinda "tries" to restore itself to the original position, it will still creep upwards with each well-timed touch.

Does this ring a bell with anybody? I can't find a jira -- but that doesn't mean it's not there. Or am I just doing KFM wrong?

  • Thanks 1
Link to comment
Share on other sites

Somehow, this doesn't surprise me, but I admit that I haven't found it annoying enough to pay close attention.  KFM is handled client side, like other animations. I imagine that a sequence in progress may be cached locally if you interrupt it, and then finish where it left off when you re-trigger the sequence later.  How?  I don't know.  As I said, I haven't though much about it but I have seen the behavior you describe.

  • Like 1
Link to comment
Share on other sites

I'm not sure I fully understand the use case, but is there a specific reason why you're not just using KFM_CMD_PAUSE and resuming with KFM_CMD_PLAY?

Edit: Ooof, never mind, just tried modifying the demo to use that and I'm still seeing some really noticeable drift with each click. I also tried messing around with BLOCK_GRAB to see if my clicks might accidentally be dragging, but that doesn't seem to make a difference.

Edited by Fenix Eldritch
  • Thanks 1
Link to comment
Share on other sites

43 minutes ago, Fenix Eldritch said:

I'm not sure I fully understand the use case, but is there a specific reason why you're not just using KFM_CMD_PAUSE and resuming with KFM_CMD_PLAY?

Hmmm, yeah, I may actually get away with that for this application. I'd originally intended to change the command list between invocations. (I kinda tried that, to see if the "creeping" bug would still occur, and it seemed to.) But it's "close enough" in this particular case to just resume the same KFM motion. And in cursory testing, it seems to work, so this does get me out of the woods for now -- thanks!

The actual application is a sailboat vehicle that runs in a separate script; the sample code is simplified from a gentle rocking and rolling motion, with rotation as well as translation.  Of course the vehicle has to be stopped and set non-physical when doing any KFM. (There's a whole separate physical rocking motion, too.) In the actual script, the bug happens when orientation is restored, not position, which may be many regions away from the last time KFM got to run.

I'd nonetheless be interested if others have encountered this, or otherwise have opinions on whether I should file a jira. It seems to be a defect, right?

Edited by Qie Niangao
Link to comment
Share on other sites

52 minutes ago, Profaitchikenz Haiku said:

/me agrees with Sabrina, I think you need to wait until you have a moving_end event.

I don't understand this.

It's possible I was being too cute with the title here. The KFM relative motion itself is behaving as expected, correctly starting from the beginning after being reset (either with _STOP or null arguments, or both). The problem is that it's also contaminated the "resting state" of the object: instead of applying to the new position (or rotation) established while stopped, it instead moves the object back to where it was when KFM was running before, and then restarts the motion from there.

It's possible that this is a known effect of trying to stop a looped KFM except at the last frame before looping (or ping-ponging), but that seems very strange.

Also, does looped KFM really emit a moving_end() each time through the loop? I don't see that... for that matter, I don't see that event being raised during a momentary pause, a position or rotation change, and then resuming.

Link to comment
Share on other sites

19 minutes ago, Qie Niangao said:

Also, does looped KFM really emit a moving_end() each time through the loop?

Ah, possibly not. The thing I have that is close(ish) to your problem is a pub sign swinging to and fro. I discovered with KFM looped rotations some error crept in and after a while the sign would start cranking round by a few degrees so that in the end it would be pointing vertically upwards and waving to and fro.

My solution was to not loop the KFM but make a single rotation, then at moving_end, check where the sign was supposed to have got to and move it there if it wasn't right. Then reverse the KFM rotation, check again at moving_end and so on.

Stopping KFM before the end of a (single) translation movement is also something I do with a pair of funicular cars, again using moving_end but additionally with a timer. If the car gets 30 seconds overdue at where it should be then I stop KFM and force the car to where it should have been by RegionSetPos(). This is necessary because of (you guessed it) the % scripts run issue, KFM seemed to get hit first of all when the figures went bad, then touch-menus, ...

 

Link to comment
Share on other sites

On the wiki they say this:

 

Quote

for best results use moving_end to determine when the animation has ended and confirm the target position with llSetPos

So, I think that if you reach moving_end you are at a known position. Alternatively, you can do llGetPos() to figure out what is the frame number and then make a new motion starting from that frame.

Edited by Sabrina Tamerlane
Link to comment
Share on other sites

I've got two spheres side-by side, one with Qie's script, one with an amendment to use alternately KFM_FORWARD and KFM_REVERSE at each moving_end, but I can't see any different behaviour to a touch on the altered script.

I'm getting slightly sea-sick just watching them, is that what the aim of the script is?

 

ETA

There is a difference between the two methods, using KFM_LOOP the sphere is gradually losing height and sinking to the ground, using moving end, it isn't. It suggests there's some drift in positions when using KFM_LOOP, since I am not adjusting the positions at all in the moving_end state.

The moving_end version is stable, the sphere goes up and down within the same extremes, but with KFM_LOOP there are some quite wild excursions up to the original rezzed position and then down to the gradually sinking position.

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

Does this fix your problem?

 

float PERIOD = 4.0;
float AMP = 0.1; 
integer KFM_STEPS = 25;
list kfmCommands;
vector savePos;

default
{
    state_entry()
    {
        // set up KFM command list to bounce up and back
        float interval = (integer)((PERIOD / KFM_STEPS) * 45.0) / 45.0;
        float lastZ = llSin(0.0);
        integer step = 1;
        for (step = 1; step < KFM_STEPS; ++step)
        {
            float x = (TWO_PI * step) / KFM_STEPS;
            float newZ = AMP * llSin(x);
            kfmCommands += [ <0, 0, newZ - lastZ>, interval];
            lastZ = newZ;
        }
        savePos = llGetPos();
        llSetKeyframedMotion(kfmCommands, [KFM_DATA, KFM_TRANSLATION, KFM_MODE, KFM_LOOP]);
    }
    touch_start(integer num_detected)
    {
         llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);   // Doesn't help
    }
    moving_end()
    {
        vector atPos = llGetPos();
        llSetLinkPrimitiveParams(LINK_THIS, [PRIM_POSITION, savePos]);  // Restore position
        vector setPos = llGetPos();
        llOwnerSay((string)atPos.z+" --> "+(string)setPos.z);
        llSetKeyframedMotion(kfmCommands, [KFM_DATA, KFM_TRANSLATION, KFM_MODE, KFM_LOOP]);
    }
}

 

  • Thanks 1
Link to comment
Share on other sites

KFM_LOOP has some unusual properties. At the end of the cycle, the object is instantaneously moved back to the starting position. That's how my escalators and moving sidewalks work, by the way. They move forward slowly, then instantaneously back one step or one texture repeat to give the illusion of steady escalator-like movement.

It's possible to shut down looped motion and then reposition, but you have to stop keyframed motion with KFM_STOP or "[ ]", and then allow a few seconds of dwell time for things to settle. I don't think that the SetPos family of functions is interlocked with keyframe animation. My code for this in the escalators looks like

stop_belt()
{                    
    llSetKeyframedMotion([],[]);                
    llSleep(CYCLETIME*5.0);                     
    llSetPos(gStartPos);                        
    llSetRot(gStartRot);
}   

  Where CYCLETIME is the time the looped animation cycles, 0.5 second here.

Starting up motion looks like

        llSetPos(gStartPos);                            // align to top position
        llSleep(2.0);                                   // allow time to settle                                             
        llSetKeyframedMotion([motionDir, CYCLETIME],[KFM_DATA,KFM_TRANSLATION, KFM_MODE,KFM_LOOP]);

So plan on several seconds of delay time for the system to settle before issuing commands in different modes. I've had strange things happen when I didn't do that.

Running KFM_LOOP forever seems to be reliable for translation. Rotation, not so much. I tried building a spiral escalator once, and that wouldn't repeat consistently.

You can see this working with my escalators at http://maps.secondlife.com/secondlife/Vallone/243/44/36

Those are running a KFM_LOOP animation. It's partly sim side and partly client side; if you walk on the escalator, it carries you up by friction.

At the left side of each escalator near the floor is an emergency stop button. If you push it, the escalator will stop for 30 seconds. You can see it stop, then the 2.5 second delay, then it moves to the home position with an llSetPos. After 30 seconds, it moves to the home position again (which is different for up and down motion), waits 2 seconds, and then the keyframe animation starts.

So, allow lots of time for things to settle between using KFM and SetPos.

  • Like 2
  • Thanks 2
Link to comment
Share on other sites

10 hours ago, Sabrina Tamerlane said:

Does this fix your problem?

Yes, thanks, and now I finally understand what you were suggesting all along: KFM (using STOP or []) only relinquishes control of the position at moving_end or, as @animatsshows, after a few seconds of idle time -- I'm seeing the same good result from adding llSleep(3.0) after the STOP as by putting the delayed position-restoring code in moving_end. (It might be interesting to see if the escalators could use moving_end instead of llSleep to the same effect.)

It's interesting, though, that @Fenix Eldritch's suggestion also works with neither the event wait nor sleep delay: the object responds to reposition while _PAUSEd and will happily resume the motion (as it should) at the new position without trying to restore the object to where it was when _PAUSEd, as it does when _STOPped or []'d.

So now I'm thinking rather than a jira to try to "correct" this anomalous behavior, it would be better to add a note to the KFM documentation -- if anybody still has wiki-editing permission (and understands of the elaborate templates used there).

Thanks again, all!

Link to comment
Share on other sites

11 hours ago, Sabrina Tamerlane said:

Does this fix your problem?

Interestingly, when I put a moving end event in Que's original script it was never triggered, you're getting it triggered by making discrete steps between his start and end position, and he's also saying a sleep is helping, it suggests that there needs to be enough time pause to actually detect the end of movement?

Link to comment
Share on other sites

3 hours ago, Profaitchikenz Haiku said:

Interestingly, when I put a moving end event in Que's original script it was never triggered, you're getting it triggered by making discrete steps between his start and end position, and he's also saying a sleep is helping, it suggests that there needs to be enough time pause to actually detect the end of movement?

I have noticed that if you pass KFM_CMD_STOP without a KFM_CMD_LOOP before then the moving end event will not be triggered.
 

Edited by Sabrina Tamerlane
another typo
Link to comment
Share on other sites

Should be Ok, because the position you're giving is actually a delta from the current position. What I'm not sure is giving it a delta of say <0,0,10> but then specifying a mode  KFM_REVERSE as a initial setting would move it in the opposite direction to specifiying <0,0,-10> with KFM_FORWARDS

 

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1395 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...