From 0cfefd22a1edcbadadb94e987c4c48adb6cd3f70 Mon Sep 17 00:00:00 2001
From: Jack Andersen <jackoalan@gmail.com>
Date: Sun, 2 Jul 2017 17:34:19 -1000
Subject: [PATCH] Implement CCollidableOBBTree

---
 Runtime/Collision/CCollidableOBBTree.cpp      | 692 +++++++++++++++++-
 Runtime/Collision/CCollidableOBBTree.hpp      |  57 +-
 Runtime/Collision/CCollidableOBBTreeGroup.cpp | 197 ++++-
 .../Collision/CInternalRayCastStructure.hpp   |   3 +-
 Runtime/Collision/CMetroidAreaCollider.cpp    |   6 +-
 Runtime/Collision/CMetroidAreaCollider.hpp    |   2 +
 Runtime/Collision/COBBTree.cpp                |  71 +-
 Runtime/Collision/COBBTree.hpp                |  28 +-
 Runtime/Collision/CollisionUtil.cpp           |  64 ++
 Runtime/Collision/CollisionUtil.hpp           |   3 +
 nod                                           |   2 +-
 specter                                       |   2 +-
 12 files changed, 1067 insertions(+), 60 deletions(-)

diff --git a/Runtime/Collision/CCollidableOBBTree.cpp b/Runtime/Collision/CCollidableOBBTree.cpp
index 4cec33b71..786a72b4e 100644
--- a/Runtime/Collision/CCollidableOBBTree.cpp
+++ b/Runtime/Collision/CCollidableOBBTree.cpp
@@ -1,4 +1,8 @@
 #include "CCollidableOBBTree.hpp"
