# Simultaneous Rotation Around All Three Axes

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

## Recommended Posts

I'd like to have an object rotate smoothly around all three axes simultaneously. I suppose the best RL analog would be a spacecraft tumbling around its axes. I do not want to change the position of the object.

I've tried with llTargetOmega() but setting varying values for x,y,z in the vector argument just sets an axis of rotation, that is offset from the world axes by the values, and the object rotates around that axis.

I'm experimenting with llSetLinkPrimitiveParamsFast(), code snippet below. But the motion is jerky. Right now I am just incrementing the rotation or each axis by the same amount but would vary this to give more interesting motion later.)

Any ideas on how to do with with smoother motion, maybe with KFM? Or even some way with llTargetOmega() that I am not seeing?

Thanks,

G

touch_start(integer total_number)
{
integer i = 0;

if (startRotation == TRUE)
{
llSetTimerEvent(0.022);
startRotation = FALSE;
} else
{
llSetTimerEvent(0.0);
startRotation = TRUE;
}
}

timer()
{
xDegree += 0.2; if (xDegree > 360.0) xDegree = 0.0;
yDegree += 0.2; if (yDegree > 360.0) yDegree = 0.0;
zDegree += 0.2; if (zDegree > 360.0) zDegree = 0.0;
}

##### Share on other sites

You can open up a world of possibilities if you consider a multiprim object (think of an onion), especially if you make some of the rotating links invisible.

##### Share on other sites

What does it mean to "rotate around all three axes simultaneously" and why isn't it exactly what llTargetOmega does?

After looking at your code, it seems to me that you want the axis of rotation to change over time.

This can be done relatively simply by combining 3 separate rotations (for each axis).

So let's start small, how do we rotate around one axis over time?

