Jump to content

Dials and Sliders


Quistess Alpha
 Share

You are about to reply to a thread that has been inactive for 997 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Sometimes when we get into one way of thinking about a problem, it can be hard to realize that there might be other, better solutions.

Case and point; I had convinced myself that it was necessary to use 2 separate prims for a movable dial/slider object, because I had really focused myself on using llDetectedTouchST, and if the object moves, that changes where you're touching and leads to some recursive nonsense, which it's possible but not very nice to get around:

// Not ideal single-prim dial script.
// drop into a linked prim and click-drag on face 0:
float gAngTouchStart;

default
{
    state_entry()
    {
        llMinEventDelay(0.2);
        // a slower event speed is in-fact neccessary to prevent a feedback loop.
        // expiramentally anything below 0.2 causes some kickback; below 0.1 or so it spins wildly out of control.
    }
    touch_start(integer total_number)
    {
        if(llDetectedTouchFace(0)!=0) return;
        
        vector pos = llDetectedTouchUV(0);
        gAngTouchStart = llAtan2(pos.y-0.5,pos.x-0.5);
    }
    touch(integer n)
    {
        if(llDetectedTouchFace(0)!=0) return;
        
        vector pos = llDetectedTouchUV(0);
        float angTouch = llAtan2(pos.y-0.5,pos.x-0.5);
        float angDiff=angTouch-gAngTouchStart;
        llSetLinkPrimitiveParamsFast(LINK_THIS,
        [   PRIM_ROT_LOCAL, llEuler2Rot(<0,0,angDiff>)*
            llList2Rot(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_ROT_LOCAL]),0)
        ]);
    }
}

After taking a breather though, I realized it is in-fact possible to get around this using llDetectedTouchPos, and absolute positions:

//same idea but much smoother: 

vector gPosRest;
rotation gRotRest;
float gAngGrabPrev; // previous grab angle.
float gAngSet; // angle the dial is set to.

float angNorm(float a)
{   return a-llFloor(a/TWO_PI)*TWO_PI;
}
default
{
    state_entry()
    {
        gPosRest=llList2Vector(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_POSITION]),0);
        gRotRest=llList2Rot(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_ROT_LOCAL]),0);
        llSetText((string)gAngSet,<0,1,0>,1.0);
    }
    touch_start(integer n)
    {
        if(llDetectedTouchFace(0)!=0) return;
        // dials only really make sense as disks.
        
        //gRotTStart=llList2Rot(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_ROT_LOCAL]),0);
        vector posGrabStart = (llDetectedTouchPos(0)-gPosRest)/gRotRest;
        gAngGrabPrev = llAtan2(posGrabStart.y,posGrabStart.x);
    }
    touch(integer n)
    {   
        if(llDetectedTouchFace(0)!=0) return;
        
        vector posGrab = (llDetectedTouchPos(0)-gPosRest)/gRotRest;
        float angGrab =  llAtan2(posGrab.y,posGrab.x);
        float angChange =angGrab-gAngGrabPrev;
        if(angChange<-PI)
        {   angChange+=TWO_PI;
        }else if(angChange>PI)
        {   angChange-=TWO_PI;
        }
        gAngGrabPrev=angGrab;
        
        gAngSet+=angChange;
        
        llSetText((string)gAngSet,<0,1,0>,1.0);
        
        rotation rotLocal = llList2Rot(llGetLinkPrimitiveParams(LINK_THIS,
        [   PRIM_ROT_LOCAL]),0);
        
        llSetLinkPrimitiveParamsFast(LINK_THIS,
        [   PRIM_ROT_LOCAL, llEuler2Rot(<0,0,gAngSet>)*gRotRest
        ]);
    }
    touch_end(integer n)
    {
        // inform main script about our new setting.
        llMessageLinked(LINK_ROOT,0,"Dial Setting",(key)((string)gAngSet));
    }
}

and the same idea can be applied to sliders:

vector gPosRest;
vector gPosTStart;
vector gPosGrabStart;
integer gTouchFace;
default
{
    state_entry()
    {
        gPosRest=llList2Vector(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_POS_LOCAL]),0);
    }
    touch_start(integer n)
    {
        gPosTStart=llList2Vector(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_POS_LOCAL]),0);
        gPosGrabStart = llDetectedTouchPos(0);
        gTouchFace=llDetectedTouchFace(0);
    }
    touch(integer n)
    {   
        if(llDetectedTouchFace(0)!=gTouchFace) return;
        
        vector posChange = llDetectedTouchPos(0)-gPosGrabStart;
        // choose 1 or none of the following 3 to constrain to a single axis:
        posChange = <posChange.x,0,0>;
        //posChange = <0,posChange.y,0>;
        //posChange = <0,0,posChange.z>;
        
        //limit the total movement (remove this paragraph for unlimited drag potential)
        // replace x with desired axis:
        vector posSet = gPosTStart+posChange-gPosRest;
        if(posSet.x<-0.5)
            posSet.x=-0.5;
        else if(posSet.x>0.5)
            posSet.x=0.5;
        
        llSetLinkPrimitiveParamsFast(LINK_THIS,
        [   PRIM_POS_LOCAL, posSet+gPosRest
        ]);
    }
    touch_end(integer n)
    {
        vector posSet = llList2Vector(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_POS_LOCAL]),0)-gPosRest;
        llSetText((string)posSet,<0,1,0>,1.0);
        //llMessageLinked ...
        // inform main script about our new setting.
    }
}

Of course it's better practice to integrate this sort of thing into a single script, rather than having a script per interactable (huh, not in my web-browser's dictionary. . .fixed.) element, but I'll leave that as an 'exercise for the reader' :P

Edited by Quistess Alpha
  • Like 3
  • Thanks 4
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 997 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...