Difference between revisions of "Open Prim Animator"

From Second Life Wiki
Jump to navigation Jump to search
Line 58: Line 58:


         // Might want to move llGetRot() into the loop for fast rotating objects.
         // Might want to move llGetRot() into the loop for fast rotating objects.
// Rotation during the animation playback may cause errors.
        // Rotation during the animation playback may cause errors.
         rotation rootRot = llGetRot();
         rotation rootRot = llGetRot();


Line 88: Line 88:
playAnimation(float delay, integer loop)
playAnimation(float delay, integer loop)
{
{
  if(delay < 0.1) delay = 1.0;
    if(delay < 0.1) delay = 1.0;


  if( loop == FALSE)
    if( loop == FALSE)
  playAnimationStyle = 1;
        playAnimationStyle = 1;
  else
    else
  playAnimationStyle = 2;
        playAnimationStyle = 2;


  if (recordedSnapshots >= 1)
    if (recordedSnapshots >= 1)
  llSetTimerEvent(delay);
        llSetTimerEvent(delay);
}
}


Line 116: Line 116:
         maxMemory = llGetFreeMemory();
         maxMemory = llGetFreeMemory();
         freeMemory = llGetFreeMemory();
         freeMemory = llGetFreeMemory();
       
 
         primCount = llGetNumberOfPrims();
         primCount = llGetNumberOfPrims();
         commandListenerHandle = llListen(COMMAND_CHANNEL,"", llGetOwner(), "");
         commandListenerHandle = llListen(COMMAND_CHANNEL,"", llGetOwner(), "");
Line 124: Line 124:
         rootScale = llGetScale();
         rootScale = llGetScale();
     }
     }
   
 
     //Feel free to remove this on-touch trigger if you are using your own script to control playback
     //Feel free to remove this on-touch trigger if you are using your own script to control playback
     touch_start(integer num_detected)
     touch_start(integer num_detected)
Line 130: Line 130:
         //only activate after publish.
         //only activate after publish.
         if (commandListenerHandle == -1)
         if (commandListenerHandle == -1)
{
        {
//if animation not playing start it, else stop it.
            //if animation not playing start it, else stop it.
if( playAnimationStyle == 0)
            if( playAnimationStyle == 0)
playAnimation(1.0,TRUE);
                playAnimation(1.0,TRUE);
else
            else
{
            {
playAnimationStyle = 0;
                playAnimationStyle = 0;
llSetTimerEvent(0);
                llSetTimerEvent(0);
}
            }
}
        }
     }
     }


     changed(integer change)
     changed(integer change)
     {
     {
//this is needed to detect scale changes and record the differences in order to adjust the animation accordingly.
        //this is needed to detect scale changes and record the differences in order to adjust the animation accordingly.
         if (change & CHANGED_SCALE)
         if (change & CHANGED_SCALE)
         {
         {
Line 162: Line 162:
             }
             }
         }
         }
// if new prims are added or removed from this object then the script resets  
        // if new prims are added or removed from this object then the script resets
// because the animations are now broken.
        // because the animations are now broken.
else if (change & CHANGED_LINK)
        else if (change & CHANGED_LINK)
         {
         {
if( primCount != llGetNumberOfPrims() )
            if( primCount != llGetNumberOfPrims() )
{
            {
llOwnerSay("Link change detected, reseting script.");
                llOwnerSay("Link change detected, reseting script.");
llResetScript();
                llResetScript();
}
            }
}
        }
     }
     }
   
 
     //The message link function is to allow other scripts to control the snapshot playback
     //The message link function is to allow other scripts to control the snapshot playback
//This command will display snapshot #2:  
    //This command will display snapshot #2:
//     llMessageLinked(LINK_ROOT, 2, "XDshow", NULL_KEY);  llSleep(1.0);
    //     llMessageLinked(LINK_ROOT, 2, "XDshow", NULL_KEY);  llSleep(1.0);
//
    //
//This command will play through all the recorded snapshots in ascending order.  The number "1.0" is the delay speed and can be changed.
    //This command will play through all the recorded snapshots in ascending order.  The number "1.0" is the delay speed and can be changed.
