# Movement (and!) Rotation of a Physical Object

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

## Recommended Posts

Hi
I am trying to send a physical object from A to B in a horizontal straight line, which is easy enough and I have that part working. However, I also want to be able to fully control it's rotation during movement. By 'fully' that means I may want a single 'barrel roll' during movement, or I may want it to rotate several times - independent control of all axis, so if wanted I can create a one or more axis 'tumbling' rotation during movement, too. Here's what I have so far:

```default
{
state_entry()
{
vector pos = llGetPos();
vector offset = <5, 0, 0>; // Move 5m 'east'.
pos+=offset;
llSetStatus(STATUS_PHYSICS, TRUE);
llLookAt(llGetPos()+<0.0, 0.0, 1.0>, 1.0, 0.1); // Ignore - an experiment to prove to myself that I can force a rotation during movement.
llSleep(3.0); // Not sure why a Sleep is needed but wont work without it.
llMoveToTarget(pos, 1.0);
llSetStatus(STATUS_PHYSICS, 0);
}
}```

Thank you.

Edited by Frankie Rockett
##### Share on other sites

49 minutes ago, Frankie Rockett said:
`llSleep(3.0); // Not sure why a Sleep is needed but wont work without it.`

I'm not sure why that allows it to work either, because I would expect it to never work as currently written. You call llMoveToTarget and then immediately set the physics status to false. There shouldn't be enough time between the execution of those two functions for the object to get moving before its physics is shut off. If anything, the sleep should be placed after llMoveToTarget and before turning off physics.

Does the object need to be physical? Because if not, Key Framed Motion could prove to be a more viable solution for smoothly controlling a prim's movement and rotation as it follows a predefined path.

Edited by Fenix Eldritch
• 2
##### Share on other sites

I agree.  llSetKeyframedMotion lets you do way more nuanced movement on a path.  You could either script a set of roll/pitch/yaw movements to run at specific times or you could create random ones on the fly.  You might have a look at the Bezier curve experiments that Dora Gustafson did ages ago ... https://wiki.secondlife.com/wiki/User:Dora_Gustafson/bezier_toy

• 2
##### Share on other sites

1 hour ago, Rolig Loon said:

llSetKeyframedMotion lets you do way more nuanced movement on a path.

I wouldn't say it gives you more control (if anything, its use of relative changes can and often does lead to non-negligible drift) , It's just a good deal more efficient and handles a lot of the timing logic for you.

The quality of movement is different between physical movement functions and llKeyframe as well; damping is sometimes quite nice.

Not sure what I was doing with this example:

