llSetKeyframedMotion

From Second Life Wiki
Revision as of 05:58, 25 February 2012 by Dora Gustafson (talk | contribs)
Jump to navigation Jump to search

Summary

Function: llSetKeyframedMotion( list keyframes, list options );

Specify a list of times, positions, and orientations to be followed by an object. The object will be smoothly moved between keyframes by the simulator. Collisions with other nonphysical or keyframed objects will be ignored (no script events will fire and collision processing will not occur). Collisions with physical objects will be computed and reported, but the keyframed object will be unaffected by those collisions. (The physical object will be affected, however.)

• list keyframes Strided keyframe list of the form:
  • vector position (optional via KFM_TRANSLATION and KFM_DATA)
  • rotation orientation (optional via KFM_ROTATION and KFM_DATA)
  • float time

Each keyframe is interpreted relative to the previous transform of the object. For example, consider the following list of keyframes: [<0, 0, 10>, ZERO_ROTATION, 5, <0, 0, 0>, ZERO_ROTATION, 5, <0, 0, -10>, ZERO_ROTATION, 5]. This would cause the object to move up 10m over the course of 5s. It would then remain at the location for 5s before moving down 10m over the course of another 5s. Time values must be greater than 0.1s. Linear and angular velocities will be clamped to limits set by the simulator (values TBD). An empty list will terminate any keyframed animation currently playing.

• list options modifiers and future options

Specification

The following flags will be supported within the options list:

  • KFM_MODE followed by one of: KFM_LOOP, KFM_REVERSE, KFM_FORWARD, or KFM_PING_PONG will specify the playback mode. Defaults to KFM_FORWARD. Must be specified when the keyframe list is provided.
  • KFM_DATA followed by a bitwise combination of: KFM_TRANSLATION and KFM_ROTATION. By default both rotations and translations must be provided. If you specify one or the other, you should only include translations or rotations in your keyframe list. Must be specified at the time the keyframe list is provided.
  • KFM_COMMAND followed by one of: KFM_CMD_STOP, KFM_CMD_PLAY, KFM_CMD_PAUSE. STOP will pause the animation AND reset it to the beginning. PAUSE will pause the animation without resetting. PLAY will resume a paused or stopped animation.

Note that if KFM_COMMAND is provided in the options list, it must be the only option in the list, and cannot be specified in the same function call that sets the keyframes list.

Inter-region are supported.

Caveats

  • This function does not work in attachments.
  • This function can only be called on NON-physical objects. In the future it could be extended to support physical objects, but this is more complicated as collisions could prevent the object from reaching its goal positions on time.
  • Collisions with avatars affect the angular movement of the object and it may not reach the final rotation.
  • This function can only be called on the root prim of a linkset.
  • This function requires the linkset to use the Prim Equivalency system. However, it keyframed objects will not receive the dynamics penalty and can have a physics PE of up to 64.
  • llSetKeyframedMotion is implemented in terms of frames and not real time. To avoid a drift from the expected positions and rotations, use times which are integer multiples of 1/45, e.g. 20.0/45.0, 40.0/45.0, 90.0/45.0, etc. Forum Thread
  • There are a few bugs in the avatar animation system that may cause strange looking animations to play when standing on a moving platform (e.g., walking in place, feet-at-pelvis). We hope to fix these in the future, but doing so is out of scope for this feature.
  • As with dynamic objects, objects moving using this function are paused when they are selected by an avatar with adequate permissions (object owner, passenger, etc). When such an avatar deselects the object, motion resumes, even if the object had been paused using KFM_CMD_PAUSE.

Examples

// If your client is not mesh-aware use the following line:

llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);

//

llSetKeyframedMotion([<0,0,10>, 5, <0,0,-10>, 5], [KFM_DATA, KFM_TRANSLATION, KFM_MODE, KFM_PING_PONG]); llSetKeyframedMotion([<0,0,10>, llEuler2Rot(<90, 45, 180> * DEG_TO_RAD), 5, <0,0,-10>, llEuler2Rot(<270, 225, 360> * DEG_TO_RAD), 5], [KFM_MODE, KFM_REVERSE]);

Sample Script

