Jump to content

Catmull Rom or Bezier curves in game play animal movement


VirtualKitten
 Share

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

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

Recommended Posts

Hi I am confused by al of this but found this on web  it was from a script to create a rez-able object along a bezier code . But its not really an answer on how to move a animesh along a path  that looks realistic . Does anyone have any experience with this of can give some starting points on how to learn this I know Catmull Rom is used by animation studios but the wiki page on interpolation is really bad ..http://wiki.secondlife.com/wiki/Interpolation and even looks unfinished or elsewhere . I would welcome any test code or ideas please as my creatures are al known as lumpy , lol as they have no graceful movements. Can you help , thanks for looking. 

//mu ranges from 0 to 1, start to end of the curve 
// current_mu= i * increment; in i loop, where increment = 1.0 / gNumSegments;
vector bezier3(vector p1, vector p2, vector p3, float mu)
{
    float mum1;
    float mum12;
 
    float mu2;
    vector p;
  
     mu2 = mu * mu;
     mum1 = 1 - mu;
     mum12 = mum1 * mum1;
     
     // p= (1-mu)^2 p1 + 2*mu*(1-mu)*p2 + mu^2 * p3
     p.x = p1.x * mum12 + 2. * p2.x * mum1 * mu + p3.x * mu2;
     p.y = p1.y * mum12 + 2. * p2.y * mum1 * mu + p3.y * mu2;
     p.z = p1.z * mum12 + 2. * p2.z * mum1 * mu + p3.z * mu2;
  
     return(p);
}

 

I also found this which was a bit better

http://wiki.secondlife.com/wiki/User:Dora_Gustafson/bezier_toy 

But still not sewing together the bezier curves into a flight path

Edited by VirtualKitten
Link to comment
Share on other sites

The last one was better than anything else that I have seen it shot about using up to 16 curves but is kludge using  KFM_LOOP.

I really wanted to have a  call procedure that would be returned from a convoluting series of Bezier curves  of a size vector position  in a box frame  that could be expanded easily though parameters like WIDTH=<15,15,5> its sounds dreadfully complicated to me .

Edited by VirtualKitten
Link to comment
Share on other sites

Hi @Canuck, hope you are well , I am trying  to work out how to get a new position from a random Bezier route inside an area or box frame bottom top vector positions.

I think the script above does its work in this bit :

if ( message == "New" )
            {
                homeRot = llGetRot();
                float liN = (float)frames;
                float dT = Tmotion/liN/(float)curvNumb;
                dT = llRound(45.0*dT)/45.0;
                if ( dT < 0.11111111 ) dT = 0.11111111;
                KFMlist=[];
                coefisFirst();
                b1 = Bez(0);
                r1 = Vec2Rot( dBez(0));
                integer i;
                for ( i=1; i<=frames; i++ )
                {
                    b2 = Bez( i/liN); r2 = Vec2Rot(dBez( i/liN));
                    KFMlist += [ b2-b1, r2/r1, dT];
                    b1 = b2; r1 = r2;
                }
                integer j = curvNumb;
                while ( j-- > 2 )
                {
                    coefisNext();
                    for ( i=1; i<=frames; i++ )
                    {
                        b2 = Bez( i/liN); r2 = Vec2Rot(dBez( i/liN));
                        KFMlist += [ b2-b1, r2/r1, dT];
                        b1 = b2; r1 = r2;
                    }
                }
                coefisLast();
                for ( i=1; i<=frames; i++ )
                {
                    b2 = Bez( i/liN); r2 = Vec2Rot(dBez( i/liN));
                    KFMlist += [ b2-b1, r2/r1, dT];
                    b1 = b2; r1 = r2;
                }
            }

But all I know what its doing is creating entries in a list to be fed to a KFM .

