User:Todd Borst

From Second Life Wiki
Revision as of 07:59, 18 January 2011 by Todd Borst (talk | contribs) (Open Prim Animator)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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