Under some circumstances, rotations will generate a run-time error unless they are normalized. This script illustrates a way to use llKeyframedMotion to create a follower -- think, for example, of a cart behind a vehicle -- using normalized target rotation. <lsl>rotation NormRot(rotation Q) {

   float MagQ = llSqrt(Q.x*Q.x + Q.y*Q.y +Q.z*Q.z + Q.s*Q.s);
   return <Q.x/MagQ, Q.y/MagQ, Q.z/MagQ, Q.s/MagQ>;

}

integer gON; default {

   state_entry()
   {
       llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]); 
   }
   
   touch_start(integer total_number)
   {
       gON = !gON;  //Toggle device on/off
       if (gON)
       {
           llSay(0,"Switch ON!");
           llSensorRepeat("","91b39b5b-13b1-2517-273a-67360b842c02",SCRIPTED,10.0,PI,0.1); //Following my scripted vehicle
       }
       else
       {
           llSensorRemove();
       }
   }
   
   sensor(integer num)
   {
       llSetKeyframedMotion([(llDetectedPos(0) - llGetPos()) + <-1.0,0.0,0.2>*llDetectedRot(0), NormRot(llDetectedRot(0)/llGetRot()),0.12],[]);
   }

}</lsl>

Universal Hinged Motion in 8 Key Frames

The script will turn a box prim around one edge parallel to the prim's Y-axis
The script will work for any prim orientation
Note that the smallest accepted time per frame is 1/9S=0.11111111S and NOT 0.1S
<lsl> float angleEnd=PI_BY_TWO; float speed=0.2; // m/S float steps=8.0; // number of Key Frames float step=0.0; list KFMlist=[]; vector V; integer open=TRUE; vector basePos; rotation baseRot;

float motion_time( float mt) {

   mt = llRound(45.0*mt)/45.0;
   if ( mt > 0.11111111 ) return mt;
   else return 0.11111111;

}

default {

   state_entry()
   {
       llSetMemoryLimit(0x1000);
       llSetPrimitiveParams([PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
       basePos = llGetPos();
       baseRot = llGetRot();
       vector v1 = 0.5*llGetScale()*llGetRot();
       rotation deltaRot = llEuler2Rot(< 0.0, angleEnd/steps, 0.0>);
       while ( step < steps )
       {
           V = v1*llAxisAngle2Rot(llRot2Left(llGetRot()), angleEnd*step/steps);
           V = v1*llAxisAngle2Rot(llRot2Left(llGetRot()), angleEnd*(step+1.0)/steps) - V;
           KFMlist += [V, deltaRot, motion_time(llVecMag(V)/speed)];
           step += 1.0;
       }
   }
   touch_end( integer n)
   {
       llSetKeyframedMotion( [], []);
       if ( open )
       {
           llSetPrimitiveParams([PRIM_POSITION, basePos, PRIM_ROTATION, baseRot]);
           llSetKeyframedMotion( KFMlist, []);
       }
       else
       {
           llSetKeyframedMotion( KFMlist, [KFM_MODE, KFM_REVERSE]);
       }
       open = !open;
   }
   on_rez( integer n) { llResetScript(); }

} </lsl>

After editing prim position, rotation and/or resize the script should be reset in order to update the motion

Notes

Potential Use Cases:

  • Elevators
  • Moving platforms
  • Trains/Fixed-Track Vehicles
  • Moving doors/walls/gates
  • Windmills and other machines

Targeted coordinate systems: The Translation is in Global coordinates, the Rotation in Local coordinates

  • Say: a move on the X-axis will move the object along the global, region X-axis no matter how the object is rotated
  • Say: a rotate around the X-axis will rotate the object around it's local, prim X-axis no matter the object's rotation
  • When the object is not rotated in the global system you won't notice the difference

Deep Notes

Signature

function void llSetKeyframedMotion( list keyframes, list options );
integer KFM_MODE;
integer KFM_LOOP;
integer KFM_REVERSE;
integer KFM_FORWARD;
integer KFM_PING_PONG;
integer KFM_DATA;
integer KFM_TRANSLATION;
integer KFM_ROTATION;
integer KFM_COMMAND;
integer KFM_CMD_STOP;
integer KFM_CMD_PLAY;
integer KFM_CMD_PAUSE;