Difference between revisions of "Open Prim Animator/Notecard Import"

From Second Life Wiki
Jump to navigation Jump to search
(committing notecard import script for Open Prim Animator/Import-Export)
 
 
(4 intermediate revisions by one other user not shown)
Line 1: Line 1:
<lsl>
<source lang="lsl2">
//!  @brief Imports a set of animation frames from a notecard
/**
*    @author SignpostMarv Martin
*/
 
//!  name of notecard to parse
string notecard = "Open Prim Animator: Import";
string notecard = "Open Prim Animator: Import";
//!  inventory key of notecard
key  _notecard;
key  _notecard;
//!  Temporarily stores the entire notecard contents
string contents;
string contents;
//!  Since llGetNotecardLine() only reads the first 255 bytes of a notecard line, we need to temporarily store the notecard contents in a list before dumping it to the contents string.
list  _contents;
list  _contents;
//!  Whilst not truly multi-threaded, this script attempts non-sequential loading of the notecard (as opposed to sequential loading), theoretically providing some small performance gain
list multithread_fetch = [];
list multithread_fetch = [];
//!  dataserver query variable used to determine how many lines are in a notecard- needed to pre-populate multithread_fetch to the correct size.
key nlq;
key nlq;
//!  import command
key import = "6b78fcc8-e147-4105-99a6-ff19b4bf559d";
key import = "6b78fcc8-e147-4105-99a6-ff19b4bf559d";
//!  export command
key export = "7c2ca168-2b64-4836-8727-8e62b78dbd44";
key export = "7c2ca168-2b64-4836-8727-8e62b78dbd44";
//!  command for updating the root scale in the core OPA script
key op_alter_rootScale = "f9d3389e-a78c-43f8-9e35-c11adec112a5";
key op_alter_rootScale = "f9d3389e-a78c-43f8-9e35-c11adec112a5";
//!  command for restoring link order
key op_adjust_link_order_by_names = "e907917f-e5a3-490e-aaac-4596a0dc176d";
key op_adjust_link_order_by_names = "e907917f-e5a3-490e-aaac-4596a0dc176d";


//!  temporary store of link names
list names;
list names;
//!  temporary store of animation frame positions
list pos;
list pos;
//!  temporary store of animation frame rotations
list rot;
list rot;
//!  temporary store of animation frame sizes
list size;
list size;
//!  temporary store of root scale. This is needed in case a new animation was generated on an object with a different root scale to the prim the object the notecard is being imported to
vector rootScale;
vector rootScale;


//!  I think this is here solely due to copypasta.
export_string(string _export_string){
export_string(string _export_string){
     integer s=1000;
     integer s=1000;
Line 30: Line 64:
default{
default{
     state_entry(){
     state_entry(){
         if(llGetInventoryType(notecard) == INVENTORY_NOTECARD){
         if(llGetInventoryType(notecard) == INVENTORY_NOTECARD){ // if the specified notecard is in the object,
             _notecard = llGetInventoryKey(notecard);
             _notecard = llGetInventoryKey(notecard); // get the inventory key
             state num_lines;
             state num_lines; // then attempt to get the number of lines in the notecard
         }
         }
     }
     }


     changed(integer c){
     changed(integer c){
         if(c & CHANGED_INVENTORY){
         if(c & CHANGED_INVENTORY){ // if the inventory has changed (e.g. if the notecard *wasn't* in the object),
             llResetScript();
             llResetScript(); // restart the script
         }
         }
     }
     }
}
}


state num_lines{
state num_lines{ // there should probably be a CHANGED_INVENTORY check in case the scripts are running reaaaaallllyyyy slow.
     state_entry(){
     state_entry(){
         nlq = llGetNumberOfNotecardLines(notecard);
         nlq = llGetNumberOfNotecardLines(notecard); // attempt to get the number of lines in the notecard
     }
     }


Line 62: Line 96:
             k = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
             k = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
             l = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
             l = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
             state mtf;
             state mtf; // now we've pre-populated the lists to the correct size, start the "multi-threaded fetch" of notecard contents.
         }
         }
     }
     }