Quote
```// script expects to be in a  linkset with 2 prims.

texture_setup()
[   PRIM_TYPE, PRIM_TYPE_BOX, 0, <0,1,0>, 0.0, <0,0,0>, <1,1,0>, <0,0,0>,
PRIM_SIZE, <0.25,0.25,0.25>,
PRIM_ALPHA_MODE, -1, PRIM_ALPHA_MODE_EMISSIVE, 0,
<-1,1,0>,<0,0,0>, -PI_BY_TWO,
PRIM_TEXTURE, 1, "be6091a2-5864-d306-b8df-343b80f88345", // ZX_Emmissive
<1,1,0>,<0,0,0>, 0,
PRIM_TEXTURE, 2, "6872a97f-82d7-fb5a-db11-362bfac23fff", // ZY_Emmissive
<1,1,0>,<0,0,0>, 0,
PRIM_TEXTURE, 3, "be6091a2-5864-d306-b8df-343b80f88345", // ZX_Emmissive
<-1,1,0>,<0,0,0>, 0,
PRIM_TEXTURE, 4, "6872a97f-82d7-fb5a-db11-362bfac23fff", // ZY_Emmissive
<-1,1,0>,<0,0,0>, 0,
<1,1,0>,<0,0,0>, -PI_BY_TWO,
PRIM_COLOR, 0, <0.5,0.5,1.0>, 1.0,
PRIM_COLOR, 1, <1.0,1.0,1.0>, 1.0,
PRIM_COLOR, 2, <1.0,0.5,0.5>, 1.0,
PRIM_COLOR, 3, <0.5,1.0,0.5>, 1.0,
PRIM_COLOR, 4, <1.0,1.0,1.0>, 1.0,
PRIM_COLOR, 5, <1.0,1.0,1.0>, 1.0,

PRIM_TYPE, PRIM_TYPE_CYLINDER, 0, <0,1,0>, 0.0, <0,0,0>, <1,1,0>, <0,0,0>,
PRIM_SIZE, <0.02,0.02,0.7>,
PRIM_SLICE, <0.5,1.0,0.0>,
PRIM_POS_LOCAL, <0,0,0>
]);
}
set_indicator_at_axis(vector v,integer local)
{   llSetStatus(STATUS_PHYSICS,FALSE);
if(!local)
{   v = v/llGetRootRotation();
}
float X = llAtan2(v.y,v.x)*0.5;
float Y = -llAtan2(v.z,llVecMag(<v.x,v.y,0>))*0.5;
[   PRIM_ROT_LOCAL, <0,0.71,0,0.71>*<0,llSin(Y),0,llCos(Y)>*<0,0,llSin(X),llCos(X)>
]);
llSleep(0.2);
llSetStatus(STATUS_PHYSICS,TRUE);
}
default
{
state_entry()
{   llSetStatus(STATUS_PHYSICS,FALSE);
texture_setup();
llSetPhysicsMaterial(GRAVITY_MULTIPLIER,0.0, 0,0,0);
llSetStatus(STATUS_PHYSICS,TRUE);
llMoveToTarget(llGetPos(),1.0); // remain in place.
}

touch_start(integer total_number)
{
rotation rInit = llGetRot();
rotation rSet = rInit*llAxisAngle2Rot(<0,0,1>,PI);
set_indicator_at_axis(<0,0,1>,FALSE);
llRotLookAt(rSet, 0.7, 0.3); // paradixically, lower strength means faster rotation?
llSleep(1.5);
rSet = rSet*llAxisAngle2Rot(<0,1,0>,PI);
set_indicator_at_axis(<0,1,0>,FALSE);
llRotLookAt(rSet, 0.7, 0.3);
llSleep(1.5);
rSet = rSet*llAxisAngle2Rot(<1,0,0>,PI);
set_indicator_at_axis(<1,0,0>,FALSE);
llRotLookAt(rSet, 0.7, 0.3);

llSleep(1.5);
rSet = llAxisAngle2Rot(<0,0,1>,PI)*rSet;
set_indicator_at_axis(<0,0,1>,TRUE);
llRotLookAt(rSet, 0.7, 0.3); // paradixically, lower strength means faster rotation?
llSleep(1.5);
rSet = llAxisAngle2Rot(<0,1,0>,PI)*rSet;
set_indicator_at_axis(<0,1,0>,TRUE);
llRotLookAt(rSet, 0.7, 0.3);
llSleep(1.5);
rSet = llAxisAngle2Rot(<1,0,0>,PI)*rSet;
set_indicator_at_axis(<1,0,0>,TRUE);
llRotLookAt(rSet, 0.7, 0.3);
}
}```

• 1
##### Share on other sites

35 minutes ago, Quistess Alpha said:

(if anything, its use of relative changes can and often does lead to non-negligible drift)

Quite true.  When you use llSetKeyframedMotion, you have to remember to do  a reality check every once in a while and llSetRegionPos the object to a well-defined spot. Otherwise, accumulated drift will leave you wondering where it is.  That's most important if you are making your object follow a closed path, returning to its starting point at rthe end.

• 2
##### Share on other sites

4 hours ago, Rolig Loon said:

When you use llSetKeyframedMotion, you have to remember to do  a reality check every once in a while and llSetRegionPos the object to a well-defined spot. Otherwise, accumulated drift will leave you wondering where it is.

You definitely need to correct for drift. But I recommend doing it by getting the current position, computing the appropriate move and using llSetKeyframedMotion again.

If you mix movement modes, you usually don't get what you want. SetPos, keyframe motion. move to target, physical motion, vehicle control, and pathfinding all do movement, but they are not interlocked and do not play well together. Even with several seconds of dwell time between. Most of the time it will work, but sometimes, two movement modes will conflict.

It helps to view this like robotics control. You tell the actuators to do something, you get some approximation to what you asked for, and then you issue corrections.

Edited by animats
• 2
##### Share on other sites

Wow!
Thank you Gang! There's a lot here for me to get into. I confess I have never ever played with llSetKeyframedMotion so I am very grateful to Fenix for introducing it and to Rolig et al for elaborating on it. People mention 'drift' and without experimenting I don't know how severe that is. I do know I plan to run several objects towards the viewer from a range of about 256m away, so I hope it can stick to a straight line for at least that long. Meanwhile I am very excited to go play with this key framed function and find out what it can do. Thank you - all!
Frankie.

