A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
CourseColMgr.cc
1#include "CourseColMgr.hh"
2
3#include "game/field/CollisionDirector.hh"
4
5#include "game/system/ResourceManager.hh"
6
7// Credit: em-eight/mkw
8
9namespace Kinoko::Field {
10
12void CourseColMgr::init() {
13 // In the base game, this file is loaded in CollisionDirector::CreateInstance and passed into
14 // this function. It's simpler to just keep it here.
15 void *file = LoadFile("course.kcl");
16 m_data = EGG::egg_new<KColData>(file);
17}
18
20void CourseColMgr::scaledNarrowScopeLocal(f32 scale, f32 radius, KColData *data,
21 const EGG::Vector3f &pos, KCLTypeMask mask) {
22 if (!data) {
23 data = m_data;
24 }
25
26 f32 invScale = 1.0f / scale;
27 data->narrowScopeLocal(pos * invScale, radius * invScale, mask);
28}
29
31bool CourseColMgr::checkPointPartial(f32 scale, KColData *data, const EGG::Vector3f &v0,
32 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfoPartial *info,
33 KCLTypeMask *maskOut) {
34 if (!data) {
35 data = m_data;
36 }
37
38 m_kclScale = scale;
39
40 f32 invScale = 1.0f / scale;
41 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
42
43 if (info) {
44 return doCheckWithPartialInfo(data, &KColData::checkPointCollision, info, maskOut);
45 }
46
47 return doCheckMaskOnly(data, &KColData::checkPointCollision, maskOut);
48}
49
51bool CourseColMgr::checkPointPartialPush(f32 scale, KColData *data, const EGG::Vector3f &v0,
52 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfoPartial *info,
53 KCLTypeMask *maskOut) {
54 if (!data) {
55 data = m_data;
56 }
57
58 m_kclScale = scale;
59
60 f32 invScale = 1.0f / scale;
61 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
62
63 if (info) {
64 return doCheckWithPartialInfoPush(data, &KColData::checkPointCollision, info, maskOut);
65 }
66 return doCheckMaskOnlyPush(data, &KColData::checkPointCollision, maskOut);
67}
68
70bool CourseColMgr::checkPointFull(f32 scale, KColData *data, const EGG::Vector3f &v0,
71 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut) {
72 if (!data) {
73 data = m_data;
74 }
75
76 m_kclScale = scale;
77
78 f32 invScale = 1.0f / scale;
79 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
80
81 if (info) {
82 return doCheckWithFullInfo(data, &KColData::checkPointCollision, info, maskOut);
83 }
84 return doCheckMaskOnly(data, &KColData::checkPointCollision, maskOut);
85}
86
88bool CourseColMgr::checkPointFullPush(f32 scale, KColData *data, const EGG::Vector3f &v0,
89 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut) {
90 if (!data) {
91 data = m_data;
92 }
93
94 m_kclScale = scale;
95
96 f32 invScale = 1.0f / scale;
97 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
98
99 if (info) {
100 return doCheckWithFullInfoPush(data, &KColData::checkPointCollision, info, maskOut);
101 }
102 return doCheckMaskOnlyPush(data, &KColData::checkPointCollision, maskOut);
103}
104
106bool CourseColMgr::checkSpherePartial(f32 scale, f32 radius, KColData *data,
107 const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask mask,
108 CollisionInfoPartial *info, KCLTypeMask *maskOut) {
109 if (!data) {
110 data = m_data;
111 }
112
113 m_kclScale = scale;
114
115 f32 invScale = 1.0f / scale;
116 data->lookupSphere(radius * invScale, v0 * invScale, v1 * invScale, mask);
117
118 if (info) {
119 return doCheckWithPartialInfo(data, &KColData::checkSphereCollision, info, maskOut);
120 }
121 return doCheckMaskOnly(data, &KColData::checkSphereCollision, maskOut);
122}
123
125bool CourseColMgr::checkSpherePartialPush(f32 scale, f32 radius, KColData *data,
126 const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask mask,
127 CollisionInfoPartial *info, KCLTypeMask *maskOut) {
128 if (!data) {
129 data = m_data;
130 }
131
132 m_kclScale = scale;
133
134 f32 invScale = 1.0f / scale;
135 data->lookupSphere(radius * invScale, v0 * invScale, v1 * invScale, mask);
136
137 if (info) {
138 return doCheckWithPartialInfoPush(data, &KColData::checkSphereCollision, info, maskOut);
139 }
140 return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, maskOut);
141}
142
144bool CourseColMgr::checkSphereFull(f32 scale, f32 radius, KColData *data, const EGG::Vector3f &v0,
145 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut) {
146 if (!data) {
147 data = m_data;
148 }
149
150 m_kclScale = scale;
151
152 f32 invScale = 1.0f / scale;
153 data->lookupSphere(radius * invScale, v0 * invScale, v1 * invScale, mask);
154
155 if (info) {
156 return doCheckWithFullInfo(data, &KColData::checkSphereCollision, info, maskOut);
157 }
158 return doCheckMaskOnly(data, &KColData::checkSphereCollision, maskOut);
159}
160
162bool CourseColMgr::checkSphereFullPush(f32 scale, f32 radius, KColData *data,
163 const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *info,
164 KCLTypeMask *maskOut) {
165 if (!data) {
166 data = m_data;
167 }
168
169 m_kclScale = scale;
170
171 f32 invScale = 1.0f / scale;
172 data->lookupSphere(radius * invScale, v0 * invScale, v1 * invScale, mask);
173
174 if (info) {
175 return doCheckWithFullInfoPush(data, &KColData::checkSphereCollision, info, maskOut);
176 }
177 return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, maskOut);
178}
179
181bool CourseColMgr::checkPointCachedPartial(f32 scale, KColData *data, const EGG::Vector3f &v0,
182 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfoPartial *info,
183 KCLTypeMask *maskOut) {
184 if (!data) {
185 data = m_data;
186 }
187
188 if (data->prismCache(0) == 0) {
189 return false;
190 }
191
192 m_kclScale = scale;
193
194 f32 invScale = 1.0f / scale;
195 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
196
197 if (info) {
198 return doCheckWithPartialInfo(data, &KColData::checkPointCollision, info, maskOut);
199 }
200 return doCheckMaskOnly(data, &KColData::checkPointCollision, maskOut);
201}
202
204bool CourseColMgr::checkPointCachedPartialPush(f32 scale, KColData *data, const EGG::Vector3f &v0,
205 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfoPartial *info,
206 KCLTypeMask *maskOut) {
207 if (!data) {
208 data = m_data;
209 }
210
211 if (data->prismCache(0) == 0) {
212 return false;
213 }
214
215 m_kclScale = scale;
216
217 f32 invScale = 1.0f / scale;
218 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
219
220 if (info) {
221 return doCheckWithPartialInfoPush(data, &KColData::checkPointCollision, info, maskOut);
222 }
223 return doCheckMaskOnlyPush(data, &KColData::checkPointCollision, maskOut);
224}
225
227bool CourseColMgr::checkPointCachedFull(f32 scale, KColData *data, const EGG::Vector3f &v0,
228 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *pInfo, KCLTypeMask *maskOut) {
229 if (!data) {
230 data = m_data;
231 }
232
233 if (data->prismCache(0) == 0) {
234 return false;
235 }
236
237 m_kclScale = scale;
238
239 f32 invScale = 1.0f / scale;
240 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
241
242 if (pInfo) {
243 return doCheckWithFullInfo(data, &KColData::checkPointCollision, pInfo, maskOut);
244 }
245 return doCheckMaskOnly(data, &KColData::checkPointCollision, maskOut);
246}
247
249bool CourseColMgr::checkPointCachedFullPush(f32 scale, KColData *data, const EGG::Vector3f &v0,
250 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *pInfo, KCLTypeMask *maskOut) {
251 if (!data) {
252 data = m_data;
253 }
254
255 if (data->prismCache(0) == 0) {
256 return false;
257 }
258
259 m_kclScale = scale;
260
261 f32 invScale = 1.0f / scale;
262 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
263
264 if (pInfo) {
265 return doCheckWithFullInfoPush(data, &KColData::checkPointCollision, pInfo, maskOut);
266 }
267 return doCheckMaskOnlyPush(data, &KColData::checkPointCollision, maskOut);
268}
269
271bool CourseColMgr::checkSphereCachedPartial(f32 scale, f32 radius, KColData *data,
272 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
273 CollisionInfoPartial *info, KCLTypeMask *maskOut) {
274 if (!data) {
275 data = m_data;
276 }
277
278 if (data->prismCache(0) == 0) {
279 return false;
280 }
281
282 m_kclScale = scale;
283
284 f32 invScale = 1.0f / scale;
285 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
286
287 if (info) {
288 return doCheckWithPartialInfo(data, &KColData::checkSphereCollision, info, maskOut);
289 }
290
291 return doCheckMaskOnly(data, &KColData::checkSphereCollision, maskOut);
292}
293
295bool CourseColMgr::checkSphereCachedPartialPush(f32 scale, f32 radius, KColData *data,
296 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
297 CollisionInfoPartial *info, KCLTypeMask *maskOut) {
298 if (!data) {
299 data = m_data;
300 }
301
302 if (data->prismCache(0) == 0) {
303 return false;
304 }
305
306 m_kclScale = scale;
307
308 f32 invScale = 1.0f / scale;
309 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
310
311 if (info) {
312 return doCheckWithPartialInfoPush(data, &KColData::checkSphereCollision, info, maskOut);
313 }
314
315 return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, maskOut);
316}
317
319bool CourseColMgr::checkSphereCachedFull(f32 scale, f32 radius, KColData *data,
320 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
321 CollisionInfo *pInfo, KCLTypeMask *maskOut) {
322 if (!data) {
323 data = m_data;
324 }
325
326 if (data->prismCache(0) == 0) {
327 return false;
328 }
329
330 m_kclScale = scale;
331
332 f32 invScale = 1.0f / scale;
333 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
334
335 if (pInfo) {
336 return doCheckWithFullInfo(data, &KColData::checkSphereCollision, pInfo, maskOut);
337 }
338
339 return doCheckMaskOnly(data, &KColData::checkSphereCollision, maskOut);
340}
341
343bool CourseColMgr::checkSphereCachedFullPush(f32 scale, f32 radius, KColData *data,
344 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
345 CollisionInfo *colInfo, KCLTypeMask *maskOut) {
346 if (!data) {
347 data = m_data;
348 }
349
350 if (data->prismCache(0) == 0) {
351 return false;
352 }
353
354 m_kclScale = scale;
355
356 f32 invScale = 1.0f / scale;
357 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
358
359 if (colInfo) {
360 return doCheckWithFullInfoPush(data, &KColData::checkSphereCollision, colInfo, maskOut);
361 }
362
363 return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, maskOut);
364}
365
367void *CourseColMgr::LoadFile(const char *filename) {
368 auto *resMgr = System::ResourceManager::Instance();
369 return resMgr->getFile(filename, nullptr, System::ArchiveId::Course);
370}
371
373CourseColMgr *CourseColMgr::CreateInstance() {
374 ASSERT(!s_instance);
375 s_instance = EGG::egg_new<CourseColMgr>();
376 return s_instance;
377}
378
380void CourseColMgr::DestroyInstance() {
381 ASSERT(s_instance);
382 auto *instance = s_instance;
383 s_instance = nullptr;
384 EGG::egg_delete(instance);
385}
386
388CourseColMgr::CourseColMgr()
389 : m_data(nullptr), m_kclScale(1.0f), m_noBounceWallInfo(nullptr), m_localMtx(nullptr) {}
390
392CourseColMgr::~CourseColMgr() {
393 if (s_instance) {
394 s_instance = nullptr;
395 WARN("CourseColMgr instance not explicitly handled!");
396 }
397
398 ASSERT(m_data);
399 EGG::egg_delete(m_data);
400}
401
403bool CourseColMgr::doCheckWithPartialInfo(KColData *data, CollisionCheckFunc collisionCheckFunc,
404 CollisionInfoPartial *info, KCLTypeMask *typeMask) {
405 f32 dist;
406 EGG::Vector3f fnrm;
407 u16 attribute;
408 bool hasCol = false;
409
410 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
411 hasCol = true;
412 dist *= m_kclScale;
413
414 if (m_noBounceWallInfo && (attribute & KCL_SOFT_WALL_MASK)) {
415 if (m_localMtx) {
416 fnrm = m_localMtx->multVector33(fnrm);
417 }
418 EGG::Vector3f offset = fnrm * dist;
419 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
420 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
421 if (m_noBounceWallInfo->dist < dist) {
422 m_noBounceWallInfo->dist = dist;
423 m_noBounceWallInfo->fnrm = fnrm;
424 }
425 } else {
426 u32 flags = KCL_ATTRIBUTE_TYPE_BIT(attribute);
427 if (typeMask) {
428 *typeMask = *typeMask | flags;
429 }
430 if (flags & KCL_TYPE_SOLID_SURFACE) {
431 EGG::Vector3f offset = fnrm * dist;
432 info->updateBBox(offset);
433 }
434 }
435 }
436
437 m_localMtx = nullptr;
438
439 return hasCol;
440}
441
443bool CourseColMgr::doCheckWithPartialInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc,
444 CollisionInfoPartial *info, KCLTypeMask *typeMask) {
445 f32 dist;
446 EGG::Vector3f fnrm;
447 u16 attribute;
448 bool hasCol = false;
449
450 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
451 hasCol = true;
452 dist *= m_kclScale;
453
454 if (!m_noBounceWallInfo || !(attribute & KCL_SOFT_WALL_MASK)) {
455 u32 flags = KCL_ATTRIBUTE_TYPE_BIT(attribute);
456 if (typeMask) {
457 CollisionDirector::Instance()->pushCollisionEntry(dist, typeMask, flags, attribute);
458 }
459 if (flags & KCL_TYPE_SOLID_SURFACE) {
460 EGG::Vector3f offset = fnrm * dist;
461 info->updateBBox(offset);
462 }
463 } else {
464 if (m_localMtx) {
465 fnrm = m_localMtx->multVector33(fnrm);
466 }
467 EGG::Vector3f offset = fnrm * dist;
468 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
469 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
470 if (m_noBounceWallInfo->dist < dist) {
471 m_noBounceWallInfo->dist = dist;
472 m_noBounceWallInfo->fnrm = fnrm;
473 }
474 }
475 }
476
477 m_localMtx = nullptr;
478
479 return hasCol;
480}
481
483bool CourseColMgr::doCheckWithFullInfo(KColData *data, CollisionCheckFunc collisionCheckFunc,
484 CollisionInfo *colInfo, KCLTypeMask *flagsOut) {
485 f32 dist;
486 EGG::Vector3f fnrm;
487 u16 attribute;
488 bool hasCol = false;
489
490 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
491 dist *= m_kclScale;
492
493 if (m_noBounceWallInfo && attribute & KCL_SOFT_WALL_MASK) {
494 if (m_localMtx) {
495 fnrm = m_localMtx->multVector33(fnrm);
496 }
497 EGG::Vector3f offset = fnrm * dist;
498 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
499 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
500 if (m_noBounceWallInfo->dist < dist) {
501 m_noBounceWallInfo->dist = dist;
502 m_noBounceWallInfo->fnrm = fnrm;
503 }
504 } else {
505 u32 kclAttributeTypeBit = KCL_ATTRIBUTE_TYPE_BIT(attribute);
506 if (flagsOut) {
507 *flagsOut |= kclAttributeTypeBit;
508 }
509 if (kclAttributeTypeBit & KCL_TYPE_SOLID_SURFACE) {
510 colInfo->update(dist, fnrm * dist, fnrm, kclAttributeTypeBit);
511 }
512 }
513
514 hasCol = true;
515 }
516
517 m_localMtx = nullptr;
518
519 return hasCol;
520}
521
523bool CourseColMgr::doCheckWithFullInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc,
524 CollisionInfo *colInfo, KCLTypeMask *flagsOut) {
525 f32 dist;
526 EGG::Vector3f fnrm;
527 u16 attribute;
528 bool hasCol = false;
529
530 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
531 dist *= m_kclScale;
532
533 if (m_noBounceWallInfo && attribute & KCL_SOFT_WALL_MASK) {
534 if (m_localMtx) {
535 fnrm = m_localMtx->multVector33(fnrm);
536 }
537 EGG::Vector3f offset = fnrm * dist;
538 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
539 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
540 if (m_noBounceWallInfo->dist < dist) {
541 m_noBounceWallInfo->dist = dist;
542 m_noBounceWallInfo->fnrm = fnrm;
543 }
544 } else {
545 u32 kclAttributeTypeBit = KCL_ATTRIBUTE_TYPE_BIT(attribute);
546 if (flagsOut) {
547 CollisionDirector::Instance()->pushCollisionEntry(dist, flagsOut,
548 kclAttributeTypeBit, attribute);
549 }
550 if (kclAttributeTypeBit & KCL_TYPE_SOLID_SURFACE) {
551 colInfo->update(dist, fnrm * dist, fnrm, kclAttributeTypeBit);
552 }
553 }
554
555 hasCol = true;
556 }
557
558 m_localMtx = nullptr;
559
560 return hasCol;
561}
562
563bool CourseColMgr::doCheckMaskOnly(KColData *data, CollisionCheckFunc collisionCheckFunc,
564 KCLTypeMask *maskOut) {
565 bool hasCol = false;
566 f32 dist;
567 u16 attribute;
568
569 while ((data->*collisionCheckFunc)(&dist, nullptr, &attribute)) {
570 if ((!m_noBounceWallInfo || !(attribute & KCL_SOFT_WALL_MASK)) && maskOut) {
571 *maskOut |= KCL_ATTRIBUTE_TYPE_BIT(attribute);
572 }
573 hasCol = true;
574 }
575
576 return hasCol;
577}
578
579bool CourseColMgr::doCheckMaskOnlyPush(KColData *data, CollisionCheckFunc collisionCheckFunc,
580 KCLTypeMask *maskOut) {
581 bool hasCol = false;
582 f32 dist;
583 u16 attribute;
584
585 while ((data->*collisionCheckFunc)(&dist, nullptr, &attribute)) {
586 if ((!m_noBounceWallInfo || !(attribute & KCL_SOFT_WALL_MASK)) && maskOut) {
587 CollisionDirector::Instance()->pushCollisionEntry(dist, maskOut,
588 KCL_ATTRIBUTE_TYPE_BIT(attribute), attribute);
589 }
590 hasCol = true;
591 }
592
593 return hasCol;
594}
595
596CourseColMgr *CourseColMgr::s_instance = nullptr;
597
598} // namespace Kinoko::Field
#define KCL_ATTRIBUTE_TYPE_BIT(x)
Given the full 2 byte KCL flag for a triangle, extracts the "Base Type" portion of the flag.
#define KCL_TYPE_SOLID_SURFACE
0xF0F8FFFF
Manager for course KCL interactions.
static void * LoadFile(const char *filename)
Loads a particular section of a .szs file.
Performs lookups for KCL triangles.
Definition KColData.hh:68
Pertains to collision.