mirror of
https://github.com/AxioDL/metaforce.git
synced 2025-08-06 15:35:35 +00:00
Makes arrays strongly typed and allows eliminating some hardcoded array sizes. We can also use it to make input data to some data types and functions significantly more informative. This also makes it easier for static analysis tools to track out of bounds accesses if they ever occur.
957 lines
38 KiB
C++
957 lines
38 KiB
C++
#include "Runtime/Collision/CMetroidAreaCollider.hpp"
|
|
|
|
#include "Runtime/Collision/CCollisionInfoList.hpp"
|
|
#include "Runtime/Collision/CMaterialFilter.hpp"
|
|
#include "Runtime/Collision/CollisionUtil.hpp"
|
|
|
|
namespace urde {
|
|
|
|
u32 CMetroidAreaCollider::g_CalledClip = 0;
|
|
u32 CMetroidAreaCollider::g_RejectedByClip = 0;
|
|
u32 CMetroidAreaCollider::g_TrianglesProcessed = 0;
|
|
u32 CMetroidAreaCollider::g_DupTrianglesProcessed = 0;
|
|
u16 CMetroidAreaCollider::g_DupPrimitiveCheckCount = 0;
|
|
std::array<u16, 0x5000> CMetroidAreaCollider::g_DupVertexList{};
|
|
std::array<u16, 0xC000> CMetroidAreaCollider::g_DupEdgeList{};
|
|
std::array<u16, 0x4000> CMetroidAreaCollider::g_DupTriangleList{};
|
|
|
|
CAABoxAreaCache::CAABoxAreaCache(const zeus::CAABox& aabb, const std::array<zeus::CPlane, 6>& pl,
|
|
const CMaterialFilter& filter, const CMaterialList& material,
|
|
CCollisionInfoList& collisionList)
|
|
: x0_aabb(aabb)
|
|
, x4_planes(pl)
|
|
, x8_filter(filter)
|
|
, xc_material(material)
|
|
, x10_collisionList(collisionList)
|
|
, x14_center(aabb.center())
|
|
, x20_halfExtent(aabb.extents()) {}
|
|
|
|
CBooleanAABoxAreaCache::CBooleanAABoxAreaCache(const zeus::CAABox& aabb, const CMaterialFilter& filter)
|
|
: x0_aabb(aabb), x4_filter(filter), x8_center(aabb.center()), x14_halfExtent(aabb.extents()) {}
|
|
|
|
CSphereAreaCache::CSphereAreaCache(const zeus::CAABox& aabb, const zeus::CSphere& sphere, const CMaterialFilter& filter,
|
|
const CMaterialList& material, CCollisionInfoList& collisionList)
|
|
: x0_aabb(aabb), x4_sphere(sphere), x8_filter(filter), xc_material(material), x10_collisionList(collisionList) {}
|
|
|
|
CBooleanSphereAreaCache::CBooleanSphereAreaCache(const zeus::CAABox& aabb, const zeus::CSphere& sphere,
|
|
const CMaterialFilter& filter)
|
|
: x0_aabb(aabb), x4_sphere(sphere), x8_filter(filter) {}
|
|
|
|
SBoxEdge::SBoxEdge(const zeus::CAABox& aabb, int idx, const zeus::CVector3f& dir)
|
|
: x0_seg(aabb.getEdge(zeus::CAABox::EBoxEdgeId(idx)))
|
|
, x28_start(x0_seg.x0_start)
|
|
, x40_end(x0_seg.x18_end)
|
|
, x58_delta(x40_end - x28_start)
|
|
, x70_coDir(x58_delta.cross(dir).asNormalized())
|
|
, x88_dirCoDirDot(x28_start.dot(x70_coDir)) {}
|
|
|
|
static void FlagEdgeIndicesForFace(int face, std::array<bool, 12>& edgeFlags) {
|
|
switch (face) {
|
|
case 0:
|
|
edgeFlags[10] = true;
|
|
edgeFlags[11] = true;
|
|
edgeFlags[2] = true;
|
|
edgeFlags[4] = true;
|
|
return;
|
|
case 1:
|
|
edgeFlags[8] = true;
|
|
edgeFlags[9] = true;
|
|
edgeFlags[0] = true;
|
|
edgeFlags[6] = true;
|
|
return;
|
|
case 2:
|
|
edgeFlags[4] = true;
|
|
edgeFlags[5] = true;
|
|
edgeFlags[6] = true;
|
|
edgeFlags[7] = true;
|
|
return;
|
|
case 3:
|
|
edgeFlags[0] = true;
|
|
edgeFlags[1] = true;
|
|
edgeFlags[2] = true;
|
|
edgeFlags[3] = true;
|
|
return;
|
|
case 4:
|
|
edgeFlags[7] = true;
|
|
edgeFlags[8] = true;
|
|
edgeFlags[3] = true;
|
|
edgeFlags[11] = true;
|
|
return;
|
|
case 5:
|
|
edgeFlags[1] = true;
|
|
edgeFlags[5] = true;
|
|
edgeFlags[9] = true;
|
|
edgeFlags[10] = true;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void FlagVertexIndicesForFace(int face, std::array<bool, 8>& vertFlags) {
|
|
switch (face) {
|
|
case 0:
|
|
vertFlags[1] = true;
|
|
vertFlags[3] = true;
|
|
vertFlags[5] = true;
|
|
vertFlags[7] = true;
|
|
return;
|
|
case 1:
|
|
vertFlags[0] = true;
|
|
vertFlags[2] = true;
|
|
vertFlags[4] = true;
|
|
vertFlags[6] = true;
|
|
return;
|
|
case 2:
|
|
vertFlags[2] = true;
|
|
vertFlags[3] = true;
|
|
vertFlags[6] = true;
|
|
vertFlags[7] = true;
|
|
return;
|
|
case 3:
|
|
vertFlags[0] = true;
|
|
vertFlags[1] = true;
|
|
vertFlags[4] = true;
|
|
vertFlags[5] = true;
|
|
return;
|
|
case 4:
|
|
vertFlags[4] = true;
|
|
vertFlags[5] = true;
|
|
vertFlags[6] = true;
|
|
vertFlags[7] = true;
|
|
return;
|
|
case 5:
|
|
vertFlags[0] = true;
|
|
vertFlags[1] = true;
|
|
vertFlags[2] = true;
|
|
vertFlags[3] = true;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
CMovingAABoxComponents::CMovingAABoxComponents(const zeus::CAABox& aabb, const zeus::CVector3f& dir) : x6e8_aabb(aabb) {
|
|
std::array<bool, 12> edgeFlags{};
|
|
std::array<bool, 8> vertFlags{};
|
|
int useFaces = 0;
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (dir[i] != 0.f) {
|
|
const int face = i * 2 + (dir[i] < 0.f);
|
|
FlagEdgeIndicesForFace(face, edgeFlags);
|
|
FlagVertexIndicesForFace(face, vertFlags);
|
|
useFaces += 1;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < edgeFlags.size(); ++i) {
|
|
if (edgeFlags[i]) {
|
|
x0_edges.emplace_back(aabb, s32(i), dir);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < vertFlags.size(); ++i) {
|
|
if (vertFlags[i]) {
|
|
x6c4_vertIdxs.push_back(u32(i));
|
|
}
|
|
}
|
|
|
|
if (useFaces == 1) {
|
|
x6e8_aabb = zeus::CAABox();
|
|
x6e8_aabb.accumulateBounds(aabb.getPoint(x6c4_vertIdxs[0]));
|
|
x6e8_aabb.accumulateBounds(aabb.getPoint(x6c4_vertIdxs[1]));
|
|
x6e8_aabb.accumulateBounds(aabb.getPoint(x6c4_vertIdxs[2]));
|
|
x6e8_aabb.accumulateBounds(aabb.getPoint(x6c4_vertIdxs[3]));
|
|
}
|
|
}
|
|
|
|
CMetroidAreaCollider::COctreeLeafCache::COctreeLeafCache(const CAreaOctTree& octTree) : x0_octTree(octTree) {
|
|
x908_24_overflow = false;
|
|
}
|
|
|
|
void CMetroidAreaCollider::COctreeLeafCache::AddLeaf(const CAreaOctTree::Node& node) {
|
|
if (x4_nodeCache.size() == x4_nodeCache.capacity()) {
|
|
x908_24_overflow = true;
|
|
return;
|
|
}
|
|
|
|
x4_nodeCache.push_back(node);
|
|
}
|
|
|
|
void CMetroidAreaCollider::BuildOctreeLeafCache(const CAreaOctTree::Node& node, const zeus::CAABox& aabb,
|
|
CMetroidAreaCollider::COctreeLeafCache& cache) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
CAreaOctTree::Node::ETreeType type = node.GetChildType(i);
|
|
if (type != CAreaOctTree::Node::ETreeType::Invalid) {
|
|
CAreaOctTree::Node ch = node.GetChild(i);
|
|
if (aabb.intersects(ch.GetBoundingBox())) {
|
|
if (type == CAreaOctTree::Node::ETreeType::Leaf)
|
|
cache.AddLeaf(ch);
|
|
else
|
|
BuildOctreeLeafCache(ch, aabb, cache);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static zeus::CVector3f ClipRayToPlane(const zeus::CVector3f& a, const zeus::CVector3f& b, const zeus::CPlane& plane) {
|
|
return (1.f - -plane.pointToPlaneDist(a) / (b - a).dot(plane.normal())) * (a - b) + b;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::ConvexPolyCollision(const std::array<zeus::CPlane, 6>& planes, const zeus::CVector3f* verts,
|
|
zeus::CAABox& aabb) {
|
|
std::array<rstl::reserved_vector<zeus::CVector3f, 20>, 2> vecs;
|
|
|
|
g_CalledClip += 1;
|
|
g_RejectedByClip -= 1;
|
|
|
|
vecs[0].push_back(verts[0]);
|
|
vecs[0].push_back(verts[1]);
|
|
vecs[0].push_back(verts[2]);
|
|
|
|
int vecIdx = 0;
|
|
int otherVecIdx = 1;
|
|
for (int i = 0; i < 6; ++i) {
|
|
rstl::reserved_vector<zeus::CVector3f, 20>& vec = vecs[vecIdx];
|
|
rstl::reserved_vector<zeus::CVector3f, 20>& otherVec = vecs[otherVecIdx];
|
|
otherVec.clear();
|
|
|
|
bool inFrontOf = planes[i].pointToPlaneDist(vec.front()) >= 0.f;
|
|
for (size_t j = 0; j < vec.size(); ++j) {
|
|
const zeus::CVector3f& b = vec[(j + 1) % vec.size()];
|
|
if (inFrontOf)
|
|
otherVec.push_back(vec[j]);
|
|
bool nextInFrontOf = planes[i].pointToPlaneDist(b) >= 0.f;
|
|
if (nextInFrontOf ^ inFrontOf)
|
|
otherVec.push_back(ClipRayToPlane(vec[j], b, planes[i]));
|
|
inFrontOf = nextInFrontOf;
|
|
}
|
|
|
|
if (otherVec.empty())
|
|
return false;
|
|
|
|
vecIdx ^= 1;
|
|
otherVecIdx ^= 1;
|
|
}
|
|
|
|
rstl::reserved_vector<zeus::CVector3f, 20>& accumVec = vecs[otherVecIdx ^ 1];
|
|
for (const zeus::CVector3f& point : accumVec)
|
|
aabb.accumulateBounds(point);
|
|
|
|
g_RejectedByClip -= 1;
|
|
return true;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::AABoxCollisionCheckBoolean_Cached(const COctreeLeafCache& leafCache,
|
|
const zeus::CAABox& aabb, const CMaterialFilter& filter) {
|
|
CBooleanAABoxAreaCache cache(aabb, filter);
|
|
|
|
for (const CAreaOctTree::Node& node : leafCache.x4_nodeCache) {
|
|
if (cache.x0_aabb.intersects(node.GetBoundingBox())) {
|
|
CAreaOctTree::TriListReference list = node.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
CCollisionSurface surf = node.GetOwner().GetMasterListTriangle(list.GetAt(j));
|
|
if (cache.x4_filter.Passes(CMaterialList(surf.GetSurfaceFlags()))) {
|
|
if (CollisionUtil::TriBoxOverlap(cache.x8_center, cache.x14_halfExtent, surf.GetVert(0), surf.GetVert(1),
|
|
surf.GetVert(2)))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::AABoxCollisionCheckBoolean_Internal(const CAreaOctTree::Node& node,
|
|
const CBooleanAABoxAreaCache& cache) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
CAreaOctTree::Node::ETreeType type = node.GetChildType(i);
|
|
if (type != CAreaOctTree::Node::ETreeType::Invalid) {
|
|
CAreaOctTree::Node ch = node.GetChild(i);
|
|
if (cache.x0_aabb.intersects(ch.GetBoundingBox())) {
|
|
if (type == CAreaOctTree::Node::ETreeType::Leaf) {
|
|
CAreaOctTree::TriListReference list = ch.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
CCollisionSurface surf = ch.GetOwner().GetMasterListTriangle(list.GetAt(j));
|
|
if (cache.x4_filter.Passes(CMaterialList(surf.GetSurfaceFlags()))) {
|
|
if (CollisionUtil::TriBoxOverlap(cache.x8_center, cache.x14_halfExtent, surf.GetVert(0), surf.GetVert(1),
|
|
surf.GetVert(2)))
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
if (AABoxCollisionCheckBoolean_Internal(ch, cache))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::AABoxCollisionCheckBoolean(const CAreaOctTree& octTree, const zeus::CAABox& aabb,
|
|
const CMaterialFilter& filter) {
|
|
CBooleanAABoxAreaCache cache(aabb, filter);
|
|
return AABoxCollisionCheckBoolean_Internal(octTree.GetRootNode(), cache);
|
|
}
|
|
|
|
bool CMetroidAreaCollider::SphereCollisionCheckBoolean_Cached(const COctreeLeafCache& leafCache,
|
|
const zeus::CAABox& aabb, const zeus::CSphere& sphere,
|
|
const CMaterialFilter& filter) {
|
|
CBooleanSphereAreaCache cache(aabb, sphere, filter);
|
|
|
|
for (const CAreaOctTree::Node& node : leafCache.x4_nodeCache) {
|
|
if (cache.x0_aabb.intersects(node.GetBoundingBox())) {
|
|
CAreaOctTree::TriListReference list = node.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
CCollisionSurface surf = node.GetOwner().GetMasterListTriangle(list.GetAt(j));
|
|
if (cache.x8_filter.Passes(CMaterialList(surf.GetSurfaceFlags()))) {
|
|
if (CollisionUtil::TriSphereOverlap(cache.x4_sphere, surf.GetVert(0), surf.GetVert(1), surf.GetVert(2)))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::SphereCollisionCheckBoolean_Internal(const CAreaOctTree::Node& node,
|
|
const CBooleanSphereAreaCache& cache) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
CAreaOctTree::Node::ETreeType type = node.GetChildType(i);
|
|
if (type != CAreaOctTree::Node::ETreeType::Invalid) {
|
|
CAreaOctTree::Node ch = node.GetChild(i);
|
|
if (cache.x0_aabb.intersects(ch.GetBoundingBox())) {
|
|
if (type == CAreaOctTree::Node::ETreeType::Leaf) {
|
|
CAreaOctTree::TriListReference list = ch.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
CCollisionSurface surf = ch.GetOwner().GetMasterListTriangle(list.GetAt(j));
|
|
if (cache.x8_filter.Passes(CMaterialList(surf.GetSurfaceFlags()))) {
|
|
if (CollisionUtil::TriSphereOverlap(cache.x4_sphere, surf.GetVert(0), surf.GetVert(1), surf.GetVert(2)))
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
if (SphereCollisionCheckBoolean_Internal(ch, cache))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::SphereCollisionCheckBoolean(const CAreaOctTree& octTree, const zeus::CAABox& aabb,
|
|
const zeus::CSphere& sphere, const CMaterialFilter& filter) {
|
|
CAreaOctTree::Node node = octTree.GetRootNode();
|
|
CBooleanSphereAreaCache cache(aabb, sphere, filter);
|
|
return SphereCollisionCheckBoolean_Internal(node, cache);
|
|
}
|
|
|
|
bool CMetroidAreaCollider::AABoxCollisionCheck_Cached(const COctreeLeafCache& leafCache, const zeus::CAABox& aabb,
|
|
const CMaterialFilter& filter, const CMaterialList& matList,
|
|
CCollisionInfoList& list) {
|
|
bool ret = false;
|
|
const std::array<zeus::CPlane, 6> planes{{
|
|
{zeus::skRight, aabb.min.dot(zeus::skRight)},
|
|
{zeus::skLeft, aabb.max.dot(zeus::skLeft)},
|
|
{zeus::skForward, aabb.min.dot(zeus::skForward)},
|
|
{zeus::skBack, aabb.max.dot(zeus::skBack)},
|
|
{zeus::skUp, aabb.min.dot(zeus::skUp)},
|
|
{zeus::skDown, aabb.max.dot(zeus::skDown)},
|
|
}};
|
|
CAABoxAreaCache cache(aabb, planes, filter, matList, list);
|
|
|
|
ResetInternalCounters();
|
|
|
|
for (const CAreaOctTree::Node& node : leafCache.x4_nodeCache) {
|
|
if (aabb.intersects(node.GetBoundingBox())) {
|
|
CAreaOctTree::TriListReference listRef = node.GetTriangleArray();
|
|
for (int j = 0; j < listRef.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
u16 triIdx = listRef.GetAt(j);
|
|
if (g_DupPrimitiveCheckCount == g_DupTriangleList[triIdx]) {
|
|
g_DupTrianglesProcessed += 1;
|
|
} else {
|
|
g_DupTriangleList[triIdx] = g_DupPrimitiveCheckCount;
|
|
CCollisionSurface surf = node.GetOwner().GetMasterListTriangle(triIdx);
|
|
CMaterialList material(surf.GetSurfaceFlags());
|
|
if (cache.x8_filter.Passes(material)) {
|
|
if (CollisionUtil::TriBoxOverlap(cache.x14_center, cache.x20_halfExtent, surf.GetVert(0), surf.GetVert(1),
|
|
surf.GetVert(2))) {
|
|
zeus::CAABox aabb2 = zeus::CAABox();
|
|
if (ConvexPolyCollision(cache.x4_planes, surf.GetVerts(), aabb2)) {
|
|
zeus::CPlane plane = surf.GetPlane();
|
|
CCollisionInfo collision(aabb2, cache.xc_material, material, plane.normal(), -plane.normal());
|
|
cache.x10_collisionList.Add(collision, false);
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::AABoxCollisionCheck_Internal(const CAreaOctTree::Node& node, const CAABoxAreaCache& cache) {
|
|
bool ret = false;
|
|
|
|
switch (node.GetTreeType()) {
|
|
case CAreaOctTree::Node::ETreeType::Invalid:
|
|
return false;
|
|
case CAreaOctTree::Node::ETreeType::Branch: {
|
|
for (int i = 0; i < 8; ++i) {
|
|
CAreaOctTree::Node ch = node.GetChild(i);
|
|
if (ch.GetBoundingBox().intersects(cache.x0_aabb))
|
|
if (AABoxCollisionCheck_Internal(ch, cache))
|
|
ret = true;
|
|
}
|
|
break;
|
|
}
|
|
case CAreaOctTree::Node::ETreeType::Leaf: {
|
|
CAreaOctTree::TriListReference list = node.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
u16 triIdx = list.GetAt(j);
|
|
if (g_DupPrimitiveCheckCount == g_DupTriangleList[triIdx]) {
|
|
g_DupTrianglesProcessed += 1;
|
|
} else {
|
|
g_DupTriangleList[triIdx] = g_DupPrimitiveCheckCount;
|
|
CCollisionSurface surf = node.GetOwner().GetMasterListTriangle(triIdx);
|
|
CMaterialList material(surf.GetSurfaceFlags());
|
|
if (cache.x8_filter.Passes(material)) {
|
|
if (CollisionUtil::TriBoxOverlap(cache.x14_center, cache.x20_halfExtent, surf.GetVert(0), surf.GetVert(1),
|
|
surf.GetVert(2))) {
|
|
zeus::CAABox aabb = zeus::CAABox();
|
|
if (ConvexPolyCollision(cache.x4_planes, surf.GetVerts(), aabb)) {
|
|
zeus::CPlane plane = surf.GetPlane();
|
|
CCollisionInfo collision(aabb, cache.xc_material, material, plane.normal(), -plane.normal());
|
|
cache.x10_collisionList.Add(collision, false);
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::AABoxCollisionCheck(const CAreaOctTree& octTree, const zeus::CAABox& aabb,
|
|
const CMaterialFilter& filter, const CMaterialList& matList,
|
|
CCollisionInfoList& list) {
|
|
const std::array<zeus::CPlane, 6> planes{{
|
|
{zeus::skRight, aabb.min.dot(zeus::skRight)},
|
|
{zeus::skLeft, aabb.max.dot(zeus::skLeft)},
|
|
{zeus::skForward, aabb.min.dot(zeus::skForward)},
|
|
{zeus::skBack, aabb.max.dot(zeus::skBack)},
|
|
{zeus::skUp, aabb.min.dot(zeus::skUp)},
|
|
{zeus::skDown, aabb.max.dot(zeus::skDown)},
|
|
}};
|
|
const CAABoxAreaCache cache(aabb, planes, filter, matList, list);
|
|
|
|
ResetInternalCounters();
|
|
|
|
const CAreaOctTree::Node node = octTree.GetRootNode();
|
|
return AABoxCollisionCheck_Internal(node, cache);
|
|
}
|
|
|
|
bool CMetroidAreaCollider::SphereCollisionCheck_Cached(const COctreeLeafCache& leafCache, const zeus::CAABox& aabb,
|
|
const zeus::CSphere& sphere, const CMaterialList& matList,
|
|
const CMaterialFilter& filter, CCollisionInfoList& clist) {
|
|
ResetInternalCounters();
|
|
|
|
bool ret = false;
|
|
zeus::CVector3f point, normal;
|
|
|
|
for (const CAreaOctTree::Node& node : leafCache.x4_nodeCache) {
|
|
if (aabb.intersects(node.GetBoundingBox())) {
|
|
CAreaOctTree::TriListReference list = node.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
u16 triIdx = list.GetAt(j);
|
|
if (g_DupPrimitiveCheckCount == g_DupTriangleList[triIdx]) {
|
|
g_DupTrianglesProcessed += 1;
|
|
} else {
|
|
g_DupTriangleList[triIdx] = g_DupPrimitiveCheckCount;
|
|
CCollisionSurface surf = node.GetOwner().GetMasterListTriangle(triIdx);
|
|
CMaterialList material(surf.GetSurfaceFlags());
|
|
if (filter.Passes(material)) {
|
|
if (CollisionUtil::TriSphereIntersection(sphere, surf.GetVert(0), surf.GetVert(1), surf.GetVert(2), point,
|
|
normal)) {
|
|
CCollisionInfo collision(point, matList, material, normal);
|
|
clist.Add(collision, false);
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::SphereCollisionCheck_Internal(const CAreaOctTree::Node& node,
|
|
const CSphereAreaCache& cache) {
|
|
bool ret = false;
|
|
zeus::CVector3f point, normal;
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
CAreaOctTree::Node::ETreeType chTp = node.GetChildType(i);
|
|
if (chTp != CAreaOctTree::Node::ETreeType::Invalid) {
|
|
CAreaOctTree::Node ch = node.GetChild(i);
|
|
if (cache.x0_aabb.intersects(ch.GetBoundingBox())) {
|
|
if (chTp == CAreaOctTree::Node::ETreeType::Leaf) {
|
|
CAreaOctTree::TriListReference list = ch.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
++g_TrianglesProcessed;
|
|
u16 triIdx = list.GetAt(j);
|
|
if (g_DupPrimitiveCheckCount == g_DupTriangleList[triIdx]) {
|
|
g_DupTrianglesProcessed += 1;
|
|
} else {
|
|
g_DupTriangleList[triIdx] = g_DupPrimitiveCheckCount;
|
|
CCollisionSurface surf = ch.GetOwner().GetMasterListTriangle(triIdx);
|
|
CMaterialList material(surf.GetSurfaceFlags());
|
|
if (cache.x8_filter.Passes(material)) {
|
|
if (CollisionUtil::TriSphereIntersection(cache.x4_sphere, surf.GetVert(0), surf.GetVert(1),
|
|
surf.GetVert(2), point, normal)) {
|
|
CCollisionInfo collision(point, cache.xc_material, material, normal);
|
|
cache.x10_collisionList.Add(collision, false);
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (SphereCollisionCheck_Internal(ch, cache))
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::SphereCollisionCheck(const CAreaOctTree& octTree, const zeus::CAABox& aabb,
|
|
const zeus::CSphere& sphere, const CMaterialList& matList,
|
|
const CMaterialFilter& filter, CCollisionInfoList& list) {
|
|
CSphereAreaCache cache(aabb, sphere, filter, matList, list);
|
|
ResetInternalCounters();
|
|
CAreaOctTree::Node node = octTree.GetRootNode();
|
|
return SphereCollisionCheck_Internal(node, cache);
|
|
}
|
|
|
|
bool CMetroidAreaCollider::MovingAABoxCollisionCheck_BoxVertexTri(
|
|
const CCollisionSurface& surf, const zeus::CAABox& aabb, const rstl::reserved_vector<u32, 8>& vertIndices,
|
|
const zeus::CVector3f& dir, double& d, zeus::CVector3f& normalOut, zeus::CVector3f& pointOut) {
|
|
bool ret = false;
|
|
for (u32 idx : vertIndices) {
|
|
zeus::CVector3f point = aabb.getPoint(idx);
|
|
if (CollisionUtil::RayTriangleIntersection_Double(point, dir, surf.GetVerts(), d)) {
|
|
pointOut = float(d) * dir + point;
|
|
normalOut = surf.GetNormal();
|
|
ret = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::MovingAABoxCollisionCheck_TriVertexBox(const zeus::CVector3f& vert, const zeus::CAABox& aabb,
|
|
const zeus::CVector3f& dir, double& dOut,
|
|
zeus::CVector3f& normal, zeus::CVector3f& point) {
|
|
zeus::CMRay ray(vert, -dir, dOut);
|
|
zeus::CVector3f norm;
|
|
double d;
|
|
if (CollisionUtil::RayAABoxIntersection_Double(ray, aabb, norm, d) == 2) {
|
|
d *= dOut;
|
|
if (d < dOut) {
|
|
normal = -norm;
|
|
dOut = d;
|
|
point = vert;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Edge(const zeus::CVector3f& ev0, const zeus::CVector3f& ev1,
|
|
const rstl::reserved_vector<SBoxEdge, 12>& edges,
|
|
const zeus::CVector3f& dir, double& d,
|
|
zeus::CVector3f& normal, zeus::CVector3f& point) {
|
|
bool ret = false;
|
|
|
|
for (const SBoxEdge& edge : edges) {
|
|
zeus::CVector3d ev0d = ev0;
|
|
zeus::CVector3d ev1d = ev1;
|
|
if ((edge.x70_coDir.dot(ev1d) >= edge.x88_dirCoDirDot) != (edge.x70_coDir.dot(ev0d) >= edge.x88_dirCoDirDot)) {
|
|
zeus::CVector3d delta = ev0d - ev1d;
|
|
zeus::CVector3d cross0 = edge.x58_delta.cross(delta);
|
|
if (cross0.magSquared() >= DBL_EPSILON) {
|
|
zeus::CVector3d cross0Norm = cross0.asNormalized();
|
|
if (cross0Norm.dot(dir) >= 0.0) {
|
|
ev1d = ev0;
|
|
ev0d = ev1;
|
|
delta = ev0d - ev1d;
|
|
cross0Norm = edge.x58_delta.cross(delta).asNormalized();
|
|
}
|
|
|
|
zeus::CVector3d clipped =
|
|
ev0d + (-(ev0d.dot(edge.x70_coDir) - edge.x88_dirCoDirDot) / delta.dot(edge.x70_coDir)) * delta;
|
|
int maxCompIdx = (std::fabs(edge.x70_coDir.x()) > std::fabs(edge.x70_coDir.y())) ? 0 : 1;
|
|
if (std::fabs(edge.x70_coDir[maxCompIdx]) < std::fabs(edge.x70_coDir.z()))
|
|
maxCompIdx = 2;
|
|
|
|
int ci0, ci1;
|
|
switch (maxCompIdx) {
|
|
case 0:
|
|
ci0 = 1;
|
|
ci1 = 2;
|
|
break;
|
|
case 1:
|
|
ci0 = 0;
|
|
ci1 = 2;
|
|
break;
|
|
default:
|
|
ci0 = 0;
|
|
ci1 = 1;
|
|
break;
|
|
}
|
|
|
|
double mag = (edge.x58_delta[ci0] * (clipped[ci1] - edge.x28_start[ci1]) -
|
|
edge.x58_delta[ci1] * (clipped[ci0] - edge.x28_start[ci0])) /
|
|
(edge.x58_delta[ci0] * dir[ci1] - edge.x58_delta[ci1] * dir[ci0]);
|
|
if (mag >= 0.0 && mag < d) {
|
|
zeus::CVector3d clippedMag = clipped - mag * zeus::CVector3d(dir);
|
|
if ((edge.x28_start - clippedMag).dot(edge.x40_end - clippedMag) < 0.0 && mag < d) {
|
|
normal = cross0Norm.asCVector3f();
|
|
d = mag;
|
|
point = clipped.asCVector3f();
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Cached(const COctreeLeafCache& leafCache, const zeus::CAABox& aabb,
|
|
const CMaterialFilter& filter, const CMaterialList& matList,
|
|
const zeus::CVector3f& dir, float mag,
|
|
CCollisionInfo& infoOut, double& dOut) {
|
|
bool ret = false;
|
|
ResetInternalCounters();
|
|
dOut = mag;
|
|
|
|
CMovingAABoxComponents components(aabb, dir);
|
|
|
|
zeus::CAABox movedAABB = components.x6e8_aabb;
|
|
zeus::CVector3f moveVec = mag * dir;
|
|
movedAABB.accumulateBounds(aabb.min + moveVec);
|
|
movedAABB.accumulateBounds(aabb.max + moveVec);
|
|
|
|
zeus::CVector3f center = movedAABB.center();
|
|
zeus::CVector3f extent = movedAABB.extents();
|
|
|
|
zeus::CVector3f normal, point;
|
|
|
|
for (const CAreaOctTree::Node& node : leafCache.x4_nodeCache) {
|
|
if (movedAABB.intersects(node.GetBoundingBox())) {
|
|
CAreaOctTree::TriListReference list = node.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
u16 triIdx = list.GetAt(j);
|
|
if (g_DupPrimitiveCheckCount != g_DupTriangleList[triIdx]) {
|
|
g_TrianglesProcessed += 1;
|
|
g_DupTriangleList[triIdx] = g_DupPrimitiveCheckCount;
|
|
CMaterialList triMat(node.GetOwner().GetTriangleMaterial(triIdx));
|
|
if (filter.Passes(triMat)) {
|
|
std::array<u16, 3> vertIndices;
|
|
node.GetOwner().GetTriangleVertexIndices(triIdx, vertIndices.data());
|
|
CCollisionSurface surf(node.GetOwner().GetVert(vertIndices[0]), node.GetOwner().GetVert(vertIndices[1]),
|
|
node.GetOwner().GetVert(vertIndices[2]), triMat.GetValue());
|
|
|
|
if (CollisionUtil::TriBoxOverlap(center, extent, surf.GetVert(0), surf.GetVert(1), surf.GetVert(2))) {
|
|
bool triRet = false;
|
|
double d = dOut;
|
|
if (MovingAABoxCollisionCheck_BoxVertexTri(surf, aabb, components.x6c4_vertIdxs, dir, d, normal, point) &&
|
|
d < dOut) {
|
|
triRet = true;
|
|
ret = true;
|
|
infoOut = CCollisionInfo(point, matList, triMat, normal);
|
|
dOut = d;
|
|
}
|
|
|
|
for (const u16 vertIdx : vertIndices) {
|
|
zeus::CVector3f vtx = node.GetOwner().GetVert(vertIdx);
|
|
if (g_DupPrimitiveCheckCount != g_DupVertexList[vertIdx]) {
|
|
g_DupVertexList[vertIdx] = g_DupPrimitiveCheckCount;
|
|
if (movedAABB.pointInside(vtx)) {
|
|
d = dOut;
|
|
if (MovingAABoxCollisionCheck_TriVertexBox(vtx, aabb, dir, d, normal, point) && d < dOut) {
|
|
CMaterialList vertMat(node.GetOwner().GetVertMaterial(vertIdx));
|
|
triRet = true;
|
|
ret = true;
|
|
infoOut = CCollisionInfo(point, matList, vertMat, normal);
|
|
dOut = d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const u16* edgeIndices = node.GetOwner().GetTriangleEdgeIndices(triIdx);
|
|
for (int k = 0; k < 3; ++k) {
|
|
u16 edgeIdx = edgeIndices[k];
|
|
if (g_DupPrimitiveCheckCount != g_DupEdgeList[edgeIdx]) {
|
|
g_DupEdgeList[edgeIdx] = g_DupPrimitiveCheckCount;
|
|
CMaterialList edgeMat(node.GetOwner().GetEdgeMaterial(edgeIdx));
|
|
if (!edgeMat.HasMaterial(EMaterialTypes::NoEdgeCollision)) {
|
|
d = dOut;
|
|
const CCollisionEdge& edge = node.GetOwner().GetEdge(edgeIdx);
|
|
if (MovingAABoxCollisionCheck_Edge(node.GetOwner().GetVert(edge.GetVertIndex1()),
|
|
node.GetOwner().GetVert(edge.GetVertIndex2()),
|
|
components.x0_edges, dir, d, normal, point) &&
|
|
d < dOut) {
|
|
triRet = true;
|
|
ret = true;
|
|
infoOut = CCollisionInfo(point, matList, edgeMat, normal);
|
|
dOut = d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (triRet) {
|
|
moveVec = float(dOut) * dir;
|
|
movedAABB = components.x6e8_aabb;
|
|
movedAABB.accumulateBounds(aabb.min + moveVec);
|
|
movedAABB.accumulateBounds(aabb.max + moveVec);
|
|
center = movedAABB.center();
|
|
extent = movedAABB.extents();
|
|
}
|
|
} else {
|
|
const u16* edgeIndices = node.GetOwner().GetTriangleEdgeIndices(triIdx);
|
|
g_DupEdgeList[edgeIndices[0]] = g_DupPrimitiveCheckCount;
|
|
g_DupEdgeList[edgeIndices[1]] = g_DupPrimitiveCheckCount;
|
|
g_DupEdgeList[edgeIndices[2]] = g_DupPrimitiveCheckCount;
|
|
g_DupVertexList[vertIndices[0]] = g_DupPrimitiveCheckCount;
|
|
g_DupVertexList[vertIndices[1]] = g_DupPrimitiveCheckCount;
|
|
g_DupVertexList[vertIndices[2]] = g_DupPrimitiveCheckCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMetroidAreaCollider::MovingSphereCollisionCheck_Cached(const COctreeLeafCache& leafCache,
|
|
const zeus::CAABox& aabb, const zeus::CSphere& sphere,
|
|
const CMaterialFilter& filter,
|
|
const CMaterialList& matList, const zeus::CVector3f& dir,
|
|
float mag, CCollisionInfo& infoOut, double& dOut) {
|
|
bool ret = false;
|
|
ResetInternalCounters();
|
|
dOut = mag;
|
|
|
|
zeus::CAABox movedAABB = aabb;
|
|
zeus::CVector3f moveVec = mag * dir;
|
|
movedAABB.accumulateBounds(aabb.min + moveVec);
|
|
movedAABB.accumulateBounds(aabb.max + moveVec);
|
|
|
|
zeus::CVector3f center = movedAABB.center();
|
|
zeus::CVector3f extent = movedAABB.extents();
|
|
|
|
for (const CAreaOctTree::Node& node : leafCache.x4_nodeCache) {
|
|
if (movedAABB.intersects(node.GetBoundingBox())) {
|
|
CAreaOctTree::TriListReference list = node.GetTriangleArray();
|
|
for (int j = 0; j < list.GetSize(); ++j) {
|
|
u16 triIdx = list.GetAt(j);
|
|
if (g_DupPrimitiveCheckCount != g_DupTriangleList[triIdx]) {
|
|
g_TrianglesProcessed += 1;
|
|
g_DupTriangleList[triIdx] = g_DupPrimitiveCheckCount;
|
|
CMaterialList triMat(node.GetOwner().GetTriangleMaterial(triIdx));
|
|
if (filter.Passes(triMat)) {
|
|
std::array<u16, 3> vertIndices;
|
|
node.GetOwner().GetTriangleVertexIndices(triIdx, vertIndices.data());
|
|
CCollisionSurface surf(node.GetOwner().GetVert(vertIndices[0]), node.GetOwner().GetVert(vertIndices[1]),
|
|
node.GetOwner().GetVert(vertIndices[2]), triMat.GetValue());
|
|
|
|
if (CollisionUtil::TriBoxOverlap(center, extent, surf.GetVert(0), surf.GetVert(1), surf.GetVert(2))) {
|
|
zeus::CVector3f surfNormal = surf.GetNormal();
|
|
if ((sphere.position + moveVec - surf.GetVert(0)).dot(surfNormal) <= sphere.radius) {
|
|
bool triRet = false;
|
|
|
|
float mag = (sphere.radius - (sphere.position - surf.GetVert(0)).dot(surfNormal)) / dir.dot(surfNormal);
|
|
zeus::CVector3f intersectPoint = sphere.position + mag * dir;
|
|
|
|
const std::array<bool, 3> outsideEdges{
|
|
(intersectPoint - surf.GetVert(0)).dot(surfNormal.cross(surf.GetVert(1) - surf.GetVert(0))) < 0.f,
|
|
(intersectPoint - surf.GetVert(1)).dot(surfNormal.cross(surf.GetVert(2) - surf.GetVert(1))) < 0.f,
|
|
(intersectPoint - surf.GetVert(2)).dot(surfNormal.cross(surf.GetVert(0) - surf.GetVert(2))) < 0.f,
|
|
};
|
|
|
|
if (mag >= 0.f && !outsideEdges[0] && !outsideEdges[1] && !outsideEdges[2] && mag < dOut) {
|
|
infoOut = CCollisionInfo(intersectPoint - sphere.radius * surfNormal, matList, triMat, surfNormal);
|
|
dOut = mag;
|
|
triRet = true;
|
|
ret = true;
|
|
}
|
|
|
|
bool intersects = (sphere.position - surf.GetVert(0)).dot(surfNormal) <= sphere.radius;
|
|
std::array<bool, 3> testVert{true, true, true};
|
|
const u16* edgeIndices = node.GetOwner().GetTriangleEdgeIndices(triIdx);
|
|
for (int k = 0; k < 3; ++k) {
|
|
if (intersects || outsideEdges[k]) {
|
|
u16 edgeIdx = edgeIndices[k];
|
|
if (g_DupPrimitiveCheckCount != g_DupEdgeList[edgeIdx]) {
|
|
g_DupEdgeList[edgeIdx] = g_DupPrimitiveCheckCount;
|
|
CMaterialList edgeMat(node.GetOwner().GetEdgeMaterial(edgeIdx));
|
|
if (!edgeMat.HasMaterial(EMaterialTypes::NoEdgeCollision)) {
|
|
int nextIdx = (k + 1) % 3;
|
|
zeus::CVector3f edgeVec = surf.GetVert(nextIdx) - surf.GetVert(k);
|
|
float edgeVecMag = edgeVec.magnitude();
|
|
edgeVec *= zeus::CVector3f(1.f / edgeVecMag);
|
|
float dirDotEdge = dir.dot(edgeVec);
|
|
zeus::CVector3f edgeRej = dir - dirDotEdge * edgeVec;
|
|
float edgeRejMagSq = edgeRej.magSquared();
|
|
zeus::CVector3f vertToSphere = sphere.position - surf.GetVert(k);
|
|
float vtsDotEdge = vertToSphere.dot(edgeVec);
|
|
zeus::CVector3f vtsRej = vertToSphere - vtsDotEdge * edgeVec;
|
|
if (edgeRejMagSq > 0.f) {
|
|
float tmp = 2.f * vtsRej.dot(edgeRej);
|
|
float tmp2 =
|
|
4.f * edgeRejMagSq * (vtsRej.magSquared() - sphere.radius * sphere.radius) - tmp * tmp;
|
|
if (tmp2 >= 0.f) {
|
|
float mag = 0.5f / edgeRejMagSq * (-tmp - std::sqrt(tmp2));
|
|
if (mag >= 0.f) {
|
|
float t = mag * dirDotEdge + vtsDotEdge;
|
|
if (t >= 0.f && t <= edgeVecMag && mag < dOut) {
|
|
zeus::CVector3f point = surf.GetVert(k) + t * edgeVec;
|
|
infoOut = CCollisionInfo(point, matList, edgeMat,
|
|
(sphere.position + mag * dir - point).normalized());
|
|
dOut = mag;
|
|
triRet = true;
|
|
ret = true;
|
|
testVert[k] = false;
|
|
testVert[nextIdx] = false;
|
|
} else if (t < -sphere.radius && dirDotEdge <= 0.f) {
|
|
testVert[k] = false;
|
|
} else if (t > edgeVecMag + sphere.radius && dirDotEdge >= 0.0) {
|
|
testVert[nextIdx] = false;
|
|
}
|
|
}
|
|
} else {
|
|
testVert[k] = false;
|
|
testVert[nextIdx] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int k = 0; k < 3; ++k) {
|
|
u16 vertIdx = vertIndices[k];
|
|
if (testVert[k]) {
|
|
if (g_DupPrimitiveCheckCount != g_DupVertexList[vertIdx]) {
|
|
g_DupVertexList[vertIdx] = g_DupPrimitiveCheckCount;
|
|
double d = dOut;
|
|
if (CollisionUtil::RaySphereIntersection_Double(zeus::CSphere(surf.GetVert(k), sphere.radius),
|
|
sphere.position, dir, d) &&
|
|
d >= 0.0) {
|
|
infoOut = CCollisionInfo(surf.GetVert(k), matList, node.GetOwner().GetVertMaterial(vertIdx),
|
|
(sphere.position + dir * d - surf.GetVert(k)).normalized());
|
|
dOut = d;
|
|
triRet = true;
|
|
ret = true;
|
|
}
|
|
}
|
|
} else {
|
|
g_DupVertexList[vertIdx] = g_DupPrimitiveCheckCount;
|
|
}
|
|
}
|
|
|
|
if (triRet) {
|
|
moveVec = float(dOut) * dir;
|
|
movedAABB = aabb;
|
|
movedAABB.accumulateBounds(aabb.min + moveVec);
|
|
movedAABB.accumulateBounds(aabb.max + moveVec);
|
|
center = movedAABB.center();
|
|
extent = movedAABB.extents();
|
|
}
|
|
}
|
|
} else {
|
|
const u16* edgeIndices = node.GetOwner().GetTriangleEdgeIndices(triIdx);
|
|
g_DupEdgeList[edgeIndices[0]] = g_DupPrimitiveCheckCount;
|
|
g_DupEdgeList[edgeIndices[1]] = g_DupPrimitiveCheckCount;
|
|
g_DupEdgeList[edgeIndices[2]] = g_DupPrimitiveCheckCount;
|
|
g_DupVertexList[vertIndices[0]] = g_DupPrimitiveCheckCount;
|
|
g_DupVertexList[vertIndices[1]] = g_DupPrimitiveCheckCount;
|
|
g_DupVertexList[vertIndices[2]] = g_DupPrimitiveCheckCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void CMetroidAreaCollider::ResetInternalCounters() {
|
|
g_CalledClip = 0;
|
|
g_RejectedByClip = 0;
|
|
g_TrianglesProcessed = 0;
|
|
g_DupTrianglesProcessed = 0;
|
|
if (g_DupPrimitiveCheckCount == 0xffff) {
|
|
g_DupVertexList.fill(0);
|
|
g_DupEdgeList.fill(0);
|
|
g_DupTriangleList.fill(0);
|
|
g_DupPrimitiveCheckCount += 1;
|
|
}
|
|
g_DupPrimitiveCheckCount += 1;
|
|
}
|
|
|
|
void CAreaCollisionCache::ClearCache() {
|
|
x18_leafCaches.clear();
|
|
x1b40_24_leafOverflow = false;
|
|
x1b40_25_cacheOverflow = false;
|
|
}
|
|
|
|
void CAreaCollisionCache::AddOctreeLeafCache(const CMetroidAreaCollider::COctreeLeafCache& leafCache) {
|
|
if (!leafCache.GetNumLeaves())
|
|
return;
|
|
|
|
if (leafCache.HasCacheOverflowed())
|
|
x1b40_24_leafOverflow = true;
|
|
|
|
if (x18_leafCaches.size() < 3) {
|
|
x18_leafCaches.push_back(leafCache);
|
|
} else {
|
|
x1b40_24_leafOverflow = true;
|
|
x1b40_25_cacheOverflow = true;
|
|
}
|
|
}
|
|
|
|
} // namespace urde
|