Edited by Frankie Rockett
##### Share on other sites

There's a note in the caveats on the wiki page for llSetKeyframedMotion that sheds some light on the issue of drift:

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 KFM claims delta times must be larger than 0.1 seconds; in practice, however, delta times slightly over 0.1 will produce an error on DEBUG_CHANNEL. Testing shows a minimum delta time is about 6/45 (.13333) seconds.

Some drift is therefore to be expected. As I and a couple of other people have pointed out, you can correct for drift by using any appropriate method to set the object's position to some known value ( a starting or end point for the movement, for example).

• 1
• 1
##### Share on other sites

Hi!

Thank you for that important piece of fine tuning information Rolig!

Well, after a little play with the new (to me) function, I am disappointed. A main reason and a picky, secondary reason. Picky one first. The drift happened even when dividing distance by 45. I made a simple prim to sit on and moved the prim each time I clicked it. Over some 250m I found the prim drift upwards by 2m. Changing the incremental distance traveled on each click from 12 to 100m, I found the upward drift to be much less. I conclude that drift isn't a function of overall distance traveled but the number of discrete steps to achieve that distance. Whether that's correct or not is, I guess, academic (to me). Now the main reason for disappointment is because movement really was quite jerky - in a region to myself with nothing else going on. Slowing the speed down only made the jitters more evident. I felt there wasn't a lot to choose between this keyframe approach and calling llSetPos repeatedly for a short distance in a loop (to control speed).

Just to make sure this isn't due to my clumsy coding (I must admit I have cobbled this together from looking at examples!).

```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>;
}

default
{
state_entry()
{
[PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX,
PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]);
}

touch_start(integer total_number)
{
vector ownPosition = llGetPos();
rotation ownRotation = llGetRot();

llSetKeyframedMotion(
[<0.0, 12.0, 0.1>,
NormRot(ownRotation),
30/45.0],
[]);
}
}```

Edited by Frankie Rockett
to correct my grammar!
##### Share on other sites

16 hours ago, Frankie Rockett said:

The drift happened even when dividing distance by 45.

Haven't been following the details in this thread, but this seems confused. The relevance to keyframed motion of divisibility by 45 is time, not distance, and the underlying reason for it is presumably the simulator's frame rate of 45 per second. To move across 256m in a straight line and constant speed over some period of time, one would do it in a single llSetKeyframedMotion call; to minimize a small amount of potential drift, you'd just want to make the time of that motion to be an integer multiple of 1/45 second.

(Incidentally, just looking at the code without understanding the plan here, I notice the motion is specified to creep upwards 0.1m on each call. That should move it upwards about 4.5m over forty five calls, which may have been the intent—or maybe some of that could be mistaken for drift?)

##### Share on other sites

Thank you Qie.
You are clearly correct. Doh! It is time not distance!! And the 0.1 - my error. No idea how that crept in but changed to 0.0 now.

I corrected the use of 1/45 and tried again. No change to performance though (unless I didn't really correct it?). Still quite jittery. After observing it several times I note two things about the jitter. 1. The avatar's seated position varies slightly during the journey, as if the cube it's sat on is trying to pull away, out from under his bum. 2. After repeat viewings, I can confirm that during the transit of the prim, my own camera jitters and shakes to a disturbing degree. This is regardless of whether I fix my viewpoint on the movement or look somewhere else entirely, or even change my focus from place to place. Whether the prim too is shaking during transit I cannot say. I'd need to log in an alt and watch from that point of view to be certain, but for the 'first person' it's horrible, and not, I note,  an effect duplicated with llMoveToTarget.

Here's where I'm up to with it.

```integer speed = 1000; // Less is faster!

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>;
}

default
{
state_entry()
{
[PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX,
PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]);
speed = (integer) (speed * 0.022222222222222);
}

touch_start(integer total_number)
{
vector ownPosition = llGetPos();
rotation ownRotation = llGetRot();
vector detectedPosition = llDetectedPos(0);
rotation detectedRotation = llDetectedRot(0);

llSetKeyframedMotion(
[<0.0, 250.0, 0.0>,
NormRot(ownRotation),
speed],
[]);
}
}```