1#include "RailInterpolator.hh"
3#include "game/field/RailManager.hh"
8RailInterpolator::RailInterpolator(f32 speed, u32 idx) : m_currVel(0.0f) {
10 auto *rail = RailManager::Instance()->rail(idx);
11 m_pointCount = rail->pointCount();
12 m_points = rail->points();
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_currSegmentVel = m_speed / m_currentDirection.length();
133RailInterpolator::Status RailLinearInterpolator::calc() {
135 m_curPos = m_points[m_pointCount - 1].pos;
137 return Status::ChangingDirection;
140 if (m_usePerPointVelocities) {
144 m_segmentT += m_currSegmentVel;
145 m_curPos = lerp(m_segmentT, m_currPointIdx, m_nextPointIdx);
147 if (m_segmentT <= 1.0f) {
148 return Status::InProgress;
151 Status status = Status::SegmentEnd;
155 if (shouldChangeDirection()) {
156 status = Status::ChangingDirection;
158 calcDirectionChange();
161 m_currentDirection = m_points[m_nextPointIdx].pos - m_points[m_currPointIdx].pos;
162 m_curTangentDir = m_currentDirection;
163 m_currSegmentVel = m_currVel / m_currentDirection.length();
164 m_curTangentDir.normalise2();
170void RailLinearInterpolator::setCurrVel(f32 speed) {
172 m_currSegmentVel = m_currVel / m_currentDirection.length();
176void RailLinearInterpolator::evalCubicBezierOnPath(f32 t,
EGG::Vector3f &currDir,
181 getPathLocation(t, currIdx, len);
183 s16 nextIdx = currIdx + 1;
184 if (nextIdx == m_pointCount) {
188 currDir = m_points[currIdx].pos * (1.0f - len) + m_points[nextIdx].pos * len;
189 curTangentDir = m_transitions[currIdx].m_dir;
193void RailLinearInterpolator::getPathLocation(f32 t, s16 &idx, f32 &len) {
194 if (!m_movementDirectionForward) {
198 f32 dist = m_segmentT * m_transitions[m_currPointIdx].m_length;
200 idx = m_currPointIdx;
201 len = (dist - t) * m_transitions[m_currPointIdx].m_lengthInv;
205 for (s32 i = 0; i < m_pointCount; ++i) {
206 s32 currIdx = m_currPointIdx - 1;
208 currIdx = m_pointCount - 1;
213 currIdx += m_pointCount;
216 dist += m_transitions[currIdx].m_length;
219 len = (dist - t) * m_transitions[currIdx].m_lengthInv;
226void RailLinearInterpolator::calcNextSegment() {
232 if (shouldChangeDirection()) {
235 f32 prevDirLength = m_currentDirection.length();
236 m_currentDirection = m_points[m_nextPointIdx].pos - m_points[m_currPointIdx].pos;
237 m_segmentT = ((m_segmentT - 1.0f) * prevDirLength) / m_currentDirection.length();
239 if (m_segmentT > 1.0f) {
244 if (m_usePerPointVelocities) {
250EGG::Vector3f RailLinearInterpolator::lerp(f32 t, u32 currIdx, u32 nextIdx)
const {
251 return m_points[currIdx].pos * (1.0f - t) + m_points[nextIdx].pos * t;
255RailSmoothInterpolator::RailSmoothInterpolator(f32 speed, u32 idx) : RailInterpolator(speed, idx) {
256 auto *rail = RailManager::Instance()->rail(m_railIdx);
257 m_transitions = rail->getSplineTransitions();
258 m_estimatorSampleCount =
static_cast<u32
>(rail->getEstimatorSampleCount());
259 m_estimatorStep = rail->getEstimatorStep();
260 m_pathPercentages = rail->getPathPercentages();
266RailSmoothInterpolator::~RailSmoothInterpolator() =
default;
269void RailSmoothInterpolator::init(f32 t, u32 idx) {
272 m_currPointIdx = idx;
274 if (idx ==
static_cast<u32
>(m_pointCount - 1) && m_isOscillating) {
275 m_nextPointIdx = m_currPointIdx - 1;
276 m_movementDirectionForward =
false;
277 m_curTangentDir = calcCubicBezierTangentDir(m_segmentT, m_transitions[m_currPointIdx]);
278 m_currSegmentVel = m_currVel * m_transitions[m_nextPointIdx].m_lengthInv;
280 m_nextPointIdx = idx + 1 == m_pointCount ? 0 : idx + 1;
281 m_movementDirectionForward =
true;
282 m_curTangentDir = calcCubicBezierTangentDir(m_segmentT, m_transitions[m_currPointIdx]);
283 m_currSegmentVel = m_currVel * m_transitions[m_currPointIdx].m_lengthInv;
286 m_curPos = m_points[m_currPointIdx].pos;
287 m_prevPos = m_points[m_currPointIdx].pos;
289 m_prevPointVel = m_speed;
290 m_nextPointVel = m_speed;
293 m_usePerPointVelocities =
false;
297RailInterpolator::Status RailSmoothInterpolator::calc() {
299 m_curPos = m_transitions[m_pointCount - 2].m_p3;
301 return Status::ChangingDirection;
304 if (m_usePerPointVelocities) {
308 m_prevPos = m_curPos;
310 f32 t = m_movementDirectionForward ? calcT(m_segmentT) : calcT(1.0f - m_segmentT);
312 calcCubicBezier(t, m_currPointIdx, m_nextPointIdx, m_curPos, m_curTangentDir);
315 m_velocity = deltaPos.
length();
316 m_segmentT += m_currSegmentVel;
318 if (m_segmentT <= 1.0f) {
319 return Status::InProgress;
322 Status status = Status::SegmentEnd;
326 if (shouldChangeDirection()) {
327 status = Status::ChangingDirection;
329 calcDirectionChange();
332 if (m_movementDirectionForward) {
333 m_currSegmentVel = m_currVel * m_transitions[m_currPointIdx].m_lengthInv;
335 m_currSegmentVel = m_currVel * m_transitions[m_nextPointIdx].m_lengthInv;
342void RailSmoothInterpolator::setCurrVel(f32 speed) {
345 if (m_movementDirectionForward) {
346 m_currSegmentVel = speed * m_transitions[m_currPointIdx].m_lengthInv;
348 m_currSegmentVel = speed * m_transitions[m_nextPointIdx].m_lengthInv;
353void RailSmoothInterpolator::evalCubicBezierOnPath(f32 t,
EGG::Vector3f &currDir,
358 getPathLocation(t, currIdx, len);
360 s16 nextIdx = currIdx + 1;
361 if (nextIdx == m_pointCount) {
365 len = m_movementDirectionForward ? calcT(len) : calcT(1.0f - len);
367 calcCubicBezier(len, currIdx, nextIdx, currDir, curTangentDir);
371void RailSmoothInterpolator::getPathLocation(f32 t, s16 &idx, f32 &len) {
372 if (!m_movementDirectionForward) {
376 f32 dist = m_segmentT * m_transitions[m_currPointIdx].m_length;
378 idx = m_currPointIdx;
379 len = (dist - t) * m_transitions[m_currPointIdx].m_lengthInv;
383 for (s32 i = 0; i < m_pointCount; ++i) {
384 s32 currIdx = m_currPointIdx - 1;
386 currIdx = m_pointCount - 1;
391 currIdx += m_pointCount;
394 dist += m_transitions[currIdx].m_length;
397 len = (dist - t) * m_transitions[currIdx].m_lengthInv;
404void RailSmoothInterpolator::calcCubicBezier(f32 t, u32 currIdx, u32 nextIdx,
EGG::Vector3f &pos,
406 auto &transition = m_movementDirectionForward ? m_transitions[currIdx] : m_transitions[nextIdx];
408 pos = calcCubicBezierPos(t, transition);
409 dir = calcCubicBezierTangentDir(t, transition);
413EGG::Vector3f RailSmoothInterpolator::calcCubicBezierPos(f32 t,
414 const RailSplineTransition &trans)
const {
418 res += trans.m_p1 * (3.0f * t * (dt * dt));
419 res += trans.m_p2 * (3.0f * (t * t) * dt);
420 res += trans.m_p3 * (t * t * t);
426EGG::Vector3f RailSmoothInterpolator::calcCubicBezierTangentDir(f32 t,
427 const RailSplineTransition &trans)
const {
428 EGG::Vector3f c1 = trans.m_p0 * -1.0f + trans.m_p1 * 3.0f - (trans.m_p2 * 3.0f) + trans.m_p3;
429 EGG::Vector3f c2 = trans.m_p0 * 3.0f - trans.m_p1 * 6.0f + trans.m_p2 * 3.0f;
431 EGG::Vector3f ret = c1 * 3.0f * (t * t) + c2 * 2.0f * t + c3;
435 if (!m_movementDirectionForward) {
443f32 RailSmoothInterpolator::calcT(f32 t)
const {
444 u16 sampleIdx = m_movementDirectionForward ? m_currPointIdx * m_estimatorSampleCount :
445 m_nextPointIdx * m_estimatorSampleCount;
450 for (
u16 i = 0; i < m_estimatorSampleCount - 1; ++i) {
451 f32 currPercent = m_pathPercentages[sampleIdx + i];
452 f32 nextPercent = m_pathPercentages[sampleIdx + i + 1];
454 if (currPercent <= t && nextPercent > t) {
455 delta = (t - currPercent) / (nextPercent - currPercent);
460 f32 lastPercent = m_pathPercentages[sampleIdx + m_estimatorSampleCount - 1];
462 if (lastPercent <= t && 1.0f >= t) {
463 idx = m_estimatorSampleCount - 1;
464 delta = (t - lastPercent) / (1.0f - lastPercent);
467 return m_estimatorStep *
static_cast<f32
>(idx) + m_estimatorStep * delta;
471void RailSmoothInterpolator::calcNextSegment() {
472 f32 nextT = m_segmentT - 1.0f;
474 if (m_isOscillating) {
475 if (m_nextPointIdx == 0 || m_nextPointIdx == m_pointCount - 1) {
477 }
else if (m_movementDirectionForward) {
478 m_segmentT = nextT * m_transitions[m_currPointIdx].m_length *
479 m_transitions[m_nextPointIdx].m_lengthInv;
481 m_segmentT = nextT * m_transitions[m_currPointIdx - 1].m_length *
482 m_transitions[m_nextPointIdx - 1].m_lengthInv;
485 m_segmentT = nextT * m_transitions[m_currPointIdx].m_length *
486 m_transitions[m_nextPointIdx].m_lengthInv;
489 if (m_segmentT > 1.0f) {
495 if (m_usePerPointVelocities) {
f32 length() const
The square root of the vector's dot product.