//     llMessageLinked(LINK_ROOT, 0, "XDplay", "1.0");
    //     llMessageLinked(LINK_ROOT, 0, "XDplay", "1.0");
//
    //
//This command will loop through all the recorded snapshots in ascending order.  The number "1.0" is the delay speed and can be changed.
    //This command will loop through all the recorded snapshots in ascending order.  The number "1.0" is the delay speed and can be changed.
//     llMessageLinked(LINK_ROOT, 0, "XDplayLoop", "1.0");
    //     llMessageLinked(LINK_ROOT, 0, "XDplayLoop", "1.0");
//
    //
//To stop any playing animation use
    //To stop any playing animation use
// llMessageLinked(LINK_ROOT, 0, "XDstop", NULL_KEY);
    //     llMessageLinked(LINK_ROOT, 0, "XDstop", NULL_KEY);
     link_message(integer sender_num, integer num, string str, key id)
     link_message(integer sender_num, integer num, string str, key id)
     {
     {
Line 193: Line 193:
         {
         {
             currentSnapshot = 1;
             currentSnapshot = 1;
float delay = (float)((string)id);
            float delay = (float)((string)id);
playAnimation(delay,FALSE);
            playAnimation(delay,FALSE);
}
        }
         else if ("XDplayLoop" == str)
         else if ("XDplayLoop" == str)
{
        {
float delay = (float)((string)id);
            float delay = (float)((string)id);
playAnimation(delay,TRUE);
            playAnimation(delay,TRUE);
}
        }
else if ("XDstop" == str)
        else if ("XDstop" == str)
{
        {
playAnimationStyle = 0;
            playAnimationStyle = 0;
llSetTimerEvent(0);
            llSetTimerEvent(0);
}
        }
     }
     }


//This event handler takes care of all the editing commands.
    //This event handler takes care of all the editing commands.
//Available commands are: record, play, publish, show next, show prev, show #
    //Available commands are: record, play, publish, show next, show prev, show #
     listen(integer channel, string name, key id, string message)
     listen(integer channel, string name, key id, string message)
     {
     {
Line 216: Line 216:
         string secondWord = llToLower(llList2String(parsedMessage,1));
         string secondWord = llToLower(llList2String(parsedMessage,1));


//display a snapshot
        //display a snapshot
         if("show" == firstWord && recordedSnapshots > 0)
         if("show" == firstWord && recordedSnapshots > 0)
         {
         {
//stop any currently playing animation.
            //stop any currently playing animation.
llSetTimerEvent(0);
            llSetTimerEvent(0);


             if(secondWord == "next")
             if(secondWord == "next")
Line 255: Line 255:
             }
             }
         }
         }
//record a snapshot
        //record a snapshot
         else if(firstWord == "record")
         else if(firstWord == "record")
         {
         {
Line 283: Line 283:
             freeMemory = llGetFreeMemory();
             freeMemory = llGetFreeMemory();
         }
         }
//play the animation from beginning to end once without looping.
        //play the animation from beginning to end once without looping.
         else if (firstWord == "play")
         else if (firstWord == "play")
         {
         {
             float delay = (float)secondWord;
             float delay = (float)secondWord;
currentSnapshot = 1;
            currentSnapshot = 1;
//play the animation once without loop
            //play the animation once without loop
playAnimation(delay, FALSE);
            playAnimation(delay, FALSE);
         }
         }
         //publish disables the recording features and enables the on-touch trigger
         //publish disables the recording features and enables the on-touch trigger
         else if("publish" == firstWord)
         else if("publish" == firstWord)
         {
         {
//stop any currently playing animation.
            //stop any currently playing animation.
llSetTimerEvent(0);
            llSetTimerEvent(0);
playAnimationStyle = 0;
            playAnimationStyle = 0;
currentSnapshot = 1;
            currentSnapshot = 1;


//remove listeners to disable recording
            //remove listeners to disable recording
             llListenRemove(commandListenerHandle);
             llListenRemove(commandListenerHandle);
             commandListenerHandle = -1; //indicating that it's been published
             commandListenerHandle = -1; //indicating that it's been published
Line 311: Line 311:
     }
     }


