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 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 = 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 m_kclScale = scale;
189
190 f32 invScale = 1.0f / scale;
191 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
192
193 if (info) {
194 return doCheckWithPartialInfo(data, &KColData::checkPointCollision, info, maskOut);
195 }
196 return doCheckMaskOnly(data, &KColData::checkPointCollision, maskOut);
197}
198
200bool CourseColMgr::checkPointCachedPartialPush(f32 scale, KColData *data, const EGG::Vector3f &v0,
201 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfoPartial *info,
202 KCLTypeMask *maskOut) {
203 if (!data) {
204 data = m_data;
205 }
206
207 if (data->prismCache(0) == 0) {
208 return false;
209 }
210
211 m_kclScale = scale;
212
213 f32 invScale = 1.0f / scale;
214 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
215
216 if (info) {
217 return doCheckWithPartialInfoPush(data, &KColData::checkPointCollision, info, maskOut);
218 }
219 return doCheckMaskOnlyPush(data, &KColData::checkPointCollision, maskOut);
220}
221
223bool CourseColMgr::checkPointCachedFull(f32 scale, KColData *data, const EGG::Vector3f &v0,
224 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *pInfo, KCLTypeMask *maskOut) {
225 if (!data) {
226 data = m_data;
227 }
228
229 if (data->prismCache(0) == 0) {
230 return false;
231 }
232
233 m_kclScale = scale;
234
235 f32 invScale = 1.0f / scale;
236 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
237
238 if (pInfo) {
239 return doCheckWithFullInfo(data, &KColData::checkPointCollision, pInfo, maskOut);
240 }
241 return doCheckMaskOnly(data, &KColData::checkPointCollision, maskOut);
242}
243
245bool CourseColMgr::checkPointCachedFullPush(f32 scale, KColData *data, const EGG::Vector3f &v0,
246 const EGG::Vector3f &v1, KCLTypeMask mask, CollisionInfo *pInfo, KCLTypeMask *maskOut) {
247 if (!data) {
248 data = m_data;
249 }
250
251 if (data->prismCache(0) == 0) {
252 return false;
253 }
254
255 m_kclScale = scale;
256
257 f32 invScale = 1.0f / scale;
258 data->lookupPoint(v0 * invScale, v1 * invScale, mask);
259
260 if (pInfo) {
261 return doCheckWithFullInfoPush(data, &KColData::checkPointCollision, pInfo, maskOut);
262 }
263 return doCheckMaskOnlyPush(data, &KColData::checkPointCollision, maskOut);
264}
265
267bool CourseColMgr::checkSphereCachedPartial(f32 scale, f32 radius, KColData *data,
268 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
269 CollisionInfoPartial *info, KCLTypeMask *maskOut) {
270 if (!data) {
271 data = m_data;
272 }
273
274 if (data->prismCache(0) == 0) {
275 return false;
276 }
277
278 m_kclScale = scale;
279
280 f32 invScale = 1.0f / scale;
281 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
282
283 if (info) {
284 return doCheckWithPartialInfo(data, &KColData::checkSphereCollision, info, maskOut);
285 }
286
287 return doCheckMaskOnly(data, &KColData::checkSphereCollision, maskOut);
288}
289
291bool CourseColMgr::checkSphereCachedPartialPush(f32 scale, f32 radius, KColData *data,
292 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
293 CollisionInfoPartial *info, KCLTypeMask *maskOut) {
294 if (!data) {
295 data = m_data;
296 }
297
298 if (data->prismCache(0) == 0) {
299 return false;
300 }
301
302 m_kclScale = scale;
303
304 f32 invScale = 1.0f / scale;
305 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
306
307 if (info) {
308 return doCheckWithPartialInfoPush(data, &KColData::checkSphereCollision, info, maskOut);
309 }
310
311 return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, maskOut);
312}
313
315bool CourseColMgr::checkSphereCachedFull(f32 scale, f32 radius, KColData *data,
316 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
317 CollisionInfo *pInfo, KCLTypeMask *maskOut) {
318 if (!data) {
319 data = m_data;
320 }
321
322 if (data->prismCache(0) == 0) {
323 return false;
324 }
325
326 m_kclScale = scale;
327
328 f32 invScale = 1.0f / scale;
329 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
330
331 if (pInfo) {
332 return doCheckWithFullInfo(data, &KColData::checkSphereCollision, pInfo, maskOut);
333 }
334
335 return doCheckMaskOnly(data, &KColData::checkSphereCollision, maskOut);
336}
337
339bool CourseColMgr::checkSphereCachedFullPush(f32 scale, f32 radius, KColData *data,
340 const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask,
341 CollisionInfo *colInfo, KCLTypeMask *maskOut) {
342 if (!data) {
343 data = m_data;
344 }
345
346 if (data->prismCache(0) == 0) {
347 return false;
348 }
349
350 m_kclScale = scale;
351
352 f32 invScale = 1.0f / scale;
353 data->lookupSphereCached(pos * invScale, prevPos * invScale, mask, radius * invScale);
354
355 if (colInfo) {
356 return doCheckWithFullInfoPush(data, &KColData::checkSphereCollision, colInfo, maskOut);
357 }
358
359 return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, maskOut);
360}
361
363void *CourseColMgr::LoadFile(const char *filename) {
364 auto *resMgr = System::ResourceManager::Instance();
365 return resMgr->getFile(filename, nullptr, System::ArchiveId::Course);
366}
367
369CourseColMgr *CourseColMgr::CreateInstance() {
370 ASSERT(!s_instance);
371 s_instance = new CourseColMgr;
372 return s_instance;
373}
374
376void CourseColMgr::DestroyInstance() {
377 ASSERT(s_instance);
378 auto *instance = s_instance;
379 s_instance = nullptr;
380 delete instance;
381}
382
384CourseColMgr::CourseColMgr()
385 : m_data(nullptr), m_kclScale(1.0f), m_noBounceWallInfo(nullptr), m_localMtx(nullptr) {}
386
388CourseColMgr::~CourseColMgr() {
389 if (s_instance) {
390 s_instance = nullptr;
391 WARN("CourseColMgr instance not explicitly handled!");
392 }
393
394 ASSERT(m_data);
395 delete m_data;
396}
397
399bool CourseColMgr::doCheckWithPartialInfo(KColData *data, CollisionCheckFunc collisionCheckFunc,
400 CollisionInfoPartial *info, KCLTypeMask *typeMask) {
401 f32 dist;
402 EGG::Vector3f fnrm;
403 u16 attribute;
404 bool hasCol = false;
405
406 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
407 hasCol = true;
408 dist *= m_kclScale;
409
410 if (m_noBounceWallInfo && (attribute & KCL_SOFT_WALL_MASK)) {
411 if (m_localMtx) {
412 fnrm = m_localMtx->multVector33(fnrm);
413 }
414 EGG::Vector3f offset = fnrm * dist;
415 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
416 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
417 if (m_noBounceWallInfo->dist < dist) {
418 m_noBounceWallInfo->dist = dist;
419 m_noBounceWallInfo->fnrm = fnrm;
420 }
421 } else {
422 u32 flags = KCL_ATTRIBUTE_TYPE_BIT(attribute);
423 if (typeMask) {
424 *typeMask = *typeMask | flags;
425 }
426 if (flags & KCL_TYPE_SOLID_SURFACE) {
427 EGG::Vector3f offset = fnrm * dist;
428 info->bbox.min = info->bbox.min.minimize(offset);
429 info->bbox.max = info->bbox.max.maximize(offset);
430 }
431 }
432 }
433
434 m_localMtx = nullptr;
435
436 return hasCol;
437}
438
440bool CourseColMgr::doCheckWithPartialInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc,
441 CollisionInfoPartial *info, KCLTypeMask *typeMask) {
442 f32 dist;
443 EGG::Vector3f fnrm;
444 u16 attribute;
445 bool hasCol = false;
446
447 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
448 hasCol = true;
449 dist *= m_kclScale;
450
451 if (!m_noBounceWallInfo || !(attribute & KCL_SOFT_WALL_MASK)) {
452 u32 flags = KCL_ATTRIBUTE_TYPE_BIT(attribute);
453 if (typeMask) {
454 CollisionDirector::Instance()->pushCollisionEntry(dist, typeMask, flags, attribute);
455 }
456 if (flags & KCL_TYPE_SOLID_SURFACE) {
457 EGG::Vector3f offset = fnrm * dist;
458 info->bbox.min = info->bbox.min.minimize(offset);
459 info->bbox.max = info->bbox.max.maximize(offset);
460 }
461 } else {
462 if (m_localMtx) {
463 fnrm = m_localMtx->multVector33(fnrm);
464 }
465 EGG::Vector3f offset = fnrm * dist;
466 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
467 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
468 if (m_noBounceWallInfo->dist < dist) {
469 m_noBounceWallInfo->dist = dist;
470 m_noBounceWallInfo->fnrm = fnrm;
471 }
472 }
473 }
474
475 m_localMtx = nullptr;
476
477 return hasCol;
478}
479
481bool CourseColMgr::doCheckWithFullInfo(KColData *data, CollisionCheckFunc collisionCheckFunc,
482 CollisionInfo *colInfo, KCLTypeMask *flagsOut) {
483 f32 dist;
484 EGG::Vector3f fnrm;
485 u16 attribute;
486 bool hasCol = false;
487
488 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
489 dist *= m_kclScale;
490
491 if (m_noBounceWallInfo && attribute & KCL_SOFT_WALL_MASK) {
492 if (m_localMtx) {
493 fnrm = m_localMtx->multVector33(fnrm);
494 }
495 EGG::Vector3f offset = fnrm * dist;
496 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
497 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
498 if (m_noBounceWallInfo->dist < dist) {
499 m_noBounceWallInfo->dist = dist;
500 m_noBounceWallInfo->fnrm = fnrm;
501 }
502 } else {
503 u32 kclAttributeTypeBit = KCL_ATTRIBUTE_TYPE_BIT(attribute);
504 if (flagsOut) {
505 *flagsOut |= kclAttributeTypeBit;
506 }
507 if (kclAttributeTypeBit & KCL_TYPE_SOLID_SURFACE) {
508 colInfo->update(dist, fnrm * dist, fnrm, kclAttributeTypeBit);
509 }
510 }
511
512 hasCol = true;
513 }
514
515 m_localMtx = nullptr;
516
517 return hasCol;
518}
519
521bool CourseColMgr::doCheckWithFullInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc,
522 CollisionInfo *colInfo, KCLTypeMask *flagsOut) {
523 f32 dist;
524 EGG::Vector3f fnrm;
525 u16 attribute;
526 bool hasCol = false;
527
528 while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) {
529 dist *= m_kclScale;
530
531 if (m_noBounceWallInfo && attribute & KCL_SOFT_WALL_MASK) {
532 if (m_localMtx) {
533 fnrm = m_localMtx->multVector33(fnrm);
534 }
535 EGG::Vector3f offset = fnrm * dist;
536 m_noBounceWallInfo->bbox.min = m_noBounceWallInfo->bbox.min.minimize(offset);
537 m_noBounceWallInfo->bbox.max = m_noBounceWallInfo->bbox.max.maximize(offset);
538 if (m_noBounceWallInfo->dist < dist) {
539 m_noBounceWallInfo->dist = dist;
540 m_noBounceWallInfo->fnrm = fnrm;
541 }
542 } else {
543 u32 kclAttributeTypeBit = KCL_ATTRIBUTE_TYPE_BIT(attribute);
544 if (flagsOut) {
545 CollisionDirector::Instance()->pushCollisionEntry(dist, flagsOut,
546 kclAttributeTypeBit, attribute);
547 }
548 if (kclAttributeTypeBit & KCL_TYPE_SOLID_SURFACE) {
549 colInfo->update(dist, fnrm * dist, fnrm, kclAttributeTypeBit);
550 }
551 }
552
553 hasCol = true;
554 }
555
556 m_localMtx = nullptr;
557
558 return hasCol;
559}
560
561bool CourseColMgr::doCheckMaskOnly(KColData *data, CollisionCheckFunc collisionCheckFunc,
562 KCLTypeMask *maskOut) {
563 bool hasCol = false;
564 f32 dist;
565 u16 attribute;
566
567 while ((data->*collisionCheckFunc)(&dist, nullptr, &attribute)) {
568 if ((!m_noBounceWallInfo || !(attribute & KCL_SOFT_WALL_MASK)) && maskOut) {
569 *maskOut |= KCL_ATTRIBUTE_TYPE_BIT(attribute);
570 }
571 hasCol = true;
572 }
573
574 return hasCol;
575}
576
577bool CourseColMgr::doCheckMaskOnlyPush(KColData *data, CollisionCheckFunc collisionCheckFunc,
578 KCLTypeMask *maskOut) {
579 bool hasCol = false;
580 f32 dist;
581 u16 attribute;
582
583 while ((data->*collisionCheckFunc)(&dist, nullptr, &attribute)) {
584 if ((!m_noBounceWallInfo || !(attribute & KCL_SOFT_WALL_MASK)) && maskOut) {
585 CollisionDirector::Instance()->pushCollisionEntry(dist, maskOut,
586 KCL_ATTRIBUTE_TYPE_BIT(attribute), attribute);
587 }
588 hasCol = true;
589 }
590
591 return hasCol;
592}
593
594CourseColMgr *CourseColMgr::s_instance = nullptr;
595
596} // namespace 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
Vector3f multVector33(const Vector3f &vec) const
Multiplies a 3x3 matrix by a vector.
Definition Matrix.cc:243
Manager for course KCL interactions.
static void * LoadFile(const char *filename)
Loads a particular section of a .szs file.
Pertains to collision.
A 3D float vector.
Definition Vector.hh:83
Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Definition Vector.cc:65
Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
Definition Vector.cc:77