A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
Rail.cc
1#include "Rail.hh"
2
3#include "game/field/CollisionDirector.hh"
4
5namespace Field {
6
8Rail::Rail(u16 idx, System::MapdataPointInfo *info) {
9 m_idx = idx;
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;
15 m_someScale = 0.1f;
16}
17
21 delete m_floorNrms.data();
22}
23
25void Rail::addPoint(f32 scale, const EGG::Vector3f &point) {
26 m_points.back().pos = point;
27 m_someScale = scale;
28 onPointAdded();
29}
30
32void Rail::checkSphereFull() {
33 if (m_hasCheckedCol) {
34 return;
35 }
36
37 m_floorNrms = std::span<EGG::Vector3f>(new EGG::Vector3f[m_pointCount], m_pointCount);
38
39 for (size_t i = 0; i < m_pointCount; ++i) {
40 CollisionInfo info;
41 info.bbox.setZero();
42
43 bool hasCourseCol = CollisionDirector::Instance()->checkSphereFull(100.0f, m_points[i].pos,
44 EGG::Vector3f::inf, KCL_TYPE_FLOOR, &info, nullptr, 0);
45
46 if (hasCourseCol) {
47 if (info.floorDist > std::numeric_limits<f32>::min()) {
48 m_floorNrms[i] = info.floorNrm;
49 }
50 } else {
51 m_floorNrms[i] = EGG::Vector3f::ey;
52 }
53 }
54
55 m_hasCheckedCol = true;
56}
57
59RailLine::RailLine(u16 idx, System::MapdataPointInfo *info) : Rail(idx, info) {
60 m_dirCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
61 m_transitions = std::span<RailLineTransition>(new RailLineTransition[m_dirCount], m_dirCount);
62 m_pathLength = 0.0f;
63
64 for (u16 i = 0; i < m_pointCount - 1; ++i) {
65 auto &transition = m_transitions[i];
66 transition.m_dir = m_points[i + 1].pos - m_points[i].pos;
67 transition.m_length = transition.m_dir.normalise();
68 transition.m_lengthInv = 1.0f / transition.m_length;
69 m_pathLength += transition.m_length;
70 }
71
72 if (!m_isOscillating) {
73 auto &transition = m_transitions.back();
74 transition.m_dir = m_points.front().pos - m_points.back().pos;
75 transition.m_length = transition.m_dir.normalise();
76 transition.m_lengthInv = 1.0f / transition.m_length;
77 m_pathLength += transition.m_length;
78 }
79}
80
82RailLine::~RailLine() {
83 delete m_transitions.data();
84}
85
87RailSpline::RailSpline(u16 idx, System::MapdataPointInfo *info) : Rail(idx, info) {
88 m_transitionCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
89 m_transitions = std::span<RailSplineTransition>(new RailSplineTransition[m_transitionCount],
90 m_transitionCount);
91 m_estimatorSampleCount = 10;
92 m_estimatorStep = 1.0f / static_cast<f32>(m_estimatorSampleCount);
93
94 // This is normally not set until AFTER the call to invalidateTransitions,
95 // but the expected behavior requires that this is set to false.
96 // This misordering was probably never noticed since EGG::Heap zeroes memory.
97 m_doNotAllocatePathPercentages = false;
98
99 invalidateTransitions(false);
100
101 m_pathLength = 0.0f;
102
103 for (size_t i = 0; i < m_transitionCount; ++i) {
104 m_pathLength += m_transitions[i].m_length;
105 }
106}
107
109RailSpline::~RailSpline() {
110 delete m_transitions.data();
111 delete m_pathPercentages.data();
112}
113
115void RailSpline::onPointsChanged() {
116 m_doNotAllocatePathPercentages = true;
117 m_transitionCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
118
119 invalidateTransitions(false);
120
121 m_pathLength = 0.0f;
122
123 for (auto &transition : m_transitions) {
124 m_pathLength += transition.m_length;
125 }
126}
127
129void RailSpline::onPointAdded() {
130 m_doNotAllocatePathPercentages = true;
131 m_transitionCount = m_isOscillating ? m_pointCount - 1 : m_pointCount;
132
133 invalidateTransitions(true);
134
135 m_pathLength = 0.0f;
136
137 for (auto &transition : m_transitions) {
138 m_pathLength += transition.m_length;
139 }
140}
141
143void RailSpline::invalidateTransitions(bool lastOnly) {
144 if (!m_doNotAllocatePathPercentages) {
145 size_t count = m_estimatorSampleCount * m_transitionCount + 1;
146 m_pathPercentages = std::span<f32>(new f32[count], count);
147 }
148
149 m_segmentCount = 0;
150
151 if (m_isOscillating) {
152 if (!lastOnly) {
153 auto &firstTransition = m_transitions[0];
154 firstTransition.m_p0 = m_points[0].pos;
155 firstTransition.m_p1 =
156 (m_points[1].pos - m_points[0].pos).multInv(4.0f) + m_points[0].pos;
157 firstTransition.m_p2 =
158 calcCubicBezierP2(m_points[0].pos, m_points[1].pos, m_points[2].pos);
159 firstTransition.m_p3 = m_points[1].pos;
160 firstTransition.m_length = estimateLength(firstTransition, m_estimatorSampleCount);
161 firstTransition.m_lengthInv = 1.0f / firstTransition.m_length;
162
163 for (u16 i = 1; i < m_transitionCount - 1; ++i) {
164 calcCubicBezierControlPoints(m_points[i - 1].pos, m_points[i].pos,
165 m_points[i + 1].pos, m_points[i + 2].pos, m_estimatorSampleCount,
166 m_transitions[i]);
167 }
168 }
169
170 auto &lastTransition = m_transitions.back();
171 lastTransition.m_p0 = m_points[m_transitionCount - 1].pos;
172 lastTransition.m_p1 = calcCubicBezierP1(m_points[m_transitionCount - 2].pos,
173 m_points[m_transitionCount - 1].pos, m_points[m_transitionCount].pos);
174 lastTransition.m_p2 =
175 (m_points[m_transitionCount - 1].pos - m_points[m_transitionCount].pos)
176 .multInv(4.0f) +
177 m_points[m_transitionCount].pos;
178 lastTransition.m_p3 = m_points[m_transitionCount].pos;
179 lastTransition.m_length = estimateLength(lastTransition, m_estimatorSampleCount);
180 lastTransition.m_lengthInv = 1.0f / lastTransition.m_length;
181 } else {
182 if (!lastOnly) {
183 auto &firstTransition = m_transitions[0];
184 firstTransition.m_p0 = m_points[0].pos;
185 firstTransition.m_p1 = calcCubicBezierP1(m_points[m_transitionCount - 1].pos,
186 m_points[0].pos, m_points[1].pos);
187 firstTransition.m_p2 =
188 calcCubicBezierP2(m_points[0].pos, m_points[1].pos, m_points[2].pos);
189 firstTransition.m_p3 = m_points[1].pos;
190 firstTransition.m_length = estimateLength(firstTransition, m_estimatorSampleCount);
191 firstTransition.m_lengthInv = 1.0f / firstTransition.m_length;
192
193 for (u16 i = 1; i < m_transitionCount - 1; ++i) {
194 if (i + 2 != m_transitionCount) {
195 calcCubicBezierControlPoints(m_points[i - 1].pos, m_points[i].pos,
196 m_points[i + 1].pos, m_points[i + 2].pos, m_estimatorSampleCount,
197 m_transitions[i]);
198 } else {
199 calcCubicBezierControlPoints(m_points[i - 1].pos, m_points[i].pos,
200 m_points[i + 1].pos, m_points[0].pos, m_estimatorSampleCount,
201 m_transitions[i]);
202 }
203 }
204 }
205
206 auto &lastTransition = m_transitions.back();
207 lastTransition.m_p0 = m_points[m_transitionCount - 1].pos;
208 lastTransition.m_p1 = calcCubicBezierP1(m_points[m_transitionCount - 2].pos,
209 m_points[m_transitionCount - 1].pos, m_points[0].pos);
210 lastTransition.m_p2 = calcCubicBezierP2(m_points[m_transitionCount - 1].pos,
211 m_points[0].pos, m_points[1].pos);
212 lastTransition.m_p3 = m_points[0].pos;
213 lastTransition.m_length = estimateLength(lastTransition, m_estimatorSampleCount);
214 lastTransition.m_lengthInv = 1.0f / lastTransition.m_length;
215 }
216}
217
219void RailSpline::calcCubicBezierControlPoints(const EGG::Vector3f &p0, const EGG::Vector3f &p1,
220 const EGG::Vector3f &p2, const EGG::Vector3f &p3, s32 count,
221 RailSplineTransition &transition) {
222 transition.m_p0 = p1;
223 transition.m_p1 = calcCubicBezierP1(p0, p1, p2);
224 transition.m_p2 = calcCubicBezierP2(p1, p2, p3);
225 transition.m_p3 = p2;
226 transition.m_length = estimateLength(transition, count);
227 transition.m_lengthInv = 1.0f / transition.m_length;
228}
229
231f32 RailSpline::estimateLength(const RailSplineTransition &transition, s32 count) {
232 std::array<EGG::Vector3f, 11> waypoints;
233
234 for (s32 i = 0; i < count + 1; ++i) {
235 waypoints[i] = cubicBezier(m_estimatorStep * static_cast<f32>(i), transition);
236 }
237 f32 length = 0.0f;
238
239 // Numerator loop
240 for (s32 i = 0; i < count; ++i) {
241 m_pathPercentages[m_segmentCount++] = length;
242 length += (waypoints[i] - waypoints[i + 1]).length();
243 }
244
245 // Denominator loop
246 for (s32 i = m_segmentCount - 1; i > m_segmentCount - count - 1; --i) {
247 m_pathPercentages[i] /= length;
248 }
249
250 return length;
251}
252
254EGG::Vector3f RailSpline::calcCubicBezierP1(const EGG::Vector3f &p0, const EGG::Vector3f &p1,
255 const EGG::Vector3f &p2) const {
256 EGG::Vector3f res = p2 - p0;
257 f32 len = res.length();
258 res.normalise2();
259 return p1 + res * (len * m_someScale);
260}
261
263EGG::Vector3f RailSpline::calcCubicBezierP2(const EGG::Vector3f &p0, const EGG::Vector3f &p1,
264 const EGG::Vector3f &p2) const {
265 EGG::Vector3f res = p0 - p2;
266 f32 len = res.length();
267 res.normalise2();
268 return p1 + res * (len * m_someScale);
269}
270
272EGG::Vector3f RailSpline::cubicBezier(f32 t, const RailSplineTransition &transition) const {
273 f32 dt = 1.0f - t;
274
275 EGG::Vector3f res = transition.m_p0 * (dt * dt * dt);
276 res += transition.m_p1 * (3.0f * t * (dt * dt));
277 res += transition.m_p2 * (3.0f * (t * t) * dt);
278 res += transition.m_p3 * (t * t * t);
279
280 return res;
281}
282
283} // namespace Field
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
virtual ~Rail()
Definition Rail.cc:20
Pertains to collision.
A 3D float vector.
Definition Vector.hh:83
f32 length() const
The square root of the vector's dot product.
Definition Vector.hh:187