I wanted to create this output singerly  as a feed but also inside my frame limits of movements vector bottom_left and  vector top_right_bak. This math's is a bit over my head as it uses be higher mathematics than am used to now. What I don't understand is why there is three routines acting on the keyframe. These look all the same but it uses CurveNumb to store the number of curves in the KFM. Why dos it need three ? It also looks like Cofis create three types of different curves with random elements. It still does not explain why it needs three loops unless I am missing something which is possible .

Does it have three as it needs a start and finish it can join to which are known quantities, where as he middle is somewhat random?

 

Hugs D X


 

 

Edited by VirtualKitten
spellings
Link to comment
Share on other sites

5 hours ago, VirtualKitten said:

Catmull Rom

I read Wikipedia a bit and once you get through the mathy stuff, it's quite a neat idea. My main takeaway is that C-R curves are a specific case (of another specific case) of cubic bezier curves.

The Wikipedia page on bezier curves is pretty good, but to recap, a cubic bezier function generally takes 4 points, and a float, and spits out a point that is part way between the first and last point:

File:Bézier 3 big.gif

vector bezier3(vector p0, vector p1, vector p2, vector p3, float f)
{
    float nf = (1-f);
    return nf*nf*nf*p0 + 3*f*nf*nf*p1 + 3*f*f*nf*p2 + f*f*f*p3;
}

Catmul-Rom is (more or less) a specific way of generating the in-between points p1 and p2 based on the points the curve actually passes through:

vector cardinal(list points, float time, float c) // c is a "tension" parameter, from 0 to 1; 0.5 is a catmull-rom spline.
{
    integer index = (integer)(time);
    vector pn = llList2Vector(points,index-1);
    if(0>=index) // this is a bit of a hack, ideally use a completely different formula for the end cases.
        pn = llList2Vector(points,index);
    vector p0 = llList2Vector(points,index);
    vector p3 = llList2Vector(points,index+1);
    //if(llGetListLength(points)<=index+2)
    //{   p3 = llList2Vector(index);
    //}
    vector p6 = llList2Vector(points,index+2);
    if(llGetListLength(points)<=index+3)
    {   p6 = llList2Vector(index);
    }
    vector m0 = 0.166666*(1-c)*(p3-pn);
    vector m1 = 0.166666*(1-c)*(p6-p0);

    return bezier3(p0,p0+m0,p1-m1,p3,time-index);
}

(untested, probably has (more) bugs)

Minor, incomplete and possibly incorrect

optimizations:

Quote
vector bezier3_optimization(vector p0, vector p1, vector p2, vector p3, float f)
{
    float nf = (1-f);
    float nf2 = nf*nf;
    float f2 = f*f;
    return nf*nf2*p0 + 3*f*nf2*p1 + 3*f2*nf*p2 + f2*f*p3;
}
list cardinal_2(list points, list times, float c) // c is a "tension" parameter, from 0 to 1; 0.5 is a catmull-rom spline.
{
    c = 1-c; // Just to be in line with the standard definitions.
    list ret; // our output list.
    integer index_time = llGetListLength(times)-1;

    float time = llList2Float(times,index_time);
    integer index_point = (integer)(time);

    // semi-constant setup start
    vector pn = llList2Vector(points,index_point-1);
    if(0>=index) // this is a bit of a hack, ideally use a completely different formula for the end cases.
        pn = llList2Vector(points,index_point);
    vector p0 = llList2Vector(points,index_point);
    vector p3 = llList2Vector(points,index_point+1);
    //if(llGetListLength(points)<=index_point+2)
    //    p3 = llList2Vector(index_point);
    vector p6 = llList2Vector(points,index_point+2);
    if(llGetListLength(points)<=index_point+3)
        p6 = llList2Vector(index_point);
    vector m0 = 0.166666*(c)*(p3-pn);
    vector p1 = p0+m0;
    vector m1 = 0.166666*(c)*(p6-p0);
    vector p2 = p3-m1;
    // semi-constant setup end

    ret = bezier3(p0,p1,p2,p3,f-index) + ret;

    while(~--index_time)
    {
        time = llList2Float(times,index_time);
        integer index_new = (integer)time;
		if(index_new!=index_point)
        {   index_point = index_new;
            // semi-constant setup start
            pn = llList2Vector(points,index_point-1);
            if(0>=index) // this is a bit of a hack, ideally use a completely different formula for the end cases.
                pn = llList2Vector(points,index_point);
            p0 = llList2Vector(points,index_point);
            p3 = llList2Vector(points,index_point+1);
            //if(llGetListLength(points)<=index_point+2)
            //{   p3 = llList2Vector(index_point);
            //}
            vector p6 = llList2Vector(points,index_point+2);
            if(llGetListLength(points)<=index_point+3)
               p6 = llList2Vector(index_point);
            m0 = 0.166666*(c)*(p0-pn);
            p1 = p0+m0;
            m1 = 0.166666*(c)*(p3-p1);
            p2 = p3-m1;
            // semi-constant setup end
        }
        ret = bezier3(p0,p1,p2,p3,f-index) + ret;
    }
    return ret;
}

 

 

