Jump to content
Bitsy Buccaneer

scripting a wind chime

Recommended Posts

I'm trying to figure out how to script some subtle random z-axis oscillation into a very simple mesh wind "chime" (a Roman oscillum, think dinner plate on a string and you're pretty much there). My attempts to put a llFrand into target omega have only given syntax errors, so before I plow on trying to figure that out I'd just like to ask if that's a reasonable direction.

In trying to research this further, I came across a mention that llFrand means a server call each time so now I'm trying to think of another way to do it. A steady back and forth will look odd though. A touch start giving a small series of key framed movements might do the trick, but only if people are curious enough to poke at it.

Any suggestions? Just some back and forth on the z-axis to give the illusion of movement in a gentle breeze.

Share this post


Link to post
Share on other sites

Despite your description, I don't have a clear mental image of your wind chime.  I haven't spent enough time dangling dinner plates from strings, I guess. However, if the design allows it, you might try making the chime elements physical and giving them an occasional gentle nudge with llApplyImpulse or llApplyRotationalImpulse.  You'd have to keep them trapped in a non-phantom transparent box so that they don't wander off, of course.  Messing around with a system like that could do odd things to its L.I. and might add a physics penalty for the region, so experiment to see what's acceptable.  Anyway ... it's a thought.

  • Like 1

Share this post


Link to post
Share on other sites

As an afterthought, ...... in an earlier SL era, we would have used flexi prims to make a wind chime like this.  Making the prims respond to the flexi wind parameter was a cheap way to get the effect.  Sadly, we can't make flexi mesh objects, but there might be some clever way to tack mesh items to a flexi framework to create the illusion.  I can't think of how to do it at the moment, but it's an interesting challenge.

  • Like 1

Share this post


Link to post
Share on other sites

First thoughts are to use something like llVecMag (llWind (llGetPos ())) to scale the movement — the size of the oscillation, and then feed that into a multi-stage keyframed motion (stopped, slow, faster, slow, stopped) ping-pong, remembering to keep the time per oscillation constant, and adjusting the rate or magnitude of the rotation.

I guess I'd want to resample the wind at the midpoint of each oscillation to make it easier to keep the thing facing, on average, in the same direction.

I think I'd want to avoid using target omega because you'd have to throw a timer in there too.

And make it very shiny, and put it somewhere it'll catch the light.

I'm not sure how the sound is generated by one of these, or if it is at all (Wikipedia doesn't mention sound), so I'm not sure how I'd go about doing the chime part.

  • Like 1

Share this post


Link to post
Share on other sites
3 hours ago, Bitsy Buccaneer said:

In trying to research this further, I came across a mention that llFrand means a server call each time so now I'm trying to think of another way to do it. A steady back and forth will look odd though. A touch start giving a small series of key framed movements might do the trick, but only if people are curious enough to poke at it.

picking up on this part. Reducing the number of library calls in a run time loop

a way we can do this is to create a buffer of random values which only gets filled once per cycle, rather than at each step. Example:

note that the example buffer is of integer type. A more expansive buffer could be a list

// randomly choose between 4 stations [0,1,2,3] where the station chosen is not the same as the previous

default
{
    state_entry()
    {
        integer stations;
        integer station;
        integer times;
        float time;
        integer index;

        do
        {
            if (!index)
            {
                // 32 bits gives us 16 independent stations  
                stations = (integer)llFrand(0xFFFF) << 16 | (integer)llFrand(0xFFFF);
                times = (integer)llFrand(0xFFFF) << 16 | (integer)llFrand(0xFFFF);
            }             
    
            // get next station
            integer next = stations & 0x3;
            // adjust so that next station is not the same as the previous
            if (next == station) 
                next = (next + 1) % 4;  // not the same as the last
            station = next;

            // get next time
            time = 1.0 + ((float)(times & 0x3) * 0.3333);  
            // constant 1.0 gives: 1.0  1.33  1.66  2.0
            // with constant 1.0 a full cycle will complete within 16 to 32 seconds
    
            // do something with the station
            // a station is typically a index into a list of somethings
            // one something for each station
            // somethings can be keyframes, omegas, rotations, directions, etc
            // these can also be randomly generated/assigned in the !index clause
 
            llSay(0, (string)station + " " + (string)time);
    
            // do something with the time
            llSleep(time);  

            // increment index, get next
            index = (index + 1) % 16;  
            stations = stations >> 2;
            times = times >> 2;

        } while (1);
    }
}    

 

  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)