```float time = llGetTime();
rotation r = llEuler2Rot(<time, 0, 0>);

Simple enough, we can also change the rate by multiplying time. But how do we combine two rotations? The LSL wiki (as complex as it may seem) tells us that two rotations can be combined by "multiplying" them together.

`rotation r = llEuler2Rot(<time, 0, 0>) * llEuler2Rot(<0, time*0.5, 0>);`

This'll make the object spin around the (world's) X axis and half the rate for Y axis. It'll make it "wobble" a bit. If the rates were the same, it'd repeat a certain pattern every pi seconds. The more different the rates are (especially if they're not multiples of each other), the longer and less obvious the pattern becomes.

Adding the third axis is no different.

`rotation r = llEuler2Rot(<time, 0, 0>) * llEuler2Rot(<0, time*0.5, 0>) * llEuler2Rot(<0, 0, time*2>);`
Edited by Wulfie Reanimator
• 1
##### Share on other sites

3 hours ago, GManB said:

I suppose the best RL analog would be a spacecraft tumbling around its axes. I do not want to change the position of the object.

That's rotation around a single axis. Any object rotating freely without outside forces has some axis of rotation. The axis will not change without some outside force. This is why gyroscopes work.

Rotational velocity is described by a vector. The direction of the vector is the axis of rotation, and the magnitude of the vector is the speed. That's what you feed into llTargetOmega.

Changing the axis of rotation by applying an external force is called "precession". There are good articles and videos on line about how that works.

• 1
##### Share on other sites

3 hours ago, Wulfie Reanimator said:

After looking at your code, it seems to me that you want the axis of rotation to change over time.

```
rotation r = llEuler2Rot(<time, 0, 0>) * llEuler2Rot(<0, time*0.5, 0>) * llEuler2Rot(<0, 0, time*2>);```

Yes, axis of rotation to change smoothly over time.

The video is exactly the motion I want. How did you get it so smooth? Are you using llTargetOmega or llSetLinkPrimativeParamsFast and a timer? I tried the latter and my object's motion is very jerky.

Thanks,
G

##### Share on other sites

15 minutes ago, GManB said:

The video is exactly the motion I want. How did you get it so smooth? Are you using llTargetOmega or llSetLinkPrimativeParamsFast and a timer? I tried the latter and my object's motion is very jerky.

I used an infinite loop that only contains the three lines of code I've shown in the previous post. Triggering a separate event for each update has significant overhead compared to a single loop that simply pauses and continues. Of course, infinite loops aren't usually practical.

Edited by Wulfie Reanimator
##### Share on other sites

5 hours ago, Rolig Loon said:

You can open up a world of possibilities if you consider a multiprim object (think of an onion), especially if you make some of the rotating links invisible.

Yes! Multiprim objects are next on my list. Here is a video of the object for which I want the different rotation.

Next is to make something similar where the spikes rotate individually.

G

• 1
##### Share on other sites

33 minutes ago, Wulfie Reanimator said:

I used an infinite loop that only contains the three lines of code I've shown in the previous post. Triggering a separate event for each update has significant overhead compared to a single loop that simply pauses and continues. Of course, infinite loops aren't usually practical.

Smoother, but not as smooth as in the video you gave. Is there any way for a prim that is executing such a loop to receive another event? AFAIK there is not but am hoping to be told otherwise.

Thanks,

G

##### Share on other sites

54 minutes ago, GManB said:

Smoother, but not as smooth as in the video you gave. Is there any way for a prim that is executing such a loop to receive another event? AFAIK there is not but am hoping to be told otherwise.

No, a script cannot process new events until the current one ends. An event can't end before the loop ends. And infinite loops...

The smoothness is affected by script lag in the region. You could show your script just in case.

Also, is that thing you showed available inworld somewhere?

Edited by Wulfie Reanimator
##### Share on other sites

2 hours ago, GManB said:

Is there any way for a prim that is executing such a loop to receive another event? AFAIK there is not but am hoping to be told otherwise.

not in the way you are hoping, but a way is to use two scripts

script 1 is main

script 2 does the tumbling.  The tumble loop is started with a link_message . Within the loop it polls object description field and acts on the instructions placed there by script 1,  Tumble stop. axis and rate changes, etc

when is commanded to stop, it exits the loop and waits for link message to start again

##### Share on other sites

1 minute ago, Mollymews said:

not in the way you are hoping, but a way is to use two scripts

script 1 is main

script 2 does the tumbling.  The tumble loop is started with a link_message . Within the loop it polls object description field and acts on the instructions placed there by script 1,  Tumble stop. axis and rate changes, etc

when is commanded to stop, it exits the loop and waits for link message to start again

You can also use llSetScriptState instead, to not spend time polling for external data.

• 1
##### Share on other sites

2 minutes ago, Wulfie Reanimator said:

You can also use llSetScriptState instead, to not spend time polling for external data.

yes that can work as well

a thing about polling is that it can help to reduce stutter when changing axis, spinrate, direction, etc. Stutter caused by stopping and restarting the script. Depends on how complex the movement of the tumble is tho

##### Share on other sites

3 hours ago, Wulfie Reanimator said:

Also, is that thing you showed available inworld somewhere?

@Wulfie ReanimatorNot, yet. I will probably put it on the MP but I will drop you a copy now.

I put the loop in a different state just to test if I could regain control w/o resetting the script. Of course, that didn't work. Also, I experimented with values for the argument to llSleep() from 1.0 to .01. None seemed to affect the rotation rate. My guess is that since we are using time waiting longer just results in a larger difference in the value of time.

I will try the above suggestion to get control. But I also want to investigate the motion keyframe api. I'm thinking to generate the list of rotation as we do here then feed it to mkf. Using time should still be fine because here we are just interpreting it as a float and not really a time.

G

default
{

touch_start(integer total_number)
{
if (startRotation == TRUE)
{
startRotation = FALSE;
} else
{
startRotation = TRUE;
state spinning;
}
}
}

state spinning
{
touch_start(integer num_detected)
{
llOwnerSay("foo");
}
state_entry()
{
float time;
rotation r;

while (startRotation == TRUE)
{
time = llGetTime();
r = llEuler2Rot(<time, 0, 0>) * llEuler2Rot(<0, time*0.5, 0>) * llEuler2Rot(<0, 0, time*2>);
llSleep(0.1);
}
}
}

##### Share on other sites

27 minutes ago, GManB said:

I also want to investigate the motion keyframe api. I'm thinking to generate the list of rotation as we do here then feed it to mkf.

definitely do this.  When we do pre-compile our KFMs then it makes the production release scripts a whole lot more efficient

##### Share on other sites

1 hour ago, Mollymews said:

a thing about polling is that it can help to reduce stutter when changing axis, spinrate, direction, etc. Stutter caused by stopping and restarting the script. Depends on how complex the movement of the tumble is tho

What do you mean by "stutter" exactly? Can you demonstrate?

Also, llSetScriptState should not be confused with llResetOtherScript. llSetScriptState will simply "suspend" the script the same way as the sim would when it's put into a schedule to wait for its turn to run. When the script continues, it will continue exactly from where it left off (mid-loop) as you'd normally expect.

40 minutes ago, GManB said:

@Wulfie ReanimatorNot, yet. I will probably put it on the MP but I will drop you a copy now.

I put the loop in a different state just to test if I could regain control w/o resetting the script. Of course, that didn't work. Also, I experimented with values for the argument to llSleep() from 1.0 to .01. None seemed to affect the rotation rate. My guess is that since we are using time waiting longer just results in a larger difference in the value of time.

I'd appreciate it, it looks very cool! I'll send some lindens your way as a thank you when I get it.

If I remove the llSleep and run your script, it rotates as smoothly as the script I used in my video, so the problem is probably just the sim you're in. Not much you can do about that. (Well, besides attempting to get KFM working, but that'd require unlinking parts of your object.)

Edited by Wulfie Reanimator
##### Share on other sites

1 hour ago, Wulfie Reanimator said:

What do you mean by "stutter" exactly? Can you demonstrate?

yes you are right about the difference between suspend and reset other

i was more thinking of the 2 script situation where we trigger the second script to start the tumble loop, and while the loop is running it gets change commands (within the loop) to act on, including a stop command

if we stop the looping to receive/fetch a change command then we can get stutter

##### Share on other sites

5 minutes ago, Mollymews said:

yes you are right about the difference between suspend and reset other

i was more thinking of the 2 script situation where we trigger the second script to start the tumble loop, and while the loop is running it gets change commands (within the loop) to act on, including a stop command

if we stop the looping to receive/fetch a change command then we can get stutter

I still don't understand. The only reason why you'd use two scripts is to have the infinite loop part in the second script -- and nothing else:

```default
{
state_entry()
{
while (1)
{
float time = llGetTime();
rotation r = llEuler2Rot(<time,0,0>) * llEuler2Rot(<0,time,0>) * llEuler2Rot(<0,0,time>);
}
}
}```

And then you have the main script that does everything else, including llSetScriptState to control the above script. There would be no "stopping the loop to receive commands."

##### Share on other sites

a example modding your code snippet Wulfie

```tumble()
{
while (1)
{
float time = llGetTime();
rotation r = llEuler2Rot(<time,0,0>) * llEuler2Rot(<0,time,0>) * llEuler2Rot(<0,0,time>);

if (llGetObjectDesc() == "stop")
return;
}
}