Edited by Quistess Alpha
  • Like 1
Link to comment
Share on other sites

Thanks @QuistTessa I did  think about this but still have much misunderstanding i.e that is to say my existing routine splutters out new points that are within  a clipped region, that uses vector points  I am presuming from what you suggested I need Catmull rom to create my new extra points my creature will transverse. But am unsure . If I have P1 and P2 and P3  p4 e.g

I get nextcoordinates which randomly uses three types of movement from 

    float driftRange = llFrand(MOVEMENT_RANGE);
    float circle_radius = 5.0;
    float a = llFrand(TWO_PI);
    float b = llFrand(TWO_PI);
    float c = llFrand(PI);

  where iPos is my new point  using  randomly either :

return <iPos.x + driftRange, iPos.y + llFrand(MOVEMENT_RANGE), iPos.z>;

OR

return <iPos.x + driftRange * llCos(a), iPos.y + driftRange * llSin(b), iPos.z>;

OR

 return iPos + <driftRange * llCos(a) * llCos(b), driftRange * llCos(a) * llSin(b), driftRange * llSin(a)>;

iPos is generated in vectors how can these be used to generate new points does your example do this ? Do I have to specify how many new points i need?  Or is this what this bezier toy script does Thank you  hugs Dx

 

Edited by VirtualKitten
spellings
Link to comment
Share on other sites

Calculating the curve isn't that hard. Getting the character to follow it is hard. Keyframe animation works by having the server send position updates to the viewer. Update arrival time isn't precise, so if you put too many points in your keyframe animation, you will get jitter.

Keyframe animation can move and turn at the same time. On top of that, you can run regular animations. Combining this works reasonably well, although not perfectly. If you want to see this, visit Hippotropolis, and use area search to zoom in on Cindi, one of my NPCs, and watch for a while. What you're seeing is keyframe animation plus a sort of "animation overrider" which plays the appropriate animations depending on the motion. That has "walk", "run", "turn left", "turn right", and some stand animations.

The keyframe animation paths start as a path from llGetStaticPath. llStaticPath will generate duplicate points and similar junk, so a cleanup pass is required. Then there's a maze solver to deal with non-static obstacles. Then a list of points is generated. If points are far apart, additional points are added near turns to mark the desired starting and ending points of the turn. Then a turn rate is set for the turn so that there's a smooth rotation from before the turn point to after the turn.

You don't want to start turning too far in advance of the turn or it looks bad. It's still a hard change in rotation rate, not a spline. There's no "ease in/ease out" feature for keyframe animation.

Anyway, you can get NPCs up to the movement level of typical SL users, but not to AAA title level.

 

  • Like 2
  • Haha 1
Link to comment
Share on other sites

Thanks for reminding me @Animats yes the list dump to keyframe is not a good move this is why I went to load next point so I could always change animation during event. i expected to find some examples of this only but found this wiki page which crashes  dumping KeyFrame events . What i was hoping for clarification why it required three loops?