Years ago, I made a "price tag" that looks like it's blowing in the wind. I sent you a copy. Here's the script... (Grrr, I clearly don't know how to embed multiple spoilers, so you'll have to dig a little).

 

float angle=0.2; // max swing from resting (radians)
float steps=24.0; // number of Key Frames
float step=0.0;
list KFMlist=[];
vector U;
vector V;
float angleU=0.0;
float angleV;
integer swing=TRUE;
vector basePos;
rotation baseRot;
vector wind;
vector v1;
vector size;

 
default
{
    state_entry()
    {
        llSetPrimitiveParams([PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
        basePos = llGetPos();
        baseRot = llGetRot();
        
        vector size = llGetScale();   // get size of pendulum
        float period = TWO_PI*llSqrt( size.z/9.81);   // set period according to length (z axis)
        
        float dT = period/steps;
        dT = llRound(45.0*dT)/45.0; // make sure delta times are integer multiples of 1/45
        if ( dT < 0.13333 ) dT = 0.13333; // Prevent delta times below recommended minimum
        v1.x = 0.0;
        v1.y = 0.0;
        v1 = -0.5*v1*llGetRot();
        U = v1;
        while ( step < steps )
        {
            step += 1.0;
            angleV = angle*llCos( TWO_PI*step/steps + PI_BY_TWO);
            V = v1*llAxisAngle2Rot(llRot2Left(llGetRot()), angleV);
            KFMlist += [V-U, llEuler2Rot(< 0.0, angleV-angleU, 0.0>), dT];
            angleU = angleV;
            U = V;
        }
    }
    touch_start( integer n)
    {
        llSetKeyframedMotion( [], []);
        llSleep(0.2);
        llSetPrimitiveParams([PRIM_POSITION, basePos, PRIM_ROTATION, baseRot]);
        if ( swing ) llSetKeyframedMotion( KFMlist, [ KFM_MODE, KFM_LOOP]);
        swing = !swing;
    }
    on_rez( integer n) { llResetScript(); }
    
}

If you don't need for the swing angle and spin rate to change with the wind, I think you could just re-orient the link set every few seconds to coincide with the wind. If that's not enough, you could rebuilt the key frame list at regular intervals to work wind speed into both the swing amplitude and rate, and the spin rate. I don't have time to remind myself what I was doing, nor to work on it, so I've posted the script here for others to see.

ETA: I wonder if the spin is llTargetOmega and only the swing is KFM. The script sure looks that way.
ETA2: A quick test comfirms that, and the llTargetOmega call isn't there, so my price tag must be depending on that rotation persisting from a previous build.
ETA3: Touch it to start/stop swinging.
ETA4: The tag prim of the thing I sent you contains the llTargetOmega. That would be the equivalent of your dinner plate. (And I apparently have more time to look at this than I claimed.)
ETA5: As everybody else probably knows, and I just re-learned, KFM can't be mixed with object manipulations. This means that, once every few seconds a new KFM frame list must be generated to orient the swinging action in alignment with the wind.
ETA6: Geez, maybe I shouldn't have posted this. I've no idea what I was doing way back then!
ETA7: Bitsy, the script in your "Price Tag" won't match what's shown above, cuz I'm noodling and updating my post as I go.
ETA8: I'm too tired to noodle more, but I've got llSetKeyFramedMotion inside a timer and it should be possible to construct the swinging motion using the wind vector. There might be a little jerk here and there, but the spinning dinner plate might mask some of that.
ETA9: I've moved the KFM list generation into a timer event, triggered every 5 seconds, and scaled the amount of swing to the wind speed. The next step is to bring both the x and y components of the wind vector into play, to generate a swinging motion that follows the direction as well as the velocity. That'll have to wait, I've had my glass of water and I'm going back to bed. Here's what I've got so far...

 
 
float angle; // angular swing of pendulum, in radians
float windEffect=0.1; // 0 = wind has no effect
float maxAngle=1.0; // max angular swing, in radians
float steps=24.0; // number of Key Frames
float step=0.0;
list KFMlist=[];
vector U;
vector V;
float angleU=0.0;
float angleV;
integer swing=TRUE;
vector basePos;
rotation baseRot;
vector wind;
vector v1;
vector size;
float dT;

 
default
{
    state_entry()
    {
        llSetPrimitiveParams([PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
        basePos = llGetPos();
        baseRot = llGetRot();
        
        vector size = llGetScale();   // get size of pendulum
        float period = TWO_PI*llSqrt( size.z/9.81);   // set period according to length(z-axis), using Earth gravity
        dT = period/steps;
        dT = llRound(45.0*dT)/45.0; // make sure delta times are integer multiples of 1/45
        if ( dT < 0.13333 ) dT = 0.13333; // Prevent delta times below recommended minimum
        
        v1.x = 0.0;
        v1.y = 0.0;
        v1 = -0.5*v1*llGetRot();
        U = v1;
        
//        llSetTimerEvent( dT*steps*10 ); //recompute swing vector every 10th KFM loop
        llSetTimerEvent( 5 ); //recompute swing vector every 10th KFM loop
    }

    on_rez( integer n) { llResetScript(); }
    
    timer()
    {
        angle = llVecMag(llWind(ZERO_VECTOR)); // set swing angle proportional to wind speed
        angle = llSqrt(angle); // make low wind speeds more noticable
        angle = angle*windEffect;
        if (angle>maxAngle) angle = maxAngle;
         
        while ( step < steps )
        {
            step += 1.0;
            angleV = angle*llCos( TWO_PI*step/steps + PI_BY_TWO);
            V = v1*llAxisAngle2Rot(llRot2Left(llGetRot()), angleV);
            KFMlist += [V-U, llEuler2Rot(< 0.0, angleV-angleU, 0.0>), dT];
            angleU = angleV;
            U = V;
        }

        llOwnerSay("Wind velocity: " + (string)llWind(ZERO_VECTOR)+" "+(string)angle);
        llSetPrimitiveParams([PRIM_ROTATION, llEuler2Rot(wind)]);
        llSetKeyframedMotion( KFMlist, [ KFM_MODE, KFM_LOOP]);
    }
}

 

Edited by Madelaine McMasters
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)
19 hours ago, Rolig Loon said:

As an afterthought, ...... in an earlier SL era, we would have used flexi prims to make a wind chime like this.  Making the prims respond to the flexi wind parameter was a cheap way to get the effect.  Sadly, we can't make flexi mesh objects, but there might be some clever way to tack mesh items to a flexi framework to create the illusion.  I can't think of how to do it at the moment, but it's an interesting challenge.

Flexi the string perhaps?

The oscillum itself (what we're calling the dinner plate) doesn't flex, being marble or terracotta. But maybe the flex could go in the string it hangs on...?

(Edit: No. It makes a brilliant antenna but fails as a suspension string. Can't get a cylinder (.5, .5, .01, with 90* rotation on y) to move via flexi wind.)

16 hours ago, KT Kingsley said:

I think I'd want to avoid using target omega because you'd have to throw a timer in there too.

And make it very shiny, and put it somewhere it'll catch the light.

I'm not sure how the sound is generated by one of these, or if it is at all (Wikipedia doesn't mention sound), so I'm not sure how I'd go about doing the chime part.

OK, I started with target omega because I've been using it for other things but was coming to the conclusion it was unsuitable. I'm still learning what's suitable for what.

Not shiny, at least for this but maybe another one. Also the hanging dinner plate type probably didn't make sound. "Wind chime" is just the best descriptor I could think of for the movement. It can be awkward describing historic artefacts when they don't have a clear recent parallel.

I'll read up on llWind and the others you mentioned.

@Mollymews Will look into this too.

I tried @Madelaine McMasters's second script inworld and it gave a nice movement on a cylinder, though the orientation was off. I'll try it with my mesh on the beta grid and change the orientation in Blender if needed.

Thanks everyone for your suggestions and help. It's all much appreciated. I'll post back to let you know how the experiments go.

Edited by Bitsy Buccaneer

Share this post


Link to post
Share on other sites
2 minutes ago, Bitsy Buccaneer said:

I tried @Madelaine McMasters's second script inworld and it gave a nice movement on a cylinder, though the orientation was off. I'll try it with my mesh on the beta grid and change the orientation in Blender if needed.

I'm still working on it. It should be just a matter of working the x and y axes (there is no z) of the wind vector into the computed frames of the KFM call. My script currently animates swinging around only one axis (y). I don't think you'd need to do anything in Blender if I can work this out.

Share this post


Link to post
Share on other sites
14 minutes ago, Madelaine McMasters said:

I'm still working on it. It should be just a matter of working the x and y axes (there is no z) of the wind vector into the computed frames of the KFM call. My script currently animates swinging around only one axis (y). I don't think you'd need to do anything in Blender if I can work this out.

Changing the orientation in Blender to work with the script is easy to do, and generally easier than messing with scripts. Easier with the prim shapes which allow it too.

No Z for wind vector. That's good to know :) Saves me trying to find it :D

Share this post


Link to post
Share on other sites
7 minutes ago, Bitsy Buccaneer said:

Changing the orientation in Blender to work with the script is easy to do, and generally easier than messing with scripts. Easier with the prim shapes which allow it too.

No Z for wind vector. That's good to know :) Saves me trying to find it :D

Yep, winds don't blow up or down, only horizontally. KFM rotations are cumulative, with each frame building off the position/rotation of the previous frame, so unless you rebuild the keyframe list at exactly the right spot in each swing (when the object returns to its initial orientation), there will be drift. I avoid that by resetting the prim to it's initial position/rotation when restarting the animation. That sometimes (because KFM timing isn't precise) results in a little hiccup in the animation, but I think the rotating dinner place will distract from that.

I'm busy for much of the day but will try to get in-world to see if I can't work the X/Y wind axis into rotations for the KFM frame list. I forgot how much I must have known when I originally wrote that script, which didn't care about the wind.

  • Like 1

Share this post


Link to post
Share on other sites
3 minutes ago, Madelaine McMasters said:

Yep, winds don't blow up or down, only horizontally. KFM rotations are cumulative, with each frame building off the position/rotation of the previous frame, so unless you rebuild the keyframe list at exactly the right spot in each swing (when the object returns to its initial orientation), there will be drift. I avoid that by resetting the prim to it's initial position/rotation when restarting the animation. That sometimes (because KFM timing isn't precise) results in a little hiccup in the animation, but I think the rotating dinner place will distract from that.

I'm busy for much of the day but will try to get in-world to see if I can't work the X/Y wind axis into rotations for the KFM frame list. I forgot how much I must have known when I originally wrote that script, which didn't care about the wind.

It doesn't need to care about wind. It just needs to give some semblence of movement akin to what you'd see in a gentle breeze. A bit of x and y would be nice, but even just some rotation around the Z axis would be sufficient if it's not overtly regular.

I tried your second script in my mesh and it sent the suspension string all over the place. Oh, that would be a matter of the origin being in the wrong place. I'll try it with the origin moved tomorrow, need to have a wee rest now. Thanks Maddy x

Share this post


Link to post
Share on other sites

The bit about flexies knocked something in my brain, and animesh sprung to mind. Might this be the smoothest way of doing this? With switching between more and less extreme oscillation animations, or including more and less extreme oscillations in the same animation? Have I even understood the capabilities of animesh?

Also, if you want a bit of X and Y, maybe a particle effect string (ribbon)?

In this instance I really would like to hear detailed (or just general) demolitions of this idea, because I'm really not sure just what you can do with animesh.

Share this post


Link to post
Share on other sites
4 hours ago, KT Kingsley said:

animesh sprung to mind.

That's a good thought, and could be a very good approach with potentially zero script runtime. Downside: minimum 15 Land Impact.

  • Like 1

Share this post


Link to post
Share on other sites
54 minutes ago, Mollymews said:

if want to see what KFM can do then Tapple Gao's pendulum library is a good place to start

Thank you, Molly!  I hadn't seen this before.  I've just bookmarked it.  The KFM scripts I keep coming back to for insights are Dora Gustafson's at http://wiki.secondlife.com/wiki/User:Dora_Gustafson .  Dora was a gifted mathematician, so there's a lot of very clever stuff in her work.  

  • Like 3

Share this post


Link to post
Share on other sites
1 minute ago, Rolig Loon said:

Dora Gustafson's at http://wiki.secondlife.com/wiki/User:Dora_Gustafson .  Dora was a gifted mathematician, so there's a lot of very clever stuff in her work.  

i learned ever so much from Dora about rotations, specially rotating attached linked prims.  Dora is way up there with Ordinal when it comes to this stuff. They are legend to me

i only know Tapple like I know Strife and Void and Qie and Sei and Jopsy and quite a few others for their longtime body of work, postings, insights and helpfulness. They are legends as well. As is somebody called Rolig :)

  • Like 2

Share this post


Link to post
Share on other sites

It's an honor to be in this fraternity/sorority/guild/whatever-it-is.  There are so many talented and insightful people to learn from.  Fun too.

  • Like 2

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, Rolig Loon said:

Thank you, Molly!  I hadn't seen this before.  I've just bookmarked it.  The KFM scripts I keep coming back to for insights are Dora Gustafson's at http://wiki.secondlife.com/wiki/User:Dora_Gustafson .  Dora was a gifted mathematician, so there's a lot of very clever stuff in her work.  

My script was clearly cribbed from Dora, or from someone who cribbed from Dora. This explains why it looked so foreign to me. The variable names were not like I usually generate (I'm fairly verbose) and there were no comments. I recall seeing a swing hanging from a tree in Forgotten City, which sent me searching for how it had been done. Dora's simple pendulum on a cylindrical string, with an attached llTargetOmega price tag at the end, made for a pleasingly realistic "blowing in the wind" effect.

I've now modified Tapple Gao's "Tire Swing" script to produce somewhat random swing/spin, but it's not quite as convincing as having llTargetOmega do the spinning. KFM animations can sometimes hiccup, and the smooth rotation of llTargetOmega makes that less obvious. Nevertheless, Tapple Gao's script is the bee's knees.

ETA: Dora's script has the advantage of hinging the swing at one end of the prim (by translating the prim along with the rotation), rather than at the middle, like Tapple's. This allows you to use a full uncut prim for the pendulum, avoiding the classic SL door hinge requirement that you cut the door prim in half.

Edited by Madelaine McMasters
  • Thanks 1

Share this post


Link to post
Share on other sites

i had a play with KFM with some randomisation

i came to the conclusion that a KFM plate on a string looks better when it spins with only a tiny drift

here is a pic of the object i played with.  3 prims. The blue at the top is the root.  The script as wrote pivots on the root prim

Snapshot_019.png.6613f2bb50635f79f013439bf9b0885b.png

example script


float time;
list frames;
rotation rot;
integer playing;


makeFrames()
{
    time = 0;
    vector v = llRot2Euler(rot);
    
    frames = [];
     
    integer rounds = 8;  // change rounds as you like

    list reverse;  // frames
    float delta;
        
    integer r = rounds;
    for (r; r; r--)
    {
        float x = llFrand(0.04) + 0.02;  // change x to drift (x is radians)
        delta = (float)r / 45.0;
        if (delta < 0.22222) delta = 0.22222;
        
        integer i;
        for  (i; i < r; i++)
        {
            float z = llFrand(0.5) + 1.5;   // change z to spin (z is radians)
            frames += [
                llEuler2Rot(<x, 0, z>), delta, 
                llEuler2Rot(<x, 0, -z * 2>), delta,
                llEuler2Rot(<x, 0, z>), delta
            ];        
        
            reverse += [
                llEuler2Rot(<-x, 0, -z>), delta,
                llEuler2Rot(<-x, 0, z * 2>), delta,
                llEuler2Rot(<-x, 0, -z>), delta
            ];  
   
            time += (delta * 6.0);  // * 6.0 because 6 deltas
        }
        frames += reverse;
        reverse = [];    
    };
    time -= (delta * 3.0); // trim the time here as you like
}

default 
{
    state_entry()
    {
        llSetKeyframedMotion( [], [] );       
        rot = llGetRot();
    }
    
    timer()
    {
       if (playing)
       {
            llSetKeyframedMotion( [], [] );
            llSleep(0.2);
            llSetRot(rot); // reset the rotation to prevent drift       
            
            // some time to next play
            llSetTimerEvent(3.0 + llFrand(5.0));     
        }
        else
        {
            llSetTimerEvent(0.0);
            makeFrames();
            llSetKeyframedMotion( frames, [KFM_DATA, KFM_ROTATION]);        
            llSetTimerEvent(time);        
        }
        playing = !playing;
    }
    
    touch_end(integer n)
    {
        if (playing)
        {
            llSetTimerEvent(0.0);
            llSetKeyframedMotion( [], [] );
            llSleep(0.2);
            llSetRot(rot); // reset the rotation to prevent drift        
        }
        else
        {
            makeFrames();
            llSetKeyframedMotion( frames, [KFM_DATA, KFM_ROTATION]);        
            llSetTimerEvent(time);
        }    
        playing = !playing;
    }
}

 

  • Thanks 1

Share this post


Link to post
Share on other sites
On 8/11/2019 at 6:11 PM, KT Kingsley said:

The bit about flexies knocked something in my brain, and animesh sprung to mind. Might this be the smoothest way of doing this? With switching between more and less extreme oscillation animations, or including more and less extreme oscillations in the same animation? Have I even understood the capabilities of animesh?

Also, if you want a bit of X and Y, maybe a particle effect string (ribbon)?

In this instance I really would like to hear detailed (or just general) demolitions of this idea, because I'm really not sure just what you can do with animesh.

Interesting idea, but it's a wee decorative thing that absolutely cannot command 15 LI.

I love outside the box suggestions though, as you'll see below :D

Share this post


Link to post
Share on other sites
Posted (edited)
On 8/12/2019 at 1:08 AM, Madelaine McMasters said:

I've now modified Tapple Gao's "Tire Swing" script to produce somewhat random swing/spin, but it's not quite as convincing as having llTargetOmega do the spinning. KFM animations can sometimes hiccup, and the smooth rotation of llTargetOmega makes that less obvious. Nevertheless, Tapple Gao's script is the bee's knees.

Wonderful Maddy has been sending me test scripts for the last couple of days. One is "tire swing" and it rather distracted me from Very Serious Matters that I should be working on like ancient Roman oscilla.

1154090090_belli-tipsyfish2_001.thumb.png.1e3749e0af1f82256773918dc5a3003d.png

It being somewhat difficult to adequately capture in a picture, you are all most cordially invited to come try it out for yourselves. Slurl is in my sig. :SwingingFriends:

(Technical info, fish is a child prim on a path cut "string" which I alphaed out cause it made more sense in context. Script is Maddy's modification of Gao's.)

Edited by Bitsy Buccaneer

Share this post


Link to post
Share on other sites
10 minutes ago, Mollymews said:

i had a play with KFM with some randomisation

i came to the conclusion that a KFM plate on a string looks better when it spins with only a tiny drift

here is a pic of the object i played with.  3 prims. The blue at the top is the root.  The script as wrote pivots on the root prim

Thanks Molly. I will try this soon, probably tomorrow as I've been over-doing again. Thank you :)

Share this post


Link to post
Share on other sites
Posted (edited)
37 minutes ago, Bitsy Buccaneer said:

Wonderful Maddy has been sending me test scripts for the last couple of days. One is "tire swing" and it rather distracted me from Very Serious Matters that I should be working on like ancient Roman oscilla.

1154090090_belli-tipsyfish2_001.thumb.png.1e3749e0af1f82256773918dc5a3003d.png

It being somewhat difficult to adequately capture in a picture, you are all most cordially invited to come try it out for yourselves. Slurl is in my sig. :SwingingFriends:

(Technical info, fish is a child prim on a path cut "string" which I alphaed out cause it made more sense in context. Script is Maddy's modification of Gao's.)

I gave it a ride and it's pretty nice. For your original example of a dinner plate on a string, I would use the latest script I sent. Here's my modification of Gao's script. I've trimmed out the menu and option stuff, leaving only the "tire swing" style animation, but with an option for the Z axis to either rotate at constant rate as in the original script, or spin back and forth over the course of the animation. 

In this script, if you set spinAngle = 0, you'll get Gao's original behavior, with swinging about the major and minor axes and a constant rate spin about the spinAxis. If you set spinAngle nonzero, the prim will spin back and forth around the spinAxis at the specified angle, spinCount times. This more closely models something suspended from a fixed string/spring.

The next step, if you want it, would be to restart the KFM loop now and then, using different minor/major/spinCounts to make the motion appear more random. This might be tricky, as I don't know if there's a jitter free way to anticipate the end of the loop so that you're meshing animations smoothly. By selecting appropriate major/minor/swingCounts, I think you can get random enough looking motion from a single KFM animation. The total number of frames should be a multiple of the least common multiple of all counts. In the example below, majorSwingCount*minorSwingCount*spinCount=6. Eight seconds of animation at 9 frames/second is 72 frames, which is an even multiple of 6. This ensures that you'll get a full set of swings on all axes during each animation. If you don't follow this rule, you'll get a stutter at the end of each KFM loop.

Or at least that's what I think at the moment.

 

// 3D pendulum demo script 1.4 by Tapple Gao
// http://wiki.secondlife.com/wiki/User:Tapple_Gao/3D_Spinning_Pendulum_Motion
 
// speed up or slow down all animations:
float speed = 1.0; // Speed Multiplier. 2.0 for double speed, 0.5 for half speed
  
tireSwingMotion() { // Eliptical swing; spins slower than it swings
    startPendulum(
    <0,0,1>, // spinAxis. axis to spin on
    <0,1,0>, // majorSwingAxis. axis to swing about
    8.0, // animation length, in seconds
    9.0, // frames per second. Must be between 0.0 and 9.0
    20*DEG_TO_RAD, // majorSwingAngle. How far the swing moves, forward to back
    10*DEG_TO_RAD, // minorSwingAngle. How far the swing moves, side to side
    2, // majorSwingCount. How many times I swing forward to back
    3, // minorSwingCount. How many times I swing side to side. Usually set to the same as majorSwingCount
    60*DEG_TO_RAD, // phase. timing difference between major and minor swinging motion. Set to 90 for eliptical motion
    1,  // spinCount. How many times the tire spins forward per animation, only active if spinAngle = 0, negative counts spin backwards
    360*DEG_TO_RAD); // spinAngle. How far the tire spins back and forth per animation. If zero, spin is constant and unidirectional
}
// End of user configuration
 
/////////////////////////////////////////////////////////
///////////////// 3D Pendulum Library ///////////////////
/////////////////////////////////////////////////////////
 
integer isSwinging = FALSE;
rotation savedRot;
 
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>;
}
 
stopPendulum() {
    llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);
    if (!isSwinging) return;
 
    llSetLinkPrimitiveParamsFast(!!llGetLinkNumber(), [PRIM_ROT_LOCAL, savedRot]);
    isSwinging = FALSE;
}
 
// Causes a prim to swing like a pendulum or spin like a top, depending on parameters. Implements arbitrary pendulum motion in 3d
// spinAxis: The axis to spin about, in prim-local coordinates. must be nonzero
// majorSwingAxis: The primary axis to swing about, in prim-local coordinates. must be nonzero
// length: The length of the animation, in seconds. Must be positive
// frameRate: The smoothness of the animation, in frames per second. Must be between 0.0 and 9.0
// majorSwingAngle: The angle that the prim swings around the majorSwingAxis, in radians. can be positive, negative, or zero
// minorSwingAngle: The angle that the prim swings around the minor swing axis, in radians. minor swing axis is perpendicular to majorSwingAxis. can be positive, negative, or zero
// majorSwingCount: Number of times to swing about the major axis during the animation. Can be positive, negative, or zero. Negative numbers will cause the prim to swing in the oposite direction
// minorSwingCount: Number of times to swing about the minor axis during the animation. Can be positive, negative, or zero. Negative numbers will cause the prim to swing in the oposite direction. Set this to the same number as majorSwingCount for eliptical swinging motion. Set it to other values for unrealistic, but interesting motion
// phase: timing difference between major and minor swinging, in radians. Set this to PI_BY_TWO for eliptical motion
// spinCount: Number of times to spin during the animation. Can be positive, negative, or zero. If zero, the prim will swing only, without spinning. if negative, spin the other way

startPendulum(vector spinAxis, vector majorSwingAxis, float length, float frameRate, float majorSwingAngle, float minorSwingAngle, integer majorSwingCount, integer minorSwingCount, float phase, integer spinCount, float spinAngle) {
    stopPendulum();
    savedRot = llGetRootRotation();
    isSwinging=TRUE;
 
    vector minorSwingAxis = spinAxis % majorSwingAxis;
    majorSwingAxis = minorSwingAxis % spinAxis;
    rotation currentRot;
 
    length /= speed;
    integer frameCount = (integer)(length * frameRate);
    float frameTime = length / frameCount;
    list frames = [];
    integer frame;
    if(spinAngle!=0.0)
    {
        for (frame = 0; frame <= frameCount; frame++) {
            rotation nextRot = llAxisAngle2Rot(spinAxis, frame * spinCount * TWO_PI / frameCount) *
                llAxisAngle2Rot(majorSwingAxis, llCos(frame * TWO_PI / frameCount * majorSwingCount) * majorSwingAngle) *
                llAxisAngle2Rot(minorSwingAxis, llCos(frame * TWO_PI / frameCount * minorSwingCount - phase) * minorSwingAngle);
            //llSetRot(nextRot * savedRot); llSleep(0.1);
            if (frame == 0) llSetLinkPrimitiveParamsFast(!!llGetLinkNumber(),
                [PRIM_ROT_LOCAL, nextRot*savedRot]);
            else frames += [NormRot(nextRot / currentRot), frameTime];
            currentRot = nextRot;
        }
    }
    else
    {
       for (frame = 0; frame <= frameCount; frame++) {
            rotation nextRot =
                llAxisAngle2Rot(spinAxis,       llCos(frame * TWO_PI / frameCount * spinCount) * spinAngle) *
                llAxisAngle2Rot(majorSwingAxis, llCos(frame * TWO_PI / frameCount * majorSwingCount) * majorSwingAngle) *
                llAxisAngle2Rot(minorSwingAxis, llCos(frame * TWO_PI / frameCount * minorSwingCount - phase) * minorSwingAngle);
            //llSetRot(nextRot * savedRot); llSleep(0.1);
            if (frame == 0) llSetLinkPrimitiveParamsFast(!!llGetLinkNumber(),
                [PRIM_ROT_LOCAL, nextRot*savedRot]);
            else frames += [NormRot(nextRot / currentRot), frameTime];
            currentRot = nextRot;
         }
    }
    //llOwnerSay(llList2CSV(frames)); 
    llSetKeyframedMotion(frames, [KFM_MODE, KFM_LOOP, KFM_DATA, KFM_ROTATION]);
}
 
/////////////////////////////////////////////////////////
//////////////////////// SCRIPT /////////////////////////
/////////////////////////////////////////////////////////

default {
    
    state_entry()
    {
        llSetLinkPrimitiveParamsFast(LINK_ROOT,
            [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX,
            PRIM_LINK_TARGET, LINK_ALL_CHILDREN,
            PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]);
    }
    
    touch_start(integer num) {
        if(isSwinging)
        {
            stopPendulum();
        }
        else
        {
            tireSwingMotion();
        }
    }
}

 

Edited by Madelaine McMasters
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...