diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index 446b4bc2..a704af11 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -11627,9 +11627,9 @@ GetTriangleArray__Q212CAreaOctTree4NodeCFv = .text:0x802A2D3C; // type:function GetChild__Q212CAreaOctTree4NodeCFi = .text:0x802A2D60; // type:function size:0x150 scope:global BoxFromIndex__FiRC9CVector3fRC9CVector3fRC9CVector3f = .text:0x802A2EB0; // type:function size:0x1EC scope:local __ct__Q220CMetroidAreaCollider22CMovingAABoxComponentsFRC6CAABoxRC9CVector3f = .text:0x802A309C; // type:function size:0x2E8 scope:global -fn_802A3384 = .text:0x802A3384; // type:function size:0x48 -fn_802A33CC = .text:0x802A33CC; // type:function size:0x28 -fn_802A33F4 = .text:0x802A33F4; // type:function size:0xB4 +push_back__Q24rstl53reserved_vectorFRCQ220CMetroidAreaCollider8SBoxEdge = .text:0x802A3384; // type:function size:0x48 scope:weak +construct__4rstlFPvRCQ220CMetroidAreaCollider8SBoxEdge = .text:0x802A33CC; // type:function size:0x28 scope:local +__ct__Q220CMetroidAreaCollider8SBoxEdgeFRCQ220CMetroidAreaCollider8SBoxEdge = .text:0x802A33F4; // type:function size:0xB4 scope:weak __ct__Q220CMetroidAreaCollider8SBoxEdgeFRC6CAABoxiRC9CVector3f = .text:0x802A34A8; // type:function size:0x118 scope:global FlagVertexIndicesForFace__FUiPb = .text:0x802A35C0; // type:function size:0xC4 scope:global FlagEdgeIndicesForFace__FUiPb = .text:0x802A3684; // type:function size:0xC4 scope:global @@ -11637,9 +11637,9 @@ BuildOctreeLeafCache__20CMetroidAreaColliderFRCQ212CAreaOctTree4NodeRC6CAABoxRQ2 ClearCache__19CAreaCollisionCacheFv = .text:0x802A3838; // type:function size:0xAC scope:global SetCacheBounds__19CAreaCollisionCacheFRC6CAABox = .text:0x802A38E4; // type:function size:0x34 scope:global AddOctreeLeafCache__19CAreaCollisionCacheFRCQ220CMetroidAreaCollider16COctreeLeafCache = .text:0x802A3918; // type:function size:0x78 scope:global -fn_802A3990 = .text:0x802A3990; // type:function size:0x48 -fn_802A39D8 = .text:0x802A39D8; // type:function size:0x28 -fn_802A3A00 = .text:0x802A3A00; // type:function size:0x90 +push_back__Q24rstl61reserved_vectorFRCQ220CMetroidAreaCollider16COctreeLeafCache = .text:0x802A3990; // type:function size:0x48 scope:global +construct__4rstlFPvRCQ220CMetroidAreaCollider16COctreeLeafCache = .text:0x802A39D8; // type:function size:0x28 scope:local +__ct__Q220CMetroidAreaCollider16COctreeLeafCacheFRCQ220CMetroidAreaCollider16COctreeLeafCache = .text:0x802A3A00; // type:function size:0x90 scope:global __ct__19CAreaCollisionCacheFRC6CAABox = .text:0x802A3A90; // type:function size:0x54 scope:global AddLeaf__Q220CMetroidAreaCollider16COctreeLeafCacheFRCQ212CAreaOctTree4Node = .text:0x802A3AE4; // type:function size:0x88 scope:global __ct__Q220CMetroidAreaCollider16COctreeLeafCacheFRC12CAreaOctTree = .text:0x802A3B6C; // type:function size:0x1C scope:global @@ -11704,7 +11704,7 @@ LineIntersectsLeaf__18CCollidableOBBTreeCFRCQ28COBBTree9CLeafDataR12CRayCastInfo LineIntersectsOBBTree__18CCollidableOBBTreeCFPCQ28COBBTree5CNodePCQ28COBBTree5CNodeR12CRayCastInfo = .text:0x802A9A88; // type:function size:0x2E8 scope:global LineIntersectsOBBTree__18CCollidableOBBTreeCFPCQ28COBBTree5CNodeR12CRayCastInfo = .text:0x802A9D70; // type:function size:0xF0 scope:global LineIntersectsTree__18CCollidableOBBTreeCFRC5CMRayRC15CMaterialFilterfRC12CTransform4f = .text:0x802A9E60; // type:function size:0x26C scope:global -TransformPlane__FRC6CPlaneRC12CTransform4f = .text:0x802AA0CC; // type:function size:0xEC scope:global +TransformPlane__FRC6CPlaneRC12CTransform4f = .text:0x802AA0CC; // type:function size:0xEC scope:local CastRayInternal__18CCollidableOBBTreeCFRC25CInternalRayCastStructure = .text:0x802AA1B8; // type:function size:0x38 scope:global SphereCollideWithLeafMoving__18CCollidableOBBTreeCFRCQ28COBBTree9CLeafDataRC12CTransform4fRC7CSphereRC13CMaterialListRC15CMaterialFilterRC9CVector3fRdR14CCollisionInfo = .text:0x802AA1F0; // type:function size:0xF54 scope:global SphereCollisionMoving__18CCollidableOBBTreeCFRCQ28COBBTree5CNodeRC12CTransform4fRC7CSphereRC6COBBoxRC13CMaterialListRC15CMaterialFilterRC9CVector3fRdR14CCollisionInfo = .text:0x802AB144; // type:function size:0x13C scope:global @@ -18746,8 +18746,8 @@ __vt__22CSustainedPlayerDamage = .data:0x803EC370; // type:object size:0x20 __vt__17CPoisonProjectile = .data:0x803EC390; // type:object size:0x74 scope:global jumptable_803EC408 = .data:0x803EC408; // type:object size:0x20 scope:local lbl_803EC428 = .data:0x803EC428; // type:object size:0x10 -lbl_803EC438 = .data:0x803EC438; // type:object size:0x10 -lbl_803EC448 = .data:0x803EC448; // type:object size:0x28 +mod3 = .data:0x803EC438; // type:object size:0x10 scope:local +__vt__18CCollidableOBBTree = .data:0x803EC448; // type:object size:0x28 scope:global __vt__23CCollidableOBBTreeGroup = .data:0x803EC470; // type:object size:0x28 scope:global __vt__60TObjOwnerDerivedFromIObj<32CCollidableOBBTreeGroupContainer> = .data:0x803EC498; // type:object size:0x10 jumptable_803EC4A8 = .data:0x803EC4A8; // type:object size:0x20 scope:local @@ -21282,7 +21282,7 @@ lbl_805A8620 = .sdata:0x805A8620; // type:object size:0x4 data:4byte lbl_805A8624 = .sdata:0x805A8624; // type:object size:0x4 data:4byte lbl_805A8628 = .sdata:0x805A8628; // type:object size:0x4 data:4byte lbl_805A862C = .sdata:0x805A862C; // type:object size:0x1 data:byte -lbl_805A8630 = .sdata:0x805A8630; // type:object size:0x8 data:4byte +sTableIndex__18CCollidableOBBTree = .sdata:0x805A8630; // type:object size:0x4 scope:global data:4byte lbl_805A8638 = .sdata:0x805A8638; // type:object size:0x8 data:4byte lbl_805A8640 = .sdata:0x805A8640; // type:object size:0x8 skGlobalSeed__17CProjectileWeapon = .sdata:0x805A8648; // type:object size:0x4 data:4byte diff --git a/include/Kyoto/Math/CUnitVector3f.hpp b/include/Kyoto/Math/CUnitVector3f.hpp index 651413a3..28736a72 100644 --- a/include/Kyoto/Math/CUnitVector3f.hpp +++ b/include/Kyoto/Math/CUnitVector3f.hpp @@ -13,6 +13,8 @@ class CUnitVector3f : public CVector3f { }; CUnitVector3f(const float x, const float y, const float z) : CVector3f(x, y, z) {} + CUnitVector3f(const float x, const float y, const float z, ENormalize) + : CVector3f(x, y, z) {} CUnitVector3f(const CVector3f& vec, const ENormalize normalize) : CVector3f(vec) { if (normalize == kN_Yes) { Normalize(); diff --git a/include/Kyoto/Math/CVector3d.hpp b/include/Kyoto/Math/CVector3d.hpp index 6c004080..ea029e54 100644 --- a/include/Kyoto/Math/CVector3d.hpp +++ b/include/Kyoto/Math/CVector3d.hpp @@ -17,8 +17,8 @@ class CVector3d { double GetY() const { return mY; } double GetZ() const { return mZ; } - double& operator[](int i) { return (&mX)[i]; } - const double operator[](int i) const { return (&mX)[i]; } + double& operator[](int i) { return reinterpret_cast(this)[i]; } + const double& operator[](int i) const { return reinterpret_cast(this)[i]; } static double Dot(const CVector3d& a, const CVector3d& b); static CVector3d Cross(const CVector3d& a, const CVector3d& b); diff --git a/include/WorldFormat/CAreaOctTree.hpp b/include/WorldFormat/CAreaOctTree.hpp index 9fa733aa..adfed75d 100644 --- a/include/WorldFormat/CAreaOctTree.hpp +++ b/include/WorldFormat/CAreaOctTree.hpp @@ -10,7 +10,8 @@ #include "rstl/optional_object.hpp" -class CCollisionEdge; +#include "WorldFormat/CCollisionEdge.hpp" + class CLine; class CMaterialFilter; @@ -27,8 +28,8 @@ class CAreaOctTree { class TriListReference { public: explicit TriListReference(const ushort* ptr) : m_ptr(ptr) {} - ushort GetAt(int idx) const { return m_ptr[idx + 1]; } - ushort GetSize() const { return m_ptr[0]; } + ushort GetAt(int idx) const { return m_ptr[idx + 13]; } + ushort GetSize() const { return m_ptr[12]; } private: const ushort* m_ptr; @@ -81,7 +82,14 @@ class CAreaOctTree { const void* GetTreeMemory() const { return x20_treeBuf; } const CAABox& GetBoundingBox() const { return x0_aabb; } Node::ETreeType GetTreeType() const { return x18_treeType; } - // TODO + + const CVector3f& GetVert(int idx) const { return x4c_verts[idx]; } + const CCollisionEdge& GetEdge(int idx) const { return x3c_edges[idx]; } + uint GetVertMaterial(int idx) const { return x28_materials[x2c_vertMats[idx]]; } + uint GetEdgeMaterial(int idx) const { return x28_materials[x30_edgeMats[idx]]; } + uint GetTriangleMaterial(int idx) const { return x28_materials[x34_polyMats[idx]]; } + void GetTriangleVertexIndices(ushort idx, ushort indicesOut[3]) const; + const ushort* GetTriangleEdgeIndices(ushort idx) const; private: CAABox x0_aabb; diff --git a/include/WorldFormat/CCollidableOBBTree.hpp b/include/WorldFormat/CCollidableOBBTree.hpp index 0592a158..f7bf37f4 100644 --- a/include/WorldFormat/CCollidableOBBTree.hpp +++ b/include/WorldFormat/CCollidableOBBTree.hpp @@ -1,19 +1,115 @@ #ifndef _CCOLLIDABLEOBBTREE #define _CCOLLIDABLEOBBTREE -#include +#include "Collision/CCollisionPrimitive.hpp" +#include "Collision/CMaterialFilter.hpp" +#include "Collision/CMaterialList.hpp" -class COBBTree; -class CCollidableOBBTree : public CCollisionPrimitive { +#include "Kyoto/Math/CPlane.hpp" + +#include "WorldFormat/CMetroidAreaCollider.hpp" +#include "WorldFormat/COBBTree.hpp" + +class CCollisionInfo; +class CCollisionInfoList; +class CMRay; +class COBBox; +class CSphere; + +class CRayCastInfo { + const CMRay& x0_ray; + const CMaterialFilter& x4_filter; + float x8_mag; + CPlane xc_plane; + CMaterialList x20_material; public: - CCollidableOBBTree(const COBBTree& tree, const CMaterialList& list); + CRayCastInfo(const CMRay& ray, const CMaterialFilter& filter, float mag) + : x0_ray(ray) + , x4_filter(filter) + , x8_mag(mag) + , xc_plane(CVector3f::Zero(), CUnitVector3f(CVector3f(0.f, 0.f, 1.f), CUnitVector3f::kN_Yes)) + , x20_material() {} + + const CMRay& GetRay() const { return x0_ray; } + const CMaterialFilter& GetMaterialFilter() const { return x4_filter; } + float GetMagnitude() const { return x8_mag; } + float& Magnitude() { return x8_mag; } + const CPlane& GetPlane() const { return xc_plane; } + CPlane& Plane() { return xc_plane; } + const CMaterialList& GetMaterial() const { return x20_material; } + CMaterialList& Material() { return x20_material; } +}; + +class CCollidableOBBTree : public CCollisionPrimitive { +public: + CCollidableOBBTree(COBBTree* tree, const CMaterialList& list); + ~CCollidableOBBTree() override; uint GetTableIndex() const override; CAABox CalculateAABox(const CTransform4f& xf) const override; - const CAABox CalculateLocalAABox() const override; + CAABox CalculateLocalAABox() const override; FourCC GetPrimType() const override; - CRayCastResult CastRayInternal(const CInternalRayCastStructure& intRayCast) const override; + CRayCastResult CastRayInternal(const CInternalRayCastStructure& rayCast) const override; + + const COBBTree& GetOBBTree() const { return *x10_tree; } + + bool AABoxCollision(const COBBTree::CNode& node, const CTransform4f& xf, const CAABox& aabb, + const COBBox& obb, const CMaterialList& material, + const CMaterialFilter& filter, const CPlane* planes, + CCollisionInfoList& infoList) const; + bool AABoxCollideWithLeaf(const COBBTree::CLeafData& leaf, const CTransform4f& xf, + const CAABox& aabb, const CMaterialList& material, + const CMaterialFilter& filter, const CPlane* planes, + CCollisionInfoList& infoList) const; + bool SphereCollision(const COBBTree::CNode& node, const CTransform4f& xf, + const CSphere& sphere, const COBBox& obb, const CMaterialList& material, + const CMaterialFilter& filter, CCollisionInfoList& infoList) const; + bool SphereCollideWithLeaf(const COBBTree::CLeafData& leaf, const CTransform4f& xf, + const CSphere& sphere, const CMaterialList& material, + const CMaterialFilter& filter, CCollisionInfoList& infoList) const; + bool AABoxCollisionBoolean(const COBBTree::CNode& node, const CTransform4f& xf, + const CAABox& aabb, const COBBox& obb, + const CMaterialFilter& filter) const; + bool SphereCollisionBoolean(const COBBTree::CNode& node, const CTransform4f& xf, + const CSphere& sphere, const COBBox& obb, + const CMaterialFilter& filter) const; + bool AABoxCollisionMoving(const COBBTree::CNode& node, const CTransform4f& xf, + const CAABox& aabb, const COBBox& obb, + const CMaterialList& material, const CMaterialFilter& filter, + const CMetroidAreaCollider::CMovingAABoxComponents& components, + const CVector3f& dir, double& dOut, CCollisionInfo& info) const; + bool AABoxCollideWithLeafMoving( + const COBBTree::CLeafData& leaf, const CTransform4f& xf, const CAABox& aabb, + const CMaterialList& material, const CMaterialFilter& filter, + const CMetroidAreaCollider::CMovingAABoxComponents& components, const CVector3f& dir, + double& dOut, CCollisionInfo& info) const; + bool SphereCollisionMoving(const COBBTree::CNode& node, const CTransform4f& xf, + const CSphere& sphere, const COBBox& obb, + const CMaterialList& material, const CMaterialFilter& filter, + const CVector3f& dir, double& dOut, CCollisionInfo& info) const; + bool SphereCollideWithLeafMoving(const COBBTree::CLeafData& leaf, const CTransform4f& xf, + const CSphere& sphere, const CMaterialList& material, + const CMaterialFilter& filter, const CVector3f& dir, + double& dOut, CCollisionInfo& info) const; + + CRayCastResult LineIntersectsTree(const CMRay& ray, const CMaterialFilter& filter, float maxTime, + const CTransform4f& xf) const; + bool LineIntersectsOBBTree(const COBBTree::CNode* node, CRayCastInfo& info) const; + bool LineIntersectsOBBTree(const COBBTree::CNode* n0, const COBBTree::CNode* n1, + CRayCastInfo& info) const; + bool LineIntersectsLeaf(const COBBTree::CLeafData& leaf, CRayCastInfo& info) const; + + static void SetStaticTableIndex(uint idx) { sTableIndex = idx; } + +private: + COBBTree* x10_tree; + uint x14_tries; + uint x18_misses; + uint x1c_hits; + + static uint sTableIndex; }; +CHECK_SIZEOF(CCollidableOBBTree, 0x20) #endif // _CCOLLIDABLEOBBTREE diff --git a/include/WorldFormat/CMetroidAreaCollider.hpp b/include/WorldFormat/CMetroidAreaCollider.hpp index 6706c610..7ce12444 100644 --- a/include/WorldFormat/CMetroidAreaCollider.hpp +++ b/include/WorldFormat/CMetroidAreaCollider.hpp @@ -10,8 +10,10 @@ #include "Collision/CMaterialList.hpp" #include "Kyoto/Math/CAABox.hpp" +#include "Kyoto/Math/CLineSeg.hpp" #include "Kyoto/Math/CPlane.hpp" #include "Kyoto/Math/CSphere.hpp" +#include "Kyoto/Math/CVector3d.hpp" #include "Kyoto/Math/CVector3f.hpp" #include "rstl/reserved_vector.hpp" @@ -21,14 +23,7 @@ class CAABoxAreaCache { friend class CMetroidAreaCollider; CAABoxAreaCache(const CAABox& aabb, const CPlane* 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.GetCenterPoint()) - , x20_halfExtent(aabb.GetHalfExtent()) {} + const CMaterialList& material, CCollisionInfoList& collisionList); private: const CAABox& x0_aabb; @@ -40,8 +35,76 @@ class CAABoxAreaCache { CVector3f x20_halfExtent; }; +class CBooleanAABoxAreaCache { +public: + friend class CMetroidAreaCollider; + + CBooleanAABoxAreaCache(const CAABox& aabb, const CMaterialFilter& filter); + +private: + const CAABox& x0_aabb; + const CMaterialFilter& x4_filter; + CVector3f x8_center; + CVector3f x14_halfExtent; +}; + +class CSphereAreaCache { +public: + friend class CMetroidAreaCollider; + + CSphereAreaCache(const CAABox& aabb, const 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) {} + +private: + const CAABox& x0_aabb; + const CSphere& x4_sphere; + const CMaterialFilter& x8_filter; + const CMaterialList& xc_material; + CCollisionInfoList& x10_collisionList; +}; + +class CBooleanSphereAreaCache { +public: + friend class CMetroidAreaCollider; + + CBooleanSphereAreaCache(const CAABox& aabb, const CSphere& sphere, const CMaterialFilter& filter) + : x0_aabb(aabb), x4_sphere(sphere), x8_filter(filter) {} + +private: + const CAABox& x0_aabb; + const CSphere& x4_sphere; + const CMaterialFilter& x8_filter; +}; + class CMetroidAreaCollider { public: + struct SBoxEdge { + CLineSeg x0_seg; + CVector3d x28_start; + CVector3d x40_end; + CVector3d x58_delta; + CVector3d x70_coDir; + double x88_dirCoDirDot; + SBoxEdge(const CAABox& aabb, int idx, const CVector3f& dir); + }; + + class CMovingAABoxComponents { + public: + CMovingAABoxComponents(const CAABox& aabb, const CVector3f& dir); + + private: + friend class CMetroidAreaCollider; + friend class CCollidableOBBTree; + rstl::reserved_vector< SBoxEdge, 12 > x0_edges; + rstl::reserved_vector< uint, 8 > x6c4_vertIdxs; + CAABox x6e8_aabb; + }; + class COctreeLeafCache { public: COctreeLeafCache(const CAreaOctTree& octTree); @@ -58,6 +121,7 @@ class CMetroidAreaCollider { } private: + friend class CMetroidAreaCollider; const CAreaOctTree& x0_octTree; rstl::reserved_vector< CAreaOctTree::Node, 64 > x4_nodeCache; bool x908_24_overflow : 1; @@ -106,13 +170,32 @@ class CMetroidAreaCollider { float d, CCollisionInfo& infoOut, double& dOut); + static ushort GetPrimitiveCheckCount() { return sDupPrimitiveCheckCount; } + static ushort& DupVertexListValue(uint idx) { return sDupVertexList[idx]; } + static ushort& DupEdgeListValue(uint idx) { return sDupEdgeList[idx]; } + static ushort* GetTriangleList() { return sDupTriangleList; } + private: + friend class CCollidableOBBTree; static ushort sDupPrimitiveCheckCount; static ushort sDupVertexList[0x2800]; static ushort sDupEdgeList[0x6000]; static ushort sDupTriangleList[0x4000]; static void ResetInternalCounters(); static bool AABoxCollisionCheck_Internal(const CAreaOctTree::Node&, CAABoxAreaCache&); + static bool AABoxCollisionCheckBoolean_Internal(const CAreaOctTree::Node&, + const CBooleanAABoxAreaCache&); + static bool SphereCollisionCheck_Internal(const CAreaOctTree::Node&, CSphereAreaCache&); + static bool SphereCollisionCheckBoolean_Internal(const CAreaOctTree::Node&, + const CBooleanSphereAreaCache&); + static bool MovingAABoxCollisionCheck_BoxVertexTri(const CCollisionSurface&, const CAABox&, + const rstl::reserved_vector< uint, 8 >&, + CVector3f, double&, CVector3f&, CVector3f&); + static bool MovingAABoxCollisionCheck_TriVertexBox(const CVector3f&, const CAABox&, + CVector3f, double&, CVector3f&, CVector3f&); + static bool MovingAABoxCollisionCheck_Edge(const CVector3f&, const CVector3f&, + const rstl::reserved_vector< SBoxEdge, 12 >&, + CVector3f, double&, CVector3f&, CVector3f&); }; class CAreaCollisionCache { diff --git a/include/WorldFormat/COBBTree.hpp b/include/WorldFormat/COBBTree.hpp index b6e36e58..6c69e8e7 100644 --- a/include/WorldFormat/COBBTree.hpp +++ b/include/WorldFormat/COBBTree.hpp @@ -1,14 +1,74 @@ #ifndef _COBBTREE #define _COBBTREE +#include "types.h" + +#include "Collision/COBBox.hpp" #include "Kyoto/Math/CVector3f.hpp" #include "WorldFormat/CCollisionEdge.hpp" #include "rstl/vector.hpp" +class CAABox; +class CCollisionSurface; +class CInputStream; +class CTransform4f; + class COBBTree { - class CNode {}; +public: + class CSimpleAllocator { + void* x0_buffer; + uint x4_size; + uint x8_offset; + + public: + CSimpleAllocator(uint size); + ~CSimpleAllocator(); + int Alloc(uint size); + }; + + class CLeafData { + rstl::vector< ushort > x0_surface; + + public: + CLeafData() {} + CLeafData(const rstl::vector< ushort >& surface); + CLeafData(CInputStream& in); + + const rstl::vector< ushort >& GetSurfaceVector() const { return x0_surface; } + uint GetMemoryUsage() const; + }; + + class CNode { + COBBox x0_obb; + bool x3c_isLeaf; + CNode* x40_left; + CNode* x44_right; + CLeafData* x48_leaf; + mutable bool x4c_hit; + + public: + CNode(const CTransform4f& xf, const CVector3f& extents, const CNode* left, + const CNode* right, const CLeafData* leaf); + CNode(CInputStream& in); + ~CNode(); + + const COBBox& GetOBB() const { return x0_obb; } + bool IsLeaf() const { return x3c_isLeaf; } + const CNode& GetLeftNode() const { return *x40_left; } + const CNode& GetRightNode() const { return *x44_right; } + const CLeafData& GetLeafData() const { return *x48_leaf; } + void SetHit(bool h) const { x4c_hit = h; } + bool WasHit() const { return x4c_hit; } + + uint GetMemoryUsage() const; + + static void SetAllocator(CSimpleAllocator* alloc); + void* operator new(size_t size, const char* file, int line); + void operator delete(void* ptr, size_t size); + }; + struct SIndexData { rstl::vector< uint > x0_materials; rstl::vector< uchar > x10_vertMaterials; @@ -17,16 +77,45 @@ class COBBTree { rstl::vector< CCollisionEdge > x40_edges; rstl::vector< ushort > x50_surfaceIndices; rstl::vector< CVector3f > x60_vertices; + + SIndexData(); + SIndexData(const SIndexData& other); SIndexData(CInputStream& in); }; -public: COBBTree(CInputStream& in); COBBTree(const SIndexData& indexData, const CNode* root); ~COBBTree(); + const CNode& GetRoot() const { return *x88_root; } + + CCollisionSurface GetSurface(ushort idx) const; + CCollisionSurface GetTransformedSurface(ushort idx, const CTransform4f& xf) const; + const ushort* GetTriangleEdgeIndices(ushort idx) const; + void GetTriangleVertexIndices(ushort idx, ushort* out) const; + + const CCollisionEdge& GetEdge(int idx) const { return x18_indexData.x40_edges[idx]; } + const CVector3f& GetVert(int idx) const { return x18_indexData.x60_vertices[idx]; } + uint GetVertMaterial(int idx) const { + return x18_indexData.x0_materials[x18_indexData.x10_vertMaterials[idx]]; + } + uint GetEdgeMaterial(int idx) const { + return x18_indexData.x0_materials[x18_indexData.x20_edgeMaterials[idx]]; + } + + CAABox CalculateLocalAABox() const; + + static COBBTree* BuildOrientedBoundingBoxTree(const CVector3f& extent, const CVector3f& center); + private: - char data[0x8c]; + uint x0_magic; + uint x4_version; + uint x8_memsize; + CSimpleAllocator xc_allocator; + SIndexData x18_indexData; + CNode* x88_root; }; +CHECK_SIZEOF(COBBTree, 0x8c) + #endif // _COBBTREE diff --git a/src/WorldFormat/CAreaOctTree.cpp b/src/WorldFormat/CAreaOctTree.cpp index f783def3..39fbddd4 100644 --- a/src/WorldFormat/CAreaOctTree.cpp +++ b/src/WorldFormat/CAreaOctTree.cpp @@ -3,6 +3,10 @@ #include "Kyoto/Math/CAABox.hpp" #include "Kyoto/Streams/CMemoryInStream.hpp" +const ushort* CAreaOctTree::GetTriangleEdgeIndices(ushort idx) const { + return &x44_polyEdges[idx * 3]; +} + static CAABox BoxFromIndex(int index, const CVector3f& a, const CVector3f& b, const CVector3f& c) { switch (index) { case 0: diff --git a/src/WorldFormat/CCollidableOBBTree.cpp b/src/WorldFormat/CCollidableOBBTree.cpp new file mode 100644 index 00000000..410cbb02 --- /dev/null +++ b/src/WorldFormat/CCollidableOBBTree.cpp @@ -0,0 +1,760 @@ +#include "WorldFormat/CCollidableOBBTree.hpp" + +#include "Kyoto/Basics/CCast.hpp" + +#include "Collision/CCollisionInfo.hpp" +#include "Collision/CCollisionInfoList.hpp" +#include "Collision/CInternalRayCastStructure.hpp" +#include "Collision/CMRay.hpp" +#include "Collision/COBBox.hpp" +#include "Collision/CRayCastResult.hpp" +#include "Collision/CollisionUtil.hpp" + +#include "Kyoto/Math/CSphere.hpp" + +#include "math.h" + +#include "WorldFormat/CCollisionSurface.hpp" + +static int mod3[4] = {0, 1, 2, 0}; + +static CPlane TransformPlane(const CPlane& pl, const CTransform4f& xf); + +uint CCollidableOBBTree::sTableIndex = -1; + +// === Constructor === + +CCollidableOBBTree::CCollidableOBBTree(COBBTree* tree, const CMaterialList& list) +: CCollisionPrimitive(list) +, x10_tree(tree) +, x14_tries(0) +, x18_misses(0) +, x1c_hits(0) {} + +// === CalculateAABox === + +CAABox CCollidableOBBTree::CalculateAABox(const CTransform4f& xf) const { + COBBox obb = COBBox::FromAABox(x10_tree->CalculateLocalAABox(), xf); + return obb.CalculateAABox(CTransform4f::Identity()); +} + +// === CalculateLocalAABox === + +CAABox CCollidableOBBTree::CalculateLocalAABox() const { + return x10_tree->CalculateLocalAABox(); +} + +// === GetPrimType === + +FourCC CCollidableOBBTree::GetPrimType() const { + return 'OBBT'; +} + +// === AABoxCollision === + +bool CCollidableOBBTree::AABoxCollision(const COBBTree::CNode& node, const CTransform4f& xf, + const CAABox& aabb, const COBBox& obb, + const CMaterialList& material, + const CMaterialFilter& filter, const CPlane* planes, + CCollisionInfoList& infoList) const { + bool ret = false; + + const_cast< CCollidableOBBTree* >(this)->x14_tries += 1; + if (obb.OBBIntersectsBox(node.GetOBB())) { + node.SetHit(true); + if (node.IsLeaf()) { + if (AABoxCollideWithLeaf(node.GetLeafData(), xf, aabb, material, filter, planes, infoList)) + ret = true; + } else { + if (AABoxCollision(node.GetLeftNode(), xf, aabb, obb, material, filter, planes, infoList)) + ret = true; + if (AABoxCollision(node.GetRightNode(), xf, aabb, obb, material, filter, planes, infoList)) + ret = true; + } + } else { + const_cast< CCollidableOBBTree* >(this)->x18_misses += 1; + } + + return ret; +} + +// === AABoxCollideWithLeaf === + +bool CCollidableOBBTree::AABoxCollideWithLeaf(const COBBTree::CLeafData& leaf, + const CTransform4f& xf, const CAABox& aabb, + const CMaterialList& material, + const CMaterialFilter& filter, + const CPlane* planes, + CCollisionInfoList& infoList) const { + CVector3f center = aabb.GetCenterPoint(); + CVector3f extent = aabb.GetHalfExtent(); + + int surfCount = leaf.GetSurfaceVector().size(); + bool ret = false; + for (int i = 0; i < surfCount; ++i) { + CCollisionSurface surf = x10_tree->GetTransformedSurface(leaf.GetSurfaceVector()[i], xf); + const CMaterialList& baseMat = GetMaterial(); + CMaterialList triMat(static_cast< u64 >(surf.GetSurfaceFlags()) | baseMat.GetValue()); + if (filter.Passes(triMat) && + CollisionUtil::TriBoxOverlap(center, extent, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2))) { + const_cast< CCollidableOBBTree* >(this)->x1c_hits += 1; + CAABox newAABB = CAABox::MakeMaxInvertedBox(); + if (CMetroidAreaCollider::ConvexPolyCollision(planes, &surf.GetVert(0), newAABB)) { + CPlane plane = surf.GetPlane(); + infoList.Add(CCollisionInfo(newAABB, triMat, material, plane.GetNormal(), + -plane.GetNormal()), + false); + ret = true; + } + } + } + + return ret; +} + +// === SphereCollision === + +bool CCollidableOBBTree::SphereCollision(const COBBTree::CNode& node, const CTransform4f& xf, + const CSphere& sphere, const COBBox& obb, + const CMaterialList& material, + const CMaterialFilter& filter, + CCollisionInfoList& infoList) const { + bool ret = false; + const_cast< CCollidableOBBTree* >(this)->x14_tries += 1; + if (obb.OBBIntersectsBox(node.GetOBB())) { + const_cast< COBBTree::CNode& >(node).SetHit(true); + if (node.IsLeaf()) { + if (SphereCollideWithLeaf(node.GetLeafData(), xf, sphere, material, filter, infoList)) + ret = true; + } else { + if (SphereCollision(node.GetLeftNode(), xf, sphere, obb, material, filter, infoList)) + ret = true; + if (SphereCollision(node.GetRightNode(), xf, sphere, obb, material, filter, infoList)) + ret = true; + } + } else { + const_cast< CCollidableOBBTree* >(this)->x18_misses += 1; + } + return ret; +} + +// === SphereCollideWithLeaf === + +bool CCollidableOBBTree::SphereCollideWithLeaf(const COBBTree::CLeafData& leaf, + const CTransform4f& xf, const CSphere& sphere, + const CMaterialList& material, + const CMaterialFilter& filter, + CCollisionInfoList& infoList) const { + bool ret = false; + CVector3f point = CVector3f::Zero(); + CVector3f normal = CVector3f::Zero(); + + int surfCount = leaf.GetSurfaceVector().size(); + for (int i = 0; i < surfCount; ++i) { + CCollisionSurface surf = x10_tree->GetTransformedSurface(leaf.GetSurfaceVector()[i], xf); + const CMaterialList& baseMat = GetMaterial(); + CMaterialList triMat(static_cast< u64 >(surf.GetSurfaceFlags()) | baseMat.GetValue()); + if (filter.Passes(triMat)) { + const_cast< CCollidableOBBTree* >(this)->x1c_hits += 1; + if (CollisionUtil::TriSphereIntersection(sphere, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2), point, normal)) { + infoList.Add(CCollisionInfo(point, material, triMat, normal), false); + ret = true; + } + } + } + + return ret; +} + +// === AABoxCollisionBoolean === + +bool CCollidableOBBTree::AABoxCollisionBoolean(const COBBTree::CNode& node, + const CTransform4f& xf, const CAABox& aabb, + const COBBox& obb, + const CMaterialFilter& filter) const { + CVector3f center = aabb.GetCenterPoint(); + CVector3f extent = aabb.GetHalfExtent(); + + const_cast< CCollidableOBBTree* >(this)->x14_tries += 1; + if (obb.OBBIntersectsBox(node.GetOBB())) { + node.SetHit(true); + if (node.IsLeaf()) { + const COBBTree::CLeafData& leaf = node.GetLeafData(); + int surfCount = leaf.GetSurfaceVector().size(); + for (int i = 0; i < surfCount; ++i) { + CCollisionSurface surf = x10_tree->GetTransformedSurface(leaf.GetSurfaceVector()[i], xf); + const CMaterialList& baseMat = GetMaterial(); + CMaterialList triMat(static_cast< u64 >(surf.GetSurfaceFlags()) | baseMat.GetValue()); + if (filter.Passes(triMat) && + CollisionUtil::TriBoxOverlap(center, extent, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2))) { + return true; + } + } + } else { + if (AABoxCollisionBoolean(node.GetLeftNode(), xf, aabb, obb, filter)) + return true; + if (AABoxCollisionBoolean(node.GetRightNode(), xf, aabb, obb, filter)) + return true; + } + } else { + const_cast< CCollidableOBBTree* >(this)->x18_misses += 1; + } + + return false; +} + +// === SphereCollisionBoolean === + +bool CCollidableOBBTree::SphereCollisionBoolean(const COBBTree::CNode& node, + const CTransform4f& xf, const CSphere& sphere, + const COBBox& obb, + const CMaterialFilter& filter) const { + const_cast< CCollidableOBBTree* >(this)->x14_tries += 1; + if (obb.OBBIntersectsBox(node.GetOBB())) { + const_cast< COBBTree::CNode& >(node).SetHit(true); + if (node.IsLeaf()) { + const COBBTree::CLeafData& leaf = node.GetLeafData(); + int surfCount = leaf.GetSurfaceVector().size(); + for (int i = 0; i < surfCount; ++i) { + CCollisionSurface surf = x10_tree->GetTransformedSurface(leaf.GetSurfaceVector()[i], xf); + const CMaterialList& baseMat = GetMaterial(); + CMaterialList triMat(static_cast< u64 >(surf.GetSurfaceFlags()) | baseMat.GetValue()); + if (filter.Passes(triMat) && + CollisionUtil::TriSphereOverlap(sphere, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2))) { + return true; + } + } + } else { + if (SphereCollisionBoolean(node.GetLeftNode(), xf, sphere, obb, filter)) + return true; + if (SphereCollisionBoolean(node.GetRightNode(), xf, sphere, obb, filter)) + return true; + } + } else { + const_cast< CCollidableOBBTree* >(this)->x18_misses += 1; + } + + return false; +} + +// === AABoxCollisionMoving === + +bool CCollidableOBBTree::AABoxCollisionMoving( + const COBBTree::CNode& node, const CTransform4f& xf, const CAABox& aabb, const COBBox& obb, + const CMaterialList& material, const CMaterialFilter& filter, + const CMetroidAreaCollider::CMovingAABoxComponents& components, const CVector3f& dir, + double& dOut, CCollisionInfo& info) const { + bool ret = false; + const_cast< CCollidableOBBTree* >(this)->x14_tries += 1; + if (obb.OBBIntersectsBox(node.GetOBB())) { + node.SetHit(true); + if (node.IsLeaf()) { + if (AABoxCollideWithLeafMoving(node.GetLeafData(), xf, aabb, material, filter, components, + dir, dOut, info)) + ret = true; + } else { + if (AABoxCollisionMoving(node.GetLeftNode(), xf, aabb, obb, material, filter, components, + dir, dOut, info)) + ret = true; + if (AABoxCollisionMoving(node.GetRightNode(), xf, aabb, obb, material, filter, components, + dir, dOut, info)) + ret = true; + } + } else { + const_cast< CCollidableOBBTree* >(this)->x18_misses += 1; + } + return ret; +} + +// === AABoxCollideWithLeafMoving === + +bool CCollidableOBBTree::AABoxCollideWithLeafMoving( + const COBBTree::CLeafData& leaf, const CTransform4f& xf, const CAABox& aabb, + const CMaterialList& material, const CMaterialFilter& filter, + const CMetroidAreaCollider::CMovingAABoxComponents& components, const CVector3f& dir, + double& dOut, CCollisionInfo& info) const { + CVector3f normal(CVector3f::Zero()); + CVector3f point(CVector3f::Zero()); + + CAABox movedAABB = components.x6e8_aabb; + CVector3f moveVec = static_cast< float >(dOut) * dir; + movedAABB.AccumulateBounds(aabb.GetMaxPoint() + moveVec); + movedAABB.AccumulateBounds(aabb.GetMinPoint() + moveVec); + + CVector3f center = movedAABB.GetCenterPoint(); + bool ret = false; + CVector3f extent = movedAABB.GetHalfExtent(); + + int surfCount = leaf.GetSurfaceVector().size(); + for (int i = 0; i < surfCount; ++i) { + ushort triIdx = leaf.GetSurfaceVector()[i]; + CCollisionSurface surf = x10_tree->GetTransformedSurface(triIdx, xf); + const CMaterialList& baseMat = GetMaterial(); + CMaterialList triMat(static_cast< u64 >(surf.GetSurfaceFlags()) | baseMat.GetValue()); + if (filter.Passes(triMat)) { + if (CollisionUtil::TriBoxOverlap(center, extent, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2))) { + const_cast< CCollidableOBBTree* >(this)->x1c_hits += 1; + + ushort vertIndices[3]; + x10_tree->GetTriangleVertexIndices(triIdx, vertIndices); + + double d = dOut; + if (CMetroidAreaCollider::MovingAABoxCollisionCheck_BoxVertexTri( + surf, aabb, components.x6c4_vertIdxs, dir, d, normal, point) && + d < dOut) { + info = CCollisionInfo(point, material, triMat, normal); + ret = true; + dOut = d; + } + + for (int k = 0; k < 3; ++k) { + uint vertIdx = vertIndices[k]; + if (CMetroidAreaCollider::sDupVertexList[vertIdx] != + CMetroidAreaCollider::sDupPrimitiveCheckCount) { + CMetroidAreaCollider::sDupVertexList[vertIdx] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + if (movedAABB.PointInside(surf.GetVert(k))) { + d = dOut; + if (CMetroidAreaCollider::MovingAABoxCollisionCheck_TriVertexBox( + surf.GetVert(k), aabb, dir, d, normal, point) && + d < dOut) { + info = CCollisionInfo(point, material, + CMaterialList(x10_tree->GetVertMaterial(vertIdx)), normal); + ret = true; + dOut = d; + } + } + } + } + + const ushort* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx); + for (int k = 0; k < 3; ++k) { + uint edgeIdx = edgeIndices[k]; + if (CMetroidAreaCollider::sDupEdgeList[edgeIdx] != + CMetroidAreaCollider::sDupPrimitiveCheckCount) { + CMetroidAreaCollider::sDupEdgeList[edgeIdx] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + uint edgeMatVal = x10_tree->GetEdgeMaterial(edgeIdx); + if (!(edgeMatVal & (1u << kMT_NoEdgeCollision))) { + d = dOut; + if (CMetroidAreaCollider::MovingAABoxCollisionCheck_Edge( + surf.GetVert(k), surf.GetVert(k == 2 ? 0 : k + 1), + components.x0_edges, dir, d, normal, point) && + d < dOut) { + info = CCollisionInfo(point, material, CMaterialList(edgeMatVal), normal); + ret = true; + dOut = d; + } + } + } + } + } else { + const ushort* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx); + CMetroidAreaCollider::sDupEdgeList[edgeIndices[0]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupEdgeList[edgeIndices[1]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupEdgeList[edgeIndices[2]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + + ushort vertIndices[3]; + x10_tree->GetTriangleVertexIndices(triIdx, vertIndices); + CMetroidAreaCollider::sDupVertexList[vertIndices[0]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupVertexList[vertIndices[1]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupVertexList[vertIndices[2]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + } + } + } + + return ret; +} + +// === SphereCollisionMoving === + +bool CCollidableOBBTree::SphereCollisionMoving( + const COBBTree::CNode& node, const CTransform4f& xf, const CSphere& sphere, + const COBBox& obb, const CMaterialList& material, const CMaterialFilter& filter, + const CVector3f& dir, double& dOut, CCollisionInfo& info) const { + bool ret = false; + const_cast< CCollidableOBBTree* >(this)->x14_tries += 1; + if (obb.OBBIntersectsBox(node.GetOBB())) { + const_cast< COBBTree::CNode& >(node).SetHit(true); + if (node.IsLeaf()) { + if (SphereCollideWithLeafMoving(node.GetLeafData(), xf, sphere, material, filter, dir, dOut, + info)) + ret = true; + } else { + if (SphereCollisionMoving(node.GetLeftNode(), xf, sphere, obb, material, filter, dir, dOut, + info)) + ret = true; + if (SphereCollisionMoving(node.GetRightNode(), xf, sphere, obb, material, filter, dir, dOut, + info)) + ret = true; + } + } else { + const_cast< CCollidableOBBTree* >(this)->x18_misses += 1; + } + return ret; +} + +// === SphereCollideWithLeafMoving === + +bool CCollidableOBBTree::SphereCollideWithLeafMoving( + const COBBTree::CLeafData& leaf, const CTransform4f& xf, const CSphere& sphere, + const CMaterialList& material, const CMaterialFilter& filter, const CVector3f& dir, + double& dOut, CCollisionInfo& info) const { + float radius = sphere.GetRadius(); + CVector3f radiusVec(radius, radius, radius); + CAABox aabb(sphere.GetCenter() - radiusVec, sphere.GetCenter() + radiusVec); + + CVector3f moveVec = CCast::ToReal32(dOut) * dir; + CAABox moveAABB = aabb; + moveAABB.AccumulateBounds(aabb.GetMaxPoint() + moveVec); + moveAABB.AccumulateBounds(aabb.GetMinPoint() + moveVec); + + CVector3f boxCenter = moveAABB.GetCenterPoint(); + bool ret = false; + CVector3f extent = moveAABB.GetHalfExtent(); + + int surfCount = leaf.GetSurfaceVector().size(); + for (int i = 0; i < surfCount; ++i) { + ushort triIdx = leaf.GetSurfaceVector()[i]; + CCollisionSurface surf = x10_tree->GetTransformedSurface(triIdx, xf); + const CMaterialList& baseMat = GetMaterial(); + CMaterialList triMat(static_cast< u64 >(surf.GetSurfaceFlags()) | baseMat.GetValue()); + if (filter.Passes(triMat)) { + if (CollisionUtil::TriBoxOverlap(boxCenter, extent, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2))) { + const_cast< CCollidableOBBTree* >(this)->x1c_hits += 1; + + CVector3f surfNormal = surf.GetNormal(); + + if (!(CVector3f::Dot(sphere.GetCenter() + moveVec - surf.GetVert(0), surfNormal) > + sphere.GetRadius())) { + const CVector3f& vertToSphere0 = sphere.GetCenter() - surf.GetVert(0); + float dirDotNorm = CVector3f::Dot(dir, surfNormal); + const CVector3f& edge0 = surf.GetVert(1) - surf.GetVert(0); + double mag = (double)(sphere.GetRadius() - CVector3f::Dot(vertToSphere0, surfNormal)) / dirDotNorm; + float magF = CCast::ToReal32(mag); + + CVector3f intersectPoint = sphere.GetCenter() + magF * dir; + const CVector3f& cross0 = CVector3f::Cross(surfNormal, edge0); + const CVector3f& d0 = intersectPoint - surf.GetVert(0); + bool outsideEdge0 = CVector3f::Dot(d0, cross0) < 0.f; + + const CVector3f& edge1 = surf.GetVert(2) - surf.GetVert(1); + const CVector3f& cross1 = CVector3f::Cross(surfNormal, edge1); + const CVector3f& d1 = intersectPoint - surf.GetVert(1); + bool outsideEdge1 = CVector3f::Dot(d1, cross1) < 0.f; + + const CVector3f& edge2 = surf.GetVert(0) - surf.GetVert(2); + const CVector3f& cross2 = CVector3f::Cross(surfNormal, edge2); + const CVector3f& d2 = intersectPoint - surf.GetVert(2); + bool outsideEdge2 = CVector3f::Dot(d2, cross2) < 0.f; + + if (mag >= 0.0 && !outsideEdge0 && !outsideEdge1 && !outsideEdge2 && mag < dOut) { + const CVector3f& collisionPoint = + intersectPoint - sphere.GetRadius() * surfNormal; + info = CCollisionInfo(collisionPoint, material, triMat, surfNormal); + ret = true; + dOut = mag; + } + + const CVector3f& vts2 = sphere.GetCenter() - surf.GetVert(0); + bool intersects = CVector3f::Dot(vts2, surfNormal) <= sphere.GetRadius(); + bool testVert[3] = {true, true, true}; + const ushort* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx); + for (int k = 0; k < 3; ++k) { + if (intersects || (&outsideEdge0)[k]) { + uint edgeIdx = edgeIndices[k]; + if (CMetroidAreaCollider::sDupEdgeList[edgeIdx] != + CMetroidAreaCollider::sDupPrimitiveCheckCount) { + CMetroidAreaCollider::sDupEdgeList[edgeIdx] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + uint edgeMatVal = x10_tree->GetEdgeMaterial(edgeIdx); + if (!(edgeMatVal & (1u << kMT_NoEdgeCollision))) { + CVector3f edgeVec = surf.GetVert(mod3[k + 1]) - surf.GetVert(k); + float edgeVecMag = edgeVec.Magnitude(); + edgeVec *= 1.f / edgeVecMag; + float dirDotEdge = CVector3f::Dot(dir, edgeVec); + CVector3f edgeRej = dir - dirDotEdge * edgeVec; + float edgeRejMagSq = edgeRej.MagSquared(); + CVector3f vertToSphere = sphere.GetCenter() - surf.GetVert(k); + float vtsDotEdge = CVector3f::Dot(vertToSphere, edgeVec); + CVector3f vtsRej = vertToSphere - vtsDotEdge * edgeVec; + if (edgeRejMagSq > 0.f) { + float tmp = 2.f * CVector3f::Dot(vtsRej, edgeRej); + float tmp2 = 4.f * edgeRejMagSq * + (vtsRej.MagSquared() - + sphere.GetRadius() * sphere.GetRadius()) - + tmp * tmp; + if (tmp2 >= 0.f) { + double mag2 = 0.5 / edgeRejMagSq * (-tmp - sqrt(tmp2)); + if (mag2 >= 0.0) { + double t = mag2 * dirDotEdge + vtsDotEdge; + if (t >= 0.0 && t <= edgeVecMag && mag2 < dOut) { + CVector3f point = + surf.GetVert(k) + CCast::ToReal32(t) * edgeVec; + const CVector3f& movedSphere = + sphere.GetCenter() + CCast::ToReal32(mag2) * dir; + const CVector3f& normVec = movedSphere - point; + info = CCollisionInfo( + point, material, CMaterialList(edgeMatVal), + normVec.AsNormalized()); + dOut = mag2; + ret = true; + testVert[k] = false; + testVert[mod3[k + 1]] = false; + } else if (t < -sphere.GetRadius() && dirDotEdge <= 0.f) { + testVert[k] = false; + } else if (t > edgeVecMag + sphere.GetRadius() && + dirDotEdge >= 0.f) { + testVert[mod3[k + 1]] = false; + } + } + } else { + testVert[k] = false; + testVert[mod3[k + 1]] = false; + } + } + } + } + } + } + + ushort vertIndices[3]; + x10_tree->GetTriangleVertexIndices(triIdx, vertIndices); + for (int k = 0; k < 3; ++k) { + uint vertIdx = vertIndices[k]; + if (testVert[k]) { + if (CMetroidAreaCollider::sDupVertexList[vertIdx] != + CMetroidAreaCollider::sDupPrimitiveCheckCount) { + CMetroidAreaCollider::sDupVertexList[vertIdx] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + double d = dOut; + if (CollisionUtil::RaySphereIntersection_Double( + CSphere(surf.GetVert(k), sphere.GetRadius()), + sphere.GetCenter(), dir, d) && + d >= 0.0) { + float dF = CCast::ToReal32(d); + const CVector3f& movedSph = + sphere.GetCenter() + dF * dir; + const CVector3f& normVec2 = movedSph - surf.GetVert(k); + info = CCollisionInfo( + surf.GetVert(k), material, + CMaterialList(x10_tree->GetVertMaterial(vertIdx)), + normVec2.AsNormalized()); + dOut = d; + ret = true; + } + } + } else { + CMetroidAreaCollider::sDupVertexList[vertIdx] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + } + } + } + } else { + const ushort* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx); + CMetroidAreaCollider::sDupEdgeList[edgeIndices[0]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupEdgeList[edgeIndices[1]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupEdgeList[edgeIndices[2]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + + ushort vertIndices[3]; + x10_tree->GetTriangleVertexIndices(triIdx, vertIndices); + CMetroidAreaCollider::sDupVertexList[vertIndices[0]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupVertexList[vertIndices[1]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + CMetroidAreaCollider::sDupVertexList[vertIndices[2]] = + CMetroidAreaCollider::sDupPrimitiveCheckCount; + } + } + } + + return ret; +} + +// === CastRayInternal === + +CRayCastResult +CCollidableOBBTree::CastRayInternal(const CInternalRayCastStructure& rayCast) const { + return LineIntersectsTree(rayCast.GetRay(), rayCast.GetFilter(), rayCast.GetMaxTime(), + rayCast.GetTransform()); +} + +// === TransformPlane (local free function) === + +static CPlane TransformPlane(const CPlane& pl, const CTransform4f& xf) { + CVector3f transformed = xf * (pl.GetNormal() * pl.GetConstant()); + CVector3f normal = xf.Rotate(pl.GetNormal()); + float d = CVector3f::Dot(transformed, normal); + return CPlane(d, CUnitVector3f(normal.GetX(), normal.GetY(), normal.GetZ())); +} + +// === LineIntersectsTree === + +CRayCastResult CCollidableOBBTree::LineIntersectsTree(const CMRay& ray, + const CMaterialFilter& filter, + float maxTime, + const CTransform4f& xf) const { + CMRay useRay = ray.GetInvUnscaledTransformRay(xf); + CRayCastInfo info(useRay, filter, maxTime); + if (LineIntersectsOBBTree(&x10_tree->GetRoot(), info)) { + CPlane xfPlane = TransformPlane(info.GetPlane(), xf); + return CRayCastResult(info.GetMagnitude(), + ray.GetStart() + info.GetMagnitude() * ray.GetDirection(), xfPlane, + info.GetMaterial()); + } + return CRayCastResult(); +} + +// === LineIntersectsOBBTree (single node) === + +bool CCollidableOBBTree::LineIntersectsOBBTree(const COBBTree::CNode* node, + CRayCastInfo& info) const { + float t; + bool ret = false; + + const_cast< CCollidableOBBTree* >(this)->x14_tries += 1; + if (node->GetOBB().LineIntersectsBox(info.GetRay(), t) && t < info.GetMagnitude()) { + if (node->IsLeaf() == true) { + if (LineIntersectsLeaf(node->GetLeafData(), info) == true) + ret = true; + } else { + if (LineIntersectsOBBTree(&node->GetLeftNode(), &node->GetRightNode(), info) == true) + ret = true; + } + node->SetHit(true); + } else { + const_cast< CCollidableOBBTree* >(this)->x18_misses += 1; + } + + return ret; +} + +// === LineIntersectsOBBTree (two nodes) === + +bool CCollidableOBBTree::LineIntersectsOBBTree(const COBBTree::CNode* n0, + const COBBTree::CNode* n1, + CRayCastInfo& info) const { + bool ret = false; + float t0, t1; + bool intersects0 = false; + + const_cast< CCollidableOBBTree* >(this)->x14_tries += 2; + + if (n0->GetOBB().LineIntersectsBox(info.GetRay(), t0) == true && t0 < info.GetMagnitude()) + intersects0 = true; + + bool intersects1 = false; + if (n1->GetOBB().LineIntersectsBox(info.GetRay(), t1) == true && t1 < info.GetMagnitude()) + intersects1 = true; + + if (intersects0 && intersects1) { + if (t0 < t1) { + if ((n0->IsLeaf() == true ? LineIntersectsLeaf(n0->GetLeafData(), info) + : LineIntersectsOBBTree(&n0->GetLeftNode(), &n0->GetRightNode(), + info)) == true) { + if (info.GetMagnitude() < t1) + return true; + ret = true; + } + if (n1->IsLeaf()) { + if (LineIntersectsLeaf(n1->GetLeafData(), info)) + ret = true; + } else { + if (LineIntersectsOBBTree(&n1->GetLeftNode(), &n1->GetRightNode(), info) == true) + ret = true; + } + } else { + if ((n1->IsLeaf() == true ? LineIntersectsLeaf(n1->GetLeafData(), info) + : LineIntersectsOBBTree(&n1->GetLeftNode(), &n1->GetRightNode(), + info)) == true) { + if (info.GetMagnitude() < t0) + return true; + ret = true; + } + if (n0->IsLeaf()) { + if (LineIntersectsLeaf(n0->GetLeafData(), info)) + ret = true; + } else { + if (LineIntersectsOBBTree(&n0->GetLeftNode(), &n0->GetRightNode(), info) == true) + ret = true; + } + } + } else { + if (intersects0) { + if (n0->IsLeaf() == true) { + if (LineIntersectsLeaf(n0->GetLeafData(), info)) + return true; + } else { + if (LineIntersectsOBBTree(&n0->GetLeftNode(), &n0->GetRightNode(), info) == true) + return true; + } + } + if (intersects1) { + if (n1->IsLeaf() == true) { + if (LineIntersectsLeaf(n1->GetLeafData(), info)) + return true; + } else { + if (LineIntersectsOBBTree(&n1->GetLeftNode(), &n1->GetRightNode(), info) == true) + return true; + } + } + } + + return ret; +} + +// === LineIntersectsLeaf === + +bool CCollidableOBBTree::LineIntersectsLeaf(const COBBTree::CLeafData& leaf, + CRayCastInfo& info) const { + bool ret = false; + ushort intersectIdx = 0; + int surfCount = leaf.GetSurfaceVector().size(); + const CMaterialFilter& filter = info.GetMaterialFilter(); + for (ushort i = 0; i < surfCount; ++i) { + const CCollisionSurface& surface = x10_tree->GetSurface(leaf.GetSurfaceVector()[i]); + const CMaterialList& baseMat = GetMaterial(); + CMaterialList matList(static_cast< u64 >(surface.GetSurfaceFlags()) | baseMat.GetValue()); + if (filter.Passes(matList)) { + if (CollisionUtil::RayTriangleIntersection(info.GetRay().GetStart(), + info.GetRay().GetDirection(), + &surface.GetVert(0), info.Magnitude())) { + intersectIdx = i; + ret = true; + } + } + } + + if (ret) { + const CCollisionSurface& surf = x10_tree->GetSurface(leaf.GetSurfaceVector()[intersectIdx]); + info.Plane() = surf.GetPlane(); + info.Material() = CMaterialList(surf.GetSurfaceFlags()); + } + + return ret; +} + +// === GetTableIndex === + +uint CCollidableOBBTree::GetTableIndex() const { + return sTableIndex; +} + +// === Destructor === + +CCollidableOBBTree::~CCollidableOBBTree() {} diff --git a/src/WorldFormat/CCollidableOBBTreeGroup.cpp b/src/WorldFormat/CCollidableOBBTreeGroup.cpp index a6b65dca..96a81bff 100644 --- a/src/WorldFormat/CCollidableOBBTreeGroup.cpp +++ b/src/WorldFormat/CCollidableOBBTreeGroup.cpp @@ -23,7 +23,7 @@ CCollidableOBBTreeGroupContainer::CCollidableOBBTreeGroupContainer(CInputStream& x10_aabbs.reserve(x0_trees.size()); rstl::vector< rstl::auto_ptr< COBBTree > >::iterator it = x0_trees.begin(); for (; it != x0_trees.end(); ++it) { - CCollidableOBBTree tree(*it->get(), CMaterialList()); + CCollidableOBBTree tree(it->get(), CMaterialList()); CAABox box = tree.CalculateLocalAABox(); x10_aabbs.push_back(box); x20_aabox.AccumulateBounds(box.GetMinPoint()); diff --git a/src/WorldFormat/CMetroidAreaCollider.cpp b/src/WorldFormat/CMetroidAreaCollider.cpp index 069cd745..7df3a7cd 100644 --- a/src/WorldFormat/CMetroidAreaCollider.cpp +++ b/src/WorldFormat/CMetroidAreaCollider.cpp @@ -1,7 +1,14 @@ +#pragma inline_max_size(250) + #include "WorldFormat/CMetroidAreaCollider.hpp" +#include "Collision/CMRay.hpp" #include "Collision/CollisionUtil.hpp" +#include "WorldFormat/CCollisionEdge.hpp" + +#include "Kyoto/Math/CMath.hpp" +#include #include static uint gCalledClip = 0; @@ -13,19 +20,13 @@ ushort CMetroidAreaCollider::sDupVertexList[0x2800]; ushort CMetroidAreaCollider::sDupEdgeList[0x6000]; ushort CMetroidAreaCollider::sDupTriangleList[0x4000]; -static inline CVector3f ClipRayToPlane(const CVector3f& a, const CVector3f& b, - const CPlane& plane) { - float f = -plane.GetHeight(a) / CVector3f::Dot(b - a, plane.GetNormal()); - return (a - b) * (1.f - f); -} - bool CMetroidAreaCollider::ConvexPolyCollision(const CPlane* planes, const CVector3f* verts, CAABox& aabb) { typedef rstl::reserved_vector< CVector3f, 20 > ClipVec; ++gCalledClip; - ++gRejectedByClip; ClipVec vecs[2]; + ++gRejectedByClip; int vecIdx = 0; int otherVecIdx = 1; @@ -46,7 +47,8 @@ bool CMetroidAreaCollider::ConvexPolyCollision(const CPlane* planes, const CVect } bool nextInFrontOf = planes[i].GetHeight(b) >= 0.f; if (nextInFrontOf ^ inFrontOf) { - otherVec.push_back(ClipRayToPlane(vec[j], b, planes[i]) + b); + float f = -(CVector3f::Dot(vec[j], planes[i].GetNormal()) - planes[i].GetConstant()) / CVector3f::Dot(b - vec[j], planes[i].GetNormal()); + otherVec.push_back((1.f - f) * (vec[j] - b) + b); } inFrontOf = nextInFrontOf; } @@ -82,6 +84,17 @@ void CMetroidAreaCollider::ResetInternalCounters() { ++sDupPrimitiveCheckCount; } +CAABoxAreaCache::CAABoxAreaCache(const CAABox& aabb, const CPlane* 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.GetCenterPoint()) +, x20_halfExtent(aabb.GetHalfExtent()) {} + bool CMetroidAreaCollider::AABoxCollisionCheck_Internal(const CAreaOctTree::Node& node, CAABoxAreaCache& cache) { bool ret = false; @@ -144,9 +157,9 @@ bool CMetroidAreaCollider::AABoxCollisionCheck_Cached(const COctreeLeafCache& le const CMaterialList& matList, CCollisionInfoList& list) { bool ret = false; - const CUnitVector3f right(CVector3f(1.f, 0.f, 0.f), CUnitVector3f::kN_No); - const CUnitVector3f forward(CVector3f(0.f, 1.f, 0.f), CUnitVector3f::kN_No); - const CUnitVector3f up(CVector3f(0.f, 0.f, 1.f), CUnitVector3f::kN_No); + const CUnitVector3f right(1.f, 0.f, 0.f); + const CUnitVector3f forward(0.f, 1.f, 0.f); + const CUnitVector3f up(0.f, 0.f, 1.f); const CPlane planes[6] = { CPlane(aabb.GetMinPoint(), right), CPlane(aabb.GetMaxPoint(), -right), CPlane(aabb.GetMinPoint(), forward), CPlane(aabb.GetMaxPoint(), -forward), @@ -161,14 +174,16 @@ bool CMetroidAreaCollider::AABoxCollisionCheck_Cached(const COctreeLeafCache& le const CAreaOctTree::Node& node = leafCache.GetLeaf(i); if (aabb.DoBoundsOverlap(node.GetBoundingBox())) { CAreaOctTree::TriListReference listRef = node.GetTriangleArray(); - for (int j = 0; j < listRef.GetSize(); ++j) { + const CAreaOctTree& owner = node.GetOwner(); + int size = listRef.GetSize(); + for (int j = 0; j < size; ++j) { ++gTrianglesProcessed; ushort triIdx = listRef.GetAt(j); if (sDupPrimitiveCheckCount == sDupTriangleList[triIdx]) { ++gDupTrianglesProcessed; } else { sDupTriangleList[triIdx] = sDupPrimitiveCheckCount; - const CCollisionSurface& surf = node.GetOwner().GetMasterListTriangle(triIdx); + const CCollisionSurface& surf = owner.GetMasterListTriangle(triIdx); CMaterialList material(surf.GetSurfaceFlags()); if (filter.Passes(material)) { if (CollisionUtil::TriBoxOverlap(center, halfExtent, surf.GetVert(0), surf.GetVert(1), @@ -176,9 +191,9 @@ bool CMetroidAreaCollider::AABoxCollisionCheck_Cached(const COctreeLeafCache& le CAABox aabb2 = CAABox::MakeMaxInvertedBox(); if (ConvexPolyCollision(planes, &surf.GetVert(0), aabb2)) { CPlane plane = surf.GetPlane(); - CCollisionInfo collision(aabb2, matList, material, plane.GetNormal(), - -plane.GetNormal()); - list.Add(collision, false); + list.Add(CCollisionInfo(aabb2, matList, material, plane.GetNormal(), + -plane.GetNormal()), + false); ret = true; } } @@ -190,3 +205,856 @@ bool CMetroidAreaCollider::AABoxCollisionCheck_Cached(const COctreeLeafCache& le return ret; } + +bool CMetroidAreaCollider::AABoxCollisionCheck(const CAreaOctTree& octTree, const CAABox& aabb, + const CMaterialFilter& filter, + const CMaterialList& matList, + CCollisionInfoList& list) { + CPlane planes[6] = { + CPlane(aabb.GetMinPoint(), CUnitVector3f(1.f, 0.f, 0.f)), + CPlane(aabb.GetMaxPoint(), -CUnitVector3f(1.f, 0.f, 0.f)), + CPlane(aabb.GetMinPoint(), CUnitVector3f(0.f, 1.f, 0.f)), + CPlane(aabb.GetMaxPoint(), -CUnitVector3f(0.f, 1.f, 0.f)), + CPlane(aabb.GetMinPoint(), CUnitVector3f(0.f, 0.f, 1.f)), + CPlane(aabb.GetMaxPoint(), -CUnitVector3f(0.f, 0.f, 1.f)), + }; + CAABoxAreaCache cache(aabb, planes, filter, matList, list); + + ResetInternalCounters(); + + CAreaOctTree::Node node(octTree.GetTreeMemory(), octTree.GetBoundingBox(), octTree, + octTree.GetTreeType()); + return AABoxCollisionCheck_Internal(node, cache); +} + +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::kTT_Invalid) { + CAreaOctTree::Node ch = node.GetChild(i); + if (cache.x0_aabb.DoBoundsOverlap(ch.GetBoundingBox())) { + if (type == CAreaOctTree::Node::kTT_Leaf) { + CAreaOctTree::TriListReference list = ch.GetTriangleArray(); + const CAreaOctTree& owner = ch.GetOwner(); + int size = list.GetSize(); + for (int j = 0; j < size; ++j) { + ++gTrianglesProcessed; + const CCollisionSurface& surf = owner.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)) == true) + return true; + } + } + } else { + if (AABoxCollisionCheckBoolean_Internal(ch, cache) == true) + return true; + } + } + } + } + return false; +} + +bool CMetroidAreaCollider::AABoxCollisionCheckBoolean_Cached(const COctreeLeafCache& leafCache, + const CAABox& aabb, + const CMaterialFilter& filter) { + CVector3f center = aabb.GetCenterPoint(); + CVector3f halfExtent = aabb.GetHalfExtent(); + + for (int i = 0; i < leafCache.GetNumLeaves(); ++i) { + const CAreaOctTree::Node& node = leafCache.GetLeaf(i); + if (aabb.DoBoundsOverlap(node.GetBoundingBox())) { + CAreaOctTree::TriListReference list = node.GetTriangleArray(); + const CAreaOctTree& owner = node.GetOwner(); + int size = list.GetSize(); + for (int j = 0; j < size; ++j) { + ++gTrianglesProcessed; + const CCollisionSurface& surf = owner.GetMasterListTriangle(list.GetAt(j)); + if (filter.Passes(CMaterialList(surf.GetSurfaceFlags()))) { + if (CollisionUtil::TriBoxOverlap(center, halfExtent, + surf.GetVert(0), surf.GetVert(1), surf.GetVert(2)) == true) + return true; + } + } + } + } + + return false; +} + +CBooleanAABoxAreaCache::CBooleanAABoxAreaCache(const CAABox& aabb, const CMaterialFilter& filter) +: x0_aabb(aabb) +, x4_filter(filter) +, x8_center(aabb.GetCenterPoint()) +, x14_halfExtent(aabb.GetHalfExtent()) {} + +bool CMetroidAreaCollider::AABoxCollisionCheckBoolean(const CAreaOctTree& octTree, + const CAABox& aabb, + const CMaterialFilter& filter) { + CBooleanAABoxAreaCache cache(aabb, filter); + return AABoxCollisionCheckBoolean_Internal(octTree.GetRootNode(), cache); +} + +bool CMetroidAreaCollider::SphereCollisionCheck_Internal(const CAreaOctTree::Node& node, + CSphereAreaCache& cache) { + bool ret = false; + CVector3f point = CVector3f::Zero(); + CVector3f normal = CVector3f::Zero(); + + for (int i = 0; i < 8; ++i) { + CAreaOctTree::Node::ETreeType chTp = node.GetChildType(i); + if (chTp != CAreaOctTree::Node::kTT_Invalid) { + CAreaOctTree::Node ch = node.GetChild(i); + if (cache.x0_aabb.DoBoundsOverlap(ch.GetBoundingBox())) { + if (chTp == CAreaOctTree::Node::kTT_Leaf) { + CAreaOctTree::TriListReference list = ch.GetTriangleArray(); + const CAreaOctTree& owner = ch.GetOwner(); + int size = list.GetSize(); + for (int j = 0; j < size; ++j) { + ++gTrianglesProcessed; + ushort triIdx = list.GetAt(j); + if (sDupPrimitiveCheckCount == sDupTriangleList[triIdx]) { + ++gDupTrianglesProcessed; + } else { + sDupTriangleList[triIdx] = sDupPrimitiveCheckCount; + const CCollisionSurface& surf = owner.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)) { + cache.x10_collisionList.Add( + CCollisionInfo(point, cache.xc_material, material, normal), false); + ret = true; + } + } + } + } + } else { + if (SphereCollisionCheck_Internal(ch, cache) == true) + ret = true; + } + } + } + } + + return ret; +} + +bool CMetroidAreaCollider::SphereCollisionCheck_Cached(const COctreeLeafCache& leafCache, + const CAABox& aabb, + const CSphere& sphere, + const CMaterialList& matList, + const CMaterialFilter& filter, + CCollisionInfoList& clist) { + ResetInternalCounters(); + + bool ret = false; + CVector3f point = CVector3f::Zero(); + CVector3f normal = CVector3f::Zero(); + + for (int i = 0; i < leafCache.GetNumLeaves(); ++i) { + const CAreaOctTree::Node& node = leafCache.GetLeaf(i); + if (aabb.DoBoundsOverlap(node.GetBoundingBox())) { + CAreaOctTree::TriListReference list = node.GetTriangleArray(); + const CAreaOctTree& owner = node.GetOwner(); + int size = list.GetSize(); + for (int j = 0; j < size; ++j) { + ++gTrianglesProcessed; + ushort triIdx = list.GetAt(j); + if (sDupPrimitiveCheckCount == sDupTriangleList[triIdx]) { + ++gDupTrianglesProcessed; + } else { + sDupTriangleList[triIdx] = sDupPrimitiveCheckCount; + const CCollisionSurface& surf = owner.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)) { + clist.Add(CCollisionInfo(point, matList, material, normal), false); + ret = true; + } + } + } + } + } + } + + return ret; +} + +bool CMetroidAreaCollider::SphereCollisionCheck(const CAreaOctTree& octTree, const CAABox& aabb, + const CSphere& sphere, + const CMaterialList& matList, + const CMaterialFilter& filter, + CCollisionInfoList& list) { + CSphereAreaCache cache(aabb, sphere, filter, matList, list); + ResetInternalCounters(); + return SphereCollisionCheck_Internal(octTree.GetRootNode(), cache); +} + +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::kTT_Invalid) { + CAreaOctTree::Node ch = node.GetChild(i); + if (cache.x0_aabb.DoBoundsOverlap(ch.GetBoundingBox())) { + if (type == CAreaOctTree::Node::kTT_Leaf) { + CAreaOctTree::TriListReference list = ch.GetTriangleArray(); + const CAreaOctTree& owner = ch.GetOwner(); + int size = list.GetSize(); + for (int j = 0; j < size; ++j) { + ++gTrianglesProcessed; + const CCollisionSurface& surf = owner.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)) == true) + return true; + } + } + } else { + if (SphereCollisionCheckBoolean_Internal(ch, cache) == true) + return true; + } + } + } + } + return false; +} + +bool CMetroidAreaCollider::SphereCollisionCheckBoolean_Cached(const COctreeLeafCache& leafCache, + const CAABox& aabb, + const CSphere& sphere, + const CMaterialFilter& filter) { + for (int i = 0; i < leafCache.GetNumLeaves(); ++i) { + const CAreaOctTree::Node& node = leafCache.GetLeaf(i); + if (aabb.DoBoundsOverlap(node.GetBoundingBox())) { + CAreaOctTree::TriListReference list = node.GetTriangleArray(); + const CAreaOctTree& owner = node.GetOwner(); + int size = list.GetSize(); + for (int j = 0; j < size; ++j) { + ++gTrianglesProcessed; + const CCollisionSurface& surf = owner.GetMasterListTriangle(list.GetAt(j)); + if (filter.Passes(CMaterialList(surf.GetSurfaceFlags()))) { + if (CollisionUtil::TriSphereOverlap(sphere, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2)) == true) + return true; + } + } + } + } + + return false; +} + +bool CMetroidAreaCollider::SphereCollisionCheckBoolean(const CAreaOctTree& octTree, + const CAABox& aabb, + const CSphere& sphere, + const CMaterialFilter& filter) { + CBooleanSphereAreaCache cache(aabb, sphere, filter); + return SphereCollisionCheckBoolean_Internal(octTree.GetRootNode(), cache); +} + +bool CMetroidAreaCollider::MovingSphereCollisionCheck_Cached( + const COctreeLeafCache& leafCache, const CAABox& aabb, const CSphere& sphere, + const CMaterialFilter& filter, const CMaterialList& matList, CVector3f dir, float mag, + CCollisionInfo& infoOut, double& dOut) { + dOut = mag; + ResetInternalCounters(); + + CVector3f moveVec = mag * dir; + CAABox movedAABB = aabb; + movedAABB.AccumulateBounds(aabb.GetMinPoint() + moveVec); + movedAABB.AccumulateBounds(aabb.GetMaxPoint() + moveVec); + + CVector3f center = movedAABB.GetCenterPoint(); + CVector3f extent = movedAABB.GetHalfExtent(); + bool ret = false; + + for (int i = 0; i < leafCache.GetNumLeaves(); ++i) { + const CAreaOctTree::Node& node = leafCache.GetLeaf(i); + if (movedAABB.DoBoundsOverlap(node.GetBoundingBox())) { + CAreaOctTree::TriListReference list = node.GetTriangleArray(); + const CAreaOctTree& owner = node.GetOwner(); + int listSize = list.GetSize(); + for (int j = 0; j < listSize; ++j) { + ushort triIdx = list.GetAt(j); + if (sDupPrimitiveCheckCount != sDupTriangleList[triIdx]) { + sDupTriangleList[triIdx] = sDupPrimitiveCheckCount; + ++gTrianglesProcessed; + uint matValue = owner.GetTriangleMaterial(triIdx); + CMaterialList triMat(matValue); + if (filter.Passes(triMat)) { + ushort vertIndices[3]; + owner.GetTriangleVertexIndices(triIdx, vertIndices); + CCollisionSurface surf(owner.GetVert(vertIndices[0]), + owner.GetVert(vertIndices[1]), + owner.GetVert(vertIndices[2]), matValue); + + if (CollisionUtil::TriBoxOverlap(center, extent, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2)) == true) { + CVector3f surfNormal = surf.GetNormal(); + if (CVector3f::Dot(sphere.GetCenter() + moveVec - surf.GetVert(0), surfNormal) <= + sphere.GetRadius()) { + bool triRet = false; + + double triMagD = + static_cast< double >(sphere.GetRadius() - + CVector3f::Dot(sphere.GetCenter() - surf.GetVert(0), surfNormal)) / + static_cast< double >(CVector3f::Dot(dir, surfNormal)); + float triMag = static_cast< float >(triMagD); + CVector3f intersectPoint = sphere.GetCenter() + triMag * dir; + + bool outsideEdges[3]; + outsideEdges[0] = + CVector3f::Dot(intersectPoint - surf.GetVert(0), + CVector3f::Cross(surfNormal, surf.GetVert(1) - surf.GetVert(0))) < + 0.f; + outsideEdges[1] = + CVector3f::Dot(intersectPoint - surf.GetVert(1), + CVector3f::Cross(surfNormal, surf.GetVert(2) - surf.GetVert(1))) < + 0.f; + outsideEdges[2] = + CVector3f::Dot(intersectPoint - surf.GetVert(2), + CVector3f::Cross(surfNormal, surf.GetVert(0) - surf.GetVert(2))) < + 0.f; + + if (triMagD >= 0.0 && !outsideEdges[0] && !outsideEdges[1] && !outsideEdges[2] && + triMagD < dOut) { + infoOut = CCollisionInfo(intersectPoint - sphere.GetRadius() * surfNormal, + matList, triMat, surfNormal); + dOut = triMagD; + triRet = true; + ret = true; + } + + bool intersects = + CVector3f::Dot(sphere.GetCenter() - surf.GetVert(0), surfNormal) <= sphere.GetRadius(); + bool testVert[3] = {true, true, true}; + const ushort* edgeIndices = owner.GetTriangleEdgeIndices(triIdx); + for (int k = 0; k < 3; ++k) { + if (intersects || outsideEdges[k]) { + ushort edgeIdx = edgeIndices[k]; + if (sDupPrimitiveCheckCount != sDupEdgeList[edgeIdx]) { + sDupEdgeList[edgeIdx] = sDupPrimitiveCheckCount; + uint edgeMatVal = owner.GetEdgeMaterial(edgeIdx); + if (!(edgeMatVal & (1u << kMT_NoEdgeCollision))) { + static int mod3[4] = {0, 1, 2, 0}; + int nextIdx = mod3[k + 1]; + CVector3f edgeVec = surf.GetVert(nextIdx) - surf.GetVert(k); + float edgeVecMag = edgeVec.Magnitude(); + edgeVec *= 1.f / edgeVecMag; + float dirDotEdge = CVector3f::Dot(dir, edgeVec); + CVector3f edgeRej = dir - dirDotEdge * edgeVec; + float edgeRejMagSq = edgeRej.MagSquared(); + CVector3f vertToSphere = sphere.GetCenter() - surf.GetVert(k); + float vtsDotEdge = CVector3f::Dot(vertToSphere, edgeVec); + CVector3f vtsRej = vertToSphere - vtsDotEdge * edgeVec; + if (edgeRejMagSq > 0.f) { + float tmp = 2.f * CVector3f::Dot(vtsRej, edgeRej); + float tmp2 = 4.f * edgeRejMagSq * + (vtsRej.MagSquared() - + sphere.GetRadius() * sphere.GetRadius()) - + tmp * tmp; + if (tmp2 >= 0.f) { + double eMag = 0.5 / edgeRejMagSq * (-tmp - sqrt(tmp2)); + if (eMag >= 0.0) { + double t = eMag * dirDotEdge + vtsDotEdge; + if (t >= 0.0 && t <= edgeVecMag && eMag < dOut) { + CVector3f ePoint = surf.GetVert(k) + static_cast< float >(t) * edgeVec; + CVector3f eNormal = (sphere.GetCenter() + static_cast< float >(eMag) * dir - ePoint).AsNormalized(); + infoOut = CCollisionInfo( + ePoint, matList, CMaterialList(edgeMatVal), eNormal); + dOut = eMag; + triRet = true; + ret = true; + testVert[k] = false; + testVert[nextIdx] = false; + } else if (t < -sphere.GetRadius() && dirDotEdge <= 0.f) { + testVert[k] = false; + } else if (t > edgeVecMag + sphere.GetRadius() && + dirDotEdge >= 0.f) { + testVert[nextIdx] = false; + } + } + } else { + testVert[k] = false; + testVert[nextIdx] = false; + } + } + } + } + } + } + + for (int k = 0; k < 3; ++k) { + ushort vertIdx = vertIndices[k]; + if (testVert[k]) { + if (sDupPrimitiveCheckCount != sDupVertexList[vertIdx]) { + sDupVertexList[vertIdx] = sDupPrimitiveCheckCount; + double d = dOut; + if (CollisionUtil::RaySphereIntersection_Double( + CSphere(surf.GetVert(k), sphere.GetRadius()), sphere.GetCenter(), + dir, d) && + d >= 0.0) { + CVector3f vNormal = + (sphere.GetCenter() + dir * static_cast< float >(d) - surf.GetVert(k)) + .AsNormalized(); + CMaterialList vertMat(owner.GetVertMaterial(vertIdx)); + infoOut = CCollisionInfo(surf.GetVert(k), matList, vertMat, vNormal); + dOut = d; + triRet = true; + ret = true; + } + } + } else { + sDupVertexList[vertIdx] = sDupPrimitiveCheckCount; + } + } + + if (triRet) { + moveVec = static_cast< float >(dOut) * dir; + movedAABB = aabb; + movedAABB.AccumulateBounds(aabb.GetMinPoint() + moveVec); + movedAABB.AccumulateBounds(aabb.GetMaxPoint() + moveVec); + center = movedAABB.GetCenterPoint(); + extent = movedAABB.GetHalfExtent(); + } + } + } else { + const ushort* edgeIndices = owner.GetTriangleEdgeIndices(triIdx); + sDupEdgeList[edgeIndices[0]] = sDupPrimitiveCheckCount; + sDupEdgeList[edgeIndices[1]] = sDupPrimitiveCheckCount; + sDupEdgeList[edgeIndices[2]] = sDupPrimitiveCheckCount; + sDupVertexList[vertIndices[0]] = sDupPrimitiveCheckCount; + sDupVertexList[vertIndices[1]] = sDupPrimitiveCheckCount; + sDupVertexList[vertIndices[2]] = sDupPrimitiveCheckCount; + } + } + } + } + } + } + + return ret; +} + +bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Cached( + const COctreeLeafCache& leafCache, const CAABox& aabb, const CMaterialFilter& filter, + const CMaterialList& matList, CVector3f dir, float mag, CCollisionInfo& infoOut, + double& dOut) { + dOut = mag; + ResetInternalCounters(); + + CVector3f moveVec = mag * dir; + CMovingAABoxComponents components(aabb, dir); + + CAABox movedAABB = components.x6e8_aabb; + movedAABB.AccumulateBounds(aabb.GetMinPoint() + moveVec); + movedAABB.AccumulateBounds(aabb.GetMaxPoint() + moveVec); + + CVector3f center = movedAABB.GetCenterPoint(); + CVector3f extent = movedAABB.GetHalfExtent(); + bool ret = false; + + CVector3f normal(CVector3f::Zero()); + CVector3f point(CVector3f::Zero()); + + for (int i = 0; i < leafCache.GetNumLeaves(); ++i) { + const CAreaOctTree::Node& node = leafCache.GetLeaf(i); + if (movedAABB.DoBoundsOverlap(node.GetBoundingBox())) { + CAreaOctTree::TriListReference list = node.GetTriangleArray(); + const CAreaOctTree& owner = node.GetOwner(); + int listSize = list.GetSize(); + for (int j = 0; j < listSize; ++j) { + ushort triIdx = list.GetAt(j); + if (sDupPrimitiveCheckCount != sDupTriangleList[triIdx]) { + sDupTriangleList[triIdx] = sDupPrimitiveCheckCount; + ++gTrianglesProcessed; + uint matValue = owner.GetTriangleMaterial(triIdx); + CMaterialList triMat(matValue); + if (filter.Passes(triMat)) { + ushort vertIndices[3]; + owner.GetTriangleVertexIndices(triIdx, vertIndices); + CCollisionSurface surf(owner.GetVert(vertIndices[0]), + owner.GetVert(vertIndices[1]), + owner.GetVert(vertIndices[2]), matValue); + + if (CollisionUtil::TriBoxOverlap(center, extent, surf.GetVert(0), surf.GetVert(1), + surf.GetVert(2)) == true) { + bool triRet = false; + double d = dOut; + if (MovingAABoxCollisionCheck_BoxVertexTri(surf, aabb, components.x6c4_vertIdxs, dir, + d, normal, point) && + d < dOut) { + triRet = true; + infoOut = CCollisionInfo(point, matList, triMat, normal); + ret = true; + dOut = d; + } + + for (int k = 0; k < 3; ++k) { + ushort vertIdx = vertIndices[k]; + if (sDupPrimitiveCheckCount != sDupVertexList[vertIdx]) { + sDupVertexList[vertIdx] = sDupPrimitiveCheckCount; + const CVector3f& vtx = owner.GetVert(vertIdx); + if (movedAABB.PointInside(vtx)) { + d = dOut; + if (MovingAABoxCollisionCheck_TriVertexBox(vtx, aabb, dir, d, normal, point) && + d < dOut) { + CMaterialList vertMat(owner.GetVertMaterial(vertIdx)); + triRet = true; + infoOut = CCollisionInfo(point, matList, vertMat, normal); + ret = true; + dOut = d; + } + } + } + } + + const ushort* edgeIndices = owner.GetTriangleEdgeIndices(triIdx); + for (int k = 0; k < 3; ++k) { + ushort edgeIdx = edgeIndices[k]; + if (sDupPrimitiveCheckCount != sDupEdgeList[edgeIdx]) { + sDupEdgeList[edgeIdx] = sDupPrimitiveCheckCount; + uint edgeMat = owner.GetEdgeMaterial(edgeIdx); + if (!(edgeMat & (1u << kMT_NoEdgeCollision))) { + d = dOut; + const CCollisionEdge& edge = owner.GetEdge(edgeIdx); + if (MovingAABoxCollisionCheck_Edge(owner.GetVert(edge.GetVertIndex1()), + owner.GetVert(edge.GetVertIndex2()), + components.x0_edges, dir, d, normal, + point) && + d < dOut) { + triRet = true; + infoOut = CCollisionInfo(point, matList, CMaterialList(edgeMat), normal); + ret = true; + dOut = d; + } + } + } + } + + if (triRet) { + moveVec = static_cast< float >(dOut) * dir; + movedAABB = components.x6e8_aabb; + movedAABB.AccumulateBounds(aabb.GetMinPoint() + moveVec); + movedAABB.AccumulateBounds(aabb.GetMaxPoint() + moveVec); + center = movedAABB.GetCenterPoint(); + extent = movedAABB.GetHalfExtent(); + } + } else { + const ushort* edgeIndices = owner.GetTriangleEdgeIndices(triIdx); + sDupEdgeList[edgeIndices[0]] = sDupPrimitiveCheckCount; + sDupEdgeList[edgeIndices[1]] = sDupPrimitiveCheckCount; + sDupEdgeList[edgeIndices[2]] = sDupPrimitiveCheckCount; + sDupVertexList[vertIndices[0]] = sDupPrimitiveCheckCount; + sDupVertexList[vertIndices[1]] = sDupPrimitiveCheckCount; + sDupVertexList[vertIndices[2]] = sDupPrimitiveCheckCount; + } + } + } + } + } + } + + return ret; +} + +bool CMetroidAreaCollider::MovingAABoxCollisionCheck_TriVertexBox(const CVector3f& vert, + const CAABox& aabb, + CVector3f dir, double& dOut, + CVector3f& normal, + CVector3f& point) { + bool ret = false; + float rayLen = static_cast< float >(dOut); + CMRay ray(vert, -dir, rayLen); + CVector3f norm(CVector3f::Zero()); + double d; + if (CollisionUtil::RayAABoxIntersection_Double(ray, aabb, norm, d) == 2) { + double nd = d * dOut; + if (nd < dOut) { + ret = true; + normal = -norm; + dOut = nd; + point = vert; + } + } + return ret; +} + +bool CMetroidAreaCollider::MovingAABoxCollisionCheck_BoxVertexTri( + const CCollisionSurface& surf, const CAABox& aabb, + const rstl::reserved_vector< uint, 8 >& vertIndices, CVector3f dir, double& d, + CVector3f& normalOut, CVector3f& pointOut) { + bool ret = false; + for (int i = 0; i < vertIndices.size(); ++i) { + CVector3f point = aabb.GetPoint(vertIndices[i]); + if (CollisionUtil::RayTriangleIntersection_Double(point, dir, &surf.GetVert(0), d)) { + pointOut = point + dir * static_cast< float >(d); + normalOut = surf.GetNormal(); + ret = true; + } + } + return ret; +} + +bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Edge( + const CVector3f& ev0, const CVector3f& ev1, + const rstl::reserved_vector< SBoxEdge, 12 >& edges, CVector3f dir, double& d, + CVector3f& normal, CVector3f& point) { + bool ret = false; + + for (int i = 0; i < edges.size(); ++i) { + const SBoxEdge& edge = edges[i]; + CVector3d ev0d = ev0; + CVector3d ev1d = ev1; + if ((CVector3d::Dot(edge.x70_coDir, ev0d) >= edge.x88_dirCoDirDot) == + (CVector3d::Dot(edge.x70_coDir, ev1d) >= edge.x88_dirCoDirDot)) + continue; + + CVector3d delta = ev0d - ev1d; + CVector3d cross0 = CVector3d::Cross(edge.x58_delta, delta); + if (cross0.MagSquared() < DBL_EPSILON) + continue; + + CVector3d cross0Norm = cross0.AsNormalized(); + if (CVector3d::Dot(cross0Norm, dir) >= 0.0) { + ev1d = ev0; + ev0d = ev1; + delta = ev0d - ev1d; + cross0 = CVector3d::Cross(edge.x58_delta, delta); + cross0Norm = cross0.AsNormalized(); + } + + CVector3d clipped = + ev0d + + (-(CVector3d::Dot(ev0d, edge.x70_coDir) - edge.x88_dirCoDirDot) / + CVector3d::Dot(delta, edge.x70_coDir)) * + delta; + int maxCompIdx = (CMath::AbsD(edge.x70_coDir.GetX()) > CMath::AbsD(edge.x70_coDir.GetY())) + ? 0 + : 1; + if (CMath::AbsD(edge.x70_coDir[maxCompIdx]) < CMath::AbsD(edge.x70_coDir.GetZ())) + maxCompIdx = 2; + + int ci0, ci1; + if (maxCompIdx == 0) { + ci0 = 1; + ci1 = 2; + } else if (maxCompIdx == 1) { + ci0 = 0; + ci1 = 2; + } else { + ci0 = 0; + ci1 = 1; + } + + double eMag = (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 (!(eMag < 0.0) && !(eMag >= d)) { + CVector3d clippedMag = clipped - eMag * CVector3d(dir); + double dotCheck = + (edge.x28_start.GetX() - clippedMag.GetX()) * (edge.x40_end.GetX() - clippedMag.GetX()) + + (edge.x28_start.GetY() - clippedMag.GetY()) * (edge.x40_end.GetY() - clippedMag.GetY()) + + (edge.x28_start.GetZ() - clippedMag.GetZ()) * (edge.x40_end.GetZ() - clippedMag.GetZ()); + if (dotCheck < 0.0 && eMag < d) { + normal = cross0Norm.AsCVector3f(); + d = eMag; + point = clipped.AsCVector3f(); + ret = true; + } + } + } + + return ret; +} + +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); +} + +CAreaCollisionCache::CAreaCollisionCache(const CAABox& aabb) +: x0_aabb(aabb), 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; + } +} + +void CAreaCollisionCache::SetCacheBounds(const CAABox& aabb) { x0_aabb = aabb; } + +void CAreaCollisionCache::ClearCache() { + x18_leafCaches.clear(); + x1b40_24_leafOverflow = false; + x1b40_25_cacheOverflow = false; +} + +void CMetroidAreaCollider::BuildOctreeLeafCache(const CAreaOctTree::Node& node, const CAABox& aabb, + COctreeLeafCache& cache) { + for (int i = 0; i < 8; ++i) { + CAreaOctTree::Node::ETreeType type = node.GetChildType(i); + if (type != CAreaOctTree::Node::kTT_Invalid) { + CAreaOctTree::Node ch = node.GetChild(i); + if (aabb.DoBoundsOverlap(ch.GetBoundingBox())) { + if (type == CAreaOctTree::Node::kTT_Leaf) + cache.AddLeaf(ch); + else + BuildOctreeLeafCache(ch, aabb, cache); + } + } + } +} + +static void FlagEdgeIndicesForFace(uint face, bool* 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(uint face, bool* 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; + } +} + +CMetroidAreaCollider::SBoxEdge::SBoxEdge(const CAABox& aabb, int idx, const CVector3f& dir) +: x0_seg(aabb.GetEdge(static_cast< CAABox::EBoxEdgeId >(idx))) +, x28_start(x0_seg.GetRefPoint()) +, x40_end(x0_seg.GetEndPoint()) +, x58_delta(x40_end - x28_start) +, x70_coDir(CVector3d::Cross(x58_delta, CVector3d(dir)).AsNormalized()) +, x88_dirCoDirDot(CVector3d::Dot(x28_start, x70_coDir)) {} + +CMetroidAreaCollider::CMovingAABoxComponents::CMovingAABoxComponents(const CAABox& aabb, + const CVector3f& dir) +: x6e8_aabb(aabb) { + bool edgeFlags[12] = {}; + bool vertFlags[8] = {}; + uint useFaces = 0; + + for (int i = 0; i < 3; ++i) { + if (dir[i] != 0.f) { + uint face = i * 2 + (dir[i] < 0.f); + FlagEdgeIndicesForFace(face, edgeFlags); + FlagVertexIndicesForFace(face, vertFlags); + useFaces += 1; + } + } + + for (int i = 0; i < 12; ++i) { + if (edgeFlags[i]) { + x0_edges.push_back(SBoxEdge(aabb, i, dir)); + } + } + + for (int i = 0; i < 8; ++i) { + if (vertFlags[i]) { + x6c4_vertIdxs.push_back(i); + } + } + + if (useFaces == 1) { + x6e8_aabb = CAABox::MakeMaxInvertedBox(); + 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])); + } +}