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