llSetKeyframedMotion cause stuttering and jumping motions while rotating
tracked
Nyx Onyx
Please see https://i.gyazo.com/9458091243cd7f38b5bd858165a59508.mp4 to see what's happening.
The script is put inside a root link that is the swivel bar, the mesh door is the second link in the linkset.
The function is called as:
llSetKeyframedMotion(lKeyFramesOpening, [ KFM_DATA, KFM_ROTATION, KFM_MODE, KFM_FORWARD ]);
Where lKeyFramesOpening is a list generated like so:
// Rotation for most keyframes
rotation rRotation = llEuler2Rot(<0.0, 0.0, 15.0> * DEG_TO_RAD);
// First keyframe
lKeyFramesOpening += [ llEuler2Rot(<0.0, 0.0, 15.0> * DEG_TO_RAD), 3.0 ];
// Generating in-between keyframes
integer i = 1;
for(; i < 5; i++)
{
lKeyFramesOpening += [ rRotation, i * .2 ];
}
// Ending keyframe
lKeyFramesOpening += [ llEuler2Rot(<0.0, 0.0, 15.0> * DEG_TO_RAD), 0.6 ];
Object and full script can be sent upon request. See comments in the thread here for more info.
Log In
Maestro Linden
Nyx Onyx and I met in-world on aditi and figured out the bug, which is:
If a linkset with rotational keyframed motion is duplicated, the duplicate linkset does not animate properly. Its keyframed motion triggers
many
ImprovedTerseObjectUpdate messages, which makes the keyframes animation look poor (in addition to flooding the viewer).This bug does note immediately reproduce for KFM with translational movement only, e.g.
llSetKeyframedMotion([<0,0,1>, 5], [KFM_DATA, KFM_TRANSLATION, KFM_MODE, KFM_PING_PONG])
.Repro:
- Rez 2 boxes at different positions
- Rotate one of the boxes slightly (it appears that this is necessary - this bug does not seem to reproduce if both objects have zero rotation initially, for example)
- Link the boxes together, and place this script in the root prim:
default
{
state_entry()
{
llSetLinkPrimitiveParams(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
llSetKeyframedMotion([llEuler2Rot(<0.0, 0.0, PI_BY_TWO>), 5], [KFM_DATA, KFM_ROTATION, KFM_MODE, KFM_PING_PONG]);
}
}
- Enable "Show Updates To Objects" in the viewer, and observe the terse update message rate
- Duplicate the linkset
- Compare the motion of the original object to the duplicate, along with the relative rates of terse update messages
Expected results:
The original and duplicate linksets should animate with identical motion, and should produce ImprovedTerseObjectUpdate messages at about the same rate.
Actual results:
- The duplicate linkset produces many more ImprovedTerseObjectUpdate messages (which should be redundant), and generally animates poorly. The effect on the animation depends on the geometry of the object, but can look quite poor. Aside from poor animation appearance, the high rate of ImprovedTerseObjectUpdate messages is bad for performance.
Maestro Linden
tracked
Maestro Linden
The full keyframed motion data constructed by Nyx's snippet is:
Keyframes in lKeyFramesOpening:
<0.0, 0.0, 0.130526, 0.991445>, 3.0
<0.0, 0.0, 0.130526, 0.991445>, 0.2
<0.0, 0.0, 0.130526, 0.991445>, 0.4
<0.0, 0.0, 0.130526, 0.991445>, 0.6
<0.0, 0.0, 0.130526, 0.991445>, 0.8
<0.0, 0.0, 0.130526, 0.991445>, 0.6
Total animation duration: 5.6
So it's setting a constant 15 degrees Euler rotation at variable frame intervals. The breakdown of speeds is:
- 5 degrees/s for 135 frames (of physics simulation)
- 75 degrees/s for 9 frames
- 37.5 degrees/s for 18 frames
- 25 degrees/s for 27 frames
- 18.75 degrees/s for 36 frames
- 25 degrees/s for 27 frames
I wrote the script below as an end-to-end repro case, which is attached as a child comment. I also have a side script, which triggers on the same chat message and updates the floating text to show the animation's current speed.
The door definitely shouldn't animate smoothly given the sudden changes in angular velocity, but it should definitely be in motion for the full 5.6 seconds.
In practice, I'm seeing that initial 3-second step to rotate 15 degrees stops after about 1 second, and that the door then pauses for the rest of that interval. It looks like it doesn't complete the full 15 degree rotation either - perhaps only 5 degrees?
Doing other testing, I _don't_ see the pausing behavior if I trim the KFM list to only include the 15 degrees over 3 seconds step:
lKeyFramesOpening = [
<0.0, 0.0, 0.130526, 0.991445>, 3.0
];
Maestro Linden
// Main KFM script - say 'go' to play animation
list lKeyFramesOpening;
float duration;
default
{
state_entry()
{
// Rotation for most keyframes
rotation rRotation = llEuler2Rot(<0.0, 0.0, 15.0> * DEG_TO_RAD);
// First keyframe
lKeyFramesOpening += [ llEuler2Rot(<0.0, 0.0, 15.0> * DEG_TO_RAD), 3.0 ];
// Generating in-between keyframes
integer i = 1;
for(; i < 5; i++)
{
lKeyFramesOpening += [ rRotation, i * .2 ];
}
// Ending keyframe
lKeyFramesOpening += [ llEuler2Rot(<0.0, 0.0, 15.0> * DEG_TO_RAD), 0.6 ];
// make the prim look like a door
llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_SIZE, <0.5, 2, 2.8>,
PRIM_TYPE, 0, 0, <0.375, 0.625, 0>, 0, <0, 0, 0>, <1, 1, 0>, <0, 0, 0>,
PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX
]);
// print KFM list
llOwnerSay("Keyframes in lKeyFramesOpening: ");
for(i = 0; i < llGetListLength(lKeyFramesOpening); i += 2)
{
duration += llList2Float(lKeyFramesOpening, i + 1);
llSay(0, llList2CSV(llList2List(lKeyFramesOpening, i, i + 1)));
}
llSay(0, "Total animation duration: " + (string)duration
+ "\n Say 'go' to move");
llListen(0, "", llGetOwner(), "go");
}
listen(integer channel, string name, key id, string msg)
{
// Stop any other animations
llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);
llSleep(1);
llSay(0, "Playing opening animation..");
llSetKeyframedMotion(lKeyFramesOpening, [ KFM_DATA, KFM_ROTATION, KFM_MODE, KFM_FORWARD ]);
llSleep(duration + 0.1);
llSay(0, "Animation complete.");
}
}
Maestro Linden
// KFM display script - show current speed of KFM defined in other script
// Designed to describe what the animation should be doing at any given step
list lKeyFramesOpening = [
<0.0, 0.0, 0.130526, 0.991445>, 3.0,
<0.0, 0.0, 0.130526, 0.991445>, 0.2,
<0.0, 0.0, 0.130526, 0.991445>, 0.4,
<0.0, 0.0, 0.130526, 0.991445>, 0.6,
<0.0, 0.0, 0.130526, 0.991445>, 0.8,
<0.0, 0.0, 0.130526, 0.991445>, 0.6
];
default
{
state_entry()
{
llListen(0, "", llGetOwner(), "go");
}
listen(integer channel, string name, key id, string msg)
{
llSleep(1); // to copy other script's sleep
integer i;
for(i = 0; i < llGetListLength(lKeyFramesOpening); i += 2)
{
vector rot = llRot2Euler(llList2Rot(lKeyFramesOpening, i));
float duration = llList2Float(lKeyFramesOpening, i + 1);
llSetText( (string)(rot.z * RAD_TO_DEG / duration) + " deg/s", <1,1,1>, 1);
llSleep(duration);
}
llSetText("", <1,1,1>, 1);
}
}
Nyx Onyx
Maestro LindenI don't know whether it should be rotating smoothly or not with my code, but it shouldn't be jumping around vertically no matter what, like it does in the video. Or should I say tilting? What I actually wanted to achieve was a slow motion that slows down even more, then smooth long motion, that near the end slows to a halt. Imagine pulling down the handle on the door and taking a step back to get out of the way of the door, swinging it open gently, and letting go just before it actually stops.
Maestro Linden
I don't see any vertical motion in my repro object, but I am observing that the initial animation step (15 degrees over 3 seconds) isn't animating as expected. It seems that the object is not animating over the full interval as it should be. Here's a video: https://youtu.be/6iidrueZvlU
You may want to adjust either your duration or rotation vectors in the KFM for the 'slow down' effect that you're after, but given that "rotate 15 degrees over 3 seconds" seems to be flakey, I'm not sure how well it will appear in practice.
Nyx Onyx
Maestro LindenIn addition to what you're doing in the video, can you do a drag-copy of the object and then "play" the object? In my testing, the behaviour was different for the original object. See https://i.gyazo.com/2516a986a897b7d361cb04d7a813ec7a.mp4 ... Also, I sent you my door and script inworld, on the main grid.
Maestro Linden
Nyx Onyx: Okay, that duplicated cause does raise one caveat: selecting a KFM object as the owner will definitely stunt the movement.
Here's one false positive case - in this case, the duplicated object was initially selected when the KFM routine began. KFM objects selected by a user who can move them (such as the owner) have their movement stopped while the object is selected. This is why it the one object doesn't complete the full 90 degree rotation. : https://youtu.be/AN1J51Y7iwY
Reviewing my original video, the viewer didn't indicate that the rotating object was at all selected, but the results are similar to this video - the 'stunted' object missed out on the original bit of motion, and ended up rotating <90 degrees. Its twin, which was definitely not selected, completed the full KFM as expected.
Finally, I did 1 more test with 2 objects (one original, one duplicated), in which I was very careful to not have either object selected. In _this_ case, the objects were both able to complete the full KFM circuit properly multiple consecutive times, with the objects each rotating 90 degrees each time. The only error I see in this case is that the object to the "west" (per the floor texture) actually overshot the 90 degrees on one of the runs, which remained intact for all the other rotations: https://youtu.be/DHcDH4t_E38
Nyx Onyx
Maestro LindenIn the case I'm showing on the clip I linked to above, I have selected a few other things in the environment to make sure I don't have anything selected before I click the doors to play the animation. Dropping in the script I also don't select the objects, I drop it in from inventory. In addition to doing a drag-copy, just taking it to inventory and rezzing it out again gives it a different behaviour from the original.
Nyx Onyx
https://i.gyazo.com/ec78cf31f39cbe87b5e0d1c355f74508.mp4 reproduces on the beta grid as well, with my double-doors - the original plays one way, the copy in another - and the selection before playing the animations was done on the prim platform.
Maestro Linden
Nyx Onyx: Hm, that's an interesting case. I haven't been able to reproduce any special with my own objects that goes away after re-rezzing. Except perhaps that first case when I consistently had it only rotating for part of the first step (which I now can't reproduce outside of that errant case with accidental object selection).
I don't think I've received your objects on my maestro.linden account - could you try sending it again? I saw an offline offer on aditi (I've been testing on agni), but don't seem to have the 'nyx door' in my aditi inventory.
Nyx Onyx
Maestro LindenSent you a tp on the beta grid just now, to see the original in place if you like.
Nyx Onyx
These two doors here has the very same script in them. I have rezzed the door fresh from inventory after having uploaded and textured it, put the script in it, then made a drag-copy of the door, and finally reset the script in both doors. In the video I just click both doors and... https://i.gyazo.com/2a2442c5dc1386558266315aa05c4452.mp4
And this is after taking them one by one to inventory, and rezzing them again: https://i.gyazo.com/995d072437c0875429c0b4f91edc3d74.mp4 this is the same regardless of resetting scripts or not.
Rezzed them out at a different region, and they exhibit the same behaviour as on the last link ( Second Life Server 2025-07-25.16512260888 ). My home region is running ( Second Life Preflight 2025-09-26.18046863877 ).
If wanted to check it out, let me know who to send the script and door to.
Nyx Onyx
I do find that if I rez a fresh mesh from inventory it will behave differently once I add my script, from when I've made a drag-copy of a scripted object, regardless of resetting the script or even removing and putting back in the script. I'll be doing some more experiments to see if I find a pattern to this and can describe it in more detail.
Lucia Nightfire
You can use code tags with 3 backquotes. That character below the tilde.
Nyx Onyx
Lucia Nightfire I'm not sure that it's in that location on my Scandinavian keyboard, but I found it to the right of tilde on my phone keyboard! Thank you! Would be nice if the text editor on Canny had buttons for that.