diff --git a/CMakeLists.txt b/CMakeLists.txt index f538a201..bb7a560f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,8 @@ SET (REACTPHYSICS3D_SOURCES "src/collision/broadphase/PairManager.cpp" "src/collision/broadphase/SweepAndPruneAlgorithm.h" "src/collision/broadphase/SweepAndPruneAlgorithm.cpp" + "src/collision/broadphase/DynamicAABBTree.h" + "src/collision/broadphase/DynamicAABBTree.cpp" "src/collision/narrowphase/EPA/EdgeEPA.h" "src/collision/narrowphase/EPA/EdgeEPA.cpp" "src/collision/narrowphase/EPA/EPAAlgorithm.h" diff --git a/src/collision/broadphase/BroadPhaseAlgorithm.h b/src/collision/broadphase/BroadPhaseAlgorithm.h index 79536b05..f509d351 100644 --- a/src/collision/broadphase/BroadPhaseAlgorithm.h +++ b/src/collision/broadphase/BroadPhaseAlgorithm.h @@ -30,6 +30,7 @@ #include #include "../../body/CollisionBody.h" #include "PairManager.h" +#include "DynamicAABBTree.h" /// Namespace ReactPhysics3D namespace reactphysics3d { @@ -39,9 +40,8 @@ class CollisionDetection; // Class BroadPhaseAlgorithm /** - * This class is an abstract class that represents an algorithm - * used to perform the broad-phase of a collision detection. The - * goal of the broad-phase algorithm is to compute the pair of bodies + * This class represents an algorithm the broad-phase collision detection. The + * goal of the broad-phase collision detection is to compute the pair of bodies * that can collide. But it's important to understand that the * broad-phase doesn't compute only body pairs that can collide but * could also pairs of body that doesn't collide but are very close. @@ -55,6 +55,9 @@ class BroadPhaseAlgorithm { // -------------------- Attributes -------------------- // + /// Dynamic AABB tree + DynamicAABBTree mDynamicAABBTree; + /// Pair manager containing the overlapping pairs PairManager mPairManager; diff --git a/src/collision/broadphase/DynamicAABBTree.cpp b/src/collision/broadphase/DynamicAABBTree.cpp new file mode 100644 index 00000000..d542afcc --- /dev/null +++ b/src/collision/broadphase/DynamicAABBTree.cpp @@ -0,0 +1,554 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://code.google.com/p/reactphysics3d/ * +* Copyright (c) 2010-2013 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "DynamicAABBTree.h" + +using namespace reactphysics3d; + +// Initialization of static variables +const int TreeNode::NULL_TREE_NODE = -1; + +// Constructor +DynamicAABBTree::DynamicAABBTree() { + + mRootNodeID = TreeNode::NULL_TREE_NODE; + mNbNodes = 0; + mNbAllocatedNodes = 8; + + // Allocate memory for the nodes of the tree + mNodes = (TreeNode*) malloc(mNbAllocatedNodes * sizeof(TreeNode)); + assert(mNodes); + memset(mNodes, 0, mNbAllocatedNodes * sizeof(TreeNode)); + + // Initialize the allocated nodes + for (int i=0; i 0); + assert(nodeID >= 0 && nodeID < mNbAllocatedNodes); + mNodes[nodeID].nextNodeID = mFreeNodeID; + mNodes[nodeID].height = -1; + mFreeNodeID = nodeID; + mNbNodes--; + + // Deallocate nodes memory here if the number of allocated nodes is large + // compared to the number of nodes in the tree + if ((mNbNodes < mNbAllocatedNodes / 4) && mNbNodes > 8) { + + // Allocate less nodes in the tree + mNbAllocatedNodes /= 2; + TreeNode* oldNodes = mNodes; + mNodes = (TreeNode*) malloc(mNbAllocatedNodes * sizeof(TreeNode)); + assert(mNodes); + memcpy(mNodes, oldNodes, mNbNodes * sizeof(TreeNode)); + free(oldNodes); + + // Initialize the allocated nodes + for (int i=mNbNodes; i= 0 && nodeID < mNbAllocatedNodes); + assert(mNodes[nodeID].isLeaf()); + + // Remove the node from the tree + removeLeafNode(nodeID); + releaseNode(nodeID); +} + +// Update the dynamic tree after an object has moved. +/// If the new AABB of the object that has moved is still inside its fat AABB, then +/// nothing is done. Otherwise, the corresponding node is removed and reinserted into the tree. +/// The method returns true if the object has been reinserted into the tree. +bool DynamicAABBTree::updateObject(int nodeID, const AABB& newAABB, const Vector3& displacement) { + + assert(nodeID >= 0 && nodeID < mNbAllocatedNodes); + assert(mNodes[nodeID].isLeaf()); + + // If the new AABB is still inside the fat AABB of the node + if (mNodes[nodeID].aabb.contains(newAABB)) { + return false; + } + + // If the new AABB is outside the fat AABB, we remove the corresponding node + removeLeafNode(nodeID); + + // Compute a new fat AABB for the new AABB by taking the object displacement into account + AABB fatAABB = newAABB; + const Vector3 gap(DYNAMIC_TREE_AABB_GAP, DYNAMIC_TREE_AABB_GAP, DYNAMIC_TREE_AABB_GAP); + fatAABB.mMinCoordinates -= gap; + fatAABB.mMaxCoordinates += gap; + const Vector3 displacementGap = AABB_DISPLACEMENT_MULTIPLIER * displacement; + if (displacementGap.x < decimal(0.0)) { + fatAABB.mMinCoordinates.x += displacementGap.x; + } + else { + fatAABB.mMaxCoordinates.x += displacementGap.x; + } + if (displacementGap.y < decimal(0.0)) { + fatAABB.mMinCoordinates.y += displacementGap.y; + } + else { + fatAABB.mMaxCoordinates.y += displacementGap.y; + } + if (displacementGap.z < decimal(0.0)) { + fatAABB.mMinCoordinates.z += displacementGap.z; + } + else { + fatAABB.mMaxCoordinates.z += displacementGap.z; + } + mNodes[nodeID].aabb = fatAABB; + + // Reinsert the node into the tree + insertLeafNode(nodeID); + + return true; +} + +// Insert a leaf node in the tree. The process of inserting a new leaf node +// in the dynamic tree is described in the book "Introduction to Game Physics +// with Box2D" by Ian Parberry. +void DynamicAABBTree::insertLeafNode(int nodeID) { + + // If the tree is empty + if (mRootNodeID == TreeNode::NULL_TREE_NODE) { + mRootNodeID = nodeID; + mNodes[mRootNodeID].parentID = TreeNode::NULL_TREE_NODE; + return; + } + + // Find the best sibling node for the new node + AABB newNodeAABB = mNodes[nodeID].aabb; + int currentNodeID = mRootNodeID; + while (!mNodes[currentNodeID].isLeaf()) { + + int leftChild = mNodes[currentNodeID].leftChildID; + int rightChild = mNodes[currentNodeID].rightChildID; + + // Compute the merged AABB + decimal volumeAABB = mNodes[currentNodeID].aabb.getVolume(); + AABB mergedAABBs; + mergedAABBs.mergeTwoAABBs(mNodes[currentNodeID].aabb, newNodeAABB); + decimal mergedVolume = mergedAABBs.getVolume(); + + // Compute the cost of making the current node the sibbling of the new node + decimal costS = decimal(2.0) * mergedVolume; + + // Compute the minimum cost of pushing the new node further down the tree (inheritance cost) + decimal costI = decimal(2.0) * (mergedVolume - volumeAABB); + + // Compute the cost of descending into the left child + decimal costLeft; + AABB currentAndLeftAABB; + currentAndLeftAABB.mergeTwoAABBs(newNodeAABB, mNodes[leftChild].aabb); + if (mNodes[leftChild].isLeaf()) { // If the left child is a leaf + costLeft = currentAndLeftAABB.getVolume() + costI; + } + else { + decimal leftChildVolume = mNodes[leftChild].aabb.getVolume(); + costLeft = costI + currentAndLeftAABB.getVolume() - leftChildVolume; + } + + // Compute the cost of descending into the right child + decimal costRight; + AABB currentAndRightAABB; + currentAndRightAABB.mergeTwoAABBs(newNodeAABB, mNodes[rightChild].aabb); + if (mNodes[rightChild].isLeaf()) { // If the right child is a leaf + costRight = currentAndRightAABB.getVolume() + costI; + } + else { + decimal rightChildVolume = mNodes[rightChild].aabb.getVolume(); + costRight = costI + currentAndRightAABB.getVolume() - rightChildVolume; + } + + // If the cost of making the current node a sibbling of the new node is smaller than + // the cost of going down into the left or right child + if (costS < costLeft && costS < costRight) break; + + // It is cheaper to go down into a child of the current node, choose the best child + if (costLeft < costRight) { + currentNodeID = leftChild; + } + else { + currentNodeID = rightChild; + } + } + + int siblingNode = currentNodeID; + + // Create a new parent for the new node and the sibling node + int oldParentNode = mNodes[siblingNode].parentID; + int newParentNode = allocateNode(); + mNodes[newParentNode].parentID = oldParentNode; + mNodes[newParentNode].collisionShape = NULL; + mNodes[newParentNode].aabb.mergeTwoAABBs(mNodes[siblingNode].aabb, newNodeAABB); + mNodes[newParentNode].height = mNodes[siblingNode].height + 1; + + // If the sibling node was not the root node + if (oldParentNode != TreeNode::NULL_TREE_NODE) { + if (mNodes[oldParentNode].leftChildID == siblingNode) { + mNodes[oldParentNode].leftChildID = newParentNode; + } + else { + mNodes[oldParentNode].rightChildID = newParentNode; + } + } + else { // If the sibling node was the root node + mNodes[newParentNode].leftChildID = siblingNode; + mNodes[newParentNode].rightChildID = nodeID; + mNodes[siblingNode].parentID = newParentNode; + mNodes[nodeID].parentID = newParentNode; + mRootNodeID = newParentNode; + } + + // Move up in the tree to change the AABBs that have changed + currentNodeID = mNodes[nodeID].parentID; + while (currentNodeID != TreeNode::NULL_TREE_NODE) { + + // Balance the sub-tree of the current node if it is not balanced + currentNodeID = balanceSubTreeAtNode(currentNodeID); + + int leftChild = mNodes[currentNodeID].leftChildID; + int rightChild = mNodes[currentNodeID].rightChildID; + assert(leftChild != TreeNode::NULL_TREE_NODE); + assert(rightChild != TreeNode::NULL_TREE_NODE); + + // Recompute the height of the node in the tree + mNodes[currentNodeID].height = std::max(mNodes[leftChild].height, + mNodes[rightChild].height) + 1; + + // Recompute the AABB of the node + mNodes[currentNodeID].aabb.mergeTwoAABBs(mNodes[leftChild].aabb, mNodes[rightChild].aabb); + + currentNodeID = mNodes[currentNodeID].parentID; + } +} + +// Remove a leaf node from the tree +void DynamicAABBTree::removeLeafNode(int nodeID) { + + assert(nodeID >= 0 && nodeID < mNbAllocatedNodes); + + // If we are removing the root node (root node is a leaf in this case) + if (mRootNodeID == nodeID) { + mRootNodeID = TreeNode::NULL_TREE_NODE; + return; + } + + int parentNodeID = mNodes[nodeID].parentID; + int grandParentNodeID = mNodes[parentNodeID].parentID; + int siblingNodeID; + if (mNodes[parentNodeID].leftChildID == nodeID) { + siblingNodeID = mNodes[parentNodeID].rightChildID; + } + else { + siblingNodeID = mNodes[parentNodeID].leftChildID; + } + + // If the parent of the node to remove is not the root node + if (grandParentNodeID != TreeNode::NULL_TREE_NODE) { + + // Destroy the parent node + if (mNodes[grandParentNodeID].leftChildID == parentNodeID) { + mNodes[grandParentNodeID].leftChildID = siblingNodeID; + } + else { + assert(mNodes[grandParentNodeID].rightChildID == parentNodeID); + mNodes[grandParentNodeID].rightChildID = siblingNodeID; + } + mNodes[siblingNodeID].parentID = grandParentNodeID; + releaseNode(parentNodeID); + + // Now, we need to recompute the AABBs of the node on the path back to the root + // and make sure that the tree is still balanced + int currentNodeID = grandParentNodeID; + while(currentNodeID != TreeNode::NULL_TREE_NODE) { + + // Balance the current sub-tree if necessary + currentNodeID = balanceSubTreeAtNode(currentNodeID); + + // Get the two children of the current node + int leftChildID = mNodes[currentNodeID].leftChildID; + int rightChildID = mNodes[currentNodeID].rightChildID; + + // Recompute the AABB and the height of the current node + mNodes[currentNodeID].aabb.mergeTwoAABBs(mNodes[leftChildID].aabb, + mNodes[rightChildID].aabb); + mNodes[currentNodeID].height = std::max(mNodes[leftChildID].height, + mNodes[rightChildID].height) + 1; + + currentNodeID = mNodes[currentNodeID].parentID; + } + + } + else { // If the parent of the node to remove is the root node + + // The sibling node becomes the new root node + mRootNodeID = siblingNodeID; + mNodes[siblingNodeID].parentID = TreeNode::NULL_TREE_NODE; + releaseNode(nodeID); + } +} + +// Balance the sub-tree of a given node using left or right rotations. +/// The rotation schemes are described in in the book "Introduction to Game Physics +/// with Box2D" by Ian Parberry. This method returns the new root node ID. +int DynamicAABBTree::balanceSubTreeAtNode(int nodeID) { + + assert(nodeID != TreeNode::NULL_TREE_NODE); + + TreeNode* nodeA = mNodes + nodeID; + + // If the node is a leaf or the height of A's sub-tree is less than 2 + if (nodeA->isLeaf() || nodeA->height < 2) { + + // Do not perform any rotation + return nodeID; + } + + // Get the two children nodes + int nodeBID = nodeA->leftChildID; + int nodeCID = nodeA->rightChildID; + assert(nodeBID >= 0 && nodeBID < mNbAllocatedNodes); + assert(nodeCID >= 0 && nodeCID < mNbAllocatedNodes); + TreeNode* nodeB = mNodes + nodeBID; + TreeNode* nodeC = mNodes + nodeCID; + + // Compute the factor of the left and right sub-trees + int balanceFactor = nodeC->height - nodeB->height; + + // If the right node C is 2 higher than left node B + if (balanceFactor > 1) { + + int nodeFID = nodeC->leftChildID; + int nodeGID = nodeC->rightChildID; + assert(nodeFID >= 0 && nodeFID < mNbAllocatedNodes); + assert(nodeGID >= 0 && nodeGID < mNbAllocatedNodes); + TreeNode* nodeF = mNodes + nodeFID; + TreeNode* nodeG = mNodes + nodeGID; + + nodeC->leftChildID = nodeID; + nodeC->parentID = nodeA->parentID; + nodeA->parentID = nodeCID; + + if (nodeC->parentID != TreeNode::NULL_TREE_NODE) { + + if (mNodes[nodeC->parentID].leftChildID == nodeID) { + mNodes[nodeC->parentID].leftChildID = nodeCID; + } + else { + assert(mNodes[nodeC->parentID].rightChildID == nodeID); + mNodes[nodeC->parentID].rightChildID = nodeCID; + } + } + else { + mRootNodeID = nodeCID; + } + + // If the right node C was higher than left node B because of the F node + if (nodeF->height > nodeG->height) { + nodeC->rightChildID = nodeFID; + nodeA->rightChildID = nodeGID; + nodeG->parentID = nodeID; + + // Recompute the AABB of node A and C + nodeA->aabb.mergeTwoAABBs(nodeB->aabb, nodeG->aabb); + nodeC->aabb.mergeTwoAABBs(nodeA->aabb, nodeF->aabb); + + // Recompute the height of node A and C + nodeA->height = std::max(nodeB->height, nodeG->height) + 1; + nodeC->height = std::max(nodeA->height, nodeF->height) + 1; + } + else { // If the right node C was higher than left node B because of node G + nodeC->rightChildID = nodeGID; + nodeA->rightChildID = nodeFID; + nodeF->parentID = nodeID; + + // Recompute the AABB of node A and C + nodeA->aabb.mergeTwoAABBs(nodeB->aabb, nodeF->aabb); + nodeC->aabb.mergeTwoAABBs(nodeA->aabb, nodeG->aabb); + + // Recompute the height of node A and C + nodeA->height = std::max(nodeB->height, nodeF->height) + 1; + nodeC->height = std::max(nodeA->height, nodeG->height) + 1; + } + + // Return the new root of the sub-tree + return nodeCID; + } + + // If the left node B is 2 higher than right node C + if (balanceFactor < -1) { + + int nodeFID = nodeB->leftChildID; + int nodeGID = nodeB->rightChildID; + assert(nodeFID >= 0 && nodeFID < mNbAllocatedNodes); + assert(nodeGID >= 0 && nodeGID < mNbAllocatedNodes); + TreeNode* nodeF = mNodes + nodeFID; + TreeNode* nodeG = mNodes + nodeGID; + + nodeB->leftChildID = nodeID; + nodeB->parentID = nodeA->parentID; + nodeA->parentID = nodeBID; + + if (nodeB->parentID != TreeNode::NULL_TREE_NODE) { + + if (mNodes[nodeB->parentID].leftChildID == nodeID) { + mNodes[nodeB->parentID].leftChildID = nodeBID; + } + else { + assert(mNodes[nodeB->parentID].rightChildID == nodeID); + mNodes[nodeB->parentID].rightChildID = nodeBID; + } + } + else { + mRootNodeID = nodeBID; + } + + // If the left node B was higher than right node C because of the F node + if (nodeF->height > nodeG->height) { + nodeB->rightChildID = nodeFID; + nodeA->leftChildID = nodeGID; + nodeG->parentID = nodeID; + + // Recompute the AABB of node A and B + nodeA->aabb.mergeTwoAABBs(nodeC->aabb, nodeG->aabb); + nodeB->aabb.mergeTwoAABBs(nodeA->aabb, nodeF->aabb); + + // Recompute the height of node A and B + nodeA->height = std::max(nodeC->height, nodeG->height) + 1; + nodeB->height = std::max(nodeA->height, nodeF->height) + 1; + } + else { // If the left node B was higher than right node C because of node G + nodeB->rightChildID = nodeGID; + nodeA->leftChildID = nodeFID; + nodeF->parentID = nodeID; + + // Recompute the AABB of node A and B + nodeA->aabb.mergeTwoAABBs(nodeC->aabb, nodeF->aabb); + nodeB->aabb.mergeTwoAABBs(nodeA->aabb, nodeG->aabb); + + // Recompute the height of node A and B + nodeA->height = std::max(nodeC->height, nodeF->height) + 1; + nodeB->height = std::max(nodeA->height, nodeG->height) + 1; + } + + // Return the new root of the sub-tree + return nodeBID; + } + + // If the sub-tree is balanced, return the current root node + return nodeID; +} diff --git a/src/collision/broadphase/DynamicAABBTree.h b/src/collision/broadphase/DynamicAABBTree.h new file mode 100644 index 00000000..542aeb8a --- /dev/null +++ b/src/collision/broadphase/DynamicAABBTree.h @@ -0,0 +1,147 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://code.google.com/p/reactphysics3d/ * +* Copyright (c) 2010-2014 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_DYNAMIC_AABB_TREE_H +#define REACTPHYSICS3D_DYNAMIC_AABB_TREE_H + +// Libraries +#include "../../configuration.h" +#include "../shapes/AABB.h" +#include "../shapes/CollisionShape.h" + +/// Namespace ReactPhysics3D +namespace reactphysics3d { + +// Structure TreeNode +/** + * This structure represents a node of the dynamic AABB tree. + */ +struct TreeNode { + + // -------------------- Constants -------------------- // + + /// Null tree node constant + const static int NULL_TREE_NODE; + + // -------------------- Attributes -------------------- // + + /// Parent node ID + int parentID; + + /// Left and right child of the node + int leftChildID, rightChildID; + + /// Next allocated node ID + int nextNodeID; + + /// Height of the node in the tree + int height; + + /// Fat axis aligned bounding box (AABB) corresponding to the node + AABB aabb; + + /// Pointer to the corresponding collision shape (in case this node is a leaf) + CollisionShape* collisionShape; + + // -------------------- Methods -------------------- // + + /// Return true if the node is a leaf of the tree + bool isLeaf() const; +}; + +// Class DynamicAABBTree +/** + * This class implements a dynamic AABB tree that is used for broad-phase + * collision detection. This data structure is inspired by Nathanael Presson's + * dynamic tree implementation in BulletPhysics. The following implementation is + * based on the one from Erin Catto in Box2D as described in the book + * "Introduction to Game Physics with Box2D" by Ian Parberry. + */ +class DynamicAABBTree { + + private: + + // -------------------- Attributes -------------------- // + + /// Pointer to the memory location of the nodes of the tree + TreeNode* mNodes; + + /// ID of the root node of the tree + int mRootNodeID; + + /// ID of the first node of the list of free (allocated) nodes in the tree that we can use + int mFreeNodeID; + + /// Number of allocated nodes in the tree + int mNbAllocatedNodes; + + /// Number of nodes in the tree + int mNbNodes; + + // -------------------- Methods -------------------- // + + /// Allocate and return a node to use in the tree + int allocateNode(); + + /// Release a node + void releaseNode(int nodeID); + + /// Insert a leaf node in the tree + void insertLeafNode(int nodeID); + + /// Remove a leaf node from the tree + void removeLeafNode(int nodeID); + + /// Balance the sub-tree of a given node using left or right rotations. + int balanceSubTreeAtNode(int nodeID); + + public: + + // -------------------- Methods -------------------- // + + /// Constructor + DynamicAABBTree(); + + /// Destructor + ~DynamicAABBTree(); + + /// Add an object into the tree + int addObject(CollisionShape* collisionShape, const AABB& aabb); + + /// Remove an object from the tree + void removeObject(int nodeID); + + /// Update the dynamic tree after an object has moved. + bool updateObject(int nodeID, const AABB &newAABB, const Vector3 &displacement); +}; + +// Return true if the node is a leaf of the tree +inline bool TreeNode::isLeaf() const { + return leftChildID == NULL_TREE_NODE; +} + +} + +#endif diff --git a/src/collision/shapes/AABB.cpp b/src/collision/shapes/AABB.cpp index 4e65edef..63e542a2 100644 --- a/src/collision/shapes/AABB.cpp +++ b/src/collision/shapes/AABB.cpp @@ -42,8 +42,39 @@ AABB::AABB(const Vector3& minCoordinates, const Vector3& maxCoordinates) } +// Copy-constructor +AABB::AABB(const AABB& aabb) + : mMinCoordinates(aabb.mMinCoordinates), mMaxCoordinates(aabb.mMaxCoordinates) { + +} + // Destructor AABB::~AABB() { } +// Replace the current AABB with a new AABB that is the union of two AABBs in parameters +void AABB::mergeTwoAABBs(const AABB& aabb1, const AABB& aabb2) { + mMinCoordinates.x = std::min(aabb1.mMinCoordinates.x, aabb2.mMinCoordinates.x); + mMinCoordinates.y = std::min(aabb1.mMinCoordinates.y, aabb2.mMinCoordinates.y); + mMinCoordinates.z = std::min(aabb1.mMinCoordinates.z, aabb2.mMinCoordinates.z); + + mMaxCoordinates.x = std::max(aabb1.mMaxCoordinates.x, aabb2.mMaxCoordinates.x); + mMaxCoordinates.y = std::max(aabb1.mMaxCoordinates.y, aabb2.mMaxCoordinates.y); + mMaxCoordinates.z = std::max(aabb1.mMaxCoordinates.z, aabb2.mMaxCoordinates.z); +} + +// Return true if the current AABB contains the AABB given in parameter +bool AABB::contains(const AABB& aabb) { + + bool isInside = true; + isInside = isInside && mMinCoordinates.x <= aabb.mMinCoordinates.x; + isInside = isInside && mMinCoordinates.y <= aabb.mMinCoordinates.y; + isInside = isInside && mMinCoordinates.z <= aabb.mMinCoordinates.z; + + isInside = isInside && mMaxCoordinates.x >= aabb.mMaxCoordinates.x; + isInside = isInside && mMaxCoordinates.y >= aabb.mMaxCoordinates.y; + isInside = isInside && mMaxCoordinates.z >= aabb.mMaxCoordinates.z; + return isInside; +} + diff --git a/src/collision/shapes/AABB.h b/src/collision/shapes/AABB.h index ad6dd20b..987280a9 100644 --- a/src/collision/shapes/AABB.h +++ b/src/collision/shapes/AABB.h @@ -31,9 +31,6 @@ /// ReactPhysics3D namespace namespace reactphysics3d { - -// Declaration -class Body; // Class AABB /** @@ -54,17 +51,6 @@ class AABB { /// Maximum world coordinates of the AABB on the x,y and z axis Vector3 mMaxCoordinates; - // -------------------- Methods -------------------- // - - /// Private copy-constructor - AABB(const AABB& aabb); - - /// Private assignment operator - AABB& operator=(const AABB& aabb); - - /// Constructor - AABB(const Transform& transform, const Vector3& extents); - public : // -------------------- Methods -------------------- // @@ -75,10 +61,11 @@ class AABB { /// Constructor AABB(const Vector3& minCoordinates, const Vector3& maxCoordinates); - + /// Copy-constructor + AABB(const AABB& aabb); /// Destructor - virtual ~AABB(); + ~AABB(); /// Return the center point Vector3 getCenter() const; @@ -97,11 +84,27 @@ class AABB { /// Return true if the current AABB is overlapping with the AABB in argument bool testCollision(const AABB& aabb) const; + + /// Return the volume of the AABB + decimal getVolume() const; + + /// Replace the current AABB with a new AABB that is the union of two AABBs in parameters + void mergeTwoAABBs(const AABB& aabb1, const AABB& aabb2); + + /// Return true if the current AABB contains the AABB given in parameter + bool contains(const AABB& aabb); + + /// Assignment operator + AABB& operator=(const AABB& aabb); + + // -------------------- Friendship -------------------- // + + friend class DynamicAABBTree; }; // Return the center point of the AABB in world coordinates inline Vector3 AABB::getCenter() const { - return (mMinCoordinates + mMaxCoordinates) * 0.5; + return (mMinCoordinates + mMaxCoordinates) * decimal(0.5); } // Return the minimum coordinates of the AABB @@ -119,7 +122,7 @@ inline const Vector3& AABB::getMax() const { return mMaxCoordinates; } -/// Set the maximum coordinates of the AABB +// Set the maximum coordinates of the AABB inline void AABB::setMax(const Vector3& max) { mMaxCoordinates = max; } @@ -136,6 +139,21 @@ inline bool AABB::testCollision(const AABB& aabb) const { return true; } +// Return the volume of the AABB +inline decimal AABB::getVolume() const { + const Vector3 diff = mMaxCoordinates - mMinCoordinates; + return (diff.x * diff.y * diff.z); +} + +// Assignment operator +inline AABB& AABB::operator=(const AABB& aabb) { + if (this != &aabb) { + mMinCoordinates = aabb.mMinCoordinates; + mMaxCoordinates = aabb.mMaxCoordinates; + } + return *this; +} + } #endif diff --git a/src/configuration.h b/src/configuration.h index de4d293c..63c5ad3e 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -121,6 +121,15 @@ const decimal DEFAULT_SLEEP_LINEAR_VELOCITY = decimal(0.02); /// might enter sleeping mode const decimal DEFAULT_SLEEP_ANGULAR_VELOCITY = decimal(3.0 * (PI / 180.0)); +/// In the broad-phase collision detection (dynamic AABB tree), the AABBs are +/// fatten to allow the collision shape to move a little bit without triggering +/// a large modification of the tree which can be costly +const decimal DYNAMIC_TREE_AABB_GAP = decimal(0.1); + +/// In the dynamic AABB tree, we multiply this factor by the displacement of +/// an object that has moved to recompute a new fat AABB +const decimal AABB_DISPLACEMENT_MULTIPLIER = decimal(2.0); + } #endif