Link to comment
Share on other sites

This is a pure stab in the dark, but as a bezier curve requires more then two points (minimum a start, and end, and a control), the three loop events are to create a curve segment from three such points. Coefirst starts with llGetPos() so the bezier curve begins with the current position, but coefisnext and coefislast each begin with the third point from the previous  call result (coefnext uses P3 from coefirst as it's P0. coefilast uses the P3 from coefnextt as it's P0). There's a clue here, I'm sure.

 

ETA just playing with Dora's script in a sandbox (always worth looking at Dora's things) there are a number of bezier curves produced, each starting from the last position, so the loops are starting with a point that is either an initial point or the end of a previous point. I still haven't looked at how she gets the final curve to close the circle. More reports may follow.

 

Yes, got it, look at how she uses global variable Po0.

 

There are three loops because each curve starts either at the initial point or where the last curve ended, and comprises an initial and an end point and some in-between. an initial set of curves is produced outward-bound from the start, a middle set of curves is produced following on from where the outbound ones end, and a final set of curves is produced to return to where the first set of curves began.

 

your vector bottom_left and your vector top_right combined with your starting position can provide the X Y Z figures to feed into her Range box

(something like Xrange = Xtr - Xbl, Yrange = Ytr - Ybl, Zrange = Ztr - Zbl assuming you ar stood smack in the middle of such a box)

 

If you just want the positions and rotations rather than doing KFM, grab the KfmList and work through it thus:

From your origiinal llGetPos and llGet Rot, 

add the first list item to your position and multiply your rotation by the second list item, skip the third item

Add the next (4th) list item to the last position you obtained, multiply the last rotation you obtained by the 5th list item,

continue in steps of three.

The result will be a list of region Pos and Rots constrained within the box and forming a closed path

ETA  

playing further, the number of KFM triplets of pos,rot, speed is dependent upon the twin parameters Frames/curve and Curves, so if you set those both to 3, the list length after the plotting from coefisFirst will be 9, after coefisNext 18, and after coefisEnd 27,.

With the default values a much much greater list is produced (over 144 entries), which is probably why you got a crash trying to dump the list?

 

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

@Profaitchikenz Haiku. Thanks for you reply. My test of Dora's code in a box was ok until i touched the box it shot of in  course that i could not stp i did not see kill script  so pressed new curve and it crashed not surprisingly. Yes the idea of KFM in this example is bad practice   as you cant change animations and it kills itself as you suggested if its over 144 entries i have to wonder why its on wiki and not replaced with something better X D .

Link to comment
Share on other sites

The defaults for things like range would maybe have taken it it the edge of the region? I put in 10.0,10.0, 0.0 but left the others alone, and got a nice moving display around me, but with 144 entries. I then cut down the other two parameters to three so I could have a look at what was being put out.

The reason for the three distinct segments is that to produce a  path from random numbers that actually goes somewhere with purpose instead of staggering blindly in a Drunkard's Walk is very difficult, you don't actually want random numbers at all, but random variation in a carefully controlled pattern.

I believe the wiki maintainers would only remove entries if they were fundamentally wrong, pointless,  or cribs. This particular piece of code is fundamentally right and is your best starting point.

Link to comment
Share on other sites

36 minutes ago, VirtualKitten said:

KFM in this example is bad practice   as you cant change animations

Actually, this is a misunderstanding: the wiki advises that you cannot move any child prims of a linkset whilst it is being moved in KFM mode. But if the path is made up of lots of tiny segments instead of one set , you can jiggle things around at each point where KFM has ended before setting off the next small section of the path.

Dora's code combines a lot of delta position and delta rotations plus time and plays them all as one large list, but that's simply because the code is illustrating something. It can be changed.

If you made up the list as a separate list you could then take the list contents three at a time, as a sub-list to give to the KFM command, start the KFM, set a timer for when you expect the KFM to have finished, and if moving_end hasn't triggered, the timer itself calls the KFM([],[]) to stop any remaining motion.

