1#include "RailInterpolator.hh"
3#include "game/field/RailManager.hh"
8RailInterpolator::RailInterpolator(f32 speed, u32 idx)
9 : m_points(
RailManager::Instance()->rail(idx)->points()), m_currVel(0.0f) {
11 auto *rail = RailManager::Instance()->rail(idx);
12 m_pointCount = rail->pointCount();
13 m_isOscillating = rail->isOscillating();
18RailInterpolator::~RailInterpolator() =
default;
21const EGG::Vector3f &RailInterpolator::floorNrm(
size_t idx)
const {
22 return RailManager::Instance()->rail(m_railIdx)->floorNrm(idx);
26f32 RailInterpolator::railLength()
const {
27 return RailManager::Instance()->rail(m_railIdx)->getPathLength();
31void RailInterpolator::updateVel() {
33 setCurrVel((1.0f - t) * m_prevPointVel + t * m_nextPointVel);
37void RailInterpolator::calcVelocities() {
38 m_prevPointVel =
static_cast<f32
>(m_points[m_currPointIdx].setting[0]);
40 shouldChangeDirection() ? 0.0f :
static_cast<f32
>(m_points[m_nextPointIdx].setting[0]);
42 if (m_prevPointVel == 0.0f) {
43 m_prevPointVel = m_speed;
46 if (m_nextPointVel == 0.0f) {
47 m_nextPointVel = m_speed;
53 if (shouldChangeDirection()) {
54 ASSERT(m_segmentT == 0.0f);
59bool RailInterpolator::shouldChangeDirection()
const {
60 if (!m_isOscillating) {
61 return m_pointCount == m_nextPointIdx;
64 return m_movementDirectionForward ? m_nextPointIdx == m_pointCount : m_nextPointIdx == -1;
68void RailInterpolator::calcDirectionChange() {
69 if (!m_isOscillating) {
73 if (m_movementDirectionForward) {
79 m_movementDirectionForward = !m_movementDirectionForward;
82void RailInterpolator::calcNextIndices() {
83 if (m_movementDirectionForward) {
91 if (!m_isOscillating) {
92 if (m_nextPointIdx == m_pointCount) {
96 if (m_currPointIdx == m_pointCount) {
103RailLinearInterpolator::RailLinearInterpolator(f32 speed, u32 idx) : RailInterpolator(speed, idx) {
104 m_transitions = RailManager::Instance()->rail(m_railIdx)->getLinearTransitions();
109RailLinearInterpolator::~RailLinearInterpolator() =
default;
112void RailLinearInterpolator::init(f32 t, u32 idx) {
114 m_currPointIdx = idx;
116 bool isLastPoint = (
static_cast<u16
>(idx) == m_pointCount - 1);
117 m_nextPointIdx = isLastPoint ? idx - 1 : idx + 1;
118 m_movementDirectionForward = isLastPoint ? !m_isOscillating :
true;
120 m_curPos = m_points[m_currPointIdx].pos;
121 m_currentDirection = m_points[m_nextPointIdx].pos - m_curPos;
122 m_curTangentDir = m_currentDirection;
123 m_curTangentDir.normalise2();
125 m_prevPointVel = m_speed;
126 m_nextPointVel = m_speed;
128 m_usePerPointVelocities =
false;
129 m_segmentLength = m_currentDirection.length();
130 m_currSegmentVel = m_speed / m_segmentLength;
134RailInterpolator::Status RailLinearInterpolator::calc() {
136 m_curPos = m_points[m_pointCount - 1].pos;
138 return Status::ChangingDirection;
141 if (m_usePerPointVelocities) {
145 m_segmentT += m_currSegmentVel;
146 m_curPos = lerp(m_segmentT, m_currPointIdx, m_nextPointIdx);
148 if (m_segmentT <= 1.0f) {
149 return Status::InProgress;
152 Status status = Status::SegmentEnd;
156 if (shouldChangeDirection()) {
157 status = Status::ChangingDirection;
159 calcDirectionChange();
162 m_currentDirection = m_points[m_nextPointIdx].pos - m_points[m_currPointIdx].pos;
163 m_segmentLength = m_currentDirection.length();
164 m_curTangentDir = m_currentDirection;
165 m_currSegmentVel = m_currVel / m_segmentLength;
166 m_curTangentDir.normalise2();
172void RailLinearInterpolator::setCurrVel(f32 speed) {
174 m_currSegmentVel = m_currVel / m_segmentLength;
178void RailLinearInterpolator::evalCubicBezierOnPath(f32 t, EGG::Vector3f &currDir,
179 EGG::Vector3f &curTangentDir) {
183 getPathLocation(t, currIdx, len);
185 s16 nextIdx = currIdx + 1;
186 if (nextIdx == m_pointCount) {
190 currDir = m_points[currIdx].pos * (1.0f - len) + m_points[nextIdx].pos * len;
191 curTangentDir = m_transitions[currIdx].m_dir;
195void RailLinearInterpolator::getPathLocation(f32 t, s16 &idx, f32 &len) {
196 if (!m_movementDirectionForward) {
200 f32 dist = m_segmentT * m_transitions[m_currPointIdx].m_length;
202 idx = m_currPointIdx;
203 len = (dist - t) * m_transitions[m_currPointIdx].m_lengthInv;
207 for (s32 i = 0; i < m_pointCount; ++i) {
208 s32 currIdx = m_currPointIdx - 1;
210 currIdx = m_pointCount - 1;
215 currIdx += m_pointCount;
218 dist += m_transitions[currIdx].m_length;
221 len = (dist - t) * m_transitions[currIdx].m_lengthInv;
228void RailLinearInterpolator::calcNextSegment() {
234 if (shouldChangeDirection()) {
237 f32 prevDirLength = m_segmentLength;
238 m_currentDirection = m_points[m_nextPointIdx].pos - m_points[m_currPointIdx].pos;
239 m_segmentLength = m_currentDirection.length();
240 m_segmentT = ((m_segmentT - 1.0f) * prevDirLength) / m_segmentLength;
242 if (m_segmentT > 1.0f) {
247 if (m_usePerPointVelocities) {
253EGG::Vector3f RailLinearInterpolator::lerp(f32 t, u32 currIdx, u32 nextIdx)
const {
254 return m_points[currIdx].pos * (1.0f - t) + m_points[nextIdx].pos * t;
258RailSmoothInterpolator::RailSmoothInterpolator(f32 speed, u32 idx) : RailInterpolator(speed, idx) {
259 auto *rail = RailManager::Instance()->rail(m_railIdx);
260 m_transitions = rail->getSplineTransitions();
261 m_pathPercentages = rail->getPathPercentages();
267RailSmoothInterpolator::~RailSmoothInterpolator() =
default;
270void RailSmoothInterpolator::init(f32 t, u32 idx) {
273 m_currPointIdx = idx;
275 if (idx ==
static_cast<u32
>(m_pointCount - 1) && m_isOscillating) {
276 m_nextPointIdx = m_currPointIdx - 1;
277 m_movementDirectionForward =
false;
278 m_curTangentDir = calcCubicBezierTangentDir(m_segmentT, m_transitions[m_currPointIdx]);
279 m_currSegmentVel = m_currVel * m_transitions[m_nextPointIdx].m_lengthInv;
281 m_nextPointIdx = idx + 1 == m_pointCount ? 0 : idx + 1;
282 m_movementDirectionForward =
true;
283 m_curTangentDir = calcCubicBezierTangentDir(m_segmentT, m_transitions[m_currPointIdx]);
284 m_currSegmentVel = m_currVel * m_transitions[m_currPointIdx].m_lengthInv;
287 m_curPos = m_points[m_currPointIdx].pos;
288 m_prevPos = m_points[m_currPointIdx].pos;
290 m_prevPointVel = m_speed;
291 m_nextPointVel = m_speed;
294 m_usePerPointVelocities =
false;
295 m_velocityPending =
false;
299RailInterpolator::Status RailSmoothInterpolator::calc() {
301 m_curPos = m_transitions[m_pointCount - 2].m_p3;
303 return Status::ChangingDirection;
306 if (m_usePerPointVelocities) {
310 m_prevPos = m_curPos;
312 f32 t = m_movementDirectionForward ? calcT(m_segmentT) : calcT(1.0f - m_segmentT);
314 calcCubicBezier(t, m_currPointIdx, m_nextPointIdx, m_curPos, m_curTangentDir);
316 m_velocityPending =
true;
317 m_segmentT += m_currSegmentVel;
319 if (m_segmentT <= 1.0f) {
320 return Status::InProgress;
323 Status status = Status::SegmentEnd;
327 if (shouldChangeDirection()) {
328 status = Status::ChangingDirection;
330 calcDirectionChange();
333 if (m_movementDirectionForward) {
334 m_currSegmentVel = m_currVel * m_transitions[m_currPointIdx].m_lengthInv;
336 m_currSegmentVel = m_currVel * m_transitions[m_nextPointIdx].m_lengthInv;
343void RailSmoothInterpolator::setCurrVel(f32 speed) {
346 if (m_movementDirectionForward) {
347 m_currSegmentVel = speed * m_transitions[m_currPointIdx].m_lengthInv;
349 m_currSegmentVel = speed * m_transitions[m_nextPointIdx].m_lengthInv;
354void RailSmoothInterpolator::evalCubicBezierOnPath(f32 t, EGG::Vector3f &currDir,
355 EGG::Vector3f &curTangentDir) {
359 getPathLocation(t, currIdx, len);
361 s16 nextIdx = currIdx + 1;
362 if (nextIdx == m_pointCount) {
366 len = m_movementDirectionForward ? calcT(len) : calcT(1.0f - len);
368 calcCubicBezier(len, currIdx, nextIdx, currDir, curTangentDir);
372void RailSmoothInterpolator::getPathLocation(f32 t, s16 &idx, f32 &len) {
373 if (!m_movementDirectionForward) {
377 f32 dist = m_segmentT * m_transitions[m_currPointIdx].m_length;
379 idx = m_currPointIdx;
380 len = (dist - t) * m_transitions[m_currPointIdx].m_lengthInv;
384 for (s32 i = 0; i < m_pointCount; ++i) {
385 s32 currIdx = m_currPointIdx - 1;
387 currIdx = m_pointCount - 1;
392 currIdx += m_pointCount;
395 dist += m_transitions[currIdx].m_length;
398 len = (dist - t) * m_transitions[currIdx].m_lengthInv;
405void RailSmoothInterpolator::calcCubicBezier(f32 t, u32 currIdx, u32 nextIdx, EGG::Vector3f &pos,
406 EGG::Vector3f &dir)
const {
407 const auto &transition =
408 m_movementDirectionForward ? m_transitions[currIdx] : m_transitions[nextIdx];
410 pos = calcCubicBezierPos(t, transition);
411 dir = calcCubicBezierTangentDir(t, transition);
415EGG::Vector3f RailSmoothInterpolator::calcCubicBezierPos(f32 t,
416 const RailSplineTransition &trans)
const {
419 EGG::Vector3f res = trans.m_p0 * (dt * dt * dt);
420 res += trans.m_p1 * (3.0f * t * (dt * dt));
421 res += trans.m_p2 * (3.0f * (t * t) * dt);
422 res += trans.m_p3 * (t * t * t);
428EGG::Vector3f RailSmoothInterpolator::calcCubicBezierTangentDir(f32 t,
429 const RailSplineTransition &trans)
const {
430 EGG::Vector3f c1 = trans.m_p0 * -1.0f + trans.m_p1 * 3.0f - (trans.m_p2 * 3.0f) + trans.m_p3;
431 EGG::Vector3f c2 = trans.m_p0 * 3.0f - trans.m_p1 * 6.0f + trans.m_p2 * 3.0f;
432 EGG::Vector3f c3 = trans.m_p0 * -3.0f + trans.m_p1 * 3.0f;
433 EGG::Vector3f ret = c1 * 3.0f * (t * t) + c2 * 2.0f * t + c3;
437 if (!m_movementDirectionForward) {
448 constexpr u32 ESTIMATOR_SAMPLE_COUNT = RailSpline::getEstimatorSampleCount();
449 constexpr f32 ESTIMATOR_STEP = RailSpline::getEstimatorStep();
451 u16 sampleIdx = m_movementDirectionForward ? m_currPointIdx * ESTIMATOR_SAMPLE_COUNT :
452 m_nextPointIdx * ESTIMATOR_SAMPLE_COUNT;
454 for (u16 i = 0; i < ESTIMATOR_SAMPLE_COUNT - 1; ++i) {
455 f32 currPercent = m_pathPercentages[sampleIdx + i];
456 f32 nextPercent = m_pathPercentages[sampleIdx + i + 1];
458 if (currPercent <= t && nextPercent > t) {
459 f32 delta = (t - currPercent) / (nextPercent - currPercent);
460 return ESTIMATOR_STEP *
static_cast<f32
>(i) + ESTIMATOR_STEP * delta;
464 f32 lastPercent = m_pathPercentages[sampleIdx + ESTIMATOR_SAMPLE_COUNT - 1];
466 if (lastPercent <= t && 1.0f >= t) {
467 u16 idx = ESTIMATOR_SAMPLE_COUNT - 1;
468 f32 delta = (t - lastPercent) / (1.0f - lastPercent);
469 return ESTIMATOR_STEP *
static_cast<f32
>(idx) + ESTIMATOR_STEP * delta;
476void RailSmoothInterpolator::calcNextSegment() {
477 f32 nextT = m_segmentT - 1.0f;
479 if (m_isOscillating) {
480 if (m_nextPointIdx == 0 || m_nextPointIdx == m_pointCount - 1) {
482 }
else if (m_movementDirectionForward) {
483 m_segmentT = nextT * m_transitions[m_currPointIdx].m_length *
484 m_transitions[m_nextPointIdx].m_lengthInv;
486 m_segmentT = nextT * m_transitions[m_currPointIdx - 1].m_length *
487 m_transitions[m_nextPointIdx - 1].m_lengthInv;
490 m_segmentT = nextT * m_transitions[m_currPointIdx].m_length *
491 m_transitions[m_nextPointIdx].m_lengthInv;
494 if (m_segmentT > 1.0f) {
500 if (m_usePerPointVelocities) {