//Timer event is used to handle the animation playback.
    //Timer event is used to handle the animation playback.
timer()
    timer()
     {
     {
showSnapshot(currentSnapshot);
        showSnapshot(currentSnapshot);


//if not at the end of the animation, increment the counter
        //if not at the end of the animation, increment the counter
if(currentSnapshot < recordedSnapshots)
        if(currentSnapshot < recordedSnapshots)
currentSnapshot++;
            currentSnapshot++;
else
        else
{
        {
// if animation is looping, set the counter back to 1
            // if animation is looping, set the counter back to 1
if( playAnimationStyle == 2)
            if( playAnimationStyle == 2)
currentSnapshot = 1;
                currentSnapshot = 1;
// if animation isn't looping, stop the animation
            // if animation isn't looping, stop the animation
else
            else
{
            {
llSetTimerEvent(0);
                llSetTimerEvent(0);
//if not published, show dialog menu
                //if not published, show dialog menu
if(commandListenerHandle != -1)
                if(commandListenerHandle != -1)
showMenuDialog();
                    showMenuDialog();
}
            }
}
        }
}
    }
}
}
</lsl>
</lsl>

Revision as of 08:10, 18 January 2011

<lsl> // Open Prim Animator - by Todd Borst

// This is provided AS IS without support. Please don't bug me demanding // help or custom work for this free script.

// Summary: This is a simple prim animation script. Just add this script // to your object and a dialog will automatically pop up for you to use.

// Features: // -Single script "Prim Puppeteer" like animation tool // -Playback controllable through external scripts // -Animation is scalable and resizeable // -On-touch trigger built-in // -Completely free and open sourced

// License: // You are welcome to use this anyway you like, even bring to other grids // outside of Second Life. If you try to sell what I'm giving away for // free, you will be cursed with unimaginably bad juju.

integer COMMAND_CHANNEL = 32;

integer primCount = 0; integer commandListenerHandle = -1;

list posList = []; list rotList = []; list scaleList = []; integer currentSnapshot = 0; integer recordedSnapshots = 0;

vector rootScale = ZERO_VECTOR; vector scaleChange = <1,1,1>;

// For tracking memory usage. The amount of snapshots you can record is based // on the number of prims and available memory. Less prims = more snapshots integer maxMemory = 0; integer freeMemory = 0;

integer playAnimationStyle = 0; // The values for playAnimationStyle means // 0 = no animation playing // 1 = play animation once // 2 = play animation looping

// This function is used to display a recorded snapshot showSnapshot(integer snapNumber) {

   if(snapNumber > 0 && snapNumber <= recordedSnapshots )
   {
       integer i = 0;
       vector   pos;
       rotation rot;
       vector   scale;
       vector rootPos = llGetPos();
       // Might want to move llGetRot() into the loop for fast rotating objects.
       // Rotation during the animation playback may cause errors.
       rotation rootRot = llGetRot();
       //2 is the first linked prim number.
       for( i = 2; i <= primCount; i++)
       {
           pos     = llList2Vector(posList,((snapNumber-1)*(primCount-1))+(i-2));
           rot     = llList2Rot(rotList,((snapNumber-1)*(primCount-1))+(i-2));
           scale   = llList2Vector(scaleList,((snapNumber-1)*(primCount-1))+(i-2));
           //Adjust for scale changes
           if( rootScale.x != 1.0 || rootScale.y != 1.0 || rootScale.z != 1.0 )
           {
               pos.x *= scaleChange.x;
               pos.y *= scaleChange.y;
               pos.z *= scaleChange.z;
               scale.x *= scaleChange.x;
               scale.y *= scaleChange.y;
               scale.z *= scaleChange.z;
           }
           llSetLinkPrimitiveParamsFast( i, [ PRIM_POSITION, pos, PRIM_ROTATION, rot/rootRot, PRIM_SIZE, scale ] );
       }
   }

}

