Open Prim Animator
<lsl> // Open Prim Animator - by Todd Borst // Extensive Modifications by SignpostMarv Martin
// Note from Todd to other editors: Please document changes you have made to get proper credit. // Note from Todd to users: People may have edited the script from since I've posted it originally. // You can always view the original script by clicking the history tab.
// 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. You are welcomed to sell it if you've made your // own improvements to it. This is effectively public domain. Have fun.
integer COMMAND_CHANNEL = 32;
integer primCount; integer commandListenerHandle = ERR_GENERIC;
list posList; list rotList; list scaleList; integer currentSnapshot; integer recordedSnapshots;
vector rootScale; vector scaleChange = <1.0, 1.0, 1.0>;
integer maxMemory; integer freeMemory;
// The values for playAnimationStyle means // 0 := no animation playing // 1 := play animation once // 2 := play animation looping
integer playAnimationStyle;
key op_import = "6b78fcc8-e147-4105-99a6-ff19b4bf559d"; key op_export = "7c2ca168-2b64-4836-8727-8e62b78dbd44"; key op_alter_rootScale = "f9d3389e-a78c-43f8-9e35-c11adec112a5";
// _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ // _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
show_snapshot(integer snapNumber) {
if (!snapNumber || recordedSnapshots < snapNumber) return;
vector rootPos = llGetPos(); rotation rootRot = llGetRot();
vector pos; rotation rot; vector scale; list params;
integer i = 2; do { 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));
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; } params += [PRIM_LINK_TARGET, i, PRIM_POSITION, pos, PRIM_ROTATION, rot/rootRot, PRIM_SIZE, scale ];
if (64 < llGetListLength(params)) { llSetLinkPrimitiveParamsFast(LINK_THIS, params); params = []; } } while (++i <= primCount);
if (llGetListLength(params)) { llSetLinkPrimitiveParamsFast(LINK_THIS, params); params = []; }
}
playAnimation(float delay, integer loop) {
if (delay < 0.1) delay = 1.0;
if (loop == FALSE) playAnimationStyle = 1; else playAnimationStyle = 2;
if (1 <= recordedSnapshots) llSetTimerEvent(delay);
}
showMenuDialog() { // return;
string temp = (string)((float)freeMemory/(float)maxMemory * 100.0); string menuText = "Free Memory: " + (string)freeMemory + " (" + llGetSubString(temp, 0, 4) +"%)" + "\nSnapshot " + (string)currentSnapshot +" of " + (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","Loop","Stop","Export"], COMMAND_CHANNEL);
} string truncate_float(float foo) {
if (foo == 0.0) return "0"; else if (foo == (float)((integer)foo)) return (string)((integer)foo);
string bar = (string)foo;
while (llGetSubString(bar, -1, -1) == "0") bar = llGetSubString(bar, 0, -2);
if (llGetSubString(bar, -1, -1) == ".") bar = llGetSubString(bar, 0, -2);
return bar;
}
calc_scaleChange() {
if (rootScale != ZERO_VECTOR) { vector newScale = llGetScale();
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; } }
}
default {
state_entry() { maxMemory = llGetFreeMemory(); freeMemory = llGetFreeMemory();
primCount = llGetNumberOfPrims(); commandListenerHandle = llListen(COMMAND_CHANNEL,"", llGetOwner(), ""); showMenuDialog();
rootScale = llGetScale(); if (llGetInventoryType("OPA Notecard Import - 2011-11-03") == INVENTORY_SCRIPT){ llResetOtherScript("OPA Notecard Import - 2011-11-03"); } }
// Feel free to remove this on-touch trigger if you are using your own script to control playback // touch_start(integer num_detected) // { // if (commandListenerHandle == ERR_GENERIC) // { // if (playAnimationStyle == 0) // playAnimation(1.0,TRUE); // else // { // playAnimationStyle = 0; // llSetTimerEvent((float)FALSE); // } // } // }
changed(integer change) { if (change & CHANGED_SCALE) calc_scaleChange();
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 && 1 <= num && num <= recordedSnapshots) show_snapshot(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((float)FALSE); } else if ("XDexport" == str && !num) { list export = []; string foo; vector bar; rotation baa; string baz;
integer i = 2; integer j = primCount;
do export += [llGetLinkName(i)]; while (++i <= j);
llMessageLinked(sender_num, 1, llDumpList2String(export,"|") , op_export); export = [];
i = 0; j = llGetListLength(posList);
do { bar = llList2Vector(posList,i); export += ["<" + truncate_float(bar.x) + "," + truncate_float(bar.y) + "," + truncate_float(bar.z) + ">"]; } while (++i < j);
llMessageLinked(sender_num, 2, llDumpList2String(export,"|") , op_export); export = [];
i = 0: j = llGetListLength(rotList);
do { baa = llList2Rot(rotList,i); export += ["<" + truncate_float(baa.x) + "," + truncate_float(baa.y) + "," + truncate_float(baa.z) + "," + truncate_float(baa.s) + ">"]; } while (++i < j);
llMessageLinked(sender_num, 3, llDumpList2String(export,"|") , op_export); export = [];
i = 0; j = llGetListLength(scaleList);
do { bar = llList2Vector(scaleList,i); export += ["<" + truncate_float(bar.x) + "," + truncate_float(bar.y) + "," + truncate_float(bar.z) + ">"]; } while (++i < j);
llMessageLinked(sender_num, 4, llDumpList2String(export,"|") , op_export); } else if ("XDmenu" == str) { showMenuDialog(); } else if ("XDimportLength" == str && 0 < num) { list foo; list bar;
integer i; do { foo += [ZERO_VECTOR]; bar += [ZERO_ROTATION]; } while (++i < num);
posList = foo; scaleList = foo; rotList = bar; llMessageLinked(sender_num,-1,str,op_import); recordedSnapshots = num / (llGetNumberOfPrims() - 1); llMessageLinked(LINK_SET, recordedSnapshots, "XDrecordedSnapshots", NULL_KEY); currentSnapshot = 1; } else if ("XDrecordedSnapshots" == str && num == -1) { llMessageLinked(sender_num,recordedSnapshots,str,NULL_KEY); } else if (id == op_import && 0 <= num) { list params = llParseString2List(str, ["|"], []); vector impPos = (vector)llList2String(params, 0); rotation impRot = (rotation)llList2String(params, 1); vector impSize = (vector)llList2String(params, 2);
posList = llListReplaceList(posList, [impPos], num, num); rotList = llListReplaceList(rotList, [impRot], num, num); scaleList = llListReplaceList(scaleList, [impSize], num, num); } else if (id == op_alter_rootScale) { rootScale = (vector)str; calc_scaleChange(); } }
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));
if ("show" == firstWord && recordedSnapshots > 0) { llSetTimerEvent((float)FALSE);
if (secondWord == "next") { ++currentSnapshot;
if (recordedSnapshots < currentSnapshot) currentSnapshot = 1;
show_snapshot(currentSnapshot); } else if (secondWord == "prev") { --currentSnapshot;
if (currentSnapshot < 1) currentSnapshot = recordedSnapshots;
show_snapshot(currentSnapshot); } else { currentSnapshot = (integer)secondWord;
if (currentSnapshot && currentSnapshot <= recordedSnapshots) { show_snapshot(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; } } } else if (firstWord == "record") { vector rootPos = llGetPos();
integer i = 2; do { vector pos = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_POSITION]), 0);
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);
rot = rot / llGetRot(); rotList += rot;
scaleList += llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_SIZE]), 0); } while (++i <= primCount);
++recordedSnapshots;
llOwnerSay("Total number of snapshots recorded: " + (string)recordedSnapshots); freeMemory = llGetFreeMemory(); } else if (firstWord == "play") { float delay = (float)secondWord; currentSnapshot = 1; playAnimation(delay, FALSE); } else if ("publish" == firstWord) { llSetTimerEvent((float)FALSE); playAnimationStyle = 0; currentSnapshot = 1;
llListenRemove(commandListenerHandle); commandListenerHandle = -1;
llOwnerSay("Recording disabled. Publish complete.\nClick me to toggle animation on/off."); } else if ("loop" == firstWord) { llMessageLinked(LINK_THIS, 0, "XDplayLoop", NULL_KEY); } else if ("stop" == firstWord) { llMessageLinked(LINK_THIS, 0, "XDstop", NULL_KEY); } else if ("export" == firstWord) { llOwnerSay("Should be exporting"); llMessageLinked(LINK_THIS, 0, "XDexport", NULL_KEY); }
if (commandListenerHandle != ERR_GENERIC) showMenuDialog(); }
timer() { show_snapshot(currentSnapshot);
if (currentSnapshot < recordedSnapshots) ++currentSnapshot; else { if (playAnimationStyle == 2) currentSnapshot = 1; else { llSetTimerEvent((float)FALSE);
if (commandListenerHandle != ERR_GENERIC) showMenuDialog(); } } }
} </lsl>