#include "CollisionUtil.hpp" #include "CCollisionInfo.hpp" #include "CCollisionInfoList.hpp" namespace urde { namespace CollisionUtil { bool LineIntersectsOBBox(const zeus::COBBox& obb, const zeus::CMRay& ray, float& d) { const zeus::CVector3f transXf = obb.transform.toMatrix4f().vec[0].toVec3f(); return RayAABoxIntersection(ray.getInvUnscaledTransformRay(obb.transform), {-obb.extents, obb.extents}, transXf, d); } u32 RayAABoxIntersection(const zeus::CMRay& ray, const zeus::CAABox& box, const zeus::CVector3f&, float& d) { return 0; } u32 RaySphereIntersection_Double(const zeus::CSphere&, const zeus::CVector3f &, const zeus::CVector3f &, double &) { return 0; } bool RaySphereIntersection(const zeus::CSphere& sphere, const zeus::CVector3f& pos, const zeus::CVector3f& dir, float mag, float& T, zeus::CVector3f& point) { zeus::CVector3f rayToSphere = sphere.position - pos; float magSq = rayToSphere.magSquared(); float dirDot = rayToSphere.dot(dir); float radSq = sphere.radius * sphere.radius; if (dirDot < 0.f && magSq > radSq) return false; float intersectSq = radSq - (magSq - dirDot * dirDot); if (intersectSq < 0.f) return false; T = magSq > radSq ? dirDot - std::sqrt(intersectSq) : dirDot + std::sqrt(intersectSq); if (T < mag || mag == 0.f) { point = pos + T * dir; return true; } return false; } void FilterOutBackfaces(const zeus::CVector3f& vec, const CCollisionInfoList& in, CCollisionInfoList& out) { if (vec.canBeNormalized()) { zeus::CVector3f norm = vec.normalized(); for (const CCollisionInfo& info : in) { if (info.GetNormalLeft().dot(norm) < 0.001f) out.Add(info, false); } } else { out = in; } } void FilterByClosestNormal(const zeus::CVector3f& norm, const CCollisionInfoList& in, CCollisionInfoList& out) { float maxDot = -1.1f; int idx = -1; int i=0; for (const CCollisionInfo& info : in) { float dot = info.GetNormalLeft().dot(norm); if (dot > maxDot) { maxDot = dot; idx = i; } ++i; } if (idx != -1) out.Add(in.GetItem(i), false); } static const zeus::CVector3f AABBNormalTable[] = { {-1.f, 0.f, 0.f}, {1.f, 0.f, 0.f}, {0.f, -1.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, -1.f}, {0.f, 0.f, 1.f} }; bool AABoxAABoxIntersection(const zeus::CAABox& aabb0, const CMaterialList& list0, const zeus::CAABox& aabb1, const CMaterialList& list1, CCollisionInfoList& infoList) { zeus::CAABox boolAABB = aabb0.booleanIntersection(aabb1); if (boolAABB.invalid()) return false; /* TODO: Finish */ if (!infoList.GetCount()) { infoList.Add(CCollisionInfo(boolAABB, list0, list1, AABBNormalTable[4], -AABBNormalTable[4]), false); infoList.Add(CCollisionInfo(boolAABB, list0, list1, AABBNormalTable[5], -AABBNormalTable[5]), false); } return true; } bool AABoxAABoxIntersection(const zeus::CAABox& aabb0, const zeus::CAABox& aabb1) { return aabb0.intersects(aabb1); } /* http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/tribox2.txt */ /********************************************************/ /* AABB-triangle overlap test code */ /* by Tomas Akenine-Möller */ /* Function: int triBoxOverlap(float boxcenter[3], */ /* float boxhalfsize[3],float triverts[3][3]); */ /* History: */ /* 2001-03-05: released the code in its first version */ /* 2001-06-18: changed the order of the tests, faster */ /* */ /* Acknowledgement: Many thanks to Pierre Terdiman for */ /* suggestions and discussions on how to optimize code. */ /* Thanks to David Hunt for finding a ">="-bug! */ /********************************************************/ #define FINDMINMAX(x0,x1,x2,min,max) \ min = max = x0; \ if (x1max) max = x1;\ if (x2max) max = x2; static bool planeBoxOverlap(const zeus::CVector3f& normal, float d, const zeus::CVector3f& maxbox) { zeus::CVector3f vmin, vmax; for (int q=0 ; q<=2 ; q++) { if (normal[q] > 0.0f) { vmin[q] = -maxbox[q]; vmax[q] = maxbox[q]; } else { vmin[q] = maxbox[q]; vmax[q] = -maxbox[q]; } } if (normal.dot(vmin) + d > 0.0f) return false; if (normal.dot(vmax) + d >= 0.0f) return true; return false; } /*======================== X-tests ========================*/ #define AXISTEST_X01(a, b, fa, fb) \ p0 = a*v0.y - b*v0.z; \ p2 = a*v2.y - b*v2.z; \ if(p0rad || max<-rad) return false; #define AXISTEST_X2(a, b, fa, fb) \ p0 = a*v0.y - b*v0.z; \ p1 = a*v1.y - b*v1.z; \ if(p0rad || max<-rad) return false; /*======================== Y-tests ========================*/ #define AXISTEST_Y02(a, b, fa, fb) \ p0 = -a*v0.x + b*v0.z; \ p2 = -a*v2.x + b*v2.z; \ if(p0rad || max<-rad) return false; #define AXISTEST_Y1(a, b, fa, fb) \ p0 = -a*v0.x + b*v0.z; \ p1 = -a*v1.x + b*v1.z; \ if(p0rad || max<-rad) return false; /*======================== Z-tests ========================*/ #define AXISTEST_Z12(a, b, fa, fb) \ p1 = a*v1.x - b*v1.y; \ p2 = a*v2.x - b*v2.y; \ if(p2rad || max<-rad) return false; #define AXISTEST_Z0(a, b, fa, fb) \ p0 = a*v0.x - b*v0.y; \ p1 = a*v1.x - b*v1.y; \ if(p0rad || max<-rad) return false; bool TriBoxOverlap(const zeus::CVector3f& boxcenter, const zeus::CVector3f& boxhalfsize, const zeus::CVector3f& trivert0, const zeus::CVector3f& trivert1, const zeus::CVector3f& trivert2) { /* use separating axis theorem to test overlap between triangle and box */ /* need to test for overlap in these directions: */ /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ /* we do not even need to test these) */ /* 2) normal of the triangle */ /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ /* this gives 3x3=9 more tests */ zeus::CVector3f v0, v1, v2; float min, max, d, p0, p1, p2, rad, fex, fey, fez; zeus::CVector3f normal, e0, e1, e2; /* This is the fastest branch on Sun */ /* move everything so that the boxcenter is in (0,0,0) */ v0 = trivert0 - boxcenter; v1 = trivert1 - boxcenter; v2 = trivert2 - boxcenter; /* compute triangle edges */ e0 = v1 - v0; /* tri edge 0 */ e1 = v2 - v1; /* tri edge 1 */ e2 = v0 - v2; /* tri edge 2 */ /* Bullet 3: */ /* test the 9 tests first (this was faster) */ fex = std::fabs(e0.x); fey = std::fabs(e0.y); fez = std::fabs(e0.z); AXISTEST_X01(e0.z, e0.y, fez, fey); AXISTEST_Y02(e0.z, e0.x, fez, fex); AXISTEST_Z12(e0.y, e0.x, fey, fex); fex = std::fabs(e1.x); fey = std::fabs(e1.y); fez = std::fabs(e1.z); AXISTEST_X01(e1.z, e1.y, fez, fey); AXISTEST_Y02(e1.z, e1.x, fez, fex); AXISTEST_Z0(e1.y, e1.x, fey, fex); fex = std::fabs(e2.x); fey = std::fabs(e2.y); fez = std::fabs(e2.z); AXISTEST_X2(e2.z, e2.y, fez, fey); AXISTEST_Y1(e2.z, e2.x, fez, fex); AXISTEST_Z12(e2.y, e2.x, fey, fex); /* Bullet 1: */ /* first test overlap in the {x,y,z}-directions */ /* find min, max of the triangle each direction, and test for overlap in */ /* that direction -- this is equivalent to testing a minimal AABB around */ /* the triangle against the AABB */ /* test in X-direction */ FINDMINMAX(v0.x, v1.x, v2.x, min, max); if (min>boxhalfsize.x || max<-boxhalfsize.x) return false; /* test in Y-direction */ FINDMINMAX(v0.y, v1.y, v2.y, min, max); if (min>boxhalfsize.y || max<-boxhalfsize.y) return false; /* test in Z-direction */ FINDMINMAX(v0.z, v1.z, v2.z, min, max); if (min>boxhalfsize.z || max<-boxhalfsize.z) return false; /* Bullet 2: */ /* test if the box intersects the plane of the triangle */ /* compute plane equation of triangle: normal*x+d=0 */ normal = e0.cross(e1); d = -normal.dot(v0); /* plane eq: normal.x+d=0 */ if (!planeBoxOverlap(normal, d, boxhalfsize)) return false; return true; /* box and triangle overlaps */ } } }