+#include "CMaterialFilter.hpp"
+#include "CollisionUtil.hpp"
+#include "CInternalRayCastStructure.hpp"
+#include "CCollisionInfoList.hpp"
 
 namespace urde
 {
@@ -11,22 +15,704 @@ CCollidableOBBTree::CCollidableOBBTree(const COBBTree* tree, const urde::CMateri
 
 bool CCollidableOBBTree::LineIntersectsLeaf(const COBBTree::CLeafData& leaf, CRayCastInfo& info) const
 {
+    bool ret = false;
+    u16 intersectIdx = 0;
+    for (int i=0 ; i<leaf.GetSurfaceVector().size() ; ++i)
+    {
+        u16 surfIdx = leaf.GetSurfaceVector()[i];
+        CCollisionSurface surface = x10_tree->GetSurface(surfIdx);
+        if (info.GetMaterialFilter().Passes(GetMaterial()))
+        {
+            if (CollisionUtil::RayTriangleIntersection(info.GetRay().start, info.GetRay().dir,
+                                                       surface.GetVerts(), info.Magnitude()))
+            {
+                intersectIdx = i;
+                ret = true;
+            }
+        }
+    }
+
+    if (ret)
+    {
+        CCollisionSurface surf = x10_tree->GetSurface(intersectIdx);
+        info.Plane() = surf.GetPlane();
+        info.Material() = CMaterialList(surf.GetSurfaceFlags());
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::LineIntersectsOBBTree(const COBBTree::CNode& n0, const COBBTree::CNode& n1,
+                                               CRayCastInfo& info) const
+{
+    bool ret = false;
+    float t0, t1;
+    bool intersects0 = false;
+    bool intersects1 = false;
+    if (CollisionUtil::LineIntersectsOBBox(n0.GetOBB(), info.GetRay(), t0) && t0 < info.GetMagnitude())
+        intersects0 = true;
+    if (CollisionUtil::LineIntersectsOBBox(n1.GetOBB(), info.GetRay(), t1) && t1 < info.GetMagnitude())
+        intersects1 = true;
+
+    if (intersects0 && intersects1)
+    {
+        if (t0 < t1)
+        {
+            if (n0.IsLeaf() ?
+                LineIntersectsLeaf(n0.GetLeafData(), info) :
+                LineIntersectsOBBTree(n0.GetLeft(), n0.GetRight(), info))
+            {
+                if (info.GetMagnitude() < t1)
+                    return true;
+                ret = true;
+            }
+            if (n1.IsLeaf())
+            {
+                if (LineIntersectsLeaf(n1.GetLeafData(), info))
+                    return true;
+            }
+            else
+            {
+                if (LineIntersectsOBBTree(n1.GetLeft(), n1.GetRight(), info))
+                    return true;
+            }
+        }
+        else
+        {
+            if (n1.IsLeaf() ?
+                LineIntersectsLeaf(n1.GetLeafData(), info) :
+                LineIntersectsOBBTree(n1.GetLeft(), n1.GetRight(), info))
+            {
+                if (info.GetMagnitude() < t0)
+                    return true;
+                ret = true;
+            }
+            if (n0.IsLeaf())
+            {
+                if (LineIntersectsLeaf(n0.GetLeafData(), info))
+                    return true;
+            }
+            else
+            {
+                if (LineIntersectsOBBTree(n0.GetLeft(), n0.GetRight(), info))
+                    return true;
+            }
+        }
+    }
+    else if (intersects0)
+    {
+        return n0.IsLeaf() ?
+               LineIntersectsLeaf(n0.GetLeafData(), info) :
+               LineIntersectsOBBTree(n0.GetLeft(), n0.GetRight(), info);
+    }
+    else if (intersects1)
+    {
+        return n1.IsLeaf() ?
+               LineIntersectsLeaf(n1.GetLeafData(), info) :
+               LineIntersectsOBBTree(n1.GetLeft(), n1.GetRight(), info);
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::LineIntersectsOBBTree(const COBBTree::CNode& node, CRayCastInfo& info) const
+{
+    float t;
+    bool ret = false;
+
+    if (CollisionUtil::LineIntersectsOBBox(node.GetOBB(), info.GetRay(), t) && t < info.GetMagnitude())
+    {
+        if (node.IsLeaf())
+        {
+            if (LineIntersectsLeaf(node.GetLeafData(), info))
+                ret = true;
+        }
+        else
+        {
+            if (LineIntersectsOBBTree(node.GetLeft(), node.GetRight(), info))
+                ret = true;
+        }
+        const_cast<COBBTree::CNode&>(node).SetHit(true);
+    }
+    else
+    {
+        const_cast<CCollidableOBBTree&>(*this).x18_misses += 1;
+    }
+
+    return ret;
+}
+
+CRayCastResult CCollidableOBBTree::LineIntersectsTree(const zeus::CMRay& ray, const CMaterialFilter& filter,
+                                                      float maxTime, const zeus::CTransform& xf) const
+{
+    zeus::CMRay useRay = ray.getInvUnscaledTransformRay(xf);
+    CRayCastInfo info(useRay, filter, maxTime);
+    if (LineIntersectsOBBTree(x10_tree->GetRoot(), info))
+    {
+        zeus::CPlane xfPlane = TransformPlane(info.GetPlane(), xf);
+        return CRayCastResult(info.GetMagnitude(), ray.start + info.GetMagnitude() * ray.dir,
+                              xfPlane, info.GetMaterial());
+    }
+    else
+    {
+        return {};
+    }
+}
+
+zeus::CPlane CCollidableOBBTree::TransformPlane(const zeus::CPlane& pl, const zeus::CTransform& xf)
+{
+    zeus::CVector3f normal = xf.rotate(pl.normal());
+    return zeus::CPlane(normal, (xf * (pl.normal() * pl.d)).dot(normal));
+}
+
+bool CCollidableOBBTree::SphereCollideWithLeafMoving(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf,
+                                                     const zeus::CSphere& sphere, const CMaterialList& matList,
+                                                     const CMaterialFilter& filter, const zeus::CVector3f& dir,
+                                                     double& dOut, CCollisionInfo& infoOut) const
+{
+    bool ret = false;
+
+    zeus::CAABox aabb(sphere.position - sphere.radius,
+                      sphere.position + sphere.radius);
+    zeus::CAABox moveAABB = aabb;
+    zeus::CVector3f moveVec = float(dOut) * dir;
+    moveAABB.accumulateBounds(aabb.max + moveVec);
+    moveAABB.accumulateBounds(aabb.min + moveVec);
+
+    zeus::CVector3f center = moveAABB.center();
+    zeus::CVector3f extent = moveAABB.extents();
+
+    for (u16 triIdx : leaf.GetSurfaceVector())
+    {
+        CCollisionSurface surf = x10_tree->GetTransformedSurface(triIdx, xf);
+        CMaterialList triMat = GetMaterial();
+        triMat.Add(CMaterialList(surf.GetSurfaceFlags()));
+        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;
+
+                zeus::CVector3f surfNormal = surf.GetNormal();
+                if ((sphere.position + moveVec - surf.GetVert(0)).dot(surfNormal) <= sphere.radius)
+                {
+                    float mag = (sphere.radius - (sphere.position - surf.GetVert(0)).dot(surfNormal)) / dir.dot(surfNormal);
+                    zeus::CVector3f intersectPoint = sphere.position + mag * dir;
+
+                    bool outsideEdges[] =
+                    {(intersectPoint - surf.GetVert(0)).dot((surf.GetVert(1) - surf.GetVert(0)).cross(surfNormal)) < 0.f,
+                     (intersectPoint - surf.GetVert(1)).dot((surf.GetVert(2) - surf.GetVert(1)).cross(surfNormal)) < 0.f,
+                     (intersectPoint - surf.GetVert(2)).dot((surf.GetVert(0) - surf.GetVert(2)).cross(surfNormal)) < 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;
+                        ret = true;
+                    }
+
+                    bool intersects = (sphere.position - surf.GetVert(0)).dot(surfNormal) <= sphere.radius;
+                    bool testVert[] = {true, true, true};
+                    const u16* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx);
+                    for (int k=0 ; k<3 ; ++k)
+                    {
+                        if (intersects || outsideEdges[k])
+                        {
+                            u16 edgeIdx = edgeIndices[k];
+                            if (CMetroidAreaCollider::g_DupPrimitiveCheckCount != CMetroidAreaCollider::g_DupEdgeList[edgeIdx])
+                            {
+                                CMetroidAreaCollider::g_DupEdgeList[edgeIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                                CMaterialList edgeMat(x10_tree->GetEdgeMaterial(edgeIdx));
+                                if (!edgeMat.HasMaterial(EMaterialTypes::TwentyFour))
+                                {
+                                    int nextIdx = (k + 1) % 3;
+                                    zeus::CVector3f edgeVec = surf.GetVert(nextIdx) - surf.GetVert(k);
+                                    float edgeVecMag = edgeVec.magnitude();
+                                    edgeVec *= (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;
+                                                    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;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    u16 vertIndices[3];
+                    x10_tree->GetTriangleVertexIndices(triIdx, vertIndices);
+
+                    for (int k=0 ; k<3 ; ++k)
+                    {
+                        u16 vertIdx = vertIndices[k];
+                        if (testVert[k])
+                        {
+                            if (CMetroidAreaCollider::g_DupPrimitiveCheckCount != CMetroidAreaCollider::g_DupVertexList[vertIdx])
+                            {
+                                CMetroidAreaCollider::g_DupVertexList[vertIdx] = CMetroidAreaCollider::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, x10_tree->GetVertMaterial(vertIdx),
+                                                             (sphere.position + dir * d - surf.GetVert(k)).normalized());
+                                    dOut = d;
+                                    ret = true;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            CMetroidAreaCollider::g_DupVertexList[vertIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                const u16* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx);
+                CMetroidAreaCollider::g_DupEdgeList[edgeIndices[0]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupEdgeList[edgeIndices[1]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupEdgeList[edgeIndices[2]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+
+                u16 vertIndices[3];
+                x10_tree->GetTriangleVertexIndices(triIdx, vertIndices);
+                CMetroidAreaCollider::g_DupVertexList[vertIndices[0]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupVertexList[vertIndices[1]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupVertexList[vertIndices[2]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+            }
+        }
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::SphereCollisionMoving(const COBBTree::CNode& node, const zeus::CTransform& xf,
+                                               const zeus::CSphere& sphere, const zeus::COBBox& obb,
+                                               const CMaterialList& material, const CMaterialFilter& filter,
+                                               const zeus::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.GetLeft(), xf, sphere, obb, material, filter, dir, dOut, info))
+                ret = true;
+            if (SphereCollisionMoving(node.GetRight(), xf, sphere, obb, material, filter, dir, dOut, info))
+                ret = true;
+        }
+    }
+    else
+    {
+        const_cast<CCollidableOBBTree&>(*this).x18_misses += 1;
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::AABoxCollideWithLeafMoving(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf,
+                                                    const zeus::CAABox& aabb, const CMaterialList& matList,
+                                                    const CMaterialFilter& filter, const CMovingAABoxComponents& components,
+                                                    const zeus::CVector3f& dir, double& dOut, CCollisionInfo& infoOut) const
+{
+    bool ret = false;
+
+    zeus::CAABox movedAABB = components.x6e8_aabb;
+    zeus::CVector3f moveVec = float(dOut) * 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 (u16 triIdx : leaf.GetSurfaceVector())
+    {
+        CCollisionSurface surf = x10_tree->GetTransformedSurface(triIdx, xf);
+        CMaterialList triMat = GetMaterial();
+        triMat.Add(CMaterialList(surf.GetSurfaceFlags()));
+        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;
+
+                u16 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)
+                {
+                    ret = true;
+                    infoOut = CCollisionInfo(point, matList, triMat, normal);
+                    dOut = d;
+                }
+
+                for (int k=0 ; k<3 ; ++k)
+                {
+                    u16 vertIdx = vertIndices[k];
+                    const zeus::CVector3f& vtx = x10_tree->GetVert(vertIdx);
+                    if (CMetroidAreaCollider::g_DupPrimitiveCheckCount != CMetroidAreaCollider::g_DupVertexList[vertIdx])
+                    {
+                        CMetroidAreaCollider::g_DupVertexList[vertIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                        if (movedAABB.pointInside(vtx))
+                        {
+                            d = dOut;
+                            if (CMetroidAreaCollider::MovingAABoxCollisionCheck_TriVertexBox(vtx, aabb, dir, d,
+                                                                                             normal, point) && d < dOut)
+                            {
+                                CMaterialList vertMat(x10_tree->GetVertMaterial(vertIdx));
+                                ret = true;
+                                infoOut = CCollisionInfo(point, matList, vertMat, normal);
+                                dOut = d;
+                            }
+                        }
+                    }
+                }
+
+                const u16* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx);
+                for (int k=0 ; k<3 ; ++k)
+                {
+                    u16 edgeIdx = edgeIndices[k];
+                    if (CMetroidAreaCollider::g_DupPrimitiveCheckCount != CMetroidAreaCollider::g_DupEdgeList[edgeIdx])
+                    {
+                        CMetroidAreaCollider::g_DupEdgeList[edgeIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                        CMaterialList edgeMat(x10_tree->GetEdgeMaterial(edgeIdx));
+                        if (!edgeMat.HasMaterial(EMaterialTypes::TwentyFour))
+                        {
+                            d = dOut;
+                            const CCollisionEdge& edge = x10_tree->GetEdge(edgeIdx);
+                            if (CMetroidAreaCollider::MovingAABoxCollisionCheck_Edge(x10_tree->GetVert(edge.GetVertIndex1()),
+                                                                                     x10_tree->GetVert(edge.GetVertIndex2()),
+                                                                                     components.x0_edges, dir, d, normal, point) &&
+                                d < dOut)
+                            {
+                                ret = true;
+                                infoOut = CCollisionInfo(point, matList, edgeMat, normal);
+                                dOut = d;
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                const u16* edgeIndices = x10_tree->GetTriangleEdgeIndices(triIdx);
+                CMetroidAreaCollider::g_DupEdgeList[edgeIndices[0]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupEdgeList[edgeIndices[1]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupEdgeList[edgeIndices[2]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+
+                u16 vertIndices[3];
+                x10_tree->GetTriangleVertexIndices(triIdx, vertIndices);
+                CMetroidAreaCollider::g_DupVertexList[vertIndices[0]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupVertexList[vertIndices[1]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+                CMetroidAreaCollider::g_DupVertexList[vertIndices[2]] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
+            }
+        }
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::AABoxCollisionMoving(const COBBTree::CNode& node, const zeus::CTransform& xf,
+                                              const zeus::CAABox& aabb, const zeus::COBBox& obb,
+                                              const CMaterialList& material, const CMaterialFilter& filter,
+                                              const CMovingAABoxComponents& components, const zeus::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 (AABoxCollideWithLeafMoving(node.GetLeafData(), xf, aabb, material, filter, components, dir, dOut, info))
+                ret = true;
+        }
+        else
+        {
+            if (AABoxCollisionMoving(node.GetLeft(), xf, aabb, obb, material, filter, components, dir, dOut, info))
+                ret = true;
+            if (AABoxCollisionMoving(node.GetRight(), xf, aabb, obb, material, filter, components, dir, dOut, info))
+                ret = true;
+        }
+    }
+    else
+    {
+        const_cast<CCollidableOBBTree&>(*this).x18_misses += 1;
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::SphereCollisionBoolean(const COBBTree::CNode& node, const zeus::CTransform& xf,
+                                                const zeus::CSphere& sphere, const zeus::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())
+        {
+            for (u16 surfIdx : node.GetLeafData().GetSurfaceVector())
+            {
+                CCollisionSurface surf = x10_tree->GetTransformedSurface(surfIdx, xf);
+                CMaterialList triMat = GetMaterial();
+                triMat.Add(CMaterialList(surf.GetSurfaceFlags()));
+                if (filter.Passes(triMat) && CollisionUtil::TriSphereOverlap(sphere,
+                                             surf.GetVert(0), surf.GetVert(1), surf.GetVert(2)))
+                    return true;
+            }
+        }
+        else
+        {
+            if (SphereCollisionBoolean(node.GetLeft(), xf, sphere, obb, filter))
+                return true;
+            if (SphereCollisionBoolean(node.GetRight(), xf, sphere, obb, filter))
+                return true;
+        }
+    }
+    else
+    {
+        const_cast<CCollidableOBBTree&>(*this).x18_misses += 1;
+    }
+
     return false;
 }
 
+bool CCollidableOBBTree::AABoxCollisionBoolean(const COBBTree::CNode& node, const zeus::CTransform& xf,
+                                               const zeus::CAABox& aabb, const zeus::COBBox& obb,
+                                               const CMaterialFilter& filter) const
+{
+    zeus::CVector3f center = aabb.center();
+    zeus::CVector3f extent = aabb.extents();
+
+    const_cast<CCollidableOBBTree&>(*this).x14_tries += 1;
+    if (obb.OBBIntersectsBox(node.GetOBB()))
+    {
+        const_cast<COBBTree::CNode&>(node).SetHit(true);
+        if (node.IsLeaf())
+        {
+            for (u16 surfIdx : node.GetLeafData().GetSurfaceVector())
+            {
+                CCollisionSurface surf = x10_tree->GetTransformedSurface(surfIdx, xf);
+                CMaterialList triMat = GetMaterial();
+                triMat.Add(CMaterialList(surf.GetSurfaceFlags()));
+                if (filter.Passes(triMat) && CollisionUtil::TriBoxOverlap(center, extent,
+                                             surf.GetVert(0), surf.GetVert(1), surf.GetVert(2)))
+                    return true;
+            }
+        }
+        else
+        {
+            if (AABoxCollisionBoolean(node.GetLeft(), xf, aabb, obb, filter))
+                return true;
+            if (AABoxCollisionBoolean(node.GetRight(), xf, aabb, obb, filter))
+                return true;
+        }
+    }
+    else
+    {
+        const_cast<CCollidableOBBTree&>(*this).x18_misses += 1;
+    }
+
+    return false;
+}
+
+bool CCollidableOBBTree::SphereCollideWithLeaf(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf,
+                                               const zeus::CSphere& sphere, const CMaterialList& material,
+                                               const CMaterialFilter& filter, CCollisionInfoList& infoList) const
+{
+    bool ret = false;
+    zeus::CVector3f point, normal;
+
+    for (u16 surfIdx : leaf.GetSurfaceVector())
+    {
+        CCollisionSurface surf = x10_tree->GetTransformedSurface(surfIdx, xf);
+        CMaterialList triMat = GetMaterial();
+        triMat.Add(CMaterialList(surf.GetSurfaceFlags()));
+        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))
+            {
+                CCollisionInfo collision(point, material, triMat, normal);
+                infoList.Add(collision, false);
+                ret = true;
+            }
+        }
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::SphereCollision(const COBBTree::CNode& node, const zeus::CTransform& xf,
+                                         const zeus::CSphere& sphere, const zeus::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.GetLeft(), xf, sphere, obb, material, filter, infoList))
+                ret = true;
+            if (SphereCollision(node.GetRight(), xf, sphere, obb, material, filter, infoList))
+                ret = true;
+        }
+    }
+    else
+    {
+        const_cast<CCollidableOBBTree&>(*this).x18_misses += 1;
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::AABoxCollideWithLeaf(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf,
+                                              const zeus::CAABox& aabb, const CMaterialList& material,
+                                              const CMaterialFilter& filter, const zeus::CPlane* planes,
+                                              CCollisionInfoList& infoList) const
+{
+    bool ret = false;
+    zeus::CVector3f center = aabb.center();
+    zeus::CVector3f extent = aabb.extents();
+
+    for (u16 surfIdx : leaf.GetSurfaceVector())
+    {
+        CCollisionSurface surf = x10_tree->GetTransformedSurface(surfIdx, xf);
+        CMaterialList triMat = GetMaterial();
+        triMat.Add(CMaterialList(surf.GetSurfaceFlags()));
+        if (filter.Passes(triMat) && CollisionUtil::TriBoxOverlap(center, extent,
+                                     surf.GetVert(0), surf.GetVert(1), surf.GetVert(2)))
+        {
+            zeus::CAABox newAABB = zeus::CAABox::skInvertedBox;
+            const_cast<CCollidableOBBTree&>(*this).x1c_hits += 1;
+            if (CMetroidAreaCollider::ConvexPolyCollision(planes, surf.GetVerts(), newAABB))
+            {
+                zeus::CPlane plane = surf.GetPlane();
+                CCollisionInfo collision(newAABB, triMat, material,
+                                         plane.normal(), -plane.normal());
+                infoList.Add(collision, false);
+                ret = true;
+            }
+        }
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTree::AABoxCollision(const COBBTree::CNode& node, const zeus::CTransform& xf,
+                                        const zeus::CAABox& aabb, const zeus::COBBox& obb,
+                                        const CMaterialList& material, const CMaterialFilter& filter,
+                                        const zeus::CPlane* planes, 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 (AABoxCollideWithLeaf(node.GetLeafData(), xf, aabb, material, filter, planes, infoList))
+                ret = true;
+        }
+        else
+        {
+            if (AABoxCollision(node.GetLeft(), xf, aabb, obb, material, filter, planes, infoList))
+                ret = true;
+            if (AABoxCollision(node.GetRight(), xf, aabb, obb, material, filter, planes, infoList))
+                ret = true;
+        }
+    }
+    else
+    {
+        const_cast<CCollidableOBBTree&>(*this).x18_misses += 1;
+    }
+
+    return ret;
+}
+
 FourCC CCollidableOBBTree::GetPrimType() const
 {
     return SBIG('OBBT');
 }
 
-CRayCastResult CCollidableOBBTree::CastRayInternal(const CInternalRayCastStructure&) const
+CRayCastResult CCollidableOBBTree::CastRayInternal(const CInternalRayCastStructure& rayCast) const
 {
-    return {};
+    return LineIntersectsTree(rayCast.GetRay(), rayCast.GetFilter(),
+                              rayCast.GetMaxTime(), rayCast.GetTransform());
 }
 
 zeus::CAABox CCollidableOBBTree::CalculateAABox(const zeus::CTransform& xf) const
 {
-    return x10_tree->CalculateAABox(xf);
+    return zeus::COBBox::FromAABox(x10_tree->CalculateLocalAABox(), xf).calculateAABox();
 }
 
 zeus::CAABox CCollidableOBBTree::CalculateLocalAABox() const
diff --git a/Runtime/Collision/CCollidableOBBTree.hpp b/Runtime/Collision/CCollidableOBBTree.hpp
index cf7d94bfb..47b288e2b 100644
--- a/Runtime/Collision/CCollidableOBBTree.hpp
+++ b/Runtime/Collision/CCollidableOBBTree.hpp
@@ -3,6 +3,8 @@
 
 #include "Collision/CCollisionPrimitive.hpp"
 #include "COBBTree.hpp"
+#include "zeus/COBBox.hpp"
+#include "CMetroidAreaCollider.hpp"
 
 namespace urde
 {
@@ -11,19 +13,64 @@ class CRayCastInfo
     const zeus::CMRay& x0_ray;
     const CMaterialFilter& x4_filter;
     float x8_mag;
+    zeus::CPlane xc_plane = {zeus::CVector3f::skUp, 0.f};
+    CMaterialList x20_material;
 public:
     CRayCastInfo(const zeus::CMRay& ray, const CMaterialFilter& filter, float mag)
     : x0_ray(ray), x4_filter(filter), x8_mag(mag) {}
+    const zeus::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 zeus::CPlane& GetPlane() const { return xc_plane; }
+    zeus::CPlane& Plane() { return xc_plane; }
+    const CMaterialList& GetMaterial() const { return x20_material; }
+    CMaterialList& Material() { return x20_material; }
 };
 
 class CCollidableOBBTree : public CCollisionPrimitive
 {
+    friend class CCollidableOBBTreeGroup;
     COBBTree* x10_tree = nullptr;
-    u32 x14_ = 0;
-    u32 x18_ = 0;
-    u32 x1c_ = 0;
+    u32 x14_tries = 0;
+    u32 x18_misses = 0;
+    u32 x1c_hits = 0;
     static u32 sTableIndex;
     bool LineIntersectsLeaf(const COBBTree::CLeafData& leaf, CRayCastInfo& info) const;
+    bool LineIntersectsOBBTree(const COBBTree::CNode& n0, const COBBTree::CNode& n1, CRayCastInfo& info) const;
+    bool LineIntersectsOBBTree(const COBBTree::CNode& node, CRayCastInfo& info) const;
+    CRayCastResult LineIntersectsTree(const zeus::CMRay& ray, const CMaterialFilter& filter, float maxTime,
+                                      const zeus::CTransform& xf) const;
+    static zeus::CPlane TransformPlane(const zeus::CPlane& pl, const zeus::CTransform& xf);
+    bool SphereCollideWithLeafMoving(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf, const zeus::CSphere& sphere,
+                                     const CMaterialList& material, const CMaterialFilter& filter, const zeus::CVector3f& dir,
+                                     double& dOut, CCollisionInfo& info) const;
+    bool SphereCollisionMoving(const COBBTree::CNode& node, const zeus::CTransform& xf, const zeus::CSphere& sphere,
+                               const zeus::COBBox& obb, const CMaterialList& material, const CMaterialFilter& filter,
+                               const zeus::CVector3f& dir, double& dOut, CCollisionInfo& info) const;
+    bool AABoxCollideWithLeafMoving(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf, const zeus::CAABox& aabb,
+                                    const CMaterialList& material, const CMaterialFilter& filter,
+                                    const CMovingAABoxComponents& components, const zeus::CVector3f& dir,
+                                    double& dOut, CCollisionInfo& info) const;
+    bool AABoxCollisionMoving(const COBBTree::CNode& node, const zeus::CTransform& xf, const zeus::CAABox& aabb,
+                              const zeus::COBBox& obb, const CMaterialList& material, const CMaterialFilter& filter,
+                              const CMovingAABoxComponents& components, const zeus::CVector3f& dir,
+                              double& dOut, CCollisionInfo& info) const;
+    bool SphereCollisionBoolean(const COBBTree::CNode& node, const zeus::CTransform& xf, const zeus::CSphere& sphere,
+                                const zeus::COBBox& obb, const CMaterialFilter& filter) const;
+    bool AABoxCollisionBoolean(const COBBTree::CNode& node, const zeus::CTransform& xf, const zeus::CAABox& aabb,
+                               const zeus::COBBox& obb, const CMaterialFilter& filter) const;
+    bool SphereCollideWithLeaf(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf, const zeus::CSphere& sphere,
+                               const CMaterialList& material, const CMaterialFilter& filter, CCollisionInfoList& infoList) const;
+    bool SphereCollision(const COBBTree::CNode& node, const zeus::CTransform& xf, const zeus::CSphere& sphere,
+                         const zeus::COBBox& obb, const CMaterialList& material, const CMaterialFilter& filter,
+                         CCollisionInfoList& infoList) const;
+    bool AABoxCollideWithLeaf(const COBBTree::CLeafData& leaf, const zeus::CTransform& xf, const zeus::CAABox& aabb,
+                              const CMaterialList& material, const CMaterialFilter& filter, const zeus::CPlane* planes,
+                              CCollisionInfoList& infoList) const;
+    bool AABoxCollision(const COBBTree::CNode& node, const zeus::CTransform& xf, const zeus::CAABox& aabb,
+                        const zeus::COBBox& obb, const CMaterialList& material, const CMaterialFilter& filter,
+                        const zeus::CPlane* planes, CCollisionInfoList& infoList) const;
 public:
     CCollidableOBBTree(const COBBTree* tree, const CMaterialList& material);
     virtual ~CCollidableOBBTree() = default;
@@ -32,8 +79,8 @@ public:
     u32 GetTableIndex() const { return sTableIndex; }
     zeus::CAABox CalculateAABox(const zeus::CTransform &) const;
     zeus::CAABox CalculateLocalAABox() const;
-    virtual FourCC GetPrimType() const;
-    virtual CRayCastResult CastRayInternal(const CInternalRayCastStructure&) const;
+    FourCC GetPrimType() const;
+    CRayCastResult CastRayInternal(const CInternalRayCastStructure&) const;
 };
 
 }
diff --git a/Runtime/Collision/CCollidableOBBTreeGroup.cpp b/Runtime/Collision/CCollidableOBBTreeGroup.cpp
index de3cdf98e..5dd2bb15d 100644
--- a/Runtime/Collision/CCollidableOBBTreeGroup.cpp
+++ b/Runtime/Collision/CCollidableOBBTreeGroup.cpp
@@ -1,5 +1,9 @@
 #include "CCollidableOBBTreeGroup.hpp"
 #include "CCollidableOBBTree.hpp"
+#include "CCollidableAABox.hpp"
+#include "CCollidableSphere.hpp"
+#include "CInternalRayCastStructure.hpp"
+#include "CollisionUtil.hpp"
 #include "CToken.hpp"
 
 namespace urde
@@ -37,7 +41,7 @@ CCollidableOBBTreeGroup::CCollidableOBBTreeGroup(const CCollidableOBBTreeGroupCo
 
 void CCollidableOBBTreeGroup::ResetTestStats() const
 {
-
+    /* Remove me? */
 }
 
 u32 CCollidableOBBTreeGroup::GetTableIndex() const
@@ -60,9 +64,35 @@ FourCC CCollidableOBBTreeGroup::GetPrimType() const
     return SBIG('OBTG');
 }
 
-CRayCastResult CCollidableOBBTreeGroup::CastRayInternal(const CInternalRayCastStructure&) const
+CRayCastResult CCollidableOBBTreeGroup::CastRayInternal(const CInternalRayCastStructure& rayCast) const
 {
-    return {};
+    CRayCastResult ret;
+
+    zeus::CMRay xfRay = rayCast.GetRay().getInvUnscaledTransformRay(rayCast.GetTransform());
+    auto aabbIt = x10_container->x10_aabbs.cbegin();
+    float mag = rayCast.GetMaxTime();
+    for (const std::unique_ptr<COBBTree>& tree : x10_container->x0_trees)
+    {
+        CCollidableOBBTree obbTree(tree.get(), GetMaterial());
+        float tMin = 0.f;
+        float tMax = 0.f;
+        if (CollisionUtil::RayAABoxIntersection(xfRay, *aabbIt++, tMin, tMax))
+        {
+            CInternalRayCastStructure localCast(xfRay.start, xfRay.dir, mag,
+                                                zeus::CTransform::Identity(), rayCast.GetFilter());
+            CRayCastResult localResult = obbTree.CastRayInternal(localCast);
+            if (localResult.IsValid())
+            {
+                if (ret.IsInvalid() || localResult.GetT() < ret.GetT())
+                {
+                    ret = localResult;
+                    mag = localResult.GetT();
+                }
+            }
+        }
+    }
+
+    return ret;
 }
 
 const CCollisionPrimitive::Type& CCollidableOBBTreeGroup::GetType()
@@ -75,34 +105,163 @@ void CCollidableOBBTreeGroup::SetStaticTableIndex(u32 index)
     sTableIndex = index;
 }
 
-bool CCollidableOBBTreeGroup::SphereCollide(const CInternalCollisionStructure &, CCollisionInfoList &)
+bool CCollidableOBBTreeGroup::SphereCollide(const CInternalCollisionStructure& collision, CCollisionInfoList& list)
 {
+    bool ret = false;
+    const CCollidableSphere& p0 = static_cast<const CCollidableSphere&>(collision.GetLeft().GetPrim());
+    const CCollidableOBBTreeGroup& p1 = static_cast<const CCollidableOBBTreeGroup&>(collision.GetRight().GetPrim());
+
+    zeus::CSphere s0 = p0.Transform(collision.GetLeft().GetTransform());
+    zeus::COBBox obb1 = zeus::COBBox::FromAABox(p0.CalculateLocalAABox(),
+    collision.GetRight().GetTransform().inverse() * collision.GetLeft().GetTransform());
+
+    for (const std::unique_ptr<COBBTree>& tree : p1.x10_container->x0_trees)
+    {
+        CCollidableOBBTree obbTree(tree.get(), p1.GetMaterial());
+        if (obbTree.SphereCollision(obbTree.x10_tree->GetRoot(), collision.GetRight().GetTransform(),
+                                    s0, obb1, p0.GetMaterial(), collision.GetLeft().GetFilter(), list))
+            ret = true;
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTreeGroup::SphereCollideBoolean(const CInternalCollisionStructure& collision)
+{
+    const CCollidableSphere& p0 = static_cast<const CCollidableSphere&>(collision.GetLeft().GetPrim());
+    const CCollidableOBBTreeGroup& p1 = static_cast<const CCollidableOBBTreeGroup&>(collision.GetRight().GetPrim());
+
+    zeus::CSphere s0 = p0.Transform(collision.GetLeft().GetTransform());
+    zeus::COBBox obb1 = zeus::COBBox::FromAABox(p0.CalculateLocalAABox(),
+    collision.GetRight().GetTransform().inverse() * collision.GetLeft().GetTransform());
+
+    for (const std::unique_ptr<COBBTree>& tree : p1.x10_container->x0_trees)
+    {
+        CCollidableOBBTree obbTree(tree.get(), p1.GetMaterial());
+        if (obbTree.SphereCollisionBoolean(obbTree.x10_tree->GetRoot(), collision.GetRight().GetTransform(),
+                                           s0, obb1, collision.GetLeft().GetFilter()))
+            return true;
+    }
+
     return false;
 }
 
-bool CCollidableOBBTreeGroup::SphereCollideBoolean(const CInternalCollisionStructure &)
+bool CCollidableOBBTreeGroup::CollideMovingSphere(const CInternalCollisionStructure& collision, const zeus::CVector3f& dir,
+                                                  double& mag, CCollisionInfo& info)
 {
+    bool ret = false;
+    const CCollidableSphere& p0 = static_cast<const CCollidableSphere&>(collision.GetLeft().GetPrim());
+    const CCollidableOBBTreeGroup& p1 = static_cast<const CCollidableOBBTreeGroup&>(collision.GetRight().GetPrim());
+
+    zeus::CSphere s0 = p0.Transform(collision.GetLeft().GetTransform());
+
+    zeus::CAABox movedAABB = p0.CalculateLocalAABox();
+    zeus::CVector3f moveVec = float(mag) * dir;
+    movedAABB.accumulateBounds(movedAABB.min + moveVec);
+    movedAABB.accumulateBounds(movedAABB.max + moveVec);
+
+    zeus::COBBox p0Obb =
+    zeus::COBBox::FromAABox(movedAABB,
+                            collision.GetRight().GetTransform().inverse() * collision.GetLeft().GetTransform());
+
+    for (const std::unique_ptr<COBBTree>& tree : p1.x10_container->x0_trees)
+    {
+        CCollidableOBBTree obbTree(tree.get(), p1.GetMaterial());
+        CMetroidAreaCollider::ResetInternalCounters();
+        if (obbTree.SphereCollisionMoving(obbTree.x10_tree->GetRoot(), collision.GetRight().GetTransform(),
+                                          s0, p0Obb, p0.GetMaterial(), collision.GetLeft().GetFilter(),
+                                          dir, mag, info))
+            ret = true;
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTreeGroup::AABoxCollide(const CInternalCollisionStructure& collision, CCollisionInfoList& list)
+{
+    bool ret = false;
+    const CCollidableAABox& p0 = static_cast<const CCollidableAABox&>(collision.GetLeft().GetPrim());
+    const CCollidableOBBTreeGroup& p1 = static_cast<const CCollidableOBBTreeGroup&>(collision.GetRight().GetPrim());
+
+    zeus::CAABox b0 = p0.CalculateAABox(collision.GetLeft().GetTransform());
+    zeus::COBBox p0Obb =
+    zeus::COBBox::FromAABox(p0.CalculateLocalAABox(),
+                            collision.GetRight().GetTransform().inverse() * collision.GetLeft().GetTransform());
+
+    zeus::CPlane planes[] =
+    {
+        {zeus::CVector3f::skRight, b0.min.dot(zeus::CVector3f::skRight)},
+        {zeus::CVector3f::skLeft, b0.max.dot(zeus::CVector3f::skLeft)},
+        {zeus::CVector3f::skForward, b0.min.dot(zeus::CVector3f::skForward)},
+        {zeus::CVector3f::skBack, b0.max.dot(zeus::CVector3f::skBack)},
+        {zeus::CVector3f::skUp, b0.min.dot(zeus::CVector3f::skUp)},
+        {zeus::CVector3f::skDown, b0.max.dot(zeus::CVector3f::skDown)}
+    };
+
+    for (const std::unique_ptr<COBBTree>& tree : p1.x10_container->x0_trees)
+    {
+        CCollidableOBBTree obbTree(tree.get(), p1.GetMaterial());
+        if (obbTree.AABoxCollision(obbTree.x10_tree->GetRoot(), collision.GetRight().GetTransform(),
+                                   b0, p0Obb, p0.GetMaterial(), collision.GetLeft().GetFilter(),
+                                   planes, list))
+            ret = true;
+    }
+
+    return ret;
+}
+
+bool CCollidableOBBTreeGroup::AABoxCollideBoolean(const CInternalCollisionStructure& collision)
+{
+    const CCollidableAABox& p0 = static_cast<const CCollidableAABox&>(collision.GetLeft().GetPrim());
+    const CCollidableOBBTreeGroup& p1 = static_cast<const CCollidableOBBTreeGroup&>(collision.GetRight().GetPrim());
+
+    zeus::CAABox b0 = p0.CalculateAABox(collision.GetLeft().GetTransform());
+    zeus::COBBox p0Obb =
+    zeus::COBBox::FromAABox(p0.CalculateLocalAABox(),
+                            collision.GetRight().GetTransform().inverse() * collision.GetLeft().GetTransform());
+
+    for (const std::unique_ptr<COBBTree>& tree : p1.x10_container->x0_trees)
+    {
+        CCollidableOBBTree obbTree(tree.get(), p1.GetMaterial());
+        if (obbTree.AABoxCollisionBoolean(obbTree.x10_tree->GetRoot(), collision.GetRight().GetTransform(),
+                                          b0, p0Obb, collision.GetLeft().GetFilter()))
+            return true;
+    }
+
     return false;
 }
 
-bool CCollidableOBBTreeGroup::CollideMovingSphere(const CInternalCollisionStructure &, const zeus::CVector3f &, double &, CCollisionInfo &)
+bool CCollidableOBBTreeGroup::CollideMovingAABox(const CInternalCollisionStructure& collision, const zeus::CVector3f& dir,
+                                                 double& mag, CCollisionInfo& info)
 {
-    return false;
-}
+    bool ret = false;
+    const CCollidableAABox& p0 = static_cast<const CCollidableAABox&>(collision.GetLeft().GetPrim());
+    const CCollidableOBBTreeGroup& p1 = static_cast<const CCollidableOBBTreeGroup&>(collision.GetRight().GetPrim());
 
-bool CCollidableOBBTreeGroup::AABoxCollide(const CInternalCollisionStructure &, CCollisionInfoList &)
-{
-    return false;
-}
+    zeus::CAABox b0 = p0.CalculateAABox(collision.GetLeft().GetTransform());
 
-bool CCollidableOBBTreeGroup::AABoxCollideBoolean(const CInternalCollisionStructure &)
-{
-    return false;
-}
+    CMovingAABoxComponents components(b0, dir);
 
-bool CCollidableOBBTreeGroup::CollideMovingAABox(const CInternalCollisionStructure &, const zeus::CVector3f &, double &, CCollisionInfo &)
-{
-    return false;
+    zeus::CAABox movedAABB = p0.CalculateLocalAABox();
+    zeus::CVector3f moveVec = float(mag) * dir;
+    movedAABB.accumulateBounds(movedAABB.min + moveVec);
+    movedAABB.accumulateBounds(movedAABB.max + moveVec);
+
+    zeus::COBBox p0Obb =
+    zeus::COBBox::FromAABox(movedAABB,
+                            collision.GetRight().GetTransform().inverse() * collision.GetLeft().GetTransform());
+
+    for (const std::unique_ptr<COBBTree>& tree : p1.x10_container->x0_trees)
+    {
+        CCollidableOBBTree obbTree(tree.get(), p1.GetMaterial());
+        CMetroidAreaCollider::ResetInternalCounters();
+        if (obbTree.AABoxCollisionMoving(obbTree.x10_tree->GetRoot(), collision.GetRight().GetTransform(),
+                                         b0, p0Obb, p0.GetMaterial(), collision.GetLeft().GetFilter(),
+                                         components, dir, mag, info))
+            ret = true;
+    }
+
+    return ret;
 }
 
 CFactoryFnReturn FCollidableOBBTreeGroupFactory(const SObjectTag &tag, CInputStream& in,
diff --git a/Runtime/Collision/CInternalRayCastStructure.hpp b/Runtime/Collision/CInternalRayCastStructure.hpp
index 929956f71..06ebe0661 100644
--- a/Runtime/Collision/CInternalRayCastStructure.hpp
+++ b/Runtime/Collision/CInternalRayCastStructure.hpp
@@ -11,11 +11,12 @@ class CInternalRayCastStructure
     zeus::CMRay x0_ray;
     float x38_maxTime;
     zeus::CTransform x3c_xf;
-    CMaterialFilter x6c_filter;
+    const CMaterialFilter& x6c_filter;
 public:
     CInternalRayCastStructure(const zeus::CVector3f& start, const zeus::CVector3f& dir, float length,
                               const zeus::CTransform& xf, const CMaterialFilter& filter)
         : x0_ray(start, dir, length),
+          x38_maxTime(length),
           x3c_xf(xf),
           x6c_filter(filter)
     {
diff --git a/Runtime/Collision/CMetroidAreaCollider.cpp b/Runtime/Collision/CMetroidAreaCollider.cpp
index d282ca26e..62945c89a 100644
--- a/Runtime/Collision/CMetroidAreaCollider.cpp
+++ b/Runtime/Collision/CMetroidAreaCollider.cpp
@@ -820,14 +820,14 @@ bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Cached(const COctreeLeafCac
                             for (int k=0 ; k<3 ; ++k)
                             {
                                 u16 vertIdx = vertIndices[k];
-                                zeus::CVector3f point = node.GetOwner().GetVert(vertIdx);
+                                zeus::CVector3f vtx = node.GetOwner().GetVert(vertIdx);
                                 if (g_DupPrimitiveCheckCount != g_DupVertexList[vertIdx])
                                 {
                                     g_DupVertexList[vertIdx] = g_DupPrimitiveCheckCount;
-                                    if (movedAABB.pointInside(point))
+                                    if (movedAABB.pointInside(vtx))
                                     {
                                         d = dOut;
-                                        if (MovingAABoxCollisionCheck_TriVertexBox(point, aabb, dir, d,
+                                        if (MovingAABoxCollisionCheck_TriVertexBox(vtx, aabb, dir, d,
                                                                                    normal, point) && d < dOut)
                                         {
                                             CMaterialList vertMat(node.GetOwner().GetVertMaterial(vertIdx));
diff --git a/Runtime/Collision/CMetroidAreaCollider.hpp b/Runtime/Collision/CMetroidAreaCollider.hpp
index f1c453f69..12d33d3ae 100644
--- a/Runtime/Collision/CMetroidAreaCollider.hpp
+++ b/Runtime/Collision/CMetroidAreaCollider.hpp
@@ -75,6 +75,7 @@ struct SBoxEdge
 class CMovingAABoxComponents
 {
     friend class CMetroidAreaCollider;
+    friend class CCollidableOBBTree;
     rstl::reserved_vector<SBoxEdge, 12> x0_edges;
     rstl::reserved_vector<u32, 8> x6c4_vertIdxs;
     zeus::CAABox x6e8_aabb;
@@ -84,6 +85,7 @@ public:
 
 class CMetroidAreaCollider
 {
+    friend class CCollidableOBBTree;
     static u32 g_CalledClip;
     static u32 g_RejectedByClip;
     static u32 g_TrianglesProcessed;
diff --git a/Runtime/Collision/COBBTree.cpp b/Runtime/Collision/COBBTree.cpp
index a49d88c09..2490ef54b 100644
--- a/Runtime/Collision/COBBTree.cpp
+++ b/Runtime/Collision/COBBTree.cpp
@@ -1,4 +1,5 @@
 #include "COBBTree.hpp"
+#include "CCollidableOBBTreeGroup.hpp"
 
 namespace urde
 {
@@ -29,6 +30,12 @@ COBBTree::COBBTree(CInputStream& in)
 {
 }
 
+std::unique_ptr<CCollidableOBBTreeGroupContainer>
+COBBTree::BuildOrientedBoundingBoxTree(const zeus::CVector3f& a, const zeus::CVector3f& b)
+{
+    return std::make_unique<CCollidableOBBTreeGroupContainer>(a, b);
+}
+
 CCollisionSurface COBBTree::GetSurface(u16 idx) const
 {
     u32 surfIdx = idx * 3;
@@ -52,6 +59,50 @@ CCollisionSurface COBBTree::GetSurface(u16 idx) const
                              x18_indexData.x60_vertices[vert3], mat);
 }
 
+void COBBTree::GetTriangleVertexIndices(u16 idx, u16 indicesOut[3]) const
+{
+    const CCollisionEdge& e0 = x18_indexData.x40_edges[x18_indexData.x50_surfaceIndices[idx*3]];
+    const CCollisionEdge& e1 = x18_indexData.x40_edges[x18_indexData.x50_surfaceIndices[idx*3+1]];
+    indicesOut[2] =
+        (e1.GetVertIndex1() != e0.GetVertIndex1() && e1.GetVertIndex1() != e0.GetVertIndex2()) ?
+        e1.GetVertIndex1() : e1.GetVertIndex2();
+
+    u32 material = x18_indexData.x0_materials[x18_indexData.x30_surfaceMaterials[idx]];
+    if (material & 0x2000000)
+    {
+        indicesOut[0] = e0.GetVertIndex2();
+        indicesOut[1] = e0.GetVertIndex1();
+    }
+    else
+    {
+        indicesOut[0] = e0.GetVertIndex1();
+        indicesOut[1] = e0.GetVertIndex2();
+    }
+}
+
+CCollisionSurface COBBTree::GetTransformedSurface(u16 idx, const zeus::CTransform& xf) const
+{
+    u32 surfIdx = idx * 3;
+    CCollisionEdge edge1 = x18_indexData.x40_edges[x18_indexData.x50_surfaceIndices[surfIdx]];
+    CCollisionEdge edge2 = x18_indexData.x40_edges[x18_indexData.x50_surfaceIndices[surfIdx + 1]];
+    u16 vert1 = edge2.GetVertIndex1();
+    u16 vert2 = edge2.GetVertIndex2();
+    u16 vert3 = edge1.GetVertIndex1();
+
+    if (vert3 == vert1 || vert3 == edge2.GetVertIndex2())
+        vert3 = edge1.GetVertIndex2();
+
+    u32 mat = x18_indexData.x0_materials[x18_indexData.x30_surfaceMaterials[idx]];
+
+    if ((mat & 0x2000000) != 0)
+    {
+        return CCollisionSurface(xf * x18_indexData.x60_vertices[vert2], xf * x18_indexData.x60_vertices[vert1],
+                                 xf * x18_indexData.x60_vertices[vert3], mat);
+    }
+    return CCollisionSurface(xf * x18_indexData.x60_vertices[vert1], xf * x18_indexData.x60_vertices[vert2],
+                             xf * x18_indexData.x60_vertices[vert3], mat);
+}
+
 zeus::CAABox COBBTree::CalculateLocalAABox() const
 {
     return CalculateAABox(zeus::CTransform::Identity());
@@ -118,26 +169,6 @@ COBBTree::CNode::CNode(CInputStream& in)
     }
 }
 
-COBBTree::CNode* COBBTree::CNode::GetLeft() const
-{
-    return x40_left.get();
-}
-
-COBBTree::CNode*COBBTree::CNode::GetRight() const
-{
-    return x44_right.get();
-}
-
-COBBTree::CLeafData*COBBTree::CNode::GetLeafData() const
-{
-    return x48_leaf.get();
-}
-
-const zeus::COBBox& COBBTree::CNode::GetOBB() const
-{
-    return x0_obb;
-}
-
 size_t COBBTree::CNode::GetMemoryUsage() const
 {
     size_t ret = 0;
diff --git a/Runtime/Collision/COBBTree.hpp b/Runtime/Collision/COBBTree.hpp
index df47e15fb..d29cfb54a 100644
--- a/Runtime/Collision/COBBTree.hpp
+++ b/Runtime/Collision/COBBTree.hpp
@@ -8,6 +8,8 @@
 
 namespace urde
 {
+class CCollidableOBBTreeGroupContainer;
+
 class COBBTree
 {
 public:
@@ -43,19 +45,21 @@ public:
         std::unique_ptr<CNode> x40_left;
         std::unique_ptr<CNode> x44_right;
         std::unique_ptr<CLeafData> x48_leaf;
+        bool x4c_hit;
     public:
         CNode() = default;
         CNode(const CNode&)=default;
         CNode(const zeus::CTransform&, const zeus::CVector3f&, const CNode*, const CNode*, const CLeafData*);
         CNode(CInputStream&);
 
-        bool WasHit() const;
-        void SetHit(bool) const;
-        CNode* GetLeft() const;
-        CNode* GetRight() const;
-        CLeafData* GetLeafData() const;
-        const zeus::COBBox& GetOBB() const;
+        bool WasHit() const { return x4c_hit; }
+        void SetHit(bool h) { x4c_hit = h; }
+        const CNode& GetLeft() const { return *x40_left; }
+        const CNode& GetRight() const { return *x44_right; }
+        const CLeafData& GetLeafData() const { return *x48_leaf; }
+        const zeus::COBBox& GetOBB() const { return x0_obb; }
         size_t GetMemoryUsage() const;
+        bool IsLeaf() const { return x3c_isLeaf; }
     };
 
 private:
@@ -71,9 +75,19 @@ public:
     COBBTree(const COBBTree::SIndexData&, const CNode*);
     COBBTree(CInputStream&);
 
-    CCollisionSurface GetSurface(u16) const;
+    static std::unique_ptr<CCollidableOBBTreeGroupContainer>
+    BuildOrientedBoundingBoxTree(const zeus::CVector3f&, const zeus::CVector3f&);
+    CCollisionSurface GetSurface(u16 idx) const;
+    const u16* GetTriangleEdgeIndices(u16 idx) const { return &x18_indexData.x50_surfaceIndices[idx * 3]; }
+    void GetTriangleVertexIndices(u16 idx, u16 indicesOut[3]) const;
+    const CCollisionEdge& GetEdge(int idx) const { return x18_indexData.x40_edges[idx]; }
+    const zeus::CVector3f& GetVert(int idx) const { return x18_indexData.x60_vertices[idx]; }
+    u32 GetVertMaterial(u16 idx) const { return x18_indexData.x0_materials[x18_indexData.x10_vertMaterials[idx]]; }
+    u32 GetEdgeMaterial(u16 idx) const { return x18_indexData.x0_materials[x18_indexData.x20_edgeMaterials[idx]]; }
+    CCollisionSurface GetTransformedSurface(u16 idx, const zeus::CTransform& xf) const;
     zeus::CAABox CalculateLocalAABox() const;
     zeus::CAABox CalculateAABox(const zeus::CTransform&) const;
+    const CNode& GetRoot() const { return *x88_root; }
 };
 }
 
diff --git a/Runtime/Collision/CollisionUtil.cpp b/Runtime/Collision/CollisionUtil.cpp
index 1bfe32d48..2e68dafb2 100644
--- a/Runtime/Collision/CollisionUtil.cpp
+++ b/Runtime/Collision/CollisionUtil.cpp
@@ -14,6 +14,46 @@ bool LineIntersectsOBBox(const zeus::COBBox& obb, const zeus::CMRay& ray, float&
                                 norm, d);
 }
 
+u32 RayAABoxIntersection(const zeus::CMRay& ray, const zeus::CAABox& aabb, float& tMin, float& tMax)
+{
+    tMin = -999999.f;
+    tMax = 999999.f;
+
+    for (int i=0 ; i<3 ; ++i)
+    {
+        if (std::fabs(ray.dir[i]) < 0.00001f)
+        {
+            if (ray.start[i] < aabb.min[i] || ray.start[i] > aabb.max[i])
+                return 0;
+        }
+        else
+        {
+            if (ray.dir[i] < 0.f)
+            {
+                float startToMax = aabb.max[i] - ray.start[i];
+                float startToMin = aabb.min[i] - ray.start[i];
+                float dirRecip = 1.f / ray.dir[i];
+                if (startToMax < tMin * ray.dir[i])
+                    tMin = startToMax * dirRecip;
+                if (startToMin > tMax * ray.dir[i])
+                    tMax = startToMin * dirRecip;
+            }
+            else
+            {
+                float startToMin = aabb.min[i] - ray.start[i];
+                float startToMax = aabb.max[i] - ray.start[i];
+                float dirRecip = 1.f / ray.dir[i];
+                if (startToMin > tMin * ray.dir[i])
+                    tMin = startToMin * dirRecip;
+                if (startToMax < tMax * ray.dir[i])
+                    tMax = startToMax * dirRecip;
+            }
+        }
+    }
+
+    return tMin <= tMax ? 2 : 0;
+}
+
 u32 RayAABoxIntersection(const zeus::CMRay& ray, const zeus::CAABox& aabb,
                          zeus::CVector3f& norm, float& d)
 {
@@ -277,6 +317,30 @@ bool RayTriangleIntersection_Double(const zeus::CVector3f& point, const zeus::CV
     return true;
 }
 
+bool RayTriangleIntersection(const zeus::CVector3f& point, const zeus::CVector3f& dir,
+                             const zeus::CVector3f* verts, float& d)
+{
+    zeus::CVector3f v0tov1 = verts[1] - verts[0];
+    zeus::CVector3f v0tov2 = verts[2] - verts[0];
+    zeus::CVector3f cross0 = dir.cross(v0tov2);
+    float dot0 = v0tov1.dot(cross0);
+    if (dot0 < DBL_EPSILON)
+        return false;
+    zeus::CVector3f v0toPoint = point - verts[0];
+    float dot1 = v0toPoint.dot(cross0);
+    if (dot1 < 0.0 || dot1 > dot0)
+        return false;
+    zeus::CVector3f cross1 = v0toPoint.cross(v0tov1);
+    float dot2 = cross1.dot(dir);
+    if (dot2 < 0.0 || dot1 + dot2 > dot0)
+        return false;
+    float final = 1.0 / dot0 * cross1.dot(v0tov2);
+    if (final < 0.0 || final >= d)
+        return false;
+    d = final;
+    return true;
+}
+
 void FilterOutBackfaces(const zeus::CVector3f& vec, const CCollisionInfoList& in, CCollisionInfoList& out)
 {
     if (vec.canBeNormalized())
diff --git a/Runtime/Collision/CollisionUtil.hpp b/Runtime/Collision/CollisionUtil.hpp
index 7544c23e7..da39e47e0 100644
--- a/Runtime/Collision/CollisionUtil.hpp
+++ b/Runtime/Collision/CollisionUtil.hpp
@@ -11,6 +11,7 @@ class CCollisionInfoList;
 namespace CollisionUtil
 {
 bool LineIntersectsOBBox(const zeus::COBBox&, const zeus::CMRay&, float&);
+u32 RayAABoxIntersection(const zeus::CMRay&, const zeus::CAABox&, float&, float&);
 u32 RayAABoxIntersection(const zeus::CMRay&, const zeus::CAABox&, zeus::CVector3f&, float&);
 u32 RayAABoxIntersection_Double(const zeus::CMRay&, const zeus::CAABox&, zeus::CVector3f&, double&);
 bool RaySphereIntersection_Double(const zeus::CSphere&, const zeus::CVector3f&, const zeus::CVector3f&, double&);
@@ -18,6 +19,8 @@ bool RaySphereIntersection(const zeus::CSphere& sphere, const zeus::CVector3f& p
                            float mag, float& T, zeus::CVector3f& point);
 bool RayTriangleIntersection_Double(const zeus::CVector3f& point, const zeus::CVector3f& dir,
                                     const zeus::CVector3f* verts, double& d);
+bool RayTriangleIntersection(const zeus::CVector3f& point, const zeus::CVector3f& dir,
+                             const zeus::CVector3f* verts, float& d);
 void FilterOutBackfaces(const zeus::CVector3f& vec, const CCollisionInfoList& in, CCollisionInfoList& out);
 void FilterByClosestNormal(const zeus::CVector3f& norm, const CCollisionInfoList& in, CCollisionInfoList& out);
 bool AABoxAABoxIntersection(const zeus::CAABox& aabb0, const CMaterialList& list0,
diff --git a/nod b/nod
index 6454d68ab..8bdbcf0c9 160000
--- a/nod
+++ b/nod
@@ -1 +1 @@
-Subproject commit 6454d68abc2180a33aa4359c0ed3906519083c7e
+Subproject commit 8bdbcf0c9080f26c7fdb9364af03f1dd6b74c350
diff --git a/specter b/specter
index 1c17a2864..c35ccdd3c 160000
--- a/specter
+++ b/specter
@@ -1 +1 @@
-Subproject commit 1c17a28648e33d52a0eaea1e272da49ac6a940dd
+Subproject commit c35ccdd3cd7517e4e277d02e336997b519d00162