Open Prim Animator

From Second Life Wiki
Revision as of 08:15, 25 January 2015 by ObviousAltIsObvious Resident (talk | contribs) (<lsl> tag to <source>)
(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.
// 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();
            }
        }
    }
}