Once KFM has been stopped, you can alter the linkset, then call the next section of the KFM path, and so on. LLSetLinkPrimitiveParamsFast ensures that your list of changes get prioritised. Just don't try and make too many at once.

For things like propellers you can use target omega, because that is client-side only and so doesn't have the same complications that altering child positions server-side would do.

 

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

1 hour ago, VirtualKitten said:

i have to wonder why its on wiki and not replaced with something better

It's only in the wiki by reference.  Dora posted it in her own user space, along with a collection of other KFM scripts.  If she were still alive, I have no doubt that she would have posted many more sophisticated scripts by now. 

  • Like 5
Link to comment
Share on other sites

I'm glad I got pointed to her script in question, it has some very interesting techniques that I'd not really thought of before, (such as adding an offset to the base channel in the text boxes to simplify the flow control in the listen handler).

In terms of generating lifelike paths with a random element, it is also a brilliant starting point. With a little change to it, you could add in collision avoidance by making an invisible prim to go round the route as it is being calculated and make modifications when a potential collision is detected, or simply take the finished route and go around making modifications..

I didn't realise Dora wasn't with us any more, I think that makes it doubly important to defend her scripting point out the good things that can be gleaned from studying it, since she isn't here to do that for herself.

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

  • 3 weeks later...

@Profaitchikenz Haiku thanks for your reply about placing object keys in a list. I presume this cant be added to llSetKeyframedMotion as it has no option to specify keys  and acts on the object its in  I am curious how you are doing this  in KFM my animesh will move me if i am seated on it however. I need a particle ball  or saddle to keep pace . I don't really understand this bezier code stuff and how it can be implemented into my next coordinate proc point and rotation plotter to give smooth movement in an animalistic flight pattern.

I did notice you said use LLSetLinkPrimitiveParamsFast but not sure how stable it will be if its sending out  particle fire to a position at same time 

I am sorry i dont understand this can you help? D x

Edited by VirtualKitten
Link to comment
Share on other sites

14 minutes ago, VirtualKitten said:

@Profaitchikenz Haiku thanks for your reply about placing object keys in a list. I presume this cant be added to llSetKeyframedMotion as it has no option to specify keys  and acts on the object its in 

Not me, I've not mentioned object keys. are you perhaps referring to this?

 

On 10/30/2021 at 11:49 PM, Profaitchikenz Haiku said:

Dora's code combines a lot of delta position and delta rotations plus time and plays them all as one large list, but that's simply because the code is illustrating something. It can be changed.

If you made up the list as a separate list you could then take the list contents three at a time, as a sub-list to give to the KFM command, start the KFM, set a timer for when you expect the KFM to have finished, and if moving_end hasn't triggered, the timer itself calls the KFM([],[]) to stop any remaining motion.

You are right in that the Keyframedmotion command acts only on the object it is in, but it can be given the essential data from an external source, so what I envisaged is you used Dora's code to generate a large list of position, rotation and time values 

 

On 10/30/2021 at 11:49 PM, Profaitchikenz Haiku said:

If you made up the list as a separate list you could then take the list contents three at a time, as a sub-list to give to the KFM command,

What I was suggesting is that you modify Dora's code so that instead of generating a list of position and rotation deltas with times and whizzing off to show you what they looked like, it generated a list of actual region position and rotations and chatted that out or gave it to a prim that acts as a server, which stores it as a list ready for when the animal wants to start moving. Or, you could capture the output in a notecard and give that notecard to the server.

What you would get is a list of position1, rotation1, time1, position2, rotation2, time2,... and so on

You then work through this list taking them three at a time, and doing this:

KFM_Position = list_position1 - current position

KFM_Rotation = list_rotation1 / current rotation

KFM_time = list_time1

the prim then chats those three KFM_values to the animal, which does a KFM movement using them.

At moving_end, the animal does two things:

Sends the server it's new Position and Rotation.

Does any linkset re-arrangements.

