Open Prim Animator/Notecard Import

From Second Life Wiki
< Open Prim Animator
Revision as of 04:23, 14 December 2011 by SignpostMarv Martin (talk | contribs) (documentation)
Jump to navigation Jump to search

<lsl> //! @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);
       for(i=0;i<j;++i){
           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]);
       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 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 
           contents = llDumpList2String(_contents,"");
           state done_reading;
       }
   }
   state_exit(){
       _contents = [];
       multithread_fetch = [];
   }

}

state done_reading{

   state_entry(){
       integer rs_pos    = llSubStringIndex(contents,"rootscale:");
       integer names_pos = llSubStringIndex(contents,"names:");
       integer pos_pos   = llSubStringIndex(contents,"pos:");
       integer rot_pos   = llSubStringIndex(contents,"rot:");
       integer size_pos  = llSubStringIndex(contents,"size:");
       rootScale  = (vector)llGetSubString(contents, rs_pos + 10, names_pos - 1);
       names      = llParseString2List(llGetSubString(contents,names_pos + 6,pos_pos - 1),["|"],[]);
       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){
           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);
           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);
           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
               llMessageLinked(s,i,llDumpList2String([llList2Vector(pos,l),llList2Rot(rot,l),llList2Vector(size,l)],"|"),import);
           }
           llMessageLinked(s,0,(string)rootScale, op_alter_rootScale);
           llOwnerSay("Done importing");
       }
   }
   touch_start(integer t){
       llMessageLinked(LINK_THIS,0,"XDmenu",NULL_KEY);   
   }

} </lsl>