A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectObakeManager.cc
1#include "ObjectObakeManager.hh"
2
3#include "game/field/CollisionDirector.hh"
4
5#include "game/system/RaceManager.hh"
6
7namespace Field {
8
10ObjectObakeManager::ObjectObakeManager(const System::MapdataGeoObj &params)
11 : ObjectDrivable(params), m_blockCache({nullptr}) {
12 static constexpr f32 BLOCK_WIDTH = 195.00002f;
13 static constexpr f32 BLOCK_HEIGHT = 130.0f;
14 static constexpr size_t MAX_FALLING_BLOCKS = 256;
15
16 m_colBox = new ObjectCollisionBox(BLOCK_WIDTH, BLOCK_HEIGHT, BLOCK_WIDTH, EGG::Vector3f::zero);
17 m_colSphere = new ObjectCollisionSphere(1.0f, EGG::Vector3f::zero);
18
19 addBlock(params);
20
21 // Pre-allocate max size now to avoid re-allocation during race when heap is locked.
22 m_calcBlocks.reserve(MAX_FALLING_BLOCKS);
23}
24
26ObjectObakeManager::~ObjectObakeManager() {
27 delete m_colBox;
28 delete m_colSphere;
29
30 for (auto *&block : m_blocks) {
31 delete block;
32 }
33}
34
36void ObjectObakeManager::calc() {
37 u32 frame = System::RaceManager::Instance()->timer();
38
39 for (auto *&block : m_blocks) {
40 u32 fallFrame = block->fallFrame();
41
42 // Block is starting to fall
43 if (fallFrame > 0 && fallFrame <= frame &&
44 block->fallState() == ObjectObakeBlock::FallState::Rest) {
45 block->setFallState(ObjectObakeBlock::FallState::Falling);
46 block->calc();
47 m_calcBlocks.push_back(block);
48 }
49 }
50
51 for (auto *&block : m_calcBlocks) {
52 block->calc();
53 }
54}
55
57bool ObjectObakeManager::checkPointPartial(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos,
58 KCLTypeMask mask, CollisionInfoPartial *info, KCLTypeMask *maskOut) {
59 return checkSpherePartialImpl(0.0f, pos, prevPos, mask, info, maskOut);
60}
61
63bool ObjectObakeManager::checkPointPartialPush(const EGG::Vector3f &pos,
64 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,
65 KCLTypeMask *maskOut) {
66 return checkSpherePartialPushImpl(0.0f, pos, prevPos, mask, info, maskOut);
67}
68
70bool ObjectObakeManager::checkPointFull(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos,
71 KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut) {
72 return checkSphereFullImpl(0.0f, pos, prevPos, mask, info, maskOut);
73}
74
76bool ObjectObakeManager::checkPointFullPush(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos,
77 KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut) {
78 return checkSphereFullPushImpl(0.0f, pos, prevPos, mask, info, maskOut);
79}
80
82bool ObjectObakeManager::checkSpherePartial(f32 radius, const EGG::Vector3f &pos,
83 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,
84 KCLTypeMask *maskOut, u32 /*timeOffset*/) {
85 return checkSpherePartialImpl(radius, pos, prevPos, mask, info, maskOut);
86}
87
89bool ObjectObakeManager::checkSpherePartialPush(f32 radius, const EGG::Vector3f &pos,
90 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,
91 KCLTypeMask *maskOut, u32 /*timeOffset*/) {
92 return checkSpherePartialPushImpl(radius, pos, prevPos, mask, info, maskOut);
93}
94
96bool ObjectObakeManager::checkSphereFull(f32 radius, const EGG::Vector3f &pos,
97 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut,
98 u32 /*timeOffset*/) {
99 return checkSphereFullImpl(radius, pos, prevPos, mask, info, maskOut);
100}
101
103bool ObjectObakeManager::checkSphereFullPush(f32 radius, const EGG::Vector3f &pos,
104 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut,
105 u32 /*timeOffset*/) {
106 return checkSphereFullPushImpl(radius, pos, prevPos, mask, info, maskOut);
107}
108
110bool ObjectObakeManager::checkPointCachedPartial(const EGG::Vector3f &pos,
111 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,
112 KCLTypeMask *maskOut) {
113 return checkSpherePartialImpl(0.0f, pos, prevPos, mask, info, maskOut);
114}
115
117bool ObjectObakeManager::checkPointCachedPartialPush(const EGG::Vector3f &pos,
118 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,
119 KCLTypeMask *maskOut) {
120 return checkSpherePartialPushImpl(0.0f, pos, prevPos, mask, info, maskOut);
121}
122
124bool ObjectObakeManager::checkPointCachedFull(const EGG::Vector3f &pos,
125 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut) {
126 return checkSphereFullImpl(0.0f, pos, prevPos, mask, info, maskOut);
127}
128
130bool ObjectObakeManager::checkPointCachedFullPush(const EGG::Vector3f &pos,
131 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut) {
132 return checkSphereFullPushImpl(0.0f, pos, prevPos, mask, info, maskOut);
133}
134
136bool ObjectObakeManager::checkSphereCachedPartial(f32 radius, const EGG::Vector3f &pos,
137 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,
138 KCLTypeMask *maskOut, u32 /*timeOffset*/) {
139 return checkSpherePartialImpl(radius, pos, prevPos, mask, info, maskOut);
140}
141
143bool ObjectObakeManager::checkSphereCachedPartialPush(f32 radius, const EGG::Vector3f &pos,
144 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,
145 KCLTypeMask *maskOut, u32 /*timeOffset*/) {
146 return checkSpherePartialPushImpl(radius, pos, prevPos, mask, info, maskOut);
147}
148
150bool ObjectObakeManager::checkSphereCachedFull(f32 radius, const EGG::Vector3f &pos,
151 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut,
152 u32 /*timeOffset*/) {
153 return checkSphereFullImpl(radius, pos, prevPos, mask, info, maskOut);
154}
155
157bool ObjectObakeManager::checkSphereCachedFullPush(f32 radius, const EGG::Vector3f &pos,
158 const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut,
159 u32 /*timeOffset*/) {
160 return checkSphereFullPushImpl(radius, pos, prevPos, mask, info, maskOut);
161}
162
164void ObjectObakeManager::addBlock(const System::MapdataGeoObj &params) {
165 auto *block = new ObjectObakeBlock(params);
166 m_blocks.push_back(block);
167 auto [spatialX, spatialZ] = SpatialIndex(block->pos());
168 m_blockCache[spatialZ][spatialX] = block;
169}
170
172bool ObjectObakeManager::checkSpherePartialImpl(f32 radius, const EGG::Vector3f &pos,
173 const EGG::Vector3f & /*prevPos*/, KCLTypeMask mask, CollisionInfoPartial *info,
174 KCLTypeMask *maskOut) {
175 bool collision = false;
176 auto [spatialX, spatialZ] = SpatialIndex(pos);
177
179 t.makeT(pos);
180 m_colSphere->transform(t, EGG::Vector3f(radius, radius, radius), EGG::Vector3f::zero);
181
182 for (s32 i = spatialZ - 1; i <= spatialZ + 1; ++i) {
183 for (s32 j = spatialX - 1; j <= spatialX + 1; ++j) {
184 // Make sure we're in bounds of the cache
185 if (j < 0 || static_cast<size_t>(j) >= CACHE_SIZE_X || i < 0 ||
186 static_cast<size_t>(i) >= CACHE_SIZE_Z) {
187 continue;
188 }
189
190 auto *block = m_blockCache[i][j];
191 if (!block) {
192 continue;
193 }
194
196 t.makeT(block->pos());
197 m_colBox->setBoundingRadius(SPECIAL_WALL_BOUNDING_RADIUS);
198 m_colBox->transform(t, SPECIAL_WALL_SCALE, EGG::Vector3f::zero);
199
200 EGG::Vector3f dist;
201 bool collided = m_colSphere->check(*m_colBox, dist);
202
203 if (collided) {
204 EGG::Vector3f distNrm = dist;
205 distNrm.normalise();
206
207 if (0.0f > distNrm.y || distNrm.y > 0.9f) {
208 collided = false;
209 } else {
210 if (info) {
211 info->update(dist);
212 }
213
214 if (maskOut) {
216 }
217 }
218 }
219
220 collision |= collided;
221 }
222
223 if (mask & KCL_TYPE_BIT(COL_TYPE_ROAD)) {
224 t.makeT(block->pos());
225 m_colBox->transform(t, ROAD_SCALE, EGG::Vector3f::zero);
226
227 EGG::Vector3f dist;
228 bool collided = m_colSphere->check(*m_colBox, dist);
229
230 if (collided) {
231 EGG::Vector3f distNrm = dist;
232 distNrm.normalise();
233
234 if (0.9f >= distNrm.y) {
235 collided = false;
236 } else {
237 if (info) {
238 info->update(dist);
239 }
240
241 if (maskOut) {
242 *maskOut |= KCL_TYPE_BIT(COL_TYPE_ROAD);
243 }
244 }
245 }
246
247 collision |= collided;
248 }
249 }
250 }
251
252 return collision;
253}
254
256bool ObjectObakeManager::checkSpherePartialPushImpl(f32 radius, const EGG::Vector3f &pos,
257 const EGG::Vector3f & /*prevPos*/, KCLTypeMask mask, CollisionInfoPartial *info,
258 KCLTypeMask *maskOut) {
259 bool collision = false;
260 auto [spatialX, spatialZ] = SpatialIndex(pos);
261
263 t.makeT(pos);
264
265 m_colSphere->transform(t, EGG::Vector3f(radius, radius, radius), EGG::Vector3f::zero);
266
267 for (s32 i = spatialZ - 1; i <= spatialZ + 1; ++i) {
268 for (s32 j = spatialX - 1; j <= spatialX + 1; ++j) {
269 // Make sure we're in bounds of the cache
270 if (j < 0 || static_cast<size_t>(j) >= CACHE_SIZE_X || i < 0 ||
271 static_cast<size_t>(i) >= CACHE_SIZE_Z) {
272 continue;
273 }
274
275 auto *block = m_blockCache[i][j];
276 if (!block) {
277 continue;
278 }
279
281 t.makeT(block->pos());
282 m_colBox->setBoundingRadius(SPECIAL_WALL_BOUNDING_RADIUS);
283 m_colBox->transform(t, SPECIAL_WALL_SCALE, EGG::Vector3f::zero);
284
285 EGG::Vector3f dist;
286 bool collided = m_colSphere->check(*m_colBox, dist);
287
288 if (collided) {
289 EGG::Vector3f distNrm = dist;
290 distNrm.normalise();
291
292 if (0.0f > distNrm.y || distNrm.y > 0.9f) {
293 collided = false;
294 } else {
295 if (info) {
296 info->update(dist);
297 }
298
299 if (maskOut) {
300 auto *colDir = CollisionDirector::Instance();
301 colDir->pushCollisionEntry(dist.length(), maskOut,
303 colDir->setCurrentCollisionVariant(2);
304 }
305 }
306 }
307
308 collision |= collided;
309 }
310
311 if (mask & KCL_TYPE_BIT(COL_TYPE_ROAD)) {
312 t.makeT(block->pos());
313 m_colBox->transform(t, ROAD_SCALE, EGG::Vector3f::zero);
314
315 EGG::Vector3f dist;
316 bool collided = m_colSphere->check(*m_colBox, dist);
317
318 if (collided) {
319 EGG::Vector3f distNrm = dist;
320 distNrm.normalise();
321
322 if (0.9f >= distNrm.y) {
323 collided = false;
324 } else {
325 if (info) {
326 info->update(dist);
327 }
328
329 if (maskOut) {
330 auto *colDir = CollisionDirector::Instance();
331 colDir->pushCollisionEntry(dist.length(), maskOut,
333 }
334 }
335 }
336
337 collision |= collided;
338 }
339 }
340 }
341
342 return collision;
343}
344
346bool ObjectObakeManager::checkSphereFullImpl(f32 radius, const EGG::Vector3f &pos,
347 const EGG::Vector3f & /*prevPos*/, KCLTypeMask mask, CollisionInfo *info,
348 KCLTypeMask *maskOut) {
349 bool collision = false;
350 auto [spatialX, spatialZ] = SpatialIndex(pos);
351
353 t.makeT(pos);
354 m_colSphere->transform(t, EGG::Vector3f(radius, radius, radius), EGG::Vector3f::zero);
355
356 for (s32 i = spatialZ - 1; i <= spatialZ + 1; ++i) {
357 for (s32 j = spatialX - 1; j <= spatialX + 1; ++j) {
358 // Make sure we're in bounds of the cache
359 if (j < 0 || static_cast<size_t>(j) >= CACHE_SIZE_X || i < 0 ||
360 static_cast<size_t>(i) >= CACHE_SIZE_Z) {
361 continue;
362 }
363
364 auto *block = m_blockCache[i][j];
365 if (!block) {
366 continue;
367 }
368
369 // Bonking on top of block
371 t.makeT(block->pos());
372 m_colBox->setBoundingRadius(SPECIAL_WALL_BOUNDING_RADIUS);
373 m_colBox->transform(t, SPECIAL_WALL_SCALE, EGG::Vector3f::zero);
374
375 EGG::Vector3f dist;
376 bool collided = m_colSphere->check(*m_colBox, dist);
377
378 if (collided) {
379 EGG::Vector3f distNrm = dist;
380 distNrm.normalise();
381
382 if (0.0f > distNrm.y || distNrm.y > 0.9f) {
383 collided = false;
384 } else {
385 if (info) {
386 info->update(dist.length(), dist, distNrm, KCL_TYPE_WALL);
387 }
388
389 if (maskOut) {
391 }
392 }
393 }
394
395 collision |= collided;
396 }
397
398 if (mask & KCL_TYPE_BIT(COL_TYPE_ROAD)) {
399 t.makeT(block->pos());
400 m_colBox->transform(t, ROAD_SCALE, EGG::Vector3f::zero);
401
402 EGG::Vector3f dist;
403 bool collided = m_colSphere->check(*m_colBox, dist);
404
405 if (collided) {
406 EGG::Vector3f distNrm = dist;
407 distNrm.normalise();
408
409 if (0.9f >= distNrm.y) {
410 collided = false;
411 } else {
412 if (info) {
413 info->update(dist.length(), dist, distNrm, KCL_TYPE_FLOOR);
414 }
415
416 if (maskOut) {
417 *maskOut |= KCL_TYPE_BIT(COL_TYPE_ROAD);
418 }
419 }
420 }
421
422 collision |= collided;
423 }
424 }
425 }
426
427 return collision;
428}
429
431bool ObjectObakeManager::checkSphereFullPushImpl(f32 radius, const EGG::Vector3f &pos,
432 const EGG::Vector3f & /*prevPos*/, KCLTypeMask mask, CollisionInfo *info,
433 KCLTypeMask *maskOut) {
434 bool collision = false;
435 auto [spatialX, spatialZ] = SpatialIndex(pos);
436
438 t.makeT(pos);
439 m_colSphere->transform(t, EGG::Vector3f(radius, radius, radius), EGG::Vector3f::zero);
440
441 for (s32 i = spatialZ - 1; i <= spatialZ + 1; ++i) {
442 for (s32 j = spatialX - 1; j <= spatialX + 1; ++j) {
443 // Make sure we're in bounds of the cache
444 if (j < 0 || static_cast<size_t>(j) >= CACHE_SIZE_X || i < 0 ||
445 static_cast<size_t>(i) >= CACHE_SIZE_Z) {
446 continue;
447 }
448
449 auto *block = m_blockCache[i][j];
450 if (!block) {
451 continue;
452 }
453
454 // Bonking on top of block
456 t.makeT(block->pos());
457 m_colBox->setBoundingRadius(SPECIAL_WALL_BOUNDING_RADIUS);
458 m_colBox->transform(t, SPECIAL_WALL_SCALE, EGG::Vector3f::zero);
459
460 EGG::Vector3f dist;
461 bool collided = m_colSphere->check(*m_colBox, dist);
462
463 if (collided) {
464 EGG::Vector3f distNrm = dist;
465 distNrm.normalise();
466
467 if (0.0f > distNrm.y || distNrm.y > 0.9f) {
468 collided = false;
469 } else {
470 if (info) {
471 info->update(dist.length(), dist, distNrm, KCL_TYPE_WALL);
472 }
473
474 if (maskOut) {
475 auto *colDir = CollisionDirector::Instance();
476 colDir->pushCollisionEntry(dist.length(), maskOut,
478 colDir->setCurrentCollisionVariant(2);
479 }
480 }
481 }
482
483 collision |= collided;
484 }
485
486 if (mask & KCL_TYPE_BIT(COL_TYPE_ROAD)) {
487 t.makeT(block->pos());
488 m_colBox->transform(t, ROAD_SCALE, EGG::Vector3f::zero);
489
490 EGG::Vector3f dist;
491 bool collided = m_colSphere->check(*m_colBox, dist);
492
493 if (collided) {
494 EGG::Vector3f distNrm = dist;
495 distNrm.normalise();
496
497 if (0.9f >= distNrm.y) {
498 collided = false;
499 } else {
500 if (info) {
501 info->update(dist.length(), dist, distNrm, KCL_TYPE_FLOOR);
502 }
503
504 if (maskOut) {
505 auto *colDir = CollisionDirector::Instance();
506 colDir->pushCollisionEntry(dist.length(), maskOut,
508 }
509 }
510 }
511
512 collision |= collided;
513 }
514 }
515 }
516
517 return collision;
518}
519
521std::pair<s32, s32> ObjectObakeManager::SpatialIndex(const EGG::Vector3f &pos) {
522 constexpr f32 ORIGIN_OFFSET_X = -30647.498f;
523 constexpr f32 ORIGIN_OFFSET_Z = -21092.5f;
524 constexpr f32 GRID_WIDTH = 325.0f; // The "width" of each cell in the spatial grid
525 constexpr f32 GRID_HALF_WIDTH = 162.5f;
526
527 s32 x = (pos.x - ORIGIN_OFFSET_X + GRID_HALF_WIDTH) / GRID_WIDTH;
528 s32 z = (pos.z - ORIGIN_OFFSET_Z + GRID_HALF_WIDTH) / GRID_WIDTH;
529
530 return std::make_pair(x, z);
531}
532
533} // namespace Field
@ COL_TYPE_SPECIAL_WALL
Various other wall types, determined by variant.
@ COL_TYPE_ROAD
Default road.
#define KCL_TYPE_BIT(x)
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
#define KCL_TYPE_WALL
0xD010F000
A 3 x 4 matrix.
Definition Matrix.hh:8
Pertains to collision.
A 3D float vector.
Definition Vector.hh:87
f32 normalise()
Normalizes the vector and returns the original length.
Definition Vector.cc:44
f32 length() const
The square root of the vector's dot product.
Definition Vector.hh:191