The server now increments the index to point to position2, rotation2, time2 and goes through the same process, sending three values to the animal, waiting for it to send back it's new position and rotation.

 

Link to comment
Share on other sites

Hmm not really the answer I needed @Profaitchikenz Haiku thanks but what I said above my procedure  returns coordinates and rotation in a list to  llSetKeyframedMotion( as described in my post above with next move position this uses two forms . Let me try and explain via my code:

vector nextCoordinates(integer TYPE) {
    float driftRange = llFrand(MOVEMENT_RANGE);
    float circle_radius = 5.0;
    float a = llFrand(TWO_PI);
    float b = llFrand(TWO_PI);
    float c = llFrand(PI);
    if(TYPE == 2) return <iPos.x + driftRange, iPos.y + llFrand(MOVEMENT_RANGE), iPos.z>;
    if(TYPE == 4) return <iPos.x + driftRange * llCos(a), iPos.y + driftRange * llSin(b), iPos.z>;
   // if(TYPE == 8) return iPos + <driftRange * llCos(a) * llCos(b), driftRange * llCos(a) * llSin(b), driftRange * llSin(a)>;
}

and

 

integer __loadCoordinates(vector l_pos ,vector l_dest,rotation l_rot, float time , integer target) 
{
             time = (float)llVecDist(l_pos,(l_pos+l_dest))/TARGET_AFFINITY; 
             if(time < 0.2) time = .3;
             lCoordinate = [];
             lCoordinate +=l_dest; 
             lCoordinate += l_rot; // * any new rot relative to current position.
             lCoordinate +=time;
             pos =  l_pos+l_dest;
             vLastRelPos =pos;
             lastRot = llGetRot();
             iStartTimer = TRUE;
             target = __llTargetRemove(target);
             return  llTarget(pos, 50.0);
}
                                                     
_llWanderWithinKeyframe (vector home,  vector range, list c) {
    pos = llGetPos();
    iPos = llGetPos();
    list TYPE = [ 2,4,8]; //4,8,16,32,64,128,126];
  
   
    integer vAreaParcelsqm = llList2Integer(llGetParcelDetails(iPos,[PARCEL_DETAILS_AREA]),0);
    vector area = < vAreaParcelsqm -5, vAreaParcelsqm -5,vAreaParcelsqm -5>;
    vector bot = my_home-WANDER_RANGE  /2;
    vector top = my_home+WANDER_RANGE/2;
   // llInstantMessage(llGetOwner(),"bot:"+(string)bot+"top:"+(string)top);
    if(TYPE_COUNT++ > 1) { TYPE_COUNT = 0; TYPE_FLIGHT = (integer)(llFrand(7.0)); iPos = llGetPos();} 
    if( TYPE_FLIGHT == 0) {TYPE_FLIGHT  == 8; iPos = llGetPos();}
    integer i = 20;
    timer_wander = llGetUnixTime();
    do {
        i--;
        nex_pos = clipRegion(bot,top,nextCoordinates(llList2Integer(TYPE,TYPE_FLIGHT) )); 
    }  while(checkInside2(bot,top,nex_pos) == FALSE && i>0);
      
    if(nex_pos != ZERO_VECTOR && i!=0) {
         //Debug("_llWanderWithinKeyframe: Moving : "+ (string)nex_pos); 
         target_wander_id = __loadCoordinates(iPos,(vector)(nex_pos-iPos),NormRot(llGetRot()), llFrand(FUDGE)+ (nex_pos.z)/SPEED,target_wander_id);
        
    }
}

 

Called from above I did this so I could change animations and possible move a particle ball with  LLSetLinkPrimitiveParamsFast(

I was trying to Understand Dora's  bezier movement code so it would get next points in this way as an option in nextCoordinates()  does this make more sense?

 

I was hopeful Dora's Bezier method would make better flight pattern which it does by loading everything into a KFM array I think bt this is doe though a menu  and I want a random flight path.

 

 

Edited by VirtualKitten
Link to comment
Share on other sites

I really wanted Doras thing to do next point  like mine  as this one doesnt really give good points that flow  or keep animal upright https://i.gyazo.com/2afce31b39e9e9e22c1b8150ff4d555f.mp4 oddly my hair flies off too Laughs :D if i narrow box area on z  i.e vector WANDER_RANGE = <15.0,15.0,1.0>; he behaves but doesn't move far https://i.gyazo.com/0d9340e4142f9375836897fdc3719a61.mp4 this is how far I got he seems to get stuck in a get stuck routine as he has not moved of spot.

Edited by VirtualKitten
added video
Link to comment
Share on other sites

The part of Doras script that is most impressive is how she generates a  path that shows purpose and intent. Creating points along a bezier curv e is simply a matter of implementing a formula, constraining them within bounds a matter of setting range and limits, it's the coice of direction to move in that needs careful thought.

 

With the exception of gas molecules in  sealed containers or certain literary heroes in contrived circumstancs, most of nature's creatures do not go blindly to and fro with no apparent reason:

 

286726788_randomness-Copy.png.29b4d6fcd092fdb5cb841b54831aef61.png

for "sxeprion" read "exception". I can only wonder at my fingers....

1984942758_Daisy01-Copy.png.94475cbacf715c148fae23af8cad46ed.png

Sharp changes in course are rare and suggest something out of the ordinary

 

 

113775343_Daisy02-Copy.png.e749b0e082c981611a46bf2dcacbfb40.png

 

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

Hmm, is that helpful idk did you see my videos prof ?  I have DoCheckMakingProgress(llGetPos())

You seem to suggest i need to write AI this is never going to fit in a script

Which is

integer DoCheckMakingProgress(vector l) {
    
    //Debug("Checking we are moving");
    if(LOC == ZERO_VECTOR) LOC = llGetPos();
    Tolerances += (llVecDist(l,LOC));
    if( llGetListLength(Tolerances) < 10 ) return TRUE;
        else if((llListStatistics(LIST_STAT_SUM , Tolerances )/10) == llList2Float(Tolerances,0)) { time_unit = llGetUnixTime();LOC = ZERO_VECTOR; Tolerances=[]; return FALSE; }
    LOC = ZERO_VECTOR;
    Tolerances=[];
    return TRUE;
    
} 

 But it gets stuck not moving so I don't think my points work for next point very well and Doras code is to complicated for little me .

Edited by VirtualKitten
Link to comment
Share on other sites

No, I'm afraid I didn't. I'm not a very videoish person, more of a bookish one.

My point is that Dora's code example gives you everything you need to work with on the task as you initially stated it. You can modify it to turn off KFM and simply get a list of points to feed to your beast in whatever manner works for you. The main thing is it is one of the best simulations of animal pathing you're likely to find.

To do what I think you're now saying you want to do, I think it's a case of determining the general direction for the next movement, determining the actual amount of moment in that general direction and then getting an actual position and rotation from a call to a routine that generates a bezier curve between From and To.

As far as siting on the beast or keeping your hair on I'm afraid that's best addressed after you get the pathing working.

Link to comment
Share on other sites

Yes but there are three bits of code :

first is ok 

vector randomP()
{
    return llGetPos() + < range.x*(llFrand( 2.0)-1.0), range.y*(llFrand( 2.0)-1.0), range.z*(llFrand( 2.0)-1.0) > * homeRot;
}

 

Next not so good idk no list as you suggested what does coefisFirst, coefisNext, coefisLast actually mean ? and how does fit in with other code at bottom i found on net for bezier curves

vector P1;
vector P2;
vector P3;
vector Q1;
vector Q2;
vector Q3;

vector randomP()
{
    return llGetPos() + < range.x*(llFrand( 2.0)-1.0), range.y*(llFrand( 2.0)-1.0), range.z*(llFrand( 2.0)-1.0) > * homeRot;
}

coefisFirst()
{
    P0 = llGetPos();
    Po0 = P0;
    P3 = randomP();
    P2 = randomP();
    P1 = randomP();
    Po1 = P1;
    Q1 = 3.0*P1-3.0*P0;
    Q2 = 3.0*P0-6.0*P1+3.0*P2;
    Q3 = 3.0*P1-P0+P3-3.0*P2;
}
 
coefisNext()
{
    P0 = P3;
    P1 = 2.0*P3-P2;
    P3 = randomP();
    P2 = randomP();
    Q1 = 3.0*P1-3.0*P0;
    Q2 = 3.0*P0-6.0*P1+3.0*P2;
    Q3 = 3.0*P1-P0+P3-3.0*P2;
}
coefisLast()
{
    P0 = P3;
    P1 = 2.0*P3-P2;
    P2 = 2.0*Po0-Po1;
    P3 = Po0;
    Q1 = 3.0*P1-3.0*P0;
    Q2 = 3.0*P0-6.0*P1+3.0*P2;
    Q3 = 3.0*P1-P0+P3-3.0*P2;
}

vector Bez( float x ) { return P0 + (Q1 + (Q2 + Q3*x)*x)*x; }
 
vector dBez( float x ) { return Q1 + (2.0*Q2 + 3.0*Q3*x)*x; }
 
vector sBez( float x ) { return 2.0*Q2 + 6.0*Q3*x; }

//Three control point Bezier interpolation
//mu ranges from 0 to 1, start to end of the curve 
// current_mu= i * increment; in i loop, where increment = 1.0 / gNumSegments;
vector bezier3(vector p1, vector p2, vector p3, float mu)
{
    float mum1;
    float mum12;
 
    float mu2;
    vector p;
  
     mu2 = mu * mu;
     mum1 = 1 - mu;
     mum12 = mum1 * mum1;
     
     // p= (1-mu)^2 p1 + 2*mu*(1-mu)*p2 + mu^2 * p3
     p.x = p1.x * mum12 + 2. * p2.x * mum1 * mu + p3.x * mu2;
     p.y = p1.y * mum12 + 2. * p2.y * mum1 * mu + p3.y * mu2;
     p.z = p1.z * mum12 + 2. * p2.z * mum1 * mu + p3.z * mu2;
  
     return(p);
}

I don't understand why this is not scripted anywhere most of the code online  out there doesn't do this at all .

 

Edited by VirtualKitten
Link to comment
Share on other sites

35 minutes ago, VirtualKitten said:

I dont understand why this is not scripted anywhere most of the code out there doesn't do this at all 

I'm struggling to see the relevance, you say this is not scripted anywhere but it obviously is, they are support routines called as required.

I mentioned way back in this thread hat there are three parts to the production of the path: an initial move outwards from the starting point, a second stage of going from where stage one ends up to a new ending point, and a final third stage that goes from where stage 2 ended to where stage 1 began, thus closing the path.

If you don't want to go around in a closed path then you can dispense with stages 2 and 3, (drop coefisnext and coefislast) and simply generate a bezier curve from where you are now to some now position. At the new position, repeat.

So in Dora's code, look ate where coefisFirst is called, and from there look at all the subsequent calls up to but not including coefisNext and this will show you how those support routines are used to generate a path away from an initial position.

 

ETA

Just got inworld and had a look through Dora's code. of the routines you have posted, only sBez() is never called, Bez() and dBez() are both called when creating a new set of curves.

 

Missing from your snippet is a function Vec2Rot() which is called with dBez as the input and gives a rotation that is the new direction in which the cube is to face and therefore necessary for your animal to turn in the appropriate direction as it goes along the path.

Thinking about your hair flying off, what springs to mind is that your avatar is being made to move far too quickly and the attachments are then taking a while to get repositioned, are you perhaps trying to move yourself around by sitting on a pose ball and using llSetLinkPrimitiveparamsFast to shift the ball and yourself as an entire linkset to a new position?

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 842 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...