A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
KColData.cc
1#include "KColData.hh"
2
3#include <egg/geom/Sphere.hh>
4#include <egg/math/Math.hh>
5
6#include <cmath>
7
8// Credit: em-eight/mkw
9// Credit: stblr/Hanachan
10
11namespace Kinoko::Field {
12
14KColData::KColData(const void *file) {
15 auto addOffset = [](const void *file, u32 offset) -> const void * {
16 return reinterpret_cast<const void *>(reinterpret_cast<const u8 *>(file) + offset);
17 };
18 EGG::RamStream stream = EGG::RamStream(file, sizeof(KColHeader));
19
20 u32 posOffset = stream.read_u32();
21 u32 nrmOffset = stream.read_u32();
22 u32 prismOffset = stream.read_u32();
23 u32 blockOffset = stream.read_u32();
24
25 m_posData = addOffset(file, posOffset);
26 m_nrmData = addOffset(file, nrmOffset);
27 m_prismData = addOffset(file, prismOffset);
28 m_blockData = addOffset(file, blockOffset);
29
30 m_prismThickness = stream.read_f32();
31 m_areaMinPos.read(stream);
32 m_areaXWidthMask = stream.read_u32();
33 m_areaYWidthMask = stream.read_u32();
34 m_areaZWidthMask = stream.read_u32();
35 m_blockWidthShift = stream.read_u32();
36 m_areaXBlocksShift = stream.read_u32();
37 m_areaXYBlocksShift = stream.read_u32();
38 m_sphereRadius = stream.read_f32();
39
40 m_pos.setZero();
41 m_prevPos.setZero();
42 m_movement.setZero();
43 m_radius = 0.0f;
44 m_prismIter = nullptr;
45 m_cachedPrismArray = m_prismCache.data() - 1;
46
47 // NOTE: Collision is expensive on the CPU, so we preload all of the prism data to ensure we're
48 // not constantly handling endianness.
52
54}
55
56KColData::~KColData() = default;
57
59void KColData::narrowScopeLocal(const EGG::Vector3f &pos, f32 radius, KCLTypeMask mask) {
60 m_prismCacheTop = m_prismCache.data();
61 m_pos = pos;
62 m_radius = radius;
63 m_typeMask = mask;
64 m_cachedPos = pos;
65 m_cachedRadius = radius;
66
67 if (radius <= m_sphereRadius) {
69 }
70
71 *m_prismCacheTop = 0;
72}
73
75void KColData::narrowPolygon_EachBlock(const u16 *prismArray) {
76 m_prismIter = prismArray;
77
78 while (checkSphereSingle(nullptr, nullptr, nullptr)) {
81 *(m_prismCacheTop++) = *m_prismIter;
82
83 if (m_prismCacheTop == m_prismCache.end()) {
84 --m_prismCacheTop;
85 return;
86 }
87 }
88}
89
93 m_bbox.max.set(-999999.0f);
94 m_bbox.min.set(999999.0f);
95
96 for (size_t i = 1; i < m_prisms.size(); ++i) {
97 const auto &prism = m_prisms[i];
98 const EGG::Vector3f &fnrm = m_nrms[prism.fnrm_i];
99 const EGG::Vector3f &enrm1 = m_nrms[prism.enrm1_i];
100 const EGG::Vector3f &enrm2 = m_nrms[prism.enrm2_i];
101 const EGG::Vector3f &enrm3 = m_nrms[prism.enrm3_i];
102 const EGG::Vector3f &vtx1 = m_vertices[prism.pos_i];
103
104 const EGG::Vector3f vtx2 = GetVertex(prism.height, vtx1, fnrm, enrm3, enrm1);
105 const EGG::Vector3f vtx3 = GetVertex(prism.height, vtx1, fnrm, enrm3, enrm2);
106
107 m_bbox.min = m_bbox.min.minimize(vtx1);
108 m_bbox.min = m_bbox.min.minimize(vtx2);
109 m_bbox.min = m_bbox.min.minimize(vtx3);
110 m_bbox.max = m_bbox.max.maximize(vtx1);
111 m_bbox.max = m_bbox.max.maximize(vtx2);
112 m_bbox.max = m_bbox.max.maximize(vtx3);
113 }
114}
115
117bool KColData::checkPointCollision(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) {
118 return std::isfinite(m_prevPos.y) ? checkPointMovement(distOut, fnrmOut, flagsOut) :
119 checkPoint(distOut, fnrmOut, flagsOut);
120}
121
123bool KColData::checkSphereCollision(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) {
124 return std::isfinite(m_prevPos.y) ? checkSphereMovement(distOut, fnrmOut, flagsOut) :
125 checkSphere(distOut, fnrmOut, flagsOut);
126}
127
134bool KColData::checkSphere(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) {
135 // If there's no list of triangles to check, there's no collision
136 if (!m_prismIter) {
137 return false;
138 }
139
140 // Check collision for all triangles, and continuously call the function until we're out
141 while (*++m_prismIter != 0) {
142 const KCollisionPrism &prism = m_prisms[parse<u16>(*m_prismIter)];
143 if (checkCollision<CollisionCheckType::Plane>(prism, distOut, fnrmOut, flagsOut)) {
144 return true;
145 }
146 }
147
148 // We're out of triangles to check - another list must be prepared for subsequent calls
149 m_prismIter = nullptr;
150 return false;
151}
152
154bool KColData::checkSphereSingle(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) {
155 if (!m_prismIter) {
156 return false;
157 }
158
159 while (*++m_prismIter != 0) {
160 if (m_prismCacheTop != m_prismCache.begin()) {
161 u16 *puVar10 = m_prismCacheTop - 1;
162 while (*m_prismIter != *puVar10) {
163 if (puVar10-- < m_prismCache.begin()) {
164 break;
165 }
166 }
167
168 if (puVar10 >= m_prismCache.begin()) {
169 continue;
170 }
171 }
172
173 const KCollisionPrism &prism = m_prisms[parse<u16>(*m_prismIter)];
174 if (checkCollision<CollisionCheckType::Edge>(prism, distOut, fnrmOut, flagsOut)) {
175 return true;
176 }
177 }
178
179 m_prismIter = nullptr;
180 return false;
181}
182
185void KColData::lookupPoint(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos,
186 KCLTypeMask typeMask) {
187 m_prismIter = searchBlock(pos);
188 m_pos = pos;
189 m_prevPos = prevPos;
190 m_movement = pos - prevPos;
191 m_typeMask = typeMask;
192}
193
196void KColData::lookupSphere(f32 radius, const EGG::Vector3f &pos, const EGG::Vector3f &prevPos,
197 KCLTypeMask typeMask) {
198 m_prismIter = searchBlock(pos);
199 m_pos = pos;
200 m_prevPos = prevPos;
201 m_movement = pos - prevPos;
202 m_radius = std::min(radius, m_sphereRadius);
203 m_typeMask = typeMask;
204}
205
207void KColData::lookupSphereCached(const EGG::Vector3f &p1, const EGG::Vector3f &p2, u32 typeMask,
208 f32 radius) {
209 EGG::Sphere3f sphere1(p1, radius);
210 EGG::Sphere3f sphere2(m_cachedPos, m_cachedRadius);
211
212 if (!sphere1.isInsideOtherSphere(sphere2)) {
213 m_prismIter = searchBlock(p1);
214 m_radius = std::min(m_sphereRadius, radius);
215 } else {
216 m_radius = radius;
217 m_prismIter = m_cachedPrismArray;
218 }
219
220 m_pos = p1;
221 m_prevPos = p2;
222 m_movement = p1 - p2;
223 m_typeMask = typeMask;
224}
225
231 // Calculate the x, y, and z offsets of the point from the minimum
232 // corner of the tree's bounding box.
233 const int x = point.x - m_areaMinPos.x;
234 const int y = point.y - m_areaMinPos.y;
235 const int z = point.z - m_areaMinPos.z;
236
237 // Check if the point is outside the tree's bounding box in the x, y,
238 // or z dimensions. If it is, return 0.
239 if (x & m_areaXWidthMask || y & m_areaYWidthMask || z & m_areaZWidthMask) {
240 return nullptr;
241 }
242
243 // Initialize the current tree node to the root node of the tree.
244 u32 shift = m_blockWidthShift;
245 const u8 *curBlock = reinterpret_cast<const u8 *>(m_blockData);
246 s32 offset;
247
248 // Traverse the tree to find the leaf node containing the input point.
249 u32 index = 4 *
250 (((u32)z >> shift) << m_areaXYBlocksShift | ((u32)y >> shift) << m_areaXBlocksShift |
251 (u32)x >> shift);
252
253 while (true) {
254 // Get the offset of the current node's child node.
255 offset = parse<u32>(*reinterpret_cast<const u32 *>(curBlock + index));
256
257 // If the offset is negative, the current node is a leaf node.
258 if ((offset & 0x80000000) != 0) {
259 break;
260 }
261
262 // If the offset is non-negative, update the current node to be
263 // the child node and continue traversing the tree.
264 shift--;
265 curBlock += offset;
266
267 u32 x_shift = ((1 * ((u32)x >> shift)) & 1);
268 u32 y_shift = ((2 * ((u32)y >> shift)) & 2);
269 u32 z_shift = ((4 * ((u32)z >> shift)) & 4);
270
271 index = 4 * (x_shift | y_shift | z_shift);
272 }
273
274 // We have to remove the MSB since it's solely used to identify leaves.
275 return reinterpret_cast<const u16 *>(curBlock + (offset & ~0x80000000));
276}
277
289 const EGG::Vector3f &fnrm, const EGG::Vector3f &enrm3, const EGG::Vector3f &enrm) {
290 EGG::Vector3f cross = fnrm.cross(enrm);
291 f32 dp = cross.ps_dot(enrm3);
292 cross *= (height / dp);
293
294 return cross + vertex1;
295}
296
301 size_t prismCount =
302 (reinterpret_cast<uintptr_t>(m_blockData) - reinterpret_cast<uintptr_t>(m_prismData)) /
303 sizeof(KCollisionPrism);
304
305 EGG::RamStream stream = EGG::RamStream(m_prismData, sizeof(KCollisionPrism) * prismCount);
306
308
309 // Because the prisms are one-indexed, we insert an empty prism
310 stream.skip(sizeof(KCollisionPrism));
311
312 for (size_t i = 1; i < prismCount; ++i) {
313 KCollisionPrism &prism = m_prisms[i];
314 prism.height = stream.read_f32();
315 prism.pos_i = stream.read_u16();
316 prism.fnrm_i = stream.read_u16();
317 prism.enrm1_i = stream.read_u16();
318 prism.enrm2_i = stream.read_u16();
319 prism.enrm3_i = stream.read_u16();
320 prism.attribute = stream.read_u16();
321 }
322}
323
328 size_t normalCount = (reinterpret_cast<uintptr_t>(m_prismData) + sizeof(KCollisionPrism) -
329 reinterpret_cast<uintptr_t>(m_nrmData)) /
330 sizeof(EGG::Vector3f);
331
332 m_nrms = owning_span<EGG::Vector3f>(normalCount);
333 EGG::RamStream stream = EGG::RamStream(m_nrmData, sizeof(EGG::Vector3f) * normalCount);
334
335 for (auto &nrm : m_nrms) {
336 nrm.read(stream);
337 }
338}
339
344 size_t vertexCount =
345 (reinterpret_cast<uintptr_t>(m_nrmData) - reinterpret_cast<uintptr_t>(m_posData)) /
346 sizeof(EGG::Vector3f);
347
348 m_vertices = owning_span<EGG::Vector3f>(vertexCount);
349 EGG::RamStream stream = EGG::RamStream(m_posData, sizeof(EGG::Vector3f) * vertexCount);
350
351 for (auto &vert : m_vertices) {
352 vert.read(stream);
353 }
354}
355
361template <KColData::CollisionCheckType Type>
362bool KColData::checkCollision(const KCollisionPrism &prism, f32 *distOut, EGG::Vector3f *fnrmOut,
363 u16 *flagsOut) {
364 // Responsible for updating the output params
365 auto out = [&](f32 dist) {
366 if (distOut) {
367 *distOut = dist;
368 }
369 if (fnrmOut) {
370 *fnrmOut = m_nrms[prism.fnrm_i];
371 }
372 if (flagsOut) {
373 *flagsOut = prism.attribute;
374 }
375 return true;
376 };
377
378 // The flag check occurs earlier than in the base game here. We don't want to do math if the tri
379 // we're checking doesn't have matching flags.
380 u32 attributeMask = KCL_ATTRIBUTE_TYPE_BIT(prism.attribute);
381 if (!(attributeMask & m_typeMask)) {
382 return false;
383 }
384
385 const EGG::Vector3f relativePos = m_pos - m_vertices[prism.pos_i];
386
387 // Edge normals point outside the triangle
388 const EGG::Vector3f &enrm1 = m_nrms[prism.enrm1_i];
389 f32 dist_ca = relativePos.ps_dot(enrm1);
390 if (m_radius <= dist_ca) {
391 return false;
392 }
393
394 const EGG::Vector3f &enrm2 = m_nrms[prism.enrm2_i];
395 f32 dist_ab = relativePos.ps_dot(enrm2);
396 if (m_radius <= dist_ab) {
397 return false;
398 }
399
400 const EGG::Vector3f &enrm3 = m_nrms[prism.enrm3_i];
401 f32 dist_bc = relativePos.ps_dot(enrm3) - prism.height;
402 if (m_radius <= dist_bc) {
403 return false;
404 }
405
406 const EGG::Vector3f &fnrm = m_nrms[prism.fnrm_i];
407 f32 plane_dist = relativePos.ps_dot(fnrm);
408 f32 dist_in_plane = m_radius - plane_dist;
409 if (dist_in_plane <= 0.0f) {
410 return false;
411 }
412
413 f32 typeDistance = m_prismThickness;
414 if constexpr (Type == CollisionCheckType::Edge) {
415 typeDistance += m_radius;
416 }
417
418 if (dist_in_plane >= typeDistance) {
419 return false;
420 }
421
422 if constexpr (Type == CollisionCheckType::Movement) {
423 if (attributeMask & KCL_TYPE_DIRECTIONAL && m_movement.dot(fnrm) > 0.0f) {
424 return false;
425 }
426 }
427
428 // Originally part of the edge searching, but moved out for simplicity
429 // If these are all zero, then we're inside the triangle
430 if (dist_ab <= 0.0f && dist_bc <= 0.0f && dist_ca <= 0.0f) {
431 if constexpr (Type == CollisionCheckType::Movement) {
432 EGG::Vector3f lastPos = relativePos - m_movement;
433 // We're only colliding if we are moving towards the face
434 if (plane_dist < 0.0f && lastPos.ps_dot(fnrm) < 0.0f) {
435 return false;
436 }
437 }
438 return out(dist_in_plane);
439 }
440
441 EGG::Vector3f edge_nor, other_edge_nor;
442 f32 edge_dist, other_edge_dist;
443 bool swap = false;
444 bool swapNorms = false;
445 // > means further, < means closer, = means same distance
446 if (dist_ab >= dist_ca && dist_ab > dist_bc) {
447 // AB is the furthest edge
448 edge_nor = enrm2;
449 edge_dist = dist_ab;
450 if (dist_ca >= dist_bc) {
451 // CA is the second furthest edge
452 other_edge_nor = enrm1;
453 other_edge_dist = dist_ca;
454 swapNorms = true;
455 } else {
456 // BC is the second furthest edge
457 other_edge_nor = enrm3;
458 other_edge_dist = dist_bc;
459 swap = true;
460 }
461 } else if (dist_bc >= dist_ca) {
462 // BC is the furthest edge
463 edge_nor = enrm3;
464 edge_dist = dist_bc;
465 if (dist_ab >= dist_ca) {
466 // AB is the second furthest edge
467 other_edge_nor = enrm2;
468 other_edge_dist = dist_ab;
469 swapNorms = true;
470 } else {
471 // CA is the second furthest edge
472 other_edge_nor = enrm1;
473 other_edge_dist = dist_ca;
474 swap = true;
475 }
476 } else {
477 // CA is the furthest edge
478 edge_nor = enrm1;
479 edge_dist = dist_ca;
480 if (dist_bc >= dist_ab) {
481 // BC is the second furthest edge
482 other_edge_nor = enrm3;
483 other_edge_dist = dist_bc;
484 swapNorms = true;
485 } else {
486 // AB is the second furthest edge
487 other_edge_nor = enrm2;
488 other_edge_dist = dist_ab;
489 swap = true;
490 }
491 }
492
493 f32 cos = edge_nor.ps_dot(other_edge_nor);
494 f32 sq_dist;
495 if (cos * edge_dist > other_edge_dist) {
496 if constexpr (Type == CollisionCheckType::Plane) {
497 if (edge_dist > plane_dist) {
498 return false;
499 }
500 }
501 sq_dist = m_radius * m_radius - edge_dist * edge_dist;
502 } else {
503 f32 sq_sin = cos * cos - 1.0f;
504
505 if (swap) {
506 std::swap(edge_dist, other_edge_dist);
507 }
508
509 if (swapNorms) {
510 std::swap(edge_nor, other_edge_nor);
511 }
512
513 f32 t = (cos * edge_dist - other_edge_dist) / sq_sin;
514 f32 s = edge_dist - t * cos;
515 const EGG::Vector3f corner_pos = edge_nor * t + other_edge_nor * s;
516
517 f32 cornerDot = corner_pos.ps_squareMag();
518 if constexpr (Type == CollisionCheckType::Plane) {
519 if (cornerDot > plane_dist * plane_dist) {
520 return false;
521 }
522 }
523
524 sq_dist = m_radius * m_radius - cornerDot;
525 }
526
527 if (sq_dist < plane_dist * plane_dist || sq_dist <= 0.0f) {
528 return false;
529 }
530
531 f32 dist = EGG::Mathf::sqrt(sq_dist) - plane_dist;
532 if (dist <= 0.0f) {
533 return false;
534 }
535
536 if constexpr (Type == CollisionCheckType::Movement) {
537 EGG::Vector3f lastPos = relativePos - m_movement;
538 // We're only colliding if we are moving towards the face
539 if (lastPos.ps_dot(fnrm) < 0.0f) {
540 return false;
541 }
542 }
543
544 return out(dist);
545}
546
549bool KColData::checkPointCollision(const KCollisionPrism &prism, f32 *distOut,
550 EGG::Vector3f *fnrmOut, u16 *flagsOut, bool movement) {
551 KCLTypeMask attrMask = KCL_ATTRIBUTE_TYPE_BIT(prism.attribute);
552 if (!(attrMask & m_typeMask)) {
553 return false;
554 }
555
556 const EGG::Vector3f relativePos = m_pos - m_vertices[prism.pos_i];
557
558 const EGG::Vector3f &enrm1 = m_nrms[prism.enrm1_i];
559 f32 dist_ca = relativePos.ps_dot(enrm1);
560 if (dist_ca >= 0.01f) {
561 return false;
562 }
563
564 const EGG::Vector3f &enrm2 = m_nrms[prism.enrm2_i];
565 f32 dist_ab = relativePos.ps_dot(enrm2);
566 if (dist_ab >= 0.01f) {
567 return false;
568 }
569
570 const EGG::Vector3f &enrm3 = m_nrms[prism.enrm3_i];
571 f32 dist_bc = relativePos.ps_dot(enrm3) - prism.height;
572 if (dist_bc >= 0.01f) {
573 return false;
574 }
575
576 const EGG::Vector3f &fnrm = m_nrms[prism.fnrm_i];
577 f32 plane_dist = relativePos.ps_dot(fnrm);
578 f32 dist_in_plane = 0.01f - plane_dist;
579 if (dist_in_plane <= 0.0f) {
580 return false;
581 }
582
583 if (m_prismThickness <= dist_in_plane && 0.02f + m_prismThickness <= dist_in_plane) {
584 return false;
585 }
586
587 if (movement && (attrMask & KCL_TYPE_DIRECTIONAL) && m_movement.dot(fnrm) < 0.0f) {
588 return false;
589 }
590
591 if (distOut) {
592 *distOut = dist_in_plane;
593 }
594
595 if (fnrmOut) {
596 *fnrmOut = fnrm;
597 }
598
599 if (flagsOut) {
600 *flagsOut = prism.attribute;
601 }
602
603 return true;
604}
605
612bool KColData::checkSphereMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut) {
613 // If there's no list of triangles to check, there's no collision
614 if (!m_prismIter) {
615 return false;
616 }
617
618 // Check collision for all triangles, and continuously call the function until we're out
619 while (*++m_prismIter != 0) {
620 const KCollisionPrism &prism = m_prisms[parse<u16>(*m_prismIter)];
621 if (checkCollision<CollisionCheckType::Movement>(prism, distOut, fnrmOut, attributeOut)) {
622 return true;
623 }
624 }
625
626 // We're out of triangles to check - another list must be prepared for subsequent calls
627 m_prismIter = nullptr;
628 return false;
629}
630
632bool KColData::checkPoint(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut) {
633 // If there's no list of triangles to check, there's no collision
634 if (!m_prismIter) {
635 return false;
636 }
637
638 // Check collision for all triangles, and continuously call the function until we're out
639 while (*++m_prismIter != 0) {
640 const KCollisionPrism &prism = m_prisms[parse<u16>(*m_prismIter)];
641 if (checkPointCollision(prism, distOut, fnrmOut, attributeOut, false)) {
642 return true;
643 }
644 }
645
646 // We're out of triangles to check - another list must be prepared for subsequent calls
647 m_prismIter = nullptr;
648 return false;
649}
650
652bool KColData::checkPointMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut) {
653 // If there's no list of triangles to check, there's no collision
654 if (!m_prismIter) {
655 return false;
656 }
657
658 // Check collision for all triangles, and continuously call the function until we're out
659 while (*++m_prismIter != 0) {
660 const KCollisionPrism &prism = m_prisms[parse<u16>(*m_prismIter)];
661 if (checkPointCollision(prism, distOut, fnrmOut, attributeOut, true)) {
662 return true;
663 }
664 }
665
666 // We're out of triangles to check - another list must be prepared for subsequent calls
667 m_prismIter = nullptr;
668 return false;
669}
670
671KColData::KCollisionPrism::KCollisionPrism() = default;
672
673KColData::KCollisionPrism::KCollisionPrism(f32 height, u16 posIndex, u16 faceNormIndex,
674 u16 edge1NormIndex, u16 edge2NormIndex, u16 edge3NormIndex, u16 attribute)
675 : height(height), pos_i(posIndex), fnrm_i(faceNormIndex), enrm1_i(edge1NormIndex),
676 enrm2_i(edge2NormIndex), enrm3_i(edge3NormIndex), attribute(attribute) {}
677
678void CollisionInfo::update(f32 now_dist, const EGG::Vector3f &offset, const EGG::Vector3f &fnrm,
679 u32 kclAttributeTypeBit) {
680 bbox.min = bbox.min.minimize(offset);
681 bbox.max = bbox.max.maximize(offset);
682
683 if (kclAttributeTypeBit & KCL_TYPE_FLOOR) {
684 updateFloor(now_dist, fnrm);
685 } else if (kclAttributeTypeBit & KCL_TYPE_WALL) {
686 if (wallDist > -std::numeric_limits<f32>::min()) {
687 f32 dot = 1.0f - wallNrm.ps_dot(fnrm);
688 if (dot > perpendicularity) {
689 perpendicularity = std::min(dot, 1.0f);
690 }
691 }
692
693 updateWall(now_dist, fnrm);
694 }
695}
696
698void CollisionInfo::transformInfo(CollisionInfo &rhs, const EGG::Matrix34f &mtx,
699 const EGG::Vector3f &v) {
700 rhs.bbox.min = mtx.ps_multVector33(rhs.bbox.min);
701 rhs.bbox.max = mtx.ps_multVector33(rhs.bbox.max);
702
703 EGG::Vector3f min = rhs.bbox.min;
704
705 rhs.bbox.min = min.minimize(rhs.bbox.max);
706 rhs.bbox.max = min.maximize(rhs.bbox.max);
707
708 bbox.min = bbox.min.minimize(rhs.bbox.min);
709 bbox.max = bbox.max.maximize(rhs.bbox.max);
710
711 if (floorDist < rhs.floorDist) {
712 floorDist = rhs.floorDist;
713 floorNrm = mtx.ps_multVector33(rhs.floorNrm);
714 }
715
716 if (wallDist < rhs.wallDist) {
717 wallDist = rhs.wallDist;
718 wallNrm = mtx.ps_multVector33(rhs.wallNrm);
719 }
720
721 if (movingFloorDist < rhs.floorDist) {
722 movingFloorDist = rhs.floorDist;
723 roadVelocity = v;
724 }
725
726 perpendicularity = std::max(perpendicularity, rhs.perpendicularity);
727}
728
729} // 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_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
#define KCL_TYPE_DIRECTIONAL
0x05070000
#define KCL_TYPE_WALL
0xD010F000
A stream of data stored in memory.
Definition Stream.hh:83
void narrowPolygon_EachBlock(const u16 *prismArray)
Definition KColData.cc:75
owning_span< KCollisionPrism > m_prisms
Optimizes for time by avoiding unnecessary byteswapping. The Wii doesn't have this problem because bi...
Definition KColData.hh:174
void lookupSphere(f32 radius, const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask typeMask)
Sets members in preparation of a subsequent sphere collision check call.
Definition KColData.cc:196
u32 m_areaYWidthMask
The y dimension of the octree's bounding box.
Definition KColData.hh:153
const u16 * searchBlock(const EGG::Vector3f &pos)
Finds the data block corresponding to the provided position.
Definition KColData.cc:230
bool checkSphere(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut)
Iterates the list of looked-up triangles to see if we are colliding.
Definition KColData.cc:134
u32 m_areaXWidthMask
The x dimension of the octree's bounding box.
Definition KColData.hh:152
void preloadNormals()
Creates a copy of the normals in memory.
Definition KColData.cc:327
bool checkSphereMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut)
Iterates the local data block to check for directional collision.
Definition KColData.cc:612
static EGG::Vector3f GetVertex(f32 height, const EGG::Vector3f &vertex1, const EGG::Vector3f &fnrm, const EGG::Vector3f &enrm3, const EGG::Vector3f &enrm)
Computes a prism vertex based off of the triangle's normal vectors.
Definition KColData.cc:288
u32 m_areaXBlocksShift
Used to initialize octree navigation.
Definition KColData.hh:156
u32 m_blockWidthShift
Used to initialize octree navigation.
Definition KColData.hh:155
f32 m_sphereRadius
Clamps the sphere we check collision against.
Definition KColData.hh:158
void preloadPrisms()
Creates a copy of the prisms in memory.
Definition KColData.cc:300
void lookupPoint(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask typeMask)
Sets members in preparation of a subsequent point collision check call.
Definition KColData.cc:185
void computeBBox()
Calculates a EGG::BoundBox3f that describes the boundary of the track's KCL.
Definition KColData.cc:92
void preloadVertices()
Creates a copy of the vertices in memory.
Definition KColData.cc:343
u32 m_areaZWidthMask
The z dimension of the octree's bounding box.
Definition KColData.hh:154
u32 m_areaXYBlocksShift
Used to initialize octree navigation.
Definition KColData.hh:157
bool checkCollision(const KCollisionPrism &prism, f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut)
This is a combination of the three collision checks in the base game.
Definition KColData.cc:362
A contiguous storage container that manages the lifecycle of a buffer of a given size.
Definition Types.hh:29
Pertains to collision.
Represents a sphere in 3D space.
Definition Sphere.hh:8
A 3D float vector.
Definition Vector.hh:107
void read(Stream &stream)
Initializes a Vector3f by reading 12 bytes from the stream.
Definition Vector.hh:365
constexpr Vector3f minimize(const Vector3f &rhs) const
Returns a vector whose elements are the min of the elements of both vectors.
Definition Vector.hh:327
constexpr f32 ps_dot() const
Paired-singles dot product implementation.
Definition Vector.hh:256
constexpr Vector3f maximize(const Vector3f &rhs) const
Returns a vector whose elements are the max of the elements of both vectors.
Definition Vector.hh:315
constexpr f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
Definition Vector.hh:206
constexpr f32 ps_squareMag() const
Differs from ps_dot due to variation in which operands are fused.
Definition Vector.hh:269