CODE
// Bezier curve trajectory generator
//
// outputs series of position/rotation pairs describing a smooth, accelerating/decelerating trajectory between two positions
integer running;
vector va;
vector vb;
vector vta;
vector vtb;
rotation ra;
rotation rb;
vector vt;
rotation rt;
float dist;
float angle;
vector axis;
integer steps;
integer mul = 0;
integer max = 8;
float off;
float offa;
float offb;
float speed;
float next_speed;
float t;
float u;
float rate = 0.05;
float rad = 0.425;
float corr = 1.9;
float banking = -3.14159;
float tilting = 3.0; // one fourth the vertical tilt of the trajectory, set to 0.0 for full tilt, -2.0 for reverse
move()
{
llSetTimerEvent(rate);
va = llGetRootPosition() + llGetRegionCorner();
ra = llGetRootRotation();
vta = llRot2Fwd(ra);
vtb = llRot2Fwd(rb);
off = llSqrt(speed / next_speed);
dist = rad * llVecMag(vb - va);
vta = (dist * off * vta);
vtb = (dist * vtb / off);
dist = corr * (llVecMag(vta) + llVecMag(vta - vtb) + llVecMag(vtb));
vta = va + vta;
vtb = vb - vtb;
steps = llCeil(dist / (speed + next_speed));
float f = 1.0 / steps;
offa = f * off;
offb = f / off;
t = 0.0;
u = 1.0;
}
flat()
{
rt = llGetRootRotation();
vt = llRot2Fwd(rt);
va = <vt.x, vt.y, .0>;
rt = llRotBetween(<1,0,0>, va) * llRotBetween(va, vt);
mul = (mul + 1) % max;
vt = llGetRootPosition();
// now t and rt contain the upright static position and rotation for stopping
}
next_move()
{
vector v;
vector vh;
vector vo;
t = t + (t * offb + u * offa);
u = 1.0 - t;
mul = (mul + 1) % max;
vector corner = llGetRegionCorner();
v = vt;
vo = llRot2Left(rt);
vt = (u*u*u*(va - corner) + 3*t*u*u*(vta - corner) + 3*t*t*u*(vtb - corner) + t*t*t*(vb - corner));
v = (vt - v);
vh = <v.x, v.y, .0>;
float f = v * vo;
if (llFabs(f) > 0.0078125)
rt = llEuler2Rot(<banking * (v * vo), .0, .0>) * llRotBetween(<1,0,0>, vh) * llRotBetween(vh, v + (tilting * vh));
else rt = llRotBetween(<1,0,0>, vh) * llRotBetween(vh, v + (tilting * vh));
}
init(vector target_position, rotation target_orientation, float target_velocity)
{
rb = target_orientation;
vb = target_position + llGetRegionCorner();
next_speed = llFabs(target_velocity);
if (next_speed < 0.003125) next_speed = 0.003125;
move();
}
default
{
state_entry()
{
speed = 0.003125;
next_speed = 0.003125;
running = TRUE;
}
timer()
{
next_move();
// now your next position and rotation to move to are recorded in vt (in region coords) and rt
// PUT YOU MOVEMENT CODE HERE (llSetPos or anything else)
if (t > 1.0)
{
// done with the current step
llSetTimerEvent(0.0);
speed = next_speed;
// call flat() to get the static position and rotation (= upright) in vt and rt if you are stopping
}
}
}
To use, call the init() function with your desired next position, rotation and speed to attain smoothly. It will set the parameters (va, ra and vta for the starting position/rotation/speed, vb, rb and vtb for the targetted position/rotation/speed, and a few more parameters) by calling the move() function, then will run the timer which will call next_move(), the function that generates the interpolated steps. Add your own code in the timer to do whatever needs be done with the generated position/rotation pairs.
I have a version of this that interfaces with Multimove and reads from notecards so you can make automated vehicles, if needed.