From 59b237e9925835184091415d9161da7e267952e3 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Wed, 30 Mar 2016 07:10:15 +0200 Subject: [PATCH] Add unit tests for Dynamic AABB tree --- test/tests/collision/TestDynamicAABBTree.h | 269 ++++++++++++++++++++- 1 file changed, 267 insertions(+), 2 deletions(-) diff --git a/test/tests/collision/TestDynamicAABBTree.h b/test/tests/collision/TestDynamicAABBTree.h index 8b326ebd..163e1f6f 100644 --- a/test/tests/collision/TestDynamicAABBTree.h +++ b/test/tests/collision/TestDynamicAABBTree.h @@ -29,10 +29,53 @@ // Libraries #include "Test.h" #include "collision/broadphase/DynamicAABBTree.h" +#include /// Reactphysics3D namespace namespace reactphysics3d { +class OverlapCallback : public DynamicAABBTreeOverlapCallback { + + public : + + std::vector mOverlapNodes; + + // Called when a overlapping node has been found during the call to + // DynamicAABBTree:reportAllShapesOverlappingWithAABB() + virtual void notifyOverlappingNode(int nodeId) { + mOverlapNodes.push_back(nodeId); + } + + void reset() { + mOverlapNodes.clear(); + } + + bool isOverlapping(int nodeId) const { + return std::find(mOverlapNodes.begin(), mOverlapNodes.end(), nodeId) != mOverlapNodes.end(); + } +}; + +class DynamicTreeRaycastCallback : public DynamicAABBTreeRaycastCallback { + + public: + + std::vector mHitNodes; + + // Called when the AABB of a leaf node is hit by a ray + virtual decimal raycastBroadPhaseShape(int32 nodeId, const Ray& ray) { + mHitNodes.push_back(nodeId); + return 1.0; + } + + void reset() { + mHitNodes.clear(); + } + + bool isHit(int nodeId) const { + return std::find(mHitNodes.begin(), mHitNodes.end(), nodeId) != mHitNodes.end(); + } +}; + // Class TestDynamicAABBTree /** * Unit test for the dynamic AABB tree @@ -46,19 +89,241 @@ class TestDynamicAABBTree : public Test { // Dynamic AABB Tree DynamicAABBTree mTree; + AABB mAABB1; + AABB mAABB2; + AABB mAABB3; + AABB mAABB4; + + OverlapCallback mOverlapCallback; + DynamicTreeRaycastCallback mRaycastCallback; + + int mObject1Id; + int mObject2Id; + int mObject3Id; + int mObject4Id; + + int mObject1Data = 56; + int mObject2Data = 23; + int mObject3Data = 13; + int mObject4Data = 7; + public : // ---------- Methods ---------- // /// Constructor - TestDynamicAABBTree(const std::string& name): Test(name) {} + TestDynamicAABBTree(const std::string& name): Test(name) { + + // First object + mAABB1 = AABB(Vector3(-6, 4, -3), Vector3(4, 8, 3)); + mObject1Id = mTree.addObject(mAABB1, &mObject1Data); + + // Second object + mAABB2 = AABB(Vector3(5, 2, -3), Vector3(10, 7, 3)); + mObject2Id = mTree.addObject(mAABB2, &mObject2Data); + + // Third object + mAABB3 = AABB(Vector3(-5, 1, -3), Vector3(-2, 3, 3)); + mObject3Id = mTree.addObject(mAABB3, &mObject3Data); + + // Fourth object + mAABB4 = AABB(Vector3(0, -4, -3), Vector3(3, -2, 3)); + mObject4Id = mTree.addObject(mAABB4, &mObject4Data); + } /// Run the tests void run() { - // TODO : Implement tests here + testBasicsMethods(); + testOverlapping(); + testRaycast(); + } + void testBasicsMethods() { + + // Test data stored at the nodes of the tree + test(*(int*)(mTree.getNodeDataPointer(mObject1Id)) == mObject1Data); + test(*(int*)(mTree.getNodeDataPointer(mObject2Id)) == mObject2Data); + test(*(int*)(mTree.getNodeDataPointer(mObject3Id)) == mObject3Data); + test(*(int*)(mTree.getNodeDataPointer(mObject4Id)) == mObject4Data); + } + + void testOverlapping() { + + // AABB overlapping nothing + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-10, 12, -4), Vector3(10, 50, 4)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(!mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping everything + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-15, -15, -4), Vector3(15, 15, 4)), mOverlapCallback); + test(mOverlapCallback.isOverlapping(mObject1Id)); + test(mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 1 and 3 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-4, 2, -4), Vector3(-1, 7, 4)), mOverlapCallback); + test(mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 3 and 4 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-6, -5, -2), Vector3(2, 2, 0)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 2 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(5, -10, -2), Vector3(7, 10, 9)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(mOverlapCallback.isOverlapping(mObject2Id)); + test(!mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // ---- Update the object AABBs with the initial AABBs (no reinsertion) ----- // + + mTree.updateObject(mObject1Id, mAABB1, Vector3::zero(), false); + mTree.updateObject(mObject2Id, mAABB2, Vector3::zero(), false); + mTree.updateObject(mObject3Id, mAABB3, Vector3::zero(), false); + mTree.updateObject(mObject4Id, mAABB4, Vector3::zero(), false); + + // AABB overlapping nothing + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-10, 12, -4), Vector3(10, 50, 4)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(!mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping everything + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-15, -15, -4), Vector3(15, 15, 4)), mOverlapCallback); + test(mOverlapCallback.isOverlapping(mObject1Id)); + test(mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 1 and 3 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-4, 2, -4), Vector3(-1, 7, 4)), mOverlapCallback); + test(mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 3 and 4 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-6, -5, -2), Vector3(2, 2, 0)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 2 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(5, -10, -2), Vector3(7, 10, 9)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(mOverlapCallback.isOverlapping(mObject2Id)); + test(!mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // ---- Update the object AABBs with the initial AABBs (with reinsertion) ----- // + + mTree.updateObject(mObject1Id, mAABB1, Vector3::zero(), true); + mTree.updateObject(mObject2Id, mAABB2, Vector3::zero(), true); + mTree.updateObject(mObject3Id, mAABB3, Vector3::zero(), true); + mTree.updateObject(mObject4Id, mAABB4, Vector3::zero(), true); + + // AABB overlapping nothing + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-10, 12, -4), Vector3(10, 50, 4)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(!mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping everything + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-15, -15, -4), Vector3(15, 15, 4)), mOverlapCallback); + test(mOverlapCallback.isOverlapping(mObject1Id)); + test(mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 1 and 3 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-4, 2, -4), Vector3(-1, 7, 4)), mOverlapCallback); + test(mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 3 and 4 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(-6, -5, -2), Vector3(2, 2, 0)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(!mOverlapCallback.isOverlapping(mObject2Id)); + test(mOverlapCallback.isOverlapping(mObject3Id)); + test(mOverlapCallback.isOverlapping(mObject4Id)); + + // AABB overlapping object 2 + mOverlapCallback.reset(); + mTree.reportAllShapesOverlappingWithAABB(AABB(Vector3(5, -10, -2), Vector3(7, 10, 9)), mOverlapCallback); + test(!mOverlapCallback.isOverlapping(mObject1Id)); + test(mOverlapCallback.isOverlapping(mObject2Id)); + test(!mOverlapCallback.isOverlapping(mObject3Id)); + test(!mOverlapCallback.isOverlapping(mObject4Id)); + } + + void testRaycast() { + + // Ray with no hits + mRaycastCallback.reset(); + Ray ray1(Vector3(4.5, -10, -5), Vector3(4.5, 10, -5)); + mTree.raycast(ray1, mRaycastCallback); + test(!mRaycastCallback.isHit(mObject1Id)); + test(!mRaycastCallback.isHit(mObject2Id)); + test(!mRaycastCallback.isHit(mObject3Id)); + test(!mRaycastCallback.isHit(mObject4Id)); + + // Ray that hits object 1 + mRaycastCallback.reset(); + Ray ray2(Vector3(-1, -20, -2), Vector3(-1, 20, -2)); + mTree.raycast(ray2, mRaycastCallback); + test(mRaycastCallback.isHit(mObject1Id)); + test(!mRaycastCallback.isHit(mObject2Id)); + test(!mRaycastCallback.isHit(mObject3Id)); + test(!mRaycastCallback.isHit(mObject4Id)); + + // Ray that hits object 1 and 2 + mRaycastCallback.reset(); + Ray ray3(Vector3(-7, 6, -2), Vector3(8, 6, -2)); + mTree.raycast(ray3, mRaycastCallback); + test(mRaycastCallback.isHit(mObject1Id)); + test(mRaycastCallback.isHit(mObject2Id)); + test(!mRaycastCallback.isHit(mObject3Id)); + test(!mRaycastCallback.isHit(mObject4Id)); + + // Ray that hits object 3 + mRaycastCallback.reset(); + Ray ray4(Vector3(-7, 2, 0), Vector3(-1, 2, 0)); + mTree.raycast(ray4, mRaycastCallback); + test(!mRaycastCallback.isHit(mObject1Id)); + test(!mRaycastCallback.isHit(mObject2Id)); + test(mRaycastCallback.isHit(mObject3Id)); + test(!mRaycastCallback.isHit(mObject4Id)); + } }; }