Difference between revisions of "User:Tapple Gao/3D Spinning Pendulum Motion"

From Second Life Wiki
Jump to: navigation, search
Line 9: Line 9:
 
// http://wiki.secondlife.com/wiki/User:Tapple_Gao/3D_Spinning_Pendulum_Motion
 
// http://wiki.secondlife.com/wiki/User:Tapple_Gao/3D_Spinning_Pendulum_Motion
  
// Change these to configure the swing
+
// speed up or slow down all animations:
 +
float speed = 1.0; // Speed Multiplier. 2.0 for double speed, 0.5 for half speed
 +
 
 +
// Change these to configure the "custom" item in the menu
 
vector spinAxis = <0,0,1>;      // axis to spin on
 
vector spinAxis = <0,0,1>;      // axis to spin on
 
vector majorSwingAxis = <0,1,0>; // axis to swing about
 
vector majorSwingAxis = <0,1,0>; // axis to swing about
 
float length = 8.0;  // how long is the animation, in seconds
 
float length = 8.0;  // how long is the animation, in seconds
integer animFrameCount = 40; // how many frames is the animation? more is smoother
+
float frameRate = 5.0; // frames per second. Must be between 0.0 and 9.0
 
integer majorSwingDegrees = 40;  // How far the swing moves, forward to back
 
integer majorSwingDegrees = 40;  // How far the swing moves, forward to back
 
integer minorSwingDegrees = 20;  // How far from the vertical the swing moves, side to side
 
integer minorSwingDegrees = 20;  // How far from the vertical the swing moves, side to side
Line 36: Line 39:
  
 
stopPendulum() {
 
stopPendulum() {
 +
    llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);
 
     if (!isSwinging) return;
 
     if (!isSwinging) return;
  
    llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);
+
     llSetLinkPrimitiveParamsFast(!!llGetLinkNumber(), [PRIM_ROT_LOCAL, savedRot]);
     llSetLinkPrimitiveParamsFast(LINK_ROOT, [PRIM_ROT_LOCAL, savedRot]);
+
 
     isSwinging = FALSE;
 
     isSwinging = FALSE;
 
}
 
}
Line 47: Line 50:
 
// majorSwingAxis: The primary axis to swing 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
 
// length: The length of the animation, in seconds. Must be positive
// frameCount: The total number of frames that will be computed. Higher is smoother, but more memory intensive. 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
 
// 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
 
// 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
Line 54: Line 57:
 
// phase: timing difference between major and minor swinging, in radians. Set this to PI_BY_TWO for eliptical 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
 