Line 70: Line 104:
     state_entry(){
     state_entry(){
         integer i;
         integer i;
         integer j=llGetListLength(multithread_fetch);
         integer j=llGetListLength(multithread_fetch); // see why we pre-populated it now ? :D
         for(i=0;i<j;++i){
         for(i=0;i<j;++i){ // the reason we pre-populate rather than building the list here is to try and keep the level of consumed memory more consistent in this busy state, so the memory consumption doesn't steadily increase over time but regularly plateaus instead.
             multithread_fetch = llListReplaceList(multithread_fetch,[llGetNotecardLine(notecard,i)],i,i); // sets the notecard fetching away
             multithread_fetch = llListReplaceList(multithread_fetch,[llGetNotecardLine(notecard,i)],i,i); // sets the notecard fetching away
         }
         }
Line 77: Line 111:


     dataserver(key q, string m){
     dataserver(key q, string m){
         integer i = llListFindList(multithread_fetch,[q]);
         integer i = llListFindList(multithread_fetch,[q]); // get the position of the dataserver query id
         if(i >= 0){
         if(i >= 0){
             _contents = llListReplaceList(_contents,[m],i,i); // the traditional method of notecard fetching in LSL is single-threaded, getting one line at a time
             _contents = llListReplaceList(_contents,[m],i,i); // the traditional method of notecard fetching in LSL is single-threaded, getting one line at a time
         }
         }
         if(llListFindList(_contents,[NULL_KEY]) == -1){ // since we started out with a NULLL_KEY-filled list, if there aren't any NUL_KEY in the list then we know it's done. caveat scriptor: since key and string types are interchangeable  
         if(llListFindList(_contents,[NULL_KEY]) == -1){ // since we started out with a NULL_KEY-filled list, if there aren't any NULL_KEY in the list then we know it's done. caveat scriptor: since key and string types are interchangeable  
             contents = llDumpList2String(_contents,"");
             contents = llDumpList2String(_contents,"");
             state done_reading;
             state done_reading;
Line 87: Line 121:
     }
     }


     state_exit(){
     state_exit(){ // do some cleanup in the event the LSL VM executing this script is silly.
         _contents = [];
         _contents = [];
         multithread_fetch = [];
         multithread_fetch = [];
Line 95: Line 129:
state done_reading{
state done_reading{
     state_entry(){
     state_entry(){
         integer rs_pos    = llSubStringIndex(contents,"rootscale:");
         integer rs_pos    = llSubStringIndex(contents,"rootscale:"); // get the string index for the rootscale block
         integer names_pos = llSubStringIndex(contents,"names:");
         integer names_pos = llSubStringIndex(contents,"names:"); // get the string index for the names block
         integer pos_pos  = llSubStringIndex(contents,"pos:");
         integer pos_pos  = llSubStringIndex(contents,"pos:"); // get the string index for the position block
         integer rot_pos  = llSubStringIndex(contents,"rot:");
         integer rot_pos  = llSubStringIndex(contents,"rot:"); // get the string index for the rotation block
         integer size_pos  = llSubStringIndex(contents,"size:");
         integer size_pos  = llSubStringIndex(contents,"size:"); // get the string index for the size block


         rootScale  = (vector)llGetSubString(contents, rs_pos + 10, names_pos - 1);
         rootScale  = (vector)llGetSubString(contents, rs_pos + 10, names_pos - 1); // get the root scale from the notecard
         names      = llParseString2List(llGetSubString(contents,names_pos + 6,pos_pos - 1),["|"],[]);
         names      = llParseString2List(llGetSubString(contents,names_pos + 6,pos_pos - 1),["|"],[]); // Fun Fact! Support for correcting link order was added in May 2011 but wasn't actually implemented until November 2011!
         pos        = llParseString2List(llGetSubString(contents,pos_pos + 4,rot_pos - 1),["|"],[]);
         pos        = llParseString2List(llGetSubString(contents,pos_pos + 4,rot_pos - 1),["|"],[]);
         rot        = llParseString2List(llGetSubString(contents,rot_pos + 4,size_pos - 1),["|"],[]);
         rot        = llParseString2List(llGetSubString(contents,rot_pos + 4,size_pos - 1),["|"],[]);
Line 110: Line 144:
         integer a = llGetListLength(names);
         integer a = llGetListLength(names);
         integer b = llGetListLength(pos);
         integer b = llGetListLength(pos);
         if(b % a || llGetListLength(rot) % a || llGetListLength(size) % a){
         if(b % a || llGetListLength(rot) % a || llGetListLength(size) % a){ // since the number of names corresponds to the number of prims, we can use modulus to ensure that the notecard doesn't have a corrupt export.
             llOwnerSay("List Lengths do not match, possible corrupted notecard (" + llList2CSV([a,llGetListLength(pos),llGetListLength(rot),llGetListLength(size)]) + ")");
             llOwnerSay("List Lengths do not match, possible corrupted notecard (" + llList2CSV([a,llGetListLength(pos),llGetListLength(rot),llGetListLength(size)]) + ")");
         }else{
         }else{
             llMessageLinked(LINK_THIS,b,"XDimportLength",NULL_KEY);
             llMessageLinked(LINK_THIS,b,"XDimportLength",NULL_KEY); // notecard contents are valid, core script can start import now.
             llOwnerSay("Do import now");
             llOwnerSay("Do import now");
         }
         }
Line 127: Line 161:
         if(m == "XDimportLength" && n == -1){
         if(m == "XDimportLength" && n == -1){
             integer i;
             integer i;
             integer j=llGetListLength(pos);
             integer j=llGetListLength(pos); // we've already verified that pos, rot and size are of the appropriate length so we only need to get the length of one of these.
             integer l;
             integer l;
             integer namesLength=llGetListLength(names);
             integer namesLength=llGetListLength(names);
             for(i=0;i<j;++i){
             for(i=0;i<j;++i){
                 l = llListFindList(names, [llGetLinkName(2 + (i % namesLength))]) + (llFloor(i / namesLength) * namesLength); // this is to fix any bugs caused by link order changes
                 l = llListFindList(names, [llGetLinkName(2 + (i % namesLength))]) + (llFloor(i / namesLength) * namesLength); // this is to fix any bugs caused by link order changes (finally added in November 2011)
                 llMessageLinked(s,i,llDumpList2String([llList2Vector(pos,l),llList2Rot(rot,l),llList2Vector(size,l)],"|"),import);
                 llMessageLinked(s,i,llDumpList2String([llList2Vector(pos,l),llList2Rot(rot,l),llList2Vector(size,l)],"|"),import); // send the import string to the core OPA script
             }
             }
             llMessageLinked(s,0,(string)rootScale, op_alter_rootScale);
             llMessageLinked(s,0,(string)rootScale, op_alter_rootScale); // once we're done importing the position, rotation & size values for the animation frames, update the root scale to the value used in the creation of the animation frames.
             llOwnerSay("Done importing");
             llOwnerSay("Done importing");
         }
         }
Line 143: Line 177:
     }
     }
}
}
</lsl>
</source>
[[Open Prim Animator|Notecard Import]]
[[Category:Open Prim Animator|Notecard Import]]

Latest revision as of 13:58, 26 February 2015

//!  @brief Imports a set of animation frames from a notecard
/**
*    @author SignpostMarv Martin
*/

//!  name of notecard to parse
string notecard = "Open Prim Animator: Import";

//!  inventory key of notecard
key   _notecard;

//!  Temporarily stores the entire notecard contents
string contents;

//!  Since llGetNotecardLine() only reads the first 255 bytes of a notecard line, we need to temporarily store the notecard contents in a list before dumping it to the contents string.
list   _contents;

//!  Whilst not truly multi-threaded, this script attempts non-sequential loading of the notecard (as opposed to sequential loading), theoretically providing some small performance gain
list multithread_fetch = [];

//!  dataserver query variable used to determine how many lines are in a notecard- needed to pre-populate multithread_fetch to the correct size.
key nlq;

//!  import command
key import = "6b78fcc8-e147-4105-99a6-ff19b4bf559d";

//!  export command
key export = "7c2ca168-2b64-4836-8727-8e62b78dbd44";

//!  command for updating the root scale in the core OPA script
key op_alter_rootScale = "f9d3389e-a78c-43f8-9e35-c11adec112a5";

//!  command for restoring link order
key op_adjust_link_order_by_names = "e907917f-e5a3-490e-aaac-4596a0dc176d";

//!  temporary store of link names
list names;

//!  temporary store of animation frame positions
list pos;

//!  temporary store of animation frame rotations
list rot;

//!  temporary store of animation frame sizes
list size;

//!  temporary store of root scale. This is needed in case a new animation was generated on an object with a different root scale to the prim the object the notecard is being imported to
vector rootScale;

//!  I think this is here solely due to copypasta.
export_string(string _export_string){
    integer s=1000;
    while(llStringLength(_export_string) > s){
        llOwnerSay(llGetSubString(_export_string,0,(s-1)));
        _export_string = llGetSubString(_export_string,s,-1);
    }
    if(llStringLength(_export_string) > 0){
        llOwnerSay(_export_string);
    }
}

default{
    state_entry(){
        if(llGetInventoryType(notecard) == INVENTORY_NOTECARD){ // if the specified notecard is in the object,
            _notecard = llGetInventoryKey(notecard); // get the inventory key
            state num_lines; // then attempt to get the number of lines in the notecard
        }
    }

    changed(integer c){
        if(c & CHANGED_INVENTORY){ // if the inventory has changed (e.g. if the notecard *wasn't* in the object),
            llResetScript(); // restart the script
        }
    }
}

state num_lines{ // there should probably be a CHANGED_INVENTORY check in case the scripts are running reaaaaallllyyyy slow.
    state_entry(){
        nlq = llGetNumberOfNotecardLines(notecard); // attempt to get the number of lines in the notecard
    }

    dataserver(key q, string m){
        if(q == nlq){
            integer i;
            integer j = (integer)m;
            list    k = []; // declaring list k = []; isn't exactly necessary, you could do list k;
            list    l = [];
            for(i=0;i<j;++i){ // since we don't have the concept of transactions in LSL scripts, we're going to build up these lists separately 
                k += [NULL_KEY];
                l += [NULL_KEY];
            }
            _contents = k; // this is where we do the atomic writes due to lack of transactions on variables
            multithread_fetch = l; // this is where we do the atomic writes due to lack of transactions on variables
            k = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
            l = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
            state mtf; // now we've pre-populated the lists to the correct size, start the "multi-threaded fetch" of notecard contents.
        }
    }
}

state mtf{
    state_entry(){
        integer i;
        integer j=llGetListLength(multithread_fetch); // see why we pre-populated it now ? :D
        for(i=0;i<j;++i){ // the reason we pre-populate rather than building the list here is to try and keep the level of consumed memory more consistent in this busy state, so the memory consumption doesn't steadily increase over time but regularly plateaus instead.
            multithread_fetch = llListReplaceList(multithread_fetch,[llGetNotecardLine(notecard,i)],i,i); // sets the notecard fetching away
        }
    }

    dataserver(key q, string m){
        integer i = llListFindList(multithread_fetch,[q]); // get the position of the dataserver query id 
        if(i >= 0){
            _contents = llListReplaceList(_contents,[m],i,i); // the traditional method of notecard fetching in LSL is single-threaded, getting one line at a time
        }
        if(llListFindList(_contents,[NULL_KEY]) == -1){ // since we started out with a NULL_KEY-filled list, if there aren't any NULL_KEY in the list then we know it's done. caveat scriptor: since key and string types are interchangeable 
            contents = llDumpList2String(_contents,"");
            state done_reading;
        }
    }

    state_exit(){ // do some cleanup in the event the LSL VM executing this script is silly.
        _contents = [];
        multithread_fetch = [];
    }
}

state done_reading{
    state_entry(){
        integer rs_pos    = llSubStringIndex(contents,"rootscale:"); // get the string index for the rootscale block
        integer names_pos = llSubStringIndex(contents,"names:"); // get the string index for the names block
        integer pos_pos   = llSubStringIndex(contents,"pos:"); // get the string index for the position block
        integer rot_pos   = llSubStringIndex(contents,"rot:"); // get the string index for the rotation block
        integer size_pos  = llSubStringIndex(contents,"size:"); // get the string index for the size block

        rootScale  = (vector)llGetSubString(contents, rs_pos + 10, names_pos - 1); // get the root scale from the notecard
        names      = llParseString2List(llGetSubString(contents,names_pos + 6,pos_pos - 1),["|"],[]); // Fun Fact! Support for correcting link order was added in May 2011 but wasn't actually implemented until November 2011!
        pos        = llParseString2List(llGetSubString(contents,pos_pos + 4,rot_pos - 1),["|"],[]);
        rot        = llParseString2List(llGetSubString(contents,rot_pos + 4,size_pos - 1),["|"],[]);
        size       = llParseString2List(llGetSubString(contents,size_pos + 5,-1),["|"],[]);

        contents = ""; // clearing memory up, we don't need the string in memory now.
        integer a = llGetListLength(names);
        integer b = llGetListLength(pos);
        if(b % a || llGetListLength(rot) % a || llGetListLength(size) % a){ // since the number of names corresponds to the number of prims, we can use modulus to ensure that the notecard doesn't have a corrupt export.
            llOwnerSay("List Lengths do not match, possible corrupted notecard (" + llList2CSV([a,llGetListLength(pos),llGetListLength(rot),llGetListLength(size)]) + ")");
        }else{
            llMessageLinked(LINK_THIS,b,"XDimportLength",NULL_KEY); // notecard contents are valid, core script can start import now.
            llOwnerSay("Do import now");
        }
    }

    changed(integer c){
        if(c & CHANGED_INVENTORY && llGetInventoryKey(notecard) != _notecard){
            llResetScript();
        }
    }

    link_message(integer s, integer n, string m, key k){
        if(m == "XDimportLength" && n == -1){
            integer i;
            integer j=llGetListLength(pos); // we've already verified that pos, rot and size are of the appropriate length so we only need to get the length of one of these.
            integer l;
            integer namesLength=llGetListLength(names);
            for(i=0;i<j;++i){
                l = llListFindList(names, [llGetLinkName(2 + (i % namesLength))]) + (llFloor(i / namesLength) * namesLength); // this is to fix any bugs caused by link order changes (finally added in November 2011)
                llMessageLinked(s,i,llDumpList2String([llList2Vector(pos,l),llList2Rot(rot,l),llList2Vector(size,l)],"|"),import); // send the import string to the core OPA script
            }
            llMessageLinked(s,0,(string)rootScale, op_alter_rootScale); // once we're done importing the position, rotation & size values for the animation frames, update the root scale to the value used in the creation of the animation frames.
            llOwnerSay("Done importing");
        }
    }

    touch_start(integer t){
        llMessageLinked(LINK_THIS,0,"XDmenu",NULL_KEY);   
    }
}