Jump to content

Miranda Umino

Resident
  • Posts

    411
  • Joined

  • Last visited

Posts posted by Miranda Umino

  1. Your book has been written several years ago , at time when llgetagegentlist didn t exeist .

    at time too when some functions to monitor and mesaure teh scripts didn t exist neither

    at time too when the compiler mono was not well established...

    So it  can t be a reference for performances

    To add, i have nor readen it , but i am sure that this book talks about timers ,and  it didn t told that the timers were free-lag.

    And last point : has it talked about sensors when it detects some objects ( so max15000 present in the sim , and so an high probability to be triggered  ) or when it detects avatars ( so max 100 in the sim, 40 max in the mainland , and a few probability to be triggered in comparing ) ?? The probability is totally different

     

    JUST USE YOUR LOGIC !!!

    When you code  some  stuff who will be executed 100 times in one script , and exactly the same stuff who will be executed 50 times in one another script , which one is the laggest ???

    This is exacly what you do ith a timer in the case of the OP ( of this thread ) :

    The code you have written inside the timer  will run every 60 seconds , without condition  , when the code code you have written inside the sensor won t run every 60 seconds : if there is nobody in range , the sensor is not triggered and doesn t run .. Point !

     

    Nobody is always connected to SL : with a timer your script will run 24hours/24hours , 7 days/ 7days , even if there is nobody at the chat range , even when there is nobody in the same parcel , and even when there is nobody in the sim .

    With a sensor it will run only when there are people nearby 

     Of course , if your script codes at the same time sensor()  ( triggered only when someone is nearby ) and  no_sensor()  ( triggered only when nobody  is nearby ), your events will be always triggered , as a timer , and you may replace it with a timer .  But it s not the case of the OP . He needs only one type of event .

     

    It s worst to use llgetagentlist in this case : what does the OP : he wants to chat in a range of 20 meters with llsay .. Why should he grab the keys  of avatars  ? If he did this , he  did useless and consuming work . Why should he detect the avatars who are outside of the 20 meters range .. 

    In the case of OP , the OP doesn t care about the informations about avatars : the ony thing interesting for his script is "is there someone nearby" , because it s useless and consuming work to send messages , even when there are nobody to receive the messages

    You increase the work of the script in doing this . Your version with llgetagentlist  will be totally innefficient in comparing.

     

     

     

     

     

     

  2. No , my sensor doesn t trigger every 60 seconds ..

     

    You make a mess between the

    1 ) test of the simulator to know which event should be trigerred

    ( some code written  by Linden in C++ and  who runs fast )

    2 ) and the execution of the trigerred event ., ( the code written by yourself  in LSL who runs slow ) + the execution to "load" the event ( written  by Linden )

     

    If you set a timer of 60 seconds :

    the first part is always executed  :: the simulator needs to make a test if 60 seconds are elapsed 

    when 60 seconds are elapsed , the second part ( the code written by yourself ) is always executed

     

    If you set a llsensorrepeat of 60 seconds :

    the first part is always executed  :: the simulator needs to make a test if 60 seconds are elapsed  , and needs to detect if an avatar is in range 

    when 60 seconds are elapsed , the second part ( the code written by yourself ) is not always executed 

     

    Of course the first part of the llsensorrepeat consumes more CPU than the the first part of the timer.

    But  the second part of the llesensorrepeat will consume less CPU than the second part of the timer .

    And the second part will be considerably slower than the difference of time in the first part between sensorrepeat and ttimer

    Indeed , there is probably a save of states of variables ( on the disk ) , there is the load in memory of the script ( may cause too a disk operation )  ., the resume of o thread paused , the compilation JIT of the methode/event if it s the first time it runs , the cleanup of the memory with garbage collector  and a lot of other stuff and finally your code . 

     

  3. In this case , it s maybe preferable to use a sensorrepeat .

    Because your script ( if you use a timer ) will try to speak even when there is nobody at the range and so triggers some events for nothing.

     

    For instance if you have the script inside a skybox , and people are in other skybox or at the ground , using a timer will continue to run when it s not expected.

     

    If you use a sensorrepeat , some events will be triggered only if there are some people , and every 60 seconds .

    It s when the sensor  is triggered , there is the  request  of a new line :

    It s when the datserver is triggered , there is the display of message .

     

     

    string card = "";key linecountid;key lineid;integer linemax;integer random_integer( integer min, integer max ){  return min + (integer)( llFrand( max - min + 1 ) );}float timePeriod = 60.0;default{    state_entry()    {        card = llGetInventoryName(INVENTORY_NOTECARD, 0);        if ( card != "" )        {            linecountid = llGetNumberOfNotecardLines(card);        }        else        {            llOwnerSay("No notecards in the inventory");        }        llPlaySound("953d8b35-cdae-f316-92f6-92bf3c2264e1",0.02);        llSleep(2.0);    }    sensor(integer n)    {        lineid = llGetNotecardLine(card, random_integer(0, linemax));    }    dataserver(key id, string data)    {        if (id == linecountid)        {            linemax = (integer)data - 1;            if ( linemax > 0 )            {                llSensorRepeat("", NULL_KEY, AGENT, 20, PI, timePeriod);            }            else            {                llOwnerSay("Notecard ["+ card +"] is empty , or has no rights , or contains embedded datas ( textures, LM etc )\nReplace it ");            }        }        else if (id == lineid)        {            llSay( 0, data );        }    }    changed(integer c)    {        if ( c & CHANGED_INVENTORY )        {            llSensorRemove();            card = llGetInventoryName(INVENTORY_NOTECARD, 0);            if ( card != "" )            {                linecountid = llGetNumberOfNotecardLines(card);            }            else            {                llOwnerSay("No notecards in the inventory");            }                    }    }}

     

     

     

  4. When you want to move an object the first question is "in which coordinate system , i am ?"

     

    You write

    MyOffset = Pos - ParentPos;

     

    So, MyOffset is the position of the object B relatively to the object A  but in which coordinate system  :

    in the coordinate system  where the origine is A ( and described by ParentPos ) and the axis of the frame of refrence are again the regional axis

    But as you will move later your rezzer ( object A ) and rotates it  , and your rezzer had too a rotation mayb e not null , you want , in fact , the position of the object B relatively to the object A in this following coordinate system :

    object a ( the rezzer ) is the point of origine of the coordinate system  : the axis of the object A are the axis of the coordinate system

     

    So you need to fix your formula when you record your coordinates :

    MyOffset = Pos - ParentPos;

    by

    MyOffset = ( Pos - ParentPos ) / ParentRot ;

    where ParentRot is the rotation of the rezzer

     

     

    Your script should be something like :

    /when position & rotation are recordedlist x=llGetPrimitiveParams([PRIM_POSITION,PRIM_ROTATION]);rotation ParentRot = llList2Rot( llGetObjectDetails(ParentKey,[OBJECT_ROT]), 0);Pos=llList2Vector(x,0);MyRot=llList2Rot(x,1);MyOffset=(Pos -ParentPos)/ParentRot;//when rezzedlist x=llGetObjectDetails(ParentKey,[OBJECT_ROT]);ParentRot=llList2Rot(x,0);llSetRegionPos(llGetPos()+MyOffset*ParentRot);llSetRot(ParentRot);

     

     

    Is it celar and do you understant better , or should i give more details or an instance ?

  5. I see in your psate , you have written your rotations with 6 digits .

    When you convert a string to rotation , the precision of 6 digits are not enough .

    Your result quaternions won t be normalized ; if you manipulate your unnormalized rotations with other LSL functions, you could meet some issues

    So , 

    - either after reading your strings containing the rotations , you normalize the rotation

    rotation NormRot(rotation Q){    float MagQ = llSqrt(Q.x*Q.x + Q.y*Q.y +Q.z*Q.z + Q.s*Q.s);    return  <Q.x/MagQ, Q.y/MagQ, Q.z/MagQ, Q.s/MagQ>;}

    - either you use in your string a precision for rotations of 8 digits .

     

    - either you use in your string , some rotations written in C99 float format

     

     

  6. For me i don t see any mathematics :

     

    scalebyfactor is in fact an homothetie with center , the center of the root prim .

    So :

    - it lets invariant the rotations centered on the root prim ; your rotations returned by PRIM_ROT_LOCAL  before the call of  scalebyfactor don t need to be changed after scalebyfactor

    - it scales the transformations uniformely in the frame with center the root prim  ; so if you have scaled with scalebyfactor by X  , your posistions of your child prims returned by PRIM_POS_LOCAL   before the call of  scalebyfactor need to be multiplied by X  after scalebyfactor

    - for the same reason , rotations and translations in coordinates region  ( so with a center who doesn t match with the center of the root prim ) should be avoided ( PRIM_POS , PRIM_ROTATION ) because the centers of coordinates are different , so the composition of transformations ( translation + rotation + homothetie ) may be complex

     

    For instance 

    OriginalPosandRot(){
    llSetLinkPrimitiveParamsFast(2,[
    PRIM_POS_LOCAL,<0.06420, -0.13936, 0.02051>,
    PRIM_ROT_LOCAL,<0.97959, 0.19486, -0.04837, -0.00959>
    ]);
    llSetLinkPrimitiveParamsFast(3,[
    PRIM_POS_LOCAL,<-0.04510, 0.12622, 0.03101>,
    PRIM_ROT_LOCAL,<0.69950, 0.17172, 0.10364, 0.68590>
    ]);
    }

    You scale the object by scalefactor = 3

    Your object will have the new coordinates :
    child 2 :
    PRIM_POS_LOCAL = 3 * <0.06420, -0.13936, 0.02051>
    PRIM_ROT_LOCAL = <0.97959, 0.19486, -0.04837, -0.00959>
    child 3 :
    PRIM_POS_LOCAL = 3 * <-0.04510, 0.12622, 0.03101>
    PRIM_ROT_LOCAL = <0.69950, 0.17172, 0.10364, 0.68590>

  7. You can play sounds by UUID and have no need to put the sounds  in the content of the prim  ( if you have the correct perms )

     

     

    **edit** Ok .. rofl .. it looks there are some people who have  hacked their viewer to import long wavs . 

    Indeed , i have some sounds of 40 seconds in my cache .

    By the way , it s not a script solution ;  it s while the import : ask to those who have  hacked their viewer 

  8. For the system , nobody knows . ( so maybe it may make more work for the garbage collector  ?)

    For the memory counted by Linden  to know if your script will reach heap collision error , definitevely no.

     

     

     

     

    integer memory0;
    integer memory1;
    integer n;

    key f()
    {
    key result;
    n=2000;
    memory0 = llGetUsedMemory();
    key mykey0 = llGenerateKey();
    do
    {
    mykey0 = llGenerateKey();
    } while ( --n );
    memory1 = llGetUsedMemory();
    llOwnerSay(llList2CSV(["mem f used",memory1-memory0]));
    }
    key g()
    {
    // in this function , we declare a key , so 36 chars = 72 bytes
    // inside the loop .
    // If we had a new allocation at each step , we would get 72 bytes * 2000 steps
    // = 144 000 bytes used
    // But the script doesn t crash for heap memory .
    // So :
    // - either the memory is allocated in th system but not counted by Linden
    // but it s not counted , the garbage collector could have work on 144000 bytes
    // - either the variable inside the loop is reused ( optimization by compiler )

    n=2000;
    memory0 = llGetUsedMemory();
    do
    {
    key mykey1 = llGenerateKey();
    } while ( --n );
    memory1 = llGetUsedMemory();
    llOwnerSay(llList2CSV(["mem g used",memory1-memory0]));
    }
    default
    {

    touch_end(integer total_number)
    {
    f();
    g();
    }
    }

     

  9. // by Nitewind Armistice

    // License GNU/BSD

    // Feel free to use and modify

    // Keep the authors/ modifiers references included

    // NOT FOR SALE!

    list main_menu = ["Upper", "Lower"]; // Main menu

    list Pants_menu = ["Foot", "Full Body", "None", "Panties", "Shorts", "Thighs", "Toes"]; // Sub-Menu for Lower

    list Shirt_menu = ["Long Sleeve", "TShirt", "None"]; // Sub-Menu for Upper

    integer listen_channel = -123456; // Channel that you script will listen on make sure it is not a public channel and

    // set the same in the other scripts that you are using with the hud

    integer handle;

     

    default

    {

    on_rez(integer start_param)

    {

    // rests the script when rezzed

    llResetScript();

    }

     

    changed(integer change)

    {

    // reset script when the owner or the inventory changed

    if (change & (CHANGED_OWNER | CHANGED_INVENTORY))

    llResetScript();

    }

    state_entry()

    {

    handle = llListen(listen_channel,"",llGetOwner(),""); // only listens for the owner of the item that this

    // script is in

    llListenControl(handle,FALSE);

    }

     

    touch_start(integer total_number)

    {

    llListenControl(handle,TRUE); // open listen handler to listen for any of the buttons that are touched

    llSetTimerEvent(30.0); // set that time for the listen handler is open to be used it is done in seconds

    llDialog(llDetectedKey(0),"Try a selection...", main_menu, listen_channel);

    }

     

    listen(integer channel, string name, key id, string message)

    {

    if (message == "Lower") // if the Pant button is pressed it will goto the sub-menu pants

    {

    llDialog(id,message + " Dialog", Pants_menu, listen_channel);

    }

     

    if (message == "Upper") // if the Pant button is pressed it will goto the sub-menu shirt

    {

    llDialog(id,message + " Dialog", Shirt_menu, listen_channel);

    }

    if (message == "Back") // this is listneing for the button to be pushed it will go back to the main menu

    {

    llDialog(id,message + " Dialog", main_menu, listen_channel);

    }

    }

     

    timer()

    {

    llSetTimerEvent(0.0); // sets timer to 0.0 second

    llOwnerSay("You waited too long to pick, resetting menu."); // message owner this message when 30 seconds is up

    llListenControl(handle,FALSE); // shuts off listening handler where they will have to touch the hud again to // get the menu

    }

     

    } // End Script

    • Like 1
  10. I am not sure to understand correctly the problem ;

    For me , the main problem is that the rotation is at 0.2 degrees per frame  . So it s not sure the computing is correct because the cosinus of the half-angle of rotation is very closed 1 to and may , in some frames ,  rounded to 1 ( 1 = no rotation ) , if the program rounds too much the floats

    Actually , it s so bugged , than  even when i try to rotate to 20 degrees per seconds ( 0.4 degrees per frame ) , the rotation is either jerked either totally stopped . In the past , i was meeting some issues only under 9 degrees per seconds . I have turned on the option "developper/show info / show update objects ( control+alt+shift+u) and i see that the simulator doesn t often update the viewer  . Sometimes the omega returned by the simulator is 0  . The viewer is thinking that the roattion has finished . but some seconds later , the rotation is resuming its run 

    But if there is a problem with the center of mass , changing the gravity of the object to 0  will fix the issue ?

     

     

    I can t believe second life has so many bugs  about rotations after 12 years ....

  11. Dudes !! Use BITS !!!

    Your data structure should be a set .

    Inegers can be a set if you manipulate the binary bits .

     

    a single integer can represent a set of 32 values . It s more than 15 prices . And you can use several integers of course.

    It will less costly than using a list

     

    To check if some prices are owned , you need only a "&" bit-operator 

     

    Example /*PARAMS:arrayPrices : a set of prices stored as bit. numberPrice : number of price in decimal to set RETURN :TRUE arrayPrices modified in setting numberPriceexample : arrayPrices = 5 in decimal , so 101 in binaryit means the price #1 and the price #3 are ownedsetPriceOwned(5, 2) = 111 in binary , so 7 in decimalit will mean  the price #1 and the price #2 and  the price #3 are owned*/integer setPriceOwned( integer arrayPrices, integer numberPrice ){    numberPrice--;    return arrayPrices | ( 1 << numberPrice ); }/*PARAMS:arrayPrices : a set of prices stored as bit. numberPrice : number of price in decimal is checkedRETURN :TRUE if price # numberPrice is owned in the arrayPricesexample : arrayPrices = 5 in decimal , so 101 in binaryit means the price #1 the price #3 are ownedisPriceOwned(5, 1 ) = TRUEisPriceOwned(5, 2 ) = FALSEisPriceOwned(5, 3 ) = TRUE*/integer isPriceOwned( integer arrayPrices, integer numberPrice ){    numberPrice--;    return ( arrayPrices & ( 1 << numberPrice ) ) != 0;}/*PARAMS:arrayPrices : a set of prices stored as bit. maxPrices : number of the firsts prices are checkedRETURN :TRUE if ALL the maxPrices first prices are owned in the arrayPricesexample : arrayPrices = 7 in decimal , so 111 in binaryit means the price #1 and the price #2 and the price #3 are ownedallFistsPricesOwned(7, 3 ) = TRUEFALSE if notexample :arrayPrices = 5 in decimal , so 101 in binaryit means the price #1 and the price #3 are ownedallFistsPricesOwned(5, 3 ) = FALSE*/integer allFistsPricesOwned(integer arrayPrices, integer maxPrices){    integer mask = ( 1 << (maxPrices-1) ) -1;    return (arrayPrices &  mask ) == mask;}/*PARAMS:arrayPrices : a set of prices stored as bit. RETURN :a list of prices owned . The value in the list is in the decimal and means the nth price */list getListPricesOwned( integer arrayPrices ){    integer price = 1;    integer number = 1;    list output;    while ( price <= arrayPrices )    {        if ( price & arrayPrices )        {            output += number ;         }        price = price <<  1;        number++;    }     return output;}default{    touch_end(integer n)    {        integer t ;                t=setPriceOwned(t,2); // adding the price #2        t=setPriceOwned(t,4); // adding the price #4        t=setPriceOwned(t,5); // adding the price #5        t=setPriceOwned(t,1); // adding the price #1        if ( isPriceOwned(t,3) )   {  llOwnerSay("price number 3 is owned");} else {  llOwnerSay("price number 3 is not owned");}        if ( isPriceOwned(t,4) )   {  llOwnerSay("price number 4 is owned");} else {  llOwnerSay("price number 4 is not owned");}        if ( isPriceOwned(t,5) )   {  llOwnerSay("price number 4 is owned");} else {  llOwnerSay("price number 5 is not owned");}        if (  allFistsPricesOwned(t, 2 ) ) { llOwnerSay("prices from 1 to 2 are ALL owned") ;} else {  llOwnerSay("some of prices from 1 to 2 are not owned") ;}          if (  allFistsPricesOwned(t, 5 ) ) { llOwnerSay("prices from 1 to 5 are ALL owned") ;} else {  llOwnerSay("some of prices from 1 to 5 are not owned") ;}          llOwnerSay( llList2CSV( [ "list prices owned =" ] + getListPricesOwned(t) ));    }}

     

  12. your positions defined in ll(link)sittarget are probably wrong . ARe you sure to use local positions in parameters of ll(link)sittarget ?

    It s impossible to get 2 avatars on the same prim .

    Probably the defined positions are erroneous and points to the same position 

  13. I m not sure to understand you .

     

    Your problem is "when I click the vehicle to sit two avatars are sitting in the same position".

    But the numerical orders to link prims won t change anything about this point .

     

    If you have several seats , you will need  to have set an appropriate llSitTarget and eventually llSetClickAction .

    The scripts to set the sittarget and the clickaction can be deleted after . The infos are kept in the prims .

  14. It depends of the value of the timer in parameter of llSetTimerEvent .
    If the value is big ( as for rollig where the value is 60 seconds ) , probability to get both people in and out :

    First tick : there are 15 avatars .
    between the first tick and second tick , there are one avatar who leaves , and one avatar who enters 
    Second tick : there are always 15 avatars . 

    If the value of the timer is short , probabilty that the new list is equal with the old list is big .
    And we may optimize this , avoiding to call too much llfindlist.

    To check quickly if the lists are the same, we may call llFindList with the full new list and the ful old list , and compare their length

    The idea is there :

    IN PSEUDOCODEif ( llListFindList(beforeList, nowList) == 0 ){	if ( llGetListLength(beforeList) == llGetListLength(nowList))	{	// NO UPDATE OF OLDLIST WITH NEWLIST. And New avatars is EMPTY	}	else	{	// UPDATE OLDLIST WITH NEWLIST . And New avatars is EMPTY	}}else{// WE DON T KNOW IF New Avatars is empty or not , WE NEED TO ITERATE THE LISTS}

     

     

    list gHereAlready ;integer WhoIsNew( ){   list AllAvs = llGetAgentList(AGENT_LIST_PARCEL,[]);// if this test > 0 , so the whole AllAvs list is included in gHereAlready // with one block    if ( llListFindList(gHereAlready , AllAvs ) >= 0 )    {      // if lists have different lengths ,      // AllAvs is included in gHereAlready      // It means there are some people who have left     // So we need to update gHereAlready         if ( llGetListLength(gHereAlready) != llGetListLength(AllAvs))        {            gHereAlready = AllAvs;        }     // if lists have equal lengths     // gHereAlready is already equal to AllAvs     // We non t need to update it.     // This is what will happen the most often     // In the 2 cases , we may return directly without doing a loop     // because we know the AllAvs  is included in gHereAlready         return FALSE;    }    if ( llGetListLength(gHereAlready) == 0 )    {        gHereAlready = AllAvs;        return TRUE;    }                list NewAvs;    integer IsHere;    integer i = llGetListLength(AllAvs);    key k;    while(i--)    {        if ( !~llListFindList(gHereAlready,llList2List(AllAvs,i,i)) )        {            NewAvs += llList2List(AllAvs,i,i);            IsHere = TRUE;        }    }    gHereAlready = AllAvs;    return IsHere;}

     

    It won t optimize a lot of . It depends of the number of avatars .

    Around 35-45 avatars , i ve got around 40-60 ms  with the original version , and  25-35 ms with this version.

     

  15. Just change the status of your listener  manually , "on" when you want to listen your other avatar ( he is on the sim ) , "off" when you don t want to listen him ( he is not in the sim ) ; for instance in touching your attachment . You have not 200  attachments

     

    // constants : change these two lines integer channel = -84552147;key otherAvatarKey = "f7ce2caa-dca0-44f7-8326-7eff69b33060";// variablesinteger handle;integer toggle;default{    state_entry()    {        handle = llListen(channel, "", otherAvatarKey, "");           llListenControl(handle, TRUE);           	llOwnerSay("Listening "+(string)otherAvatarKey + " on channel " + (string)channel );    }     touch_end(integer total_number)    {        toggle = !toggle;  
    // Make the listener active or inactive as required llListenControl(handle, toggle); if(toggle) { llOwnerSay("Listening "+(string)otherAvatarKey + " on channel " + (string)channel ); } else { llOwnerSay("Not Listening "+(string)otherAvatarKey + " on channel " + (string)channel ); } } listen(integer channel, string name, key id, string message) { llOwnerSay( name + " just said " + message); }}

     

  16. To demonstate better my opinion that the problem with llsetkeyframedmotion is the same with other physics rotations :

    i ve done 2 seamless scripts :
    It fixes a rotation while 5 seconds and tests with different angular velocities .
    It returns when ( in frames of simulator ) the motion has been stopped ( more precisely when llGetOmega is null vector )

    By defaut , an iteration of test is 0.5 seconds , so 0.5 * 45 FPS = 225 frames of simulator

    Rez a prim ; move it to let it float on the air without friction with the ground or other objects ; put the script inside and touch it to start the tests


    /

    / we ll do succesive tests// From an angularvelocity of INITIALANGULARVELOCITY =0.5, we are turning along STEPTIMETEST seconds.// A timer is setup to count the number of frames in simulator :// if STEPTIMETEST is reached , we stop the motion , wait until it stops rotation and we go to the next test // If STEPTIMETEST is not reached and we reach a NULL llGetOmega before this, we stop the current test and we start the next test .// At every test , we display the angular velocity and the time in frames between the start of the current test and when the object has stopped to turn with llGetOmega = ZERO_VECTORfloat STEPANGULARVELOCITY = 0.01;float STEPTIMETEST = 5.0;float INITIALANGULARVELOCITY = 0.5;float angularvelocity;integer count;integer framestart;integer frameend;list keyframes;list buildKeyframes(float seconds, float angular_velocity){list l;rotation r = llEuler2Rot(<0,0,PI_BY_TWO>);float totalangle = seconds * angular_velocity;while ( totalangle > PI_BY_TWO ){l += [ r, PI_BY_TWO / angular_velocity ];totalangle -= PI_BY_TWO;}if ( totalangle > 0.0){float timekeyframe = totalangle / angular_velocity;if ( timekeyframe <= 0.111111 ) timekeyframe = 10/9.0;l += [ llEuler2Rot(<0,0,totalangle>), timekeyframe ] ;}return l;}default{state_entry(){llSetObjectName("test keyframemotion - low angular velocity");llSetStatus(STATUS_PHYSICS, FALSE);llSetRot(ZERO_ROTATION);llSetStatus(STATUS_PHANTOM, TRUE);llSetStatus(STATUS_BLOCK_GRAB, TRUE);}touch_end(integer total_number){llResetTime();llSetTimerEvent(2/45.0);angularvelocity = INITIALANGULARVELOCITY; framestart = (integer)llGetEnv("frame_number");llSetKeyframedMotion( buildKeyframes( STEPTIMETEST, angularvelocity ) , [ KFM_DATA, KFM_ROTATION ] );}timer(){if ( llGetTime() > STEPTIMETEST ){llSetKeyframedMotion( [], [ ] );while ( llGetOmega() != ZERO_VECTOR ) llSleep( 1/45.0);llResetTime();}frameend = (integer)llGetEnv("frame_number");if ( ( llGetOmega() == ZERO_VECTOR ) && ( frameend - framestart > 3 ) ){llOwnerSay(llList2CSV(["angularvelocity", RAD_TO_DEG* angularvelocity, "frames", frameend-framestart ]));angularvelocity -= STEPANGULARVELOCITY; if ( angularvelocity > 0.0 ){framestart = (integer)llGetEnv("frame_number");llSetKeyframedMotion( buildKeyframes( 5.0, angularvelocity ) , [ KFM_DATA, KFM_ROTATION ] );}else {llSetTimerEvent(0.0);llOwnerSay("end test");}}}}

     Results with llSetKeyframemotion

     

    angularvelocity, 28.647890, frames, 225[16:16] test keyframemotion - low angular velocity: angularvelocity, 28.074932, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 27.501976, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 26.929018, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 26.356062, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 25.783104, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 25.210146, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 24.637190, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 24.064232, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 23.491274, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 22.918318, frames, 226[16:16] test keyframemotion - low angular velocity: angularvelocity, 22.345360, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 21.772404, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 21.199446, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 20.626488, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 20.053532, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 19.480574, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 18.907618, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 18.334660, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 17.761702, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 17.188745, frames, 226[16:17] test keyframemotion - low angular velocity: angularvelocity, 16.615788, frames, 225[16:17] test keyframemotion - low angular velocity: angularvelocity, 16.042831, frames, 225[16:17] test keyframemotion - low angular velocity: angularvelocity, 15.469873, frames, 224[16:18] test keyframemotion - low angular velocity: angularvelocity, 14.896916, frames, 224[16:18] test keyframemotion - low angular velocity: angularvelocity, 14.323958, frames, 225[16:18] test keyframemotion - low angular velocity: angularvelocity, 13.751000, frames, 226[16:18] test keyframemotion - low angular velocity: angularvelocity, 13.178042, frames, 226[16:18] test keyframemotion - low angular velocity: angularvelocity, 12.605084, frames, 225[16:18] test keyframemotion - low angular velocity: angularvelocity, 12.032126, frames, 225[16:18] test keyframemotion - low angular velocity: angularvelocity, 11.459168, frames, 225[16:18] test keyframemotion - low angular velocity: angularvelocity, 10.886210, frames, 226[16:18] test keyframemotion - low angular velocity: angularvelocity, 10.313251, frames, 226[16:18] test keyframemotion - low angular velocity: angularvelocity, 9.740294, frames, 226[16:18] test keyframemotion - low angular velocity: angularvelocity, 9.167336, frames, 126[16:18] test keyframemotion - low angular velocity: angularvelocity, 8.594378, frames, 100[16:18] test keyframemotion - low angular velocity: angularvelocity, 8.021420, frames, 15[16:18] test keyframemotion - low angular velocity: angularvelocity, 7.448462, frames, 72[16:18] test keyframemotion - low angular velocity: angularvelocity, 6.875504, frames, 42[16:18] test keyframemotion - low angular velocity: angularvelocity, 6.302546, frames, 48[16:18] test keyframemotion - low angular velocity: angularvelocity, 5.729589, frames, 43[16:18] test keyframemotion - low angular velocity: angularvelocity, 5.156631, frames, 4[16:18] test keyframemotion - low angular velocity: angularvelocity, 4.583673, frames, 56[16:18] test keyframemotion - low angular velocity: angularvelocity, 4.010715, frames, 50[16:19] test keyframemotion - low angular velocity: angularvelocity, 3.437758, frames, 60[16:19] test keyframemotion - low angular velocity: angularvelocity, 2.864800, frames, 36[16:19] test keyframemotion - low angular velocity: angularvelocity, 2.291842, frames, 22[16:19] test keyframemotion - low angular velocity: angularvelocity, 1.718884, frames, 25[16:19] test keyframemotion - low angular velocity: angularvelocity, 1.145927, frames, 35[16:19] test keyframemotion - low angular velocity: angularvelocity, 0.572969, frames, 34[16:19] test keyframemotion - low angular velocity: angularvelocity, 0.000011, frames, 4[16:19] test keyframemotion - low angular velocity: end test

     

     



    The same test with llsetangularvelocity.
    Rez a prim ; move it to let it float on the air without friction with the ground or other objects ; put the script inside and touch it to start the tests .
    The script is lightly different because the motion is full physics , but it s the same algorithm

    // we ll do succesive tests// From an angularvelocity of INITIALANGULARVELOCITY =0.5, we are turning along STEPTIMETEST seconds.// A timer is setup to count the number of frames in simulator :// if STEPTIMETEST is reached , we stop the motion , wait until it stops rotation and we go to the next test // If STEPTIMETEST is not reached and we reach a NULL llGetOmega before this, we stop the current test and we start the next test .// At every test , we display the angular velocity and the time in frames between the start of the current test and when the object has stopped to turn with llGetOmega = ZERO_VECTORfloat STEPANGULARVELOCITY = 0.01;float STEPTIMETEST = 5.0; float INITIALANGULARVELOCITY = 0.5;float angularvelocity;integer count;integer framestart;integer frameend; default{state_entry(){llSetObjectName("test setangularvelocity - low angular velocity");llSetStatus(STATUS_PHYSICS, FALSE);llSetRot(ZERO_ROTATION);llSetStatus(STATUS_PHYSICS, TRUE);llSetStatus(STATUS_PHANTOM, TRUE);llSetStatus(STATUS_BLOCK_GRAB, TRUE);llSetPhysicsMaterial(GRAVITY_MULTIPLIER, 0.0,0.0,0.0,0.0);}touch_end(integer total_number){llResetTime();llSetTimerEvent(2/45.0);angularvelocity = INITIALANGULARVELOCITY; framestart = (integer)llGetEnv("frame_number");llSetAngularVelocity(<0,0,angularvelocity>, TRUE);}timer(){if ( llGetTime() > STEPTIMETEST ){llSetAngularVelocity(ZERO_VECTOR,TRUE);while ( llGetOmega() != ZERO_VECTOR ) llSleep( 1/45.0);llResetTime();}frameend = (integer)llGetEnv("frame_number");if ( ( llGetOmega() == ZERO_VECTOR ) && ( frameend - framestart > 3 ) ){llOwnerSay(llList2CSV(["angularvelocity", RAD_TO_DEG* angularvelocity, "frames", frameend-framestart ]));angularvelocity -= STEPANGULARVELOCITY; if ( angularvelocity > 0.0 ){framestart = (integer)llGetEnv("frame_number");llSetAngularVelocity(<0,0,angularvelocity>, TRUE);}else {llSetTimerEvent(0.0);llOwnerSay("end test");}}}}

     

    Results

     

    angularvelocity, 28.647890, frames, 228[16:25] test setangularvelocity - low angular velocity: angularvelocity, 28.074932, frames, 230[16:25] test setangularvelocity - low angular velocity: angularvelocity, 27.501976, frames, 229[16:25] test setangularvelocity - low angular velocity: angularvelocity, 26.929018, frames, 227[16:25] test setangularvelocity - low angular velocity: angularvelocity, 26.356062, frames, 228[16:25] test setangularvelocity - low angular velocity: angularvelocity, 25.783104, frames, 227[16:25] test setangularvelocity - low angular velocity: angularvelocity, 25.210146, frames, 210[16:25] test setangularvelocity - low angular velocity: angularvelocity, 24.637190, frames, 228[16:25] test setangularvelocity - low angular velocity: angularvelocity, 24.064232, frames, 227[16:25] test setangularvelocity - low angular velocity: angularvelocity, 23.491274, frames, 227[16:26] test setangularvelocity - low angular velocity: angularvelocity, 22.918318, frames, 228[16:26] test setangularvelocity - low angular velocity: angularvelocity, 22.345360, frames, 226[16:26] test setangularvelocity - low angular velocity: angularvelocity, 21.772404, frames, 228[16:26] test setangularvelocity - low angular velocity: angularvelocity, 21.199446, frames, 227[16:26] test setangularvelocity - low angular velocity: angularvelocity, 20.626488, frames, 227[16:26] test setangularvelocity - low angular velocity: angularvelocity, 20.053532, frames, 227[16:26] test setangularvelocity - low angular velocity: angularvelocity, 19.480574, frames, 227[16:26] test setangularvelocity - low angular velocity: angularvelocity, 18.907618, frames, 228[16:26] test setangularvelocity - low angular velocity: angularvelocity, 18.334660, frames, 227[16:26] test setangularvelocity - low angular velocity: angularvelocity, 17.761702, frames, 228[16:26] test setangularvelocity - low angular velocity: angularvelocity, 17.188745, frames, 226[16:26] test setangularvelocity - low angular velocity: angularvelocity, 16.615788, frames, 227[16:27] test setangularvelocity - low angular velocity: angularvelocity, 16.042831, frames, 228[16:27] test setangularvelocity - low angular velocity: angularvelocity, 15.469873, frames, 227[16:27] test setangularvelocity - low angular velocity: angularvelocity, 14.896916, frames, 227[16:27] test setangularvelocity - low angular velocity: angularvelocity, 14.323958, frames, 227[16:27] test setangularvelocity - low angular velocity: angularvelocity, 13.751000, frames, 228[16:27] test setangularvelocity - low angular velocity: angularvelocity, 13.178042, frames, 228[16:27] test setangularvelocity - low angular velocity: angularvelocity, 12.605084, frames, 226[16:27] test setangularvelocity - low angular velocity: angularvelocity, 12.032126, frames, 227[16:27] test setangularvelocity - low angular velocity: angularvelocity, 11.459168, frames, 228[16:27] test setangularvelocity - low angular velocity: angularvelocity, 10.886210, frames, 221[16:27] test setangularvelocity - low angular velocity: angularvelocity, 10.313251, frames, 6[16:27] test setangularvelocity - low angular velocity: angularvelocity, 9.740294, frames, 205[16:27] test setangularvelocity - low angular velocity: angularvelocity, 9.167336, frames, 22[16:27] test setangularvelocity - low angular velocity: angularvelocity, 8.594378, frames, 93[16:27] test setangularvelocity - low angular velocity: angularvelocity, 8.021420, frames, 114[16:27] test setangularvelocity - low angular velocity: angularvelocity, 7.448462, frames, 22[16:27] test setangularvelocity - low angular velocity: angularvelocity, 6.875504, frames, 49[16:27] test setangularvelocity - low angular velocity: angularvelocity, 6.302546, frames, 40[16:28] test setangularvelocity - low angular velocity: angularvelocity, 5.729589, frames, 40[16:28] test setangularvelocity - low angular velocity: angularvelocity, 5.156631, frames, 48[16:28] test setangularvelocity - low angular velocity: angularvelocity, 4.583673, frames, 51[16:28] test setangularvelocity - low angular velocity: angularvelocity, 4.010715, frames, 63[16:28] test setangularvelocity - low angular velocity: angularvelocity, 3.437758, frames, 68[16:28] test setangularvelocity - low angular velocity: angularvelocity, 2.864800, frames, 68[16:28] test setangularvelocity - low angular velocity: angularvelocity, 2.291842, frames, 30[16:28] test setangularvelocity - low angular velocity: angularvelocity, 1.718884, frames, 5[16:28] test setangularvelocity - low angular velocity: angularvelocity, 1.145927, frames, 68[16:28] test setangularvelocity - low angular velocity: angularvelocity, 0.572969, frames, 36[16:28] test setangularvelocity - low angular velocity: angularvelocity, 0.000011, frames, 36[16:28] test setangularvelocity - low angular velocity: end test

     

    In the both motions , with llsetangularvelocity or with llsetkeyframedmotion , the problems appear below 9 degrees per seconds of simulator.
    I ve not done a script with llTargetomega , but , by experience , i ve met the same comportment there too.

     

     

    No ,  the question is not purely academic :

    a clock turning at 60 beats per minute , so 6 degrees per seconds , and everybody can visually count the motions.

    It s neither the rotation of a wheel , neither the rotation of a motor, rotations who are undetectable to the sight.

    The error on rotations is huge.

    Personnaly i have no ideas how developpers on other games deal this case : are they switching non-physics motions ? are they  changing the size of floats ? are they using some accumulators ? are they changing dynamically the interpolation time : not 1/45 per second but 1/22.5 seconds to avoid floating errors?

     

     

    The stuttering is done because havarrik does a KFM_LOOP,  and llsetkeyframedmotion will send to the viewer every tiny impulsion. If he did without KFM_LOOP ; the motion will be stopped

     

  17. Dora , i am talking about frames of simulator ( so 45 frames per seconds ) ; i am not talking about frames as input/ parameters of llsetkeyframedmotion.
    If you prefer it s the time in seconds of your frame as input of llsetkeyframemotion multiplied by 45 :

    if i take your script dated at 12/Nov/14 12:55 PM :
    rotation r = llAxisAngle2Rot( <0.0, 1.0, 0.0>, TWO_PI/(float)frams);
    float angle = llRot2Angle® * RAD_TO_DEG;
    float time = 60.0/(float)frams;
    float velocity_by_frame = angle / time;
    llOwnerSay(llList2CSV( [ "angle (deg)", angle, "time (seconds)", time, "velocity rotation (deg/frame )",velocity_by_frame ]));

    => will display
    angle (deg), 120.000008, time (seconds), 20.000000, velocity rotation (deg/frame ), 6.000000

    you are at 6 deg per seconds< 9 deg per seconds => so , you meet the problem here

    If i take you second daatas :
    rotation r = <0.000000, 0.087156, 0.000000, 0.996195>;
    float angle = llRot2Angle® * RAD_TO_DEG;
    float time = 1.666667 ;
    float velocity_by_frame = angle / time;
    llOwnerSay(llList2CSV( [ "angle (deg)", angle, "time (seconds)", time, "velocity rotation (deg/frame )",velocity_by_frame ]));

    => will display
    angle (deg), 9.999578, time (seconds), 1.666667, velocity rotation (deg/frame ), 5.999746
    you are at 5.997 deg per seconds< 9 deg per seconds => so , you meet the problem here too

    In your script in your link , your move does both a translation and a rotation . So to compute the velocity of rotation in the right coordinates ; in local coordinates the velocity is different of region coordinates

    Change your linear( translation) Velocity
    vector Velocity = <0.0, -0.10471975512, 0.0>;
    by
    vector Velocity = <0.0, -0.01471975512, 0.0>; and you will watch your prim starting and stopping

     

     

    // frames for circular motion, plane, script by Dora Gustafson, Studio Dora 2014// function computes relative moves and rotations for a Key Framed Motion// function takes arguments: velocity, some radius-vector and angle for motion in degrees// the turning can be in any plane in space. Plane given by the two vectors// The two vectors must not point in same or opposite direction// turns prim forward before motion startvector Velocity = <0.0, -0.01471975512, 0.0>;vector Radius = <1.0, 0.0, 0.0>;float Angle = 360;integer rev;list L;list framesCircular( vector velocity, vector radius, float angle ){    // Compute circle center and first position    vector center = llVecMag(radius)*llVecNorm((velocity%radius)%velocity);    vector startRadiusVector = -center;    // Compute time per step    float arch = angle*DEG_TO_RAD;    float steps = (float)llRound(arch/(10.0*DEG_TO_RAD));    float dT = arch*llVecMag(radius)/llVecMag(velocity)/steps;    dT = llRound(45.0*dT)/45.0;    if ( dT < 0.11111111 ) dT = 0.11111111;    // initialize loop    list KFMlist = [];    vector axis = velocity%radius;    rotation baserot = llGetRot();    vector U = center+startRadiusVector;    rotation rotU = baserot;    vector V;    rotation rotV;    float step = 0.0;    while ( step < steps )    {        step += 1.0;        rotV = llAxisAngle2Rot( axis, arch*step/steps);        V = center+startRadiusVector*rotV;        rotV = baserot*rotV;        KFMlist += [V-U, rotV/rotU, dT];        rotU = rotV;        U = V;    }    return KFMlist;}default{    touch_start( integer n)    {        llSetKeyframedMotion( [], []);        llSleep(0.2);        if ( rev ) llSetKeyframedMotion( L, [KFM_MODE, KFM_REVERSE]);        else        {            // first turn prim correct, X-axis in velocity's direction            llSetRot( llAxes2Rot( llVecNorm( Velocity), llVecNorm(( Velocity%Radius)%Velocity), llVecNorm( Velocity%Radius)));            // then do the motion            L = framesCircular( Velocity, Radius, Angle);            llSetTimerEvent(1/45.0);            llSetKeyframedMotion( L, []);        }        rev = !rev;    }    timer()    {        llSetLinkPrimitiveParamsFast(LINK_THIS,         [ PRIM_TEXT, llList2CSV([ llVecMag(llGetVel()), llGetOmega()]), <1.0,1.0,1.0>, 1.0        ]);    }}

     

     

    And i repeat : with llTargetOmega , we meet the same problem , and with llSetAngularVelocity we meet the same problem.
    it stops too soon for low velocity.

    It s a common bug/limit for every rotations in second life

     

  18. No , there is no link with the translations

    Your rotation velocity is too small  and the half cosinus , or half sinus needed to compute the intermediar rotations may be rounded to 1 or 0:

     

    llOwnerSay(llList2CSV(
    [ "angle (radians)", llRot2Angle(normRot(<0.00000, 0.99996, 0.00000, -0.00873>)), "\n",
    "angle (rad) / frame",llRot2Angle(normRot(<0.00000, 0.99996, 0.00000, -0.00873>))/(20.0*45), "\n",
    "half cosinus angle / frame ", llCos( 0.5 * llRot2Angle(normRot(<0.00000, 0.99996, 0.00000, -0.00873>))/(20.0*45) )
    ]));

     

    angle (radians), 3.124133,
    , angle (rad) / frame, 0.003471,
    , cosinus half  angle / frame , 0,99999849379559389750526410095037 <--- may be rounded to 1 and so make no rotations

     

    LLSetKeyframedMotion work by an impulsion; if the rotation impulsion is very small , your object can move and stop at the half of its run

    Watch my Jira  https://jira.secondlife.com/browse/SVC-7471

     

     

    It s the same problem for every physical rotations :
    there is a limit to 9 degrees per seconds.
    Maybe use 64 bits float and not 32 bits float may fix it

  19. Thank you , but i  ve maybe explained incorrectly my goal :

     

    i don t want some  plants who are inside the object inventory .

    I want the plants ( trees and grasses  ) who are the ones when you own a land and you choose in build tool directly.

     

     

    It seems they consume less prims and are less laggy ; i don t know why .

     

    Do you think they are objects as every other objects , or are there special objects ?

     

     

×
×
  • Create New...