Difference between revisions of "Open Prim Animator/Notecard Import"
(documentation) |
(documentation) |
||
Line 104: | 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 111: | 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 | 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 121: | 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 129: | 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 144: | 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 165: | Line 165: | ||
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"); | ||
} | } |
Revision as of 04:41, 14 December 2011
<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); // 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); 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); }
} </lsl>