// This function is used to start a sequential animation playback. // If the delay speed is set too low, the script might not be able to keep up. playAnimation(float delay, integer loop) {

   if(delay < 0.1) delay = 1.0;
   if( loop == FALSE)
       playAnimationStyle = 1;
   else
       playAnimationStyle = 2;
   if (recordedSnapshots >= 1)
       llSetTimerEvent(delay);

}

// This shows the edit menu showMenuDialog() {

   string temp = (string)((float)freeMemory/(float)maxMemory * 100.0);
   string menuText = "Available Memory: " + (string)freeMemory + " (" + llGetSubString(temp, 0, 4) +"%)" +
   "\nCurrent Snapshot: " + (string)currentSnapshot +"\tSnapshots Recorded: " + (string)recordedSnapshots +
   "\n\n[ Record ] - Record a snapshot of prim positions\n[ Play ] - Play back all the recorded snapshots\n[ Publish ] - Finish the recording process\n[ Show Next ] - Show the next snapshot\n[ Show Prev ] - Show the previous snapshot";
   llDialog(llGetOwner(), menuText, ["Record","Play","Publish","Show Prev","Show Next"], COMMAND_CHANNEL);

}

default {

   state_entry()
   {
       maxMemory = llGetFreeMemory();
       freeMemory = llGetFreeMemory();
       primCount = llGetNumberOfPrims();
       commandListenerHandle = llListen(COMMAND_CHANNEL,"", llGetOwner(), "");
       showMenuDialog();
       //setting initial root scale.  this allows the animation to scale if the root size is changed afterwards.
       rootScale = llGetScale();
   }
   //Feel free to remove this on-touch trigger if you are using your own script to control playback
   touch_start(integer num_detected)
   {
       //only activate after publish.
       if (commandListenerHandle == -1)
       {
           //if animation not playing start it, else stop it.
           if( playAnimationStyle == 0)
               playAnimation(1.0,TRUE);
           else
           {
               playAnimationStyle = 0;
               llSetTimerEvent(0);
           }
       }
   }
   changed(integer change)
   {
       //this is needed to detect scale changes and record the differences in order to adjust the animation accordingly.
       if (change & CHANGED_SCALE)
       {
           if (rootScale != ZERO_VECTOR)
           {
               vector newScale = llGetScale();
               //since change_scale is triggered even with linked prim changes,
               //this is to filter out non-root changes.
               if( ( newScale.x / rootScale.x) != scaleChange.x ||
                   ( newScale.y / rootScale.y) != scaleChange.y ||
                   ( newScale.z / rootScale.z) != scaleChange.z )
               {
                   scaleChange.x = newScale.x / rootScale.x;
                   scaleChange.y = newScale.y / rootScale.y;
                   scaleChange.z = newScale.z / rootScale.z;
               }
           }
       }
       // if new prims are added or removed from this object then the script resets
       // because the animations are now broken.
       else if (change & CHANGED_LINK)
       {
           if( primCount != llGetNumberOfPrims() )
           {
               llOwnerSay("Link change detected, reseting script.");
               llResetScript();
           }
       }
   }
   //The message link function is to allow other scripts to control the snapshot playback
   //This command will display snapshot #2:
   //      llMessageLinked(LINK_ROOT, 2, "XDshow", NULL_KEY);  llSleep(1.0);
   //
   //This command will play through all the recorded snapshots in ascending order.  The number "1.0" is the delay speed and can be changed.
   //      llMessageLinked(LINK_ROOT, 0, "XDplay", "1.0");
   //
   //This command will loop through all the recorded snapshots in ascending order.  The number "1.0" is the delay speed and can be changed.
   //      llMessageLinked(LINK_ROOT, 0, "XDplayLoop", "1.0");
   //
   //To stop any playing animation use
   //      llMessageLinked(LINK_ROOT, 0, "XDstop", NULL_KEY);
   link_message(integer sender_num, integer num, string str, key id)
   {
       if ("XDshow" == str && num >= 1 && num <= recordedSnapshots)
           showSnapshot(num);
       else if ("XDplay" == str)
       {
           currentSnapshot = 1;
           float delay = (float)((string)id);
           playAnimation(delay,FALSE);
       }
       else if ("XDplayLoop" == str)
       {
           float delay = (float)((string)id);
           playAnimation(delay,TRUE);
       }
       else if ("XDstop" == str)
       {
           playAnimationStyle = 0;
           llSetTimerEvent(0);
       }
   }
   //This event handler takes care of all the editing commands.
   //Available commands are: record, play, publish, show next, show prev, show #
   listen(integer channel, string name, key id, string message)
   {
       list parsedMessage = llParseString2List(message, [" "], []);
       string firstWord = llToLower(llList2String(parsedMessage,0));
       string secondWord = llToLower(llList2String(parsedMessage,1));
       //display a snapshot
       if("show" == firstWord && recordedSnapshots > 0)
       {
           //stop any currently playing animation.
           llSetTimerEvent(0);
           if(secondWord == "next")
           {
               currentSnapshot++;
               if(currentSnapshot > recordedSnapshots)
                   currentSnapshot = 1;
                   
               showSnapshot(currentSnapshot);
           }
           else if(secondWord == "prev")
           {
               currentSnapshot--;
               if(currentSnapshot < 1)
                   currentSnapshot = recordedSnapshots;
               showSnapshot(currentSnapshot);
           }
           else
           {
               // when the conversion fails, snapshotNumber = 0
               currentSnapshot = (integer)secondWord;
               if(currentSnapshot > 0 && currentSnapshot <= recordedSnapshots )
               {
                   showSnapshot(currentSnapshot);
                   llOwnerSay("Showing snapshot: "+(string)currentSnapshot);
               }
               else
               {
                   llOwnerSay("Invalid snapshot number given: " + (string) currentSnapshot +
                               "\nA valid snapshot number is between 1 and " + (string) recordedSnapshots);
                   currentSnapshot = 1;
               }
           }
       }
       //record a snapshot
       else if(firstWord == "record")
       {
           integer i = 0;
           //2 is the first linked prim number.
           vector rootPos = llGetPos();
           for( i = 2; i <= primCount; i++)
           {
               vector pos = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_POSITION]),0);
               //need to convert into local position
               pos.x -= rootPos.x;
               pos.z -= rootPos.z;
               pos.y -= rootPos.y;
               pos = pos / llGetRot();
               posList += pos;
               rotation rot = llList2Rot(llGetLinkPrimitiveParams(i, [PRIM_ROTATION]),0);
               //Converting into local rot
               rot = rot / llGetRot();
               rotList += rot;
               scaleList += llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_SIZE]),0);
           }
           recordedSnapshots++;
           llOwnerSay("Total number of snapshots recorded: " + (string)recordedSnapshots);
           freeMemory = llGetFreeMemory();
       }
       //play the animation from beginning to end once without looping.
       else if (firstWord == "play")
       {
           float delay = (float)secondWord;
           currentSnapshot = 1;
           //play the animation once without loop
           playAnimation(delay, FALSE);
       }
       //publish disables the recording features and enables the on-touch trigger
       else if("publish" == firstWord)
       {
           //stop any currently playing animation.
           llSetTimerEvent(0);
           playAnimationStyle = 0;
           currentSnapshot = 1;
           //remove listeners to disable recording
           llListenRemove(commandListenerHandle);
           commandListenerHandle = -1; //indicating that it's been published
           llOwnerSay("Recording disabled. Publish complete.\nClick me to toggle animation on/off.");
       }
       //if not published, show menu
       if(commandListenerHandle != -1)
           showMenuDialog();
   }
   //Timer event is used to handle the animation playback.
   timer()
   {
       showSnapshot(currentSnapshot);
       //if not at the end of the animation, increment the counter
       if(currentSnapshot < recordedSnapshots)
           currentSnapshot++;
       else
       {
           // if animation is looping, set the counter back to 1
           if( playAnimationStyle == 2)
               currentSnapshot = 1;
           // if animation isn't looping, stop the animation
           else
           {
               llSetTimerEvent(0);
               //if not published, show dialog menu
               if(commandListenerHandle != -1)
                   showMenuDialog();
           }
       }
   }

} </lsl>