3#include "game/field/CollisionDirector.hh"
8Rail::Rail(u16 idx, System::MapdataPointInfo *info) {
10 m_pointCapacity = info->pointCount();
11 m_pointCount = info->pointCount();
12 m_isOscillating = info->setting(1) == 1;
13 m_points = info->points();
14 m_hasCheckedCol =
false;
23void Rail::addPoint(f32 scale,
const EGG::Vector3f &point) {
24 m_points.back().pos = point;
30void Rail::checkSphereFull() {
31 if (m_hasCheckedCol) {
35 m_floorNrms = owning_span<EGG::Vector3f>(m_pointCount);
37 for (
size_t i = 0; i < m_pointCount; ++i) {
41 bool hasCourseCol = CollisionDirector::Instance()->checkSphereFull(100.0f, m_points[i].pos,
45 if (info.floorDist > std::numeric_limits<f32>::min()) {
46 m_floorNrms[i] = info.floorNrm;
49 m_floorNrms[i] = EGG::Vector3f::ey;
53 m_hasCheckedCol =
true;
57RailLine::RailLine(u16 idx, System::MapdataPointInfo *info) : Rail(idx, info) {
58 m_dirCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
59 m_transitions = owning_span<RailLineTransition>(m_dirCount);
62 for (u16 i = 0; i < m_pointCount - 1; ++i) {
63 auto &transition = m_transitions[i];
64 transition.m_dir = m_points[i + 1].pos - m_points[i].pos;
65 transition.m_length = transition.m_dir.normalise();
66 transition.m_lengthInv = 1.0f / transition.m_length;
67 m_pathLength += transition.m_length;
70 if (!m_isOscillating) {
71 auto &transition = m_transitions.back();
72 transition.m_dir = m_points.front().pos - m_points.back().pos;
73 transition.m_length = transition.m_dir.normalise();
74 transition.m_lengthInv = 1.0f / transition.m_length;
75 m_pathLength += transition.m_length;
80RailLine::~RailLine() =
default;
83RailSpline::RailSpline(u16 idx, System::MapdataPointInfo *info) : Rail(idx, info) {
84 m_transitionCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
85 m_transitions = owning_span<RailSplineTransition>(m_transitionCount);
86 m_estimatorSampleCount = 10;
87 m_estimatorStep = 1.0f /
static_cast<f32
>(m_estimatorSampleCount);
92 m_doNotAllocatePathPercentages =
false;
94 invalidateTransitions(
false);
98 for (
size_t i = 0; i < m_transitionCount; ++i) {
99 m_pathLength += m_transitions[i].m_length;
104RailSpline::~RailSpline() =
default;
107void RailSpline::onPointsChanged() {
108 m_doNotAllocatePathPercentages =
true;
109 m_transitionCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
111 invalidateTransitions(
false);
115 for (
const auto &transition : m_transitions) {
116 m_pathLength += transition.m_length;
121void RailSpline::onPointAdded() {
122 m_doNotAllocatePathPercentages =
true;
123 m_transitionCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
125 invalidateTransitions(
true);
129 for (
const auto &transition : m_transitions) {
130 m_pathLength += transition.m_length;
135void RailSpline::invalidateTransitions(
bool lastOnly) {
136 if (!m_doNotAllocatePathPercentages) {
137 size_t count = m_estimatorSampleCount * m_transitionCount + 1;
138 m_pathPercentages = owning_span<f32>(count);
143 if (m_isOscillating) {
145 auto &firstTransition = m_transitions[0];
146 firstTransition.m_p0 = m_points[0].pos;
147 firstTransition.m_p1 =
148 (m_points[1].pos - m_points[0].pos).multInv(4.0f) + m_points[0].pos;
149 firstTransition.m_p2 =
150 calcCubicBezierP2(m_points[0].pos, m_points[1].pos, m_points[2].pos);
151 firstTransition.m_p3 = m_points[1].pos;
152 firstTransition.m_length = estimateLength(firstTransition, m_estimatorSampleCount);
153 firstTransition.m_lengthInv = 1.0f / firstTransition.m_length;
155 for (u16 i = 1; i < m_transitionCount - 1; ++i) {
156 calcCubicBezierControlPoints(m_points[i - 1].pos, m_points[i].pos,
157 m_points[i + 1].pos, m_points[i + 2].pos, m_estimatorSampleCount,
162 auto &lastTransition = m_transitions.back();
163 lastTransition.m_p0 = m_points[m_transitionCount - 1].pos;
164 lastTransition.m_p1 = calcCubicBezierP1(m_points[m_transitionCount - 2].pos,
165 m_points[m_transitionCount - 1].pos, m_points[m_transitionCount].pos);
166 lastTransition.m_p2 =
167 (m_points[m_transitionCount - 1].pos - m_points[m_transitionCount].pos)
169 m_points[m_transitionCount].pos;
170 lastTransition.m_p3 = m_points[m_transitionCount].pos;
171 lastTransition.m_length = estimateLength(lastTransition, m_estimatorSampleCount);
172 lastTransition.m_lengthInv = 1.0f / lastTransition.m_length;
175 auto &firstTransition = m_transitions[0];
176 firstTransition.m_p0 = m_points[0].pos;
177 firstTransition.m_p1 = calcCubicBezierP1(m_points[m_transitionCount - 1].pos,
178 m_points[0].pos, m_points[1].pos);
179 firstTransition.m_p2 =
180 calcCubicBezierP2(m_points[0].pos, m_points[1].pos, m_points[2].pos);
181 firstTransition.m_p3 = m_points[1].pos;
182 firstTransition.m_length = estimateLength(firstTransition, m_estimatorSampleCount);
183 firstTransition.m_lengthInv = 1.0f / firstTransition.m_length;
185 for (u16 i = 1; i < m_transitionCount - 1; ++i) {
186 if (i + 2 != m_transitionCount) {
187 calcCubicBezierControlPoints(m_points[i - 1].pos, m_points[i].pos,
188 m_points[i + 1].pos, m_points[i + 2].pos, m_estimatorSampleCount,
191 calcCubicBezierControlPoints(m_points[i - 1].pos, m_points[i].pos,
192 m_points[i + 1].pos, m_points[0].pos, m_estimatorSampleCount,
198 auto &lastTransition = m_transitions.back();
199 lastTransition.m_p0 = m_points[m_transitionCount - 1].pos;
200 lastTransition.m_p1 = calcCubicBezierP1(m_points[m_transitionCount - 2].pos,
201 m_points[m_transitionCount - 1].pos, m_points[0].pos);
202 lastTransition.m_p2 = calcCubicBezierP2(m_points[m_transitionCount - 1].pos,
203 m_points[0].pos, m_points[1].pos);
204 lastTransition.m_p3 = m_points[0].pos;
205 lastTransition.m_length = estimateLength(lastTransition, m_estimatorSampleCount);
206 lastTransition.m_lengthInv = 1.0f / lastTransition.m_length;
211void RailSpline::calcCubicBezierControlPoints(
const EGG::Vector3f &p0,
const EGG::Vector3f &p1,
212 const EGG::Vector3f &p2,
const EGG::Vector3f &p3, s32 count,
213 RailSplineTransition &transition) {
214 transition.m_p0 = p1;
215 transition.m_p1 = calcCubicBezierP1(p0, p1, p2);
216 transition.m_p2 = calcCubicBezierP2(p1, p2, p3);
217 transition.m_p3 = p2;
218 transition.m_length = estimateLength(transition, count);
219 transition.m_lengthInv = 1.0f / transition.m_length;
223f32 RailSpline::estimateLength(
const RailSplineTransition &transition, s32 count) {
224 std::array<EGG::Vector3f, 11> waypoints;
226 for (s32 i = 0; i < count + 1; ++i) {
227 waypoints[i] = cubicBezier(m_estimatorStep *
static_cast<f32
>(i), transition);
232 for (s32 i = 0; i < count; ++i) {
233 m_pathPercentages[m_segmentCount++] = length;
234 length += (waypoints[i] - waypoints[i + 1]).length();
238 for (s32 i = m_segmentCount - 1; i > m_segmentCount - count - 1; --i) {
239 m_pathPercentages[i] /= length;
246EGG::Vector3f RailSpline::calcCubicBezierP1(
const EGG::Vector3f &p0,
const EGG::Vector3f &p1,
247 const EGG::Vector3f &p2)
const {
248 EGG::Vector3f res = p2 - p0;
249 f32 len = res.length();
251 return p1 + res * (len * m_someScale);
255EGG::Vector3f RailSpline::calcCubicBezierP2(
const EGG::Vector3f &p0,
const EGG::Vector3f &p1,
256 const EGG::Vector3f &p2)
const {
257 EGG::Vector3f res = p0 - p2;
258 f32 len = res.length();
260 return p1 + res * (len * m_someScale);
264EGG::Vector3f RailSpline::cubicBezier(f32 t,
const RailSplineTransition &transition)
const {
267 EGG::Vector3f res = transition.m_p0 * (dt * dt * dt);
268 res += transition.m_p1 * (3.0f * t * (dt * dt));
269 res += transition.m_p2 * (3.0f * (t * t) * dt);
270 res += transition.m_p3 * (t * t * t);
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.