A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectTwistedWay.cc
1#include "ObjectTwistedWay.hh"
2
3#include "game/field/CollisionDirector.hh"
4
5#include "game/system/RaceManager.hh"
6
7#include <egg/math/Math.hh>
8
9namespace Field {
10
12ObjectTwistedWay::ObjectTwistedWay(const System::MapdataGeoObj &params) : ObjectDrivable(params) {}
13
15ObjectTwistedWay::~ObjectTwistedWay() = default;
16
18void ObjectTwistedWay::calc() {
19 if (!System::RaceManager::Instance()->isStageReached(System::RaceManager::Stage::Race)) {
20 ++m_introTimer;
21 }
22}
23
25bool ObjectTwistedWay::checkPointPartial(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
26 KCLTypeMask flags, CollisionInfoPartial *pInfo, KCLTypeMask *pFlagsOut) {
27 return checkSpherePartialImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
28}
29
31bool ObjectTwistedWay::checkPointPartialPush(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
32 KCLTypeMask flags, CollisionInfoPartial *pInfo, KCLTypeMask *pFlagsOut) {
33 return checkSpherePartialPushImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
34}
35
37bool ObjectTwistedWay::checkPointFull(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
38 KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut) {
39 return checkSphereFullImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
40}
41
43bool ObjectTwistedWay::checkPointFullPush(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
44 KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut) {
45 return checkSphereFullPushImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
46}
47
49bool ObjectTwistedWay::checkSpherePartial(f32 radius, const EGG::Vector3f &v0,
50 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfoPartial *pInfo,
51 KCLTypeMask *pFlagsOut, u32 timeOffset) {
52 return checkSpherePartialImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
53}
54
56bool ObjectTwistedWay::checkSpherePartialPush(f32 radius, const EGG::Vector3f &v0,
57 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfoPartial *pInfo,
58 KCLTypeMask *pFlagsOut, u32 timeOffset) {
59 return checkSpherePartialPushImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
60}
61
63bool ObjectTwistedWay::checkSphereFull(f32 radius, const EGG::Vector3f &v0, const EGG::Vector3f &v1,
64 KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut, u32 timeOffset) {
65 return checkSphereFullImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
66}
67
69bool ObjectTwistedWay::checkSphereFullPush(f32 radius, const EGG::Vector3f &v0,
70 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut,
71 u32 timeOffset) {
72 return checkSphereFullPushImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
73}
74
76bool ObjectTwistedWay::checkPointCachedPartial(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
77 KCLTypeMask flags, CollisionInfoPartial *pInfo, KCLTypeMask *pFlagsOut) {
78 return checkSpherePartialImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
79}
80
82bool ObjectTwistedWay::checkPointCachedPartialPush(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
83 KCLTypeMask flags, CollisionInfoPartial *pInfo, KCLTypeMask *pFlagsOut) {
84 return checkSpherePartialPushImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
85}
86
88bool ObjectTwistedWay::checkPointCachedFull(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
89 KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut) {
90 return checkSphereFullImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
91}
92
94bool ObjectTwistedWay::checkPointCachedFullPush(const EGG::Vector3f &v0, const EGG::Vector3f &v1,
95 KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut) {
96 return checkSphereFullPushImpl(0.0f, v0, v1, flags, pInfo, pFlagsOut, 0);
97}
98
100bool ObjectTwistedWay::checkSphereCachedPartial(f32 radius, const EGG::Vector3f &v0,
101 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfoPartial *pInfo,
102 KCLTypeMask *pFlagsOut, u32 timeOffset) {
103 return checkSpherePartialImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
104}
105
107bool ObjectTwistedWay::checkSphereCachedPartialPush(f32 radius, const EGG::Vector3f &v0,
108 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfoPartial *pInfo,
109 KCLTypeMask *pFlagsOut, u32 timeOffset) {
110 return checkSpherePartialPushImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
111}
112
114bool ObjectTwistedWay::checkSphereCachedFull(f32 radius, const EGG::Vector3f &v0,
115 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut,
116 u32 timeOffset) {
117 return checkSphereFullImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
118}
119
121bool ObjectTwistedWay::checkSphereCachedFullPush(f32 radius, const EGG::Vector3f &v0,
122 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut,
123 u32 timeOffset) {
124 return checkSphereFullPushImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset);
125}
126
128bool ObjectTwistedWay::checkSpherePartialImpl(f32 radius, const EGG::Vector3f &v0,
129 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfoPartial *pInfo,
130 KCLTypeMask *pFlagsOut, u32 timeOffset) {
131 return checkSphereImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset, false);
132}
133
135bool ObjectTwistedWay::checkSpherePartialPushImpl(f32 radius, const EGG::Vector3f &v0,
136 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfoPartial *pInfo,
137 KCLTypeMask *pFlagsOut, u32 timeOffset) {
138 return checkSphereImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset, true);
139}
140
142bool ObjectTwistedWay::checkSphereFullImpl(f32 radius, const EGG::Vector3f &v0,
143 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut,
144 u32 timeOffset) {
145 return checkSphereImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset, false);
146}
147
149bool ObjectTwistedWay::checkSphereFullPushImpl(f32 radius, const EGG::Vector3f &v0,
150 const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut,
151 u32 timeOffset) {
152 return checkSphereImpl(radius, v0, v1, flags, pInfo, pFlagsOut, timeOffset, true);
153}
154
158template <typename T>
159 requires std::is_same_v<T, CollisionInfo> || std::is_same_v<T, CollisionInfoPartial>
160bool ObjectTwistedWay::checkSphereImpl(f32 radius, const EGG::Vector3f &v0,
161 const EGG::Vector3f & /*v1*/, KCLTypeMask flags, T *pInfo, KCLTypeMask *pFlagsOut,
162 u32 timeOffset, bool push) {
163 EGG::Vector3f relPos = v0 - m_pos;
164
165 if (EGG::Mathf::abs(relPos.z) > HALF_DEPTH || EGG::Mathf::abs(relPos.x) > WIDTH * 2.0f) {
166 return false;
167 }
168
169 auto *raceMgr = System::RaceManager::Instance();
170 bool isInRace = raceMgr->isStageReached(System::RaceManager::Stage::Race);
171
172 u32 frameCount = timeOffset + (isInRace ? raceMgr->timer() : m_introTimer);
173 u32 t = (frameCount % PERIOD_LENGTH) * 2;
174
175 f32 angle = isInRace ? calcWave(-relPos.z / HALF_DEPTH, t) : 0.0f;
176
177 bool hasCol = false;
178 if (flags & KCL_TYPE_BIT(COL_TYPE_WALL)) {
179 hasCol |= checkWallCollision(angle, radius, t, relPos, pInfo, pFlagsOut, push);
180 }
181
182 if (flags & KCL_TYPE_BIT(COL_TYPE_ROAD)) {
183 hasCol |= checkFloorCollision(angle, radius, relPos, pInfo, pFlagsOut, push);
184 }
185
186 return hasCol;
187}
188
189template <typename T>
190 requires std::is_same_v<T, CollisionInfo> || std::is_same_v<T, CollisionInfoPartial>
191bool ObjectTwistedWay::checkWallCollision(f32 angle, f32 radius, u32 t, const EGG::Vector3f &relPos,
192 T *pInfo, KCLTypeMask *pFlagsOut, bool push) {
193 auto [sin, cos] = EGG::Mathf::SinCosFIdx(RAD2FIDX * angle);
194 f32 yProj = sin * (relPos.y - WIDTH * 0.5f);
195 f32 dist = radius + (yProj - cos * (relPos.x + WIDTH));
196
197 EGG::Vector3f bbox = EGG::Vector3f::zero;
198 EGG::Vector3f fnrm = EGG::Vector3f::zero;
199
200 if (dist <= 0.0f) {
201 dist = radius - (yProj - cos * (relPos.x - WIDTH));
202
203 if (dist > 0.0f) {
204 bbox = EGG::Vector3f(-dist * cos, dist * sin, 0.0f);
205 fnrm = EGG::Vector3f(-cos, sin, 0.0f);
206 }
207 } else {
208 bbox = EGG::Vector3f(dist * cos, dist * sin, 0.0f);
209 fnrm = EGG::Vector3f(cos, sin, 0.0f);
210 }
211
212 if (dist > 0.0f) {
213 if (pInfo) {
214 pInfo->bbox.min = pInfo->bbox.min.minimize(bbox);
215 pInfo->bbox.max = pInfo->bbox.max.maximize(bbox);
216
217 if constexpr (std::is_same_v<T, CollisionInfo>) {
218 pInfo->updateWall(dist, fnrm);
219 }
220 }
221
222 if (pFlagsOut) {
223 if (push) {
224 CollisionDirector::Instance()->pushCollisionEntry(dist, pFlagsOut,
226 } else {
227 *pFlagsOut |= KCL_TYPE_BIT(COL_TYPE_WALL);
228 }
229 }
230
231 return true;
232 }
233
234 angle = calcWave(0.0f, t);
235
236 EGG::Vector3f wnrm;
237
238 if (!checkPoleCollision(radius, angle, relPos, bbox, wnrm, dist)) {
239 return false;
240 }
241
242 if (pInfo) {
243 pInfo->bbox.min = pInfo->bbox.min.minimize(bbox);
244 pInfo->bbox.max = pInfo->bbox.max.maximize(bbox);
245
246 if constexpr (std::is_same_v<T, CollisionInfo>) {
247 pInfo->updateWall(dist, wnrm);
248 }
249 }
250
251 if (pFlagsOut) {
252 if (push) {
253 CollisionDirector::Instance()->pushCollisionEntry(dist, pFlagsOut,
255 } else {
256 *pFlagsOut |= KCL_TYPE_BIT(COL_TYPE_WALL);
257 }
258 }
259
260 return true;
261}
262
263template <typename T>
264 requires std::is_same_v<T, CollisionInfo> || std::is_same_v<T, CollisionInfoPartial>
265bool ObjectTwistedWay::checkFloorCollision(f32 angle, f32 radius, const EGG::Vector3f &relPos,
266 T *pInfo, KCLTypeMask *pFlagsOut, bool push) {
267 constexpr f32 HALF_WIDTH = WIDTH * -0.5f;
268 constexpr f32 TRICKABLE_RADIUS_FACTOR = 0.6f;
269
270 auto [sin, cos] = EGG::Mathf::SinCosFIdx(RAD2FIDX * angle);
271 f32 dist = HALF_WIDTH + ((radius + -relPos.x * sin) + cos * (-relPos.y - HALF_WIDTH));
272
273 if (dist <= 0.0f) {
274 return false;
275 }
276
277 EGG::Vector3f bbox = dist * EGG::Vector3f::ey;
278 EGG::Vector3f fnrm = EGG::Vector3f(sin, cos, 0.0f);
279
280 if (pInfo) {
281 pInfo->bbox.min = pInfo->bbox.min.minimize(bbox);
282 pInfo->bbox.max = pInfo->bbox.max.maximize(bbox);
283
284 if constexpr (std::is_same_v<T, CollisionInfo>) {
285 pInfo->updateFloor(dist, fnrm);
286 }
287 }
288
289 if (pFlagsOut) {
290 if (push) {
291 auto *colDir = CollisionDirector::Instance();
292 colDir->pushCollisionEntry(dist, pFlagsOut, KCL_TYPE_BIT(COL_TYPE_ROAD), COL_TYPE_ROAD);
293 colDir->setCurrentCollisionVariant(2);
294
295 if (EGG::Mathf::abs(relPos.z) < HALF_DEPTH * TRICKABLE_RADIUS_FACTOR) {
296 colDir->setCurrentCollisionTrickable(true);
297 }
298 } else {
299 *pFlagsOut |= KCL_TYPE_BIT(COL_TYPE_ROAD);
300 }
301 }
302
303 return true;
304}
305
307bool ObjectTwistedWay::checkPoleCollision(f32 radius, f32 angle, const EGG::Vector3f &relPos,
308 EGG::Vector3f &v0, EGG::Vector3f &wnrm, f32 &dist) {
309 constexpr EGG::Vector3f HEIGHT = EGG::Vector3f(0.0f, 0.5f * WIDTH, 0.0f);
310 constexpr f32 POLE_RADIUS = 227.0f;
311
312 auto [sin, cos] = EGG::Mathf::SinCosFIdx(angle * RAD2FIDX);
313 EGG::Vector3f sinCos = EGG::Vector3f(sin, cos, 0.0f);
314
315 wnrm = relPos - (sinCos * (relPos - HEIGHT).dot(sinCos) + HEIGHT);
316 f32 diff = POLE_RADIUS + radius - wnrm.length();
317
318 if (diff <= 0.0f) {
319 return false;
320 }
321
322 wnrm.normalise2();
323 v0 = wnrm * diff;
324 dist = diff;
325
326 return true;
327}
328
329f32 ObjectTwistedWay::calcWave(f32 zPercent, u32 t) {
330 constexpr f32 WAVINESS = 4.0f;
331 constexpr f32 AMPLITUDE = 0.2f;
332
333 f32 angle = EGG::Mathf::SinFIdx(
334 (static_cast<f32>(t) * F_PI / PERIOD_LENGTH + WAVINESS * zPercent) * RAD2FIDX);
335
336 f32 low = zPercent - 1.0f;
337 f32 high = zPercent + 1.0f;
338
339 return AMPLITUDE * (high * (high * (low * (low * angle))));
340}
341
342} // namespace Field
@ COL_TYPE_WALL
Default wall.
@ COL_TYPE_ROAD
Default road.
#define KCL_TYPE_BIT(x)
Pertains to collision.
A 3D float vector.
Definition Vector.hh:88
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
Definition Vector.hh:187
f32 length() const
The square root of the vector's dot product.
Definition Vector.hh:192