default
{
state_entry()
{
tumble();
}

link_message(integer sender_num, integer num, string str, key id)
{
if (str == "start")
tumble();
}
}```

when we do this then our main script remains responsive to events even tho the object can be tumbling infinitely

also which is the main point of doing it this way, we can add other commands into the loop so that the object will transition to different states seamlessly

Edited by Mollymews
##### Share on other sites

2 minutes ago, Mollymews said:

a example modding your code snippet Wulfie

```
tumble()
{
while (1)
{
float time = llGetTime();
rotation r = llEuler2Rot(<time,0,0>) * llEuler2Rot(<0,time,0>) * llEuler2Rot(<0,0,time>);

if (llGetObjectDesc() == "stop")
return;
}
}

default
{
state_entry()
{
tumble();
}

link_message(integer sender_num, integer num, string str, key id)
{
if (str == "start")
tumble();
}
}```

when we do this then our main script remains responsive to events even tho the object can be tumbling infinitely

I mean, you can do it like that, but to what benefit? In my opinion that's just overcomplicating the problem.

##### Share on other sites

1 minute ago, Wulfie Reanimator said:

I mean, you can do it like that, but to what benefit? In my opinion that's just overcomplicating the problem.

i did a edit while you were responding

the reason is to address the stutter which can happen when a script is waiting for an event queue instruction to change its transition state

• 1
##### Share on other sites

a situation when we might want to go into various movement states at some arbitrary time over which we (the scripter) have no control, is in a avatar HUD

where the HUD wearer chooses to do any of the scripted effects at any moment based on their whim, even partway thru the current effect. Like go left then go right now even when the go left effect hasn't completed a full turn

##### Share on other sites

4 hours ago, Wulfie Reanimator said:

If I remove the llSleep and run your script, it rotates as smoothly as the script I used in my video, so the problem is probably just the sim you're in. Not much you can do about that. (Well, besides attempting to get KFM working, but that'd require unlinking parts of your object.)

I've left mine running. with the llSleep(), for a while and it smoothed out. Could be the compute demand of the sim lessened.

Any thoughts on how we might slow the rotation speed? I think, that we would need smaller intervals between subsequent calls to llGetTime() while keeping the loop iteration time constant. Not easily done, I see. So, I'll probably collect a list of times from this loop, find the average different between two subsequent measurements, and then just increment the 'time' variable by smaller values. This increment can then be used to simply adjust the rotation speed.

Make sense?

G

##### Share on other sites

19 minutes ago, GManB said:

Any thoughts on how we might slow the rotation speed? I think, that we would need smaller intervals between subsequent calls to llGetTime() while keeping the loop iteration time constant. Not easily done, I see.

Multiply time by some value.

```float time = llGetTime(); // 100% speed
float time = llGetTime() * 2; // 200% speed
float time = llGetTime() * 0.25; // 25% speed```

Edited by Wulfie Reanimator
##### Share on other sites

If I do end up using KFM it also prevents any events being received by the script in which it it running. So, I will need one of the above techniques to keep control of the tumbler.

Seems I'll need three scripts. My current thinking is to use llResetOtherScript() when the main script needs to send a command but the main script will not be in the tumbler. So I need a script in the tumbler to receive a message for the tumbler (start, stop, change speed, change the ratios among the axes, etc), from the main script, call llResetOtherScript() on the tumbler, then send the message from the main script to the tumbler via a link_message. In the end I need only the tumbler script because the tumbler already has the script to receive commands from the main script. I am looking forward to seeing this work!!

##### Share on other sites

20 minutes ago, Wulfie Reanimator said:

Multiply time by some value.

```
float time = llGetTime(); // 100% speed
float time = llGetTime() * 2; // 200% speed
float time = llGetTime() * 0.25; // 25% speed```

duh... thanks..

G