float phasepos;
float nexttick;
vector delta;
+ vector delta2;
vector veloc;
vector nextpos;
+ delta = self.destvec;
+ delta2 = self.destvec2;
if(time < self.animstate_endtime) {
- delta = self.destvec;
nexttick = time + sys_frametime;
- if(nexttick < self.animstate_endtime) {
- traveltime = self.animstate_endtime - self.animstate_starttime;
- phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
+ traveltime = self.animstate_endtime - self.animstate_starttime;
+ phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
+ if(self.platmovetype != 1)
+ {
phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
phasepos = phasepos + 1; // correct range to [0, 2]
phasepos = phasepos / 2; // correct range to [0, 1]
- nextpos = self.origin + (delta * phasepos);
+ }
+ nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
+ // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
+ if(nexttick < self.animstate_endtime) {
veloc = nextpos - self.owner.origin;
veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
-
} else {
veloc = self.finaldest - self.owner.origin;
veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
self.owner.velocity = veloc;
self.nextthink = nexttick;
} else {
+ // derivative: delta + 2 * delta2 (e.g. for angle positioning)
oldself = self;
self.owner.think = self.think1;
self = self.owner;
}
}
-void SUB_CalcMove (vector tdest, float tspeed, void() func)
+void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest)
+{
+ // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
+ // 2 * control * t - 2 * control * t * t + dest * t * t
+ // 2 * control * t + (dest - 2 * control) * t * t
+
+ controller.origin = org; // starting point
+ control -= org;
+ dest -= org;
+
+ controller.destvec = 2 * control; // control point
+ controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
+}
+
+void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
+{
+ // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
+ // 2 * control * t - 2 * control * t * t + dest * t * t
+ // 2 * control * t + (dest - 2 * control) * t * t
+
+ controller.origin = org; // starting point
+ dest -= org;
+
+ controller.destvec = dest; // end point
+ controller.destvec2 = '0 0 0';
+}
+
+void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() func)
{
- vector delta;
float traveltime;
entity controller;
self.finaldest = tdest;
self.think = SUB_CalcMoveDone;
- if (tdest == self.origin)
- {
- self.velocity = '0 0 0';
- self.nextthink = self.ltime + 0.1;
- return;
- }
+ if(tspeed > 0) // positive: start speed
+ traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
+ else // negative: end speed
+ traveltime = 2 * vlen(tcontrol - tdest) / -tspeed;
- delta = tdest - self.origin;
- traveltime = vlen (delta) / tspeed;
-
- if (traveltime < 0.1)
+ if (traveltime < 0.1) // useless anim
{
self.velocity = '0 0 0';
self.nextthink = self.ltime + 0.1;
return;
}
- // Very short animations don't really show off the effect
- // of controlled animation, so let's just use linear movement.
- // Alternatively entities can choose to specify non-controlled movement.
- // The only currently implemented alternative movement is linear (value 1)
- if (traveltime < 0.15 || self.platmovetype == 1)
- {
- self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
- self.nextthink = self.ltime + traveltime;
- return;
- }
-
controller = spawn();
controller.classname = "SUB_CalcMove_controller";
controller.owner = self;
- controller.origin = self.origin; // starting point
+ controller.platmovetype = self.platmovetype;
+ SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
- controller.destvec = delta;
controller.animstate_starttime = time;
controller.animstate_endtime = time + traveltime;
controller.think = SUB_CalcMove_controller_think;
self = self.owner;
}
+void SUB_CalcMove (vector tdest, float tspeed, void() func)
+{
+ vector delta;
+ float traveltime;
+
+ if (!tspeed)
+ objerror ("No speed is defined!");
+
+ self.think1 = func;
+ self.finaldest = tdest;
+ self.think = SUB_CalcMoveDone;
+
+ if (tdest == self.origin)
+ {
+ self.velocity = '0 0 0';
+ self.nextthink = self.ltime + 0.1;
+ return;
+ }
+
+ delta = tdest - self.origin;
+ traveltime = vlen (delta) / tspeed;
+
+ // Very short animations don't really show off the effect
+ // of controlled animation, so let's just use linear movement.
+ // Alternatively entities can choose to specify non-controlled movement.
+ // The only currently implemented alternative movement is linear (value 1)
+ if (traveltime < 0.15 || self.platmovetype == 1)
+ {
+ self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
+ self.nextthink = self.ltime + traveltime;
+ return;
+ }
+
+ // now just run like a bezier curve...
+ SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeed, func);
+}
+
void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
{
entity oldself;