// 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
spinningPendulum(vector spinAxis, vector majorSwingAxis, float length, integer frameCount, float majorSwingAngle, float minorSwingAngle, integer majorSwingCount, integer minorSwingCount, float phase, integer spinCount) {
+
spinningPendulum(vector spinAxis, vector majorSwingAxis, float length, float frameRate, float majorSwingAngle, float minorSwingAngle, integer majorSwingCount, integer minorSwingCount, float phase, integer spinCount) {
 
     stopPendulum();
 
     stopPendulum();
     savedRot = llGetRot();
+
     savedRot = llGetRootRotation();
 
     isSwinging=TRUE;
 
     isSwinging=TRUE;
  
Line 62: Line 65:
 
     majorSwingAxis = minorSwingAxis % spinAxis;
 
     majorSwingAxis = minorSwingAxis % spinAxis;
 
     rotation currentRot;
 
     rotation currentRot;
 +
    integer frameCount = (integer)(length * frameRate);
 
     float frameTime = length / frameCount;
 
     float frameTime = length / frameCount;
 
     list frames = [];
 
     list frames = [];
Line 70: Line 74:
 
             llAxisAngle2Rot(minorSwingAxis, llCos(frame * TWO_PI / frameCount * minorSwingCount - phase) * minorSwingAngle);
 
             llAxisAngle2Rot(minorSwingAxis, llCos(frame * TWO_PI / frameCount * minorSwingCount - phase) * minorSwingAngle);
 
         //llSetRot(nextRot * savedRot); llSleep(0.1);
 
         //llSetRot(nextRot * savedRot); llSleep(0.1);
         if (frame == 0) llSetLinkPrimitiveParamsFast(LINK_ROOT,
+
         if (frame == 0) llSetLinkPrimitiveParamsFast(!!llGetLinkNumber(),
 
             [PRIM_ROT_LOCAL, nextRot*savedRot]);
 
             [PRIM_ROT_LOCAL, nextRot*savedRot]);
 
         else frames += [NormRot(nextRot / currentRot), frameTime];
 
         else frames += [NormRot(nextRot / currentRot), frameTime];
Line 106: Line 110:
 
                 <0,1,0>, // majorSwingAxis
 
                 <0,1,0>, // majorSwingAxis
 
                 4.0, // animation length, in seconds
 
                 4.0, // animation length, in seconds
                 20, // frameCount
+
                 5.0, // frameRate
 
                 50*DEG_TO_RAD, // majorSwingAngle
 
                 50*DEG_TO_RAD, // majorSwingAngle
 
                 0*DEG_TO_RAD, // minorSwingAngle
 
                 0*DEG_TO_RAD, // minorSwingAngle
Line 118: Line 122:
 
                 <0,1,0>, // majorSwingAxis
 
                 <0,1,0>, // majorSwingAxis
 
                 8.0, // animation length, in seconds
 
                 8.0, // animation length, in seconds
                 40, // frameCount
+
                 5.0, // frameRate
 
                 40*DEG_TO_RAD, // majorSwingAngle
 
                 40*DEG_TO_RAD, // majorSwingAngle
 
                 20*DEG_TO_RAD, // minorSwingAngle
 
                 20*DEG_TO_RAD, // minorSwingAngle
Line 130: Line 134:
 
                 <0,1,0>, // majorSwingAxis
 
                 <0,1,0>, // majorSwingAxis
 
                 3.0, // animation length, in seconds
 
                 3.0, // animation length, in seconds
                 16, // frameCount
+
                 5.0, // frameRate
 
                 20*DEG_TO_RAD, // majorSwingAngle
 
                 20*DEG_TO_RAD, // majorSwingAngle
 
                 20*DEG_TO_RAD, // minorSwingAngle
 
                 20*DEG_TO_RAD, // minorSwingAngle
Line 142: Line 146:
 
                 <0,1,0>, // majorSwingAxis
 
                 <0,1,0>, // majorSwingAxis
 
                 15.0, // animation length, in seconds
 
                 15.0, // animation length, in seconds
                 40, // frameCount
+
                 3.0, // frameRate
 
                 50*DEG_TO_RAD, // majorSwingAngle
 
                 50*DEG_TO_RAD, // majorSwingAngle
 
                 20*DEG_TO_RAD, // minorSwingAngle
 
                 20*DEG_TO_RAD, // minorSwingAngle
Line 154: Line 158:
 
                 <0,1,0>, // majorSwingAxis
 
                 <0,1,0>, // majorSwingAxis
 
                 60.0, // animation length, in seconds
 
                 60.0, // animation length, in seconds
                 40, // frameCount
+
                 2.5, // frameRate
 
                 5*DEG_TO_RAD, // majorSwingAngle
 
                 5*DEG_TO_RAD, // majorSwingAngle
 
                 10*DEG_TO_RAD, // minorSwingAngle
 
                 10*DEG_TO_RAD, // minorSwingAngle
Line 166: Line 170:
 
                 majorSwingAxis, // majorSwingAxis
 
                 majorSwingAxis, // majorSwingAxis
 
                 length, // animation length, in seconds
 
                 length, // animation length, in seconds
                 animFrameCount, // frameCount
+
                 frameRate, // frameRate
 
                 majorSwingDegrees*DEG_TO_RAD, // majorSwingAngle
 
                 majorSwingDegrees*DEG_TO_RAD, // majorSwingAngle
 
                 minorSwingDegrees*DEG_TO_RAD, // minorSwingAngle
 
                 minorSwingDegrees*DEG_TO_RAD, // minorSwingAngle

Revision as of 18:48, 13 May 2017

3D Spinning Pendulum Motion

Generalized undamped pendulum motion in 3 dimensions. Can swing in simple arcs, circles, elipses, or lissajous curves, and spin at the same time.
Suitable for regular pendulums, swings, spinning tire swings, spinning tops, bobbleheads, rocking boats, etc.
Script has a menu of demo motions

// Spinning pendulum script 1.2 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
 
// Change these to configure the "custom" item in the menu
vector spinAxis = <0,0,1>;      // axis to spin on
vector majorSwingAxis = <0,1,0>; // axis to swing about
float length = 8.0;  // how long is the animation, in seconds
float frameRate = 5.0; // frames per second. Must be between 0.0 and 9.0
integer majorSwingDegrees = 40;  // How far the swing moves, forward to back
integer minorSwingDegrees = 20;  // How far from the vertical the swing moves, side to side
integer majorSwingCount = -2; // How many times I swing forward to back
integer minorSwingCount = -2; // How many times I swing side to side. Usually set to the same as majorSwingCount
integer phaseDegrees = 90; // timing difference between major and minor swinging motion. Set to 90 for eliptical motion
integer spinCount = 1; // How many times the tire swing spins per animation
 
// End of user configuration
 
/////////////////////////////////////////////////////////
///////////// Spinning 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
spinningPendulum(vector spinAxis, vector majorSwingAxis, float length, float frameRate, float majorSwingAngle, float minorSwingAngle, integer majorSwingCount, integer minorSwingCount, float phase, integer spinCount) {
    stopPendulum();
    savedRot = llGetRootRotation();
    isSwinging=TRUE;
 
    vector minorSwingAxis = spinAxis % majorSwingAxis;
    majorSwingAxis = minorSwingAxis % spinAxis;
    rotation currentRot;
    integer frameCount = (integer)(length * frameRate);
    float frameTime = length / frameCount;
    list frames = [];
    integer frame;
    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;
    }
    //llOwnerSay(llList2CSV(frames)); 
    llSetKeyframedMotion(frames, [KFM_MODE, KFM_LOOP, KFM_DATA, KFM_ROTATION]);
}
 
/////////////////////////////////////////////////////////
//////////////////////// SCRIPT /////////////////////////
/////////////////////////////////////////////////////////
 
integer menuHandle;
 
doMenu(key id) {
    llListenRemove(menuHandle);
    integer menuChannel = -1 - (integer)llFrand(329847);
    menuHandle = llListen(menuChannel, "", id, "");
    llDialog(id, "Choose a demo",
        ["swing", "tire swing", "top", "bobblehead", "rocking boat", "custom", "stop"],
        menuChannel);
}
 
default {
    touch_start(integer num) {
        doMenu(llDetectedKey(0));
    }
 
    listen(integer channel, string name, key id, string msg) {
        doMenu(id);
        if (msg == "swing") {
            spinningPendulum(
                <0,0,1>, // spinAxis
                <0,1,0>, // majorSwingAxis
                4.0, // animation length, in seconds
                5.0, // frameRate
                50*DEG_TO_RAD, // majorSwingAngle
                0*DEG_TO_RAD, // minorSwingAngle
                1, // majorSwingCount
                0, // minorSwingCount
                90*DEG_TO_RAD, // phase
                0); // spinCount
        } else if (msg == "tire swing") {
            spinningPendulum(
                <0,0,1>, // spinAxis
                <0,1,0>, // majorSwingAxis
                8.0, // animation length, in seconds
                5.0, // frameRate
                40*DEG_TO_RAD, // majorSwingAngle
                20*DEG_TO_RAD, // minorSwingAngle
                -2, // majorSwingCount
                -2, // minorSwingCount
                90*DEG_TO_RAD, // phase
                1); // spinCount
        } else if (msg == "top") {
            spinningPendulum(
                <0,0,1>, // spinAxis
                <0,1,0>, // majorSwingAxis
                3.0, // animation length, in seconds
                5.0, // frameRate
                20*DEG_TO_RAD, // majorSwingAngle
                20*DEG_TO_RAD, // minorSwingAngle
                1, // majorSwingCount
                1, // minorSwingCount
                90*DEG_TO_RAD, // phase
                3); // spinCount
        } else if (msg == "bobblehead") {
            spinningPendulum(
                <0,0,1>, // spinAxis
                <0,1,0>, // majorSwingAxis
                15.0, // animation length, in seconds
                3.0, // frameRate
                50*DEG_TO_RAD, // majorSwingAngle
                20*DEG_TO_RAD, // minorSwingAngle
                4, // majorSwingCount
                3, // minorSwingCount
                30*DEG_TO_RAD, // phase
                0); // spinCount
        } else if (msg == "rocking boat") {
            spinningPendulum(
                <0,0,1>, // spinAxis
                <0,1,0>, // majorSwingAxis
                60.0, // animation length, in seconds
                2.5, // frameRate
                5*DEG_TO_RAD, // majorSwingAngle
                10*DEG_TO_RAD, // minorSwingAngle
                4, // majorSwingCount
                3, // minorSwingCount
                30*DEG_TO_RAD, // phase
                0); // spinCount
        } else if (msg == "custom") {
            spinningPendulum(
                spinAxis, // spinAxis
                majorSwingAxis, // majorSwingAxis
                length, // animation length, in seconds
                frameRate, // frameRate
                majorSwingDegrees*DEG_TO_RAD, // majorSwingAngle
                minorSwingDegrees*DEG_TO_RAD, // minorSwingAngle
                majorSwingCount, // majorSwingCount
                minorSwingCount, // minorSwingCount
                phaseDegrees*DEG_TO_RAD, // phase
                spinCount); // spinCount
        } else if (msg == "stop") {
            stopPendulum();
        }
    }
}

If you get the error "Only linksets which uses the new prim equivalency system may be animated.", set the object to physics type convex hull, or add the followin in the script:

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

I did not include that in the script since it would break mesh builds with custom physics shapes