2022-11-29 16:34:42 +01:00
|
|
|
#include "BVH.h"
|
|
|
|
|
|
|
|
|
|
using namespace Seele;
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::findOverlaps(Array<Pair<entt::entity, entt::entity>>& overlaps) {
|
2022-11-29 16:34:42 +01:00
|
|
|
overlaps.clear();
|
2024-06-09 12:20:04 +02:00
|
|
|
for (const auto& node : dynamicNodes) {
|
|
|
|
|
if (!node.isLeaf) {
|
2023-01-21 18:43:21 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2022-11-29 16:34:42 +01:00
|
|
|
traverseStaticTree(node.box, node.owner, staticRoot, overlaps);
|
|
|
|
|
traverseDynamicTree(node.box, node.owner, dynamicRoot, overlaps);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::updateDynamicCollider(entt::entity entity, AABB aabb) {
|
|
|
|
|
|
|
|
|
|
for (auto& node : dynamicNodes) {
|
|
|
|
|
if (node.owner == entity) {
|
|
|
|
|
if (!node.box.contains(aabb)) {
|
2022-11-29 16:34:42 +01:00
|
|
|
// moved out of extended bounds
|
|
|
|
|
reinsertCollider(entity, aabb);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// new collider
|
|
|
|
|
addDynamicCollider(entity, aabb);
|
2023-01-21 18:43:21 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::colliderCallback(entt::registry& registry, entt::entity entity) {
|
2023-01-21 18:43:21 +01:00
|
|
|
Component::Collider& collider = registry.get<Component::Collider>(entity);
|
|
|
|
|
Component::Transform& transform = registry.get<Component::Transform>(entity);
|
2024-06-09 12:20:04 +02:00
|
|
|
if (collider.type == Component::ColliderType::STATIC) {
|
2023-01-21 18:43:21 +01:00
|
|
|
addStaticCollider(entity, collider.boundingbox.getTransformedBox(transform.toMatrix()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::visualize() {
|
2023-08-26 15:19:12 +02:00
|
|
|
Array<DebugVertex> verts;
|
2024-06-09 12:20:04 +02:00
|
|
|
for (const auto& node : staticNodes) {
|
2023-08-26 15:19:12 +02:00
|
|
|
node.box.visualize(verts);
|
2023-01-21 18:43:21 +01:00
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
for (const auto& node : dynamicNodes) {
|
2023-08-26 15:19:12 +02:00
|
|
|
node.box.visualize(verts);
|
2023-01-21 18:43:21 +01:00
|
|
|
}
|
2023-08-26 15:19:12 +02:00
|
|
|
addDebugVertices(verts);
|
2022-11-29 16:34:42 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::traverseStaticTree(const AABB& aabb, entt::entity source, int32 nodeIndex, Array<Pair<entt::entity, entt::entity>>& overlaps) {
|
2022-11-29 16:34:42 +01:00
|
|
|
const Node& node = staticNodes[nodeIndex];
|
2024-06-09 12:20:04 +02:00
|
|
|
if (!aabb.intersects(node.box)) {
|
2022-11-29 16:34:42 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
if (node.isLeaf && node.owner != source) {
|
2022-11-29 16:34:42 +01:00
|
|
|
overlaps.add(Pair<entt::entity, entt::entity>(source, node.owner));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
traverseStaticTree(aabb, source, node.left, overlaps);
|
|
|
|
|
traverseStaticTree(aabb, source, node.right, overlaps);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::traverseDynamicTree(const AABB& aabb, entt::entity source, int32 nodeIndex, Array<Pair<entt::entity, entt::entity>>& overlaps) {
|
|
|
|
|
if (nodeIndex == -1) {
|
2023-01-21 18:43:21 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const Node& node = dynamicNodes[nodeIndex];
|
2024-06-09 12:20:04 +02:00
|
|
|
if (!aabb.intersects(node.box)) {
|
2023-01-21 18:43:21 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
if (node.isLeaf && node.owner != source) {
|
2023-01-21 18:43:21 +01:00
|
|
|
overlaps.add(Pair<entt::entity, entt::entity>(source, node.owner));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
traverseDynamicTree(aabb, source, node.left, overlaps);
|
|
|
|
|
traverseDynamicTree(aabb, source, node.right, overlaps);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::reinsertCollider(entt::entity entity, AABB aabb) {
|
|
|
|
|
|
2023-01-21 18:43:21 +01:00
|
|
|
removeCollider(entity);
|
2024-06-09 12:20:04 +02:00
|
|
|
|
2023-01-21 18:43:21 +01:00
|
|
|
addDynamicCollider(entity, aabb);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::removeCollider(entt::entity entity) {
|
2022-11-29 16:34:42 +01:00
|
|
|
int32 nodeIndex = -1;
|
2024-06-09 12:20:04 +02:00
|
|
|
for (size_t i = 0; i < dynamicNodes.size(); i++) {
|
|
|
|
|
if (dynamicNodes[i].isLeaf && dynamicNodes[i].owner == entity) {
|
2022-11-29 16:34:42 +01:00
|
|
|
nodeIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
if (nodeIndex == -1) {
|
2022-11-29 16:34:42 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
int32 parentIndex = dynamicNodes[nodeIndex].parentIndex;
|
2024-06-09 12:20:04 +02:00
|
|
|
if (parentIndex == -1) {
|
2023-01-21 18:43:21 +01:00
|
|
|
// its the root node
|
|
|
|
|
dynamicRoot = -1;
|
|
|
|
|
freeNode(nodeIndex);
|
2024-06-09 12:20:04 +02:00
|
|
|
|
2023-01-21 18:43:21 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2022-11-29 16:34:42 +01:00
|
|
|
const Node& parent = dynamicNodes[parentIndex];
|
|
|
|
|
int32 siblingIndex;
|
2024-06-09 12:20:04 +02:00
|
|
|
|
|
|
|
|
if (parent.left == nodeIndex) {
|
2022-11-29 16:34:42 +01:00
|
|
|
siblingIndex = parent.right;
|
2024-06-09 12:20:04 +02:00
|
|
|
} else {
|
2022-11-29 16:34:42 +01:00
|
|
|
siblingIndex = parent.left;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
|
2022-11-29 16:34:42 +01:00
|
|
|
// the node to remove and their sibling share a parent
|
|
|
|
|
// now that the node is removed, the parent is also useless
|
2023-01-21 18:43:21 +01:00
|
|
|
// so the grandparent should point to the sibling directly instead of the parent
|
2024-06-09 12:20:04 +02:00
|
|
|
if (parent.parentIndex != -1) {
|
2023-01-21 18:43:21 +01:00
|
|
|
Node& grandParent = dynamicNodes[parent.parentIndex];
|
2024-06-09 12:20:04 +02:00
|
|
|
if (grandParent.left == parentIndex) {
|
2023-01-21 18:43:21 +01:00
|
|
|
grandParent.left = siblingIndex;
|
2024-06-09 12:20:04 +02:00
|
|
|
} else {
|
2023-01-21 18:43:21 +01:00
|
|
|
grandParent.right = siblingIndex;
|
|
|
|
|
}
|
|
|
|
|
dynamicNodes[siblingIndex].parentIndex = parent.parentIndex;
|
2024-06-09 12:20:04 +02:00
|
|
|
} else {
|
2023-01-21 18:43:21 +01:00
|
|
|
// if the shared parent was the root, we need a new root, which is the remaining sibling
|
|
|
|
|
dynamicRoot = siblingIndex;
|
|
|
|
|
dynamicNodes[siblingIndex].parentIndex = -1;
|
2022-11-29 16:34:42 +01:00
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
|
2022-11-29 16:34:42 +01:00
|
|
|
freeNode(nodeIndex);
|
2024-06-09 12:20:04 +02:00
|
|
|
|
2023-01-21 18:43:21 +01:00
|
|
|
freeNode(parentIndex);
|
2022-11-29 16:34:42 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::addDynamicCollider(entt::entity entity, AABB aabb) {
|
2022-11-29 16:34:42 +01:00
|
|
|
auto center = (aabb.max + aabb.min) / 2.0f;
|
|
|
|
|
// enlarge box slightly to buffer movement
|
2024-06-09 12:20:04 +02:00
|
|
|
aabb = AABB{
|
2022-11-29 16:34:42 +01:00
|
|
|
.min = center + (aabb.min - center) * 1.1f,
|
|
|
|
|
.max = center + (aabb.max - center) * 1.1f,
|
|
|
|
|
};
|
|
|
|
|
int32 leafIndex = allocateNode();
|
2024-06-09 12:20:04 +02:00
|
|
|
Node newNode = Node{
|
2022-11-29 16:34:42 +01:00
|
|
|
.box = aabb,
|
|
|
|
|
.isLeaf = true,
|
2023-01-21 18:43:21 +01:00
|
|
|
.isValid = true,
|
2022-11-29 16:34:42 +01:00
|
|
|
.owner = entity,
|
|
|
|
|
};
|
|
|
|
|
dynamicNodes[leafIndex] = newNode;
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
if (dynamicRoot == -1) {
|
2022-11-29 16:34:42 +01:00
|
|
|
dynamicRoot = leafIndex;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
|
2022-11-29 16:34:42 +01:00
|
|
|
int32 bestSibling;
|
|
|
|
|
float bestCost = std::numeric_limits<float>::max();
|
|
|
|
|
findSibling(newNode, dynamicRoot, bestCost, bestSibling);
|
|
|
|
|
|
|
|
|
|
int32 oldParent = dynamicNodes[bestSibling].parentIndex;
|
|
|
|
|
int32 newParent = allocateNode();
|
2024-06-09 12:20:04 +02:00
|
|
|
dynamicNodes[newParent] = Node{
|
2022-11-29 16:34:42 +01:00
|
|
|
.box = aabb.combine(dynamicNodes[bestSibling].box),
|
|
|
|
|
.parentIndex = oldParent,
|
|
|
|
|
.isLeaf = false,
|
2023-01-21 18:43:21 +01:00
|
|
|
.isValid = true,
|
2022-11-29 16:34:42 +01:00
|
|
|
};
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
if (oldParent != -1) {
|
|
|
|
|
if (dynamicNodes[oldParent].left == bestSibling) {
|
2022-11-29 16:34:42 +01:00
|
|
|
dynamicNodes[oldParent].left = newParent;
|
2024-06-09 12:20:04 +02:00
|
|
|
} else {
|
2022-11-29 16:34:42 +01:00
|
|
|
dynamicNodes[oldParent].right = newParent;
|
|
|
|
|
}
|
|
|
|
|
dynamicNodes[newParent].left = bestSibling;
|
|
|
|
|
dynamicNodes[newParent].right = leafIndex;
|
|
|
|
|
dynamicNodes[bestSibling].parentIndex = newParent;
|
|
|
|
|
dynamicNodes[leafIndex].parentIndex = newParent;
|
2024-06-09 12:20:04 +02:00
|
|
|
|
|
|
|
|
} else {
|
2022-11-29 16:34:42 +01:00
|
|
|
dynamicNodes[newParent].left = bestSibling;
|
|
|
|
|
dynamicNodes[newParent].right = leafIndex;
|
|
|
|
|
dynamicNodes[bestSibling].parentIndex = newParent;
|
|
|
|
|
dynamicNodes[leafIndex].parentIndex = newParent;
|
|
|
|
|
dynamicRoot = newParent;
|
|
|
|
|
}
|
|
|
|
|
int32 index = dynamicNodes[leafIndex].parentIndex;
|
2024-06-09 12:20:04 +02:00
|
|
|
while (index != -1) {
|
2022-11-29 16:34:42 +01:00
|
|
|
int32 left = dynamicNodes[index].left;
|
|
|
|
|
int32 right = dynamicNodes[index].right;
|
|
|
|
|
|
|
|
|
|
dynamicNodes[index].box = dynamicNodes[left].box.combine(dynamicNodes[right].box);
|
|
|
|
|
index = dynamicNodes[index].parentIndex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::addStaticCollider(entt::entity entity, AABB boundingBox) {
|
|
|
|
|
staticCollider.add(AABBCenter{
|
2022-11-29 16:34:42 +01:00
|
|
|
.bb = boundingBox,
|
|
|
|
|
.center = (boundingBox.min + boundingBox.max) / 2.0f,
|
|
|
|
|
.id = entity,
|
|
|
|
|
});
|
|
|
|
|
staticNodes.clear();
|
2023-01-21 18:43:21 +01:00
|
|
|
staticRoot = splitNode(staticCollider);
|
2022-11-29 16:34:42 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::findSibling(Node newNode, int32 nodeIndex, float& bestCost, int32& result) {
|
|
|
|
|
if (nodeIndex == -1) {
|
2022-11-29 16:34:42 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
float lowerBound = lowerBoundCost(newNode, nodeIndex);
|
2024-06-09 12:20:04 +02:00
|
|
|
if (lowerBound < bestCost) {
|
2022-11-29 16:34:42 +01:00
|
|
|
float cost = siblingCost(newNode, nodeIndex);
|
2024-06-09 12:20:04 +02:00
|
|
|
if (cost < bestCost) {
|
2022-11-29 16:34:42 +01:00
|
|
|
bestCost = cost;
|
|
|
|
|
result = nodeIndex;
|
|
|
|
|
}
|
|
|
|
|
findSibling(newNode, dynamicNodes[nodeIndex].left, bestCost, result);
|
|
|
|
|
findSibling(newNode, dynamicNodes[nodeIndex].right, bestCost, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
float BVH::siblingCost(Node newNode, int32 siblingIndex) {
|
2022-11-29 16:34:42 +01:00
|
|
|
const Node& sibling = dynamicNodes[siblingIndex];
|
|
|
|
|
AABB newBox = sibling.box.combine(newNode.box);
|
|
|
|
|
float cost = newBox.surfaceArea();
|
|
|
|
|
int32 parentIndex = sibling.parentIndex;
|
2024-06-09 12:20:04 +02:00
|
|
|
while (parentIndex != -1) {
|
2022-11-29 16:34:42 +01:00
|
|
|
AABB parentBox = dynamicNodes[parentIndex].box;
|
|
|
|
|
cost += parentBox.combine(newBox).surfaceArea() - parentBox.surfaceArea();
|
2023-01-21 18:43:21 +01:00
|
|
|
parentIndex = dynamicNodes[parentIndex].parentIndex;
|
2022-11-29 16:34:42 +01:00
|
|
|
}
|
|
|
|
|
return cost;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
float BVH::lowerBoundCost(Node newNode, int32 branchIndex) {
|
2022-11-29 16:34:42 +01:00
|
|
|
float cost = newNode.box.surfaceArea();
|
2024-06-09 12:20:04 +02:00
|
|
|
while (branchIndex != -1) {
|
2022-11-29 16:34:42 +01:00
|
|
|
AABB box = dynamicNodes[branchIndex].box;
|
|
|
|
|
cost += box.combine(newNode.box).surfaceArea() - box.surfaceArea();
|
|
|
|
|
branchIndex = dynamicNodes[branchIndex].parentIndex;
|
|
|
|
|
}
|
|
|
|
|
return cost;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
int32 BVH::splitNode(Array<AABBCenter> aabbs) {
|
|
|
|
|
if (aabbs.size() == 1) {
|
2022-11-29 16:34:42 +01:00
|
|
|
int32 leafIndex = static_cast<int32>(staticNodes.size());
|
|
|
|
|
Node& leaf = staticNodes.add();
|
2023-01-21 18:43:21 +01:00
|
|
|
leaf.isLeaf = true;
|
2022-11-29 16:34:42 +01:00
|
|
|
leaf.box = aabbs[0].bb;
|
|
|
|
|
leaf.left = -1;
|
|
|
|
|
leaf.right = -1;
|
|
|
|
|
leaf.owner = aabbs[0].id;
|
|
|
|
|
return leafIndex;
|
|
|
|
|
}
|
|
|
|
|
AABB rootBox;
|
2024-06-09 12:20:04 +02:00
|
|
|
for (size_t i = 0; i < aabbs.size(); ++i) {
|
2022-11-29 16:34:42 +01:00
|
|
|
rootBox.adjust(aabbs[i].bb.min);
|
|
|
|
|
rootBox.adjust(aabbs[i].bb.max);
|
|
|
|
|
}
|
|
|
|
|
float xlen = rootBox.max.x - rootBox.min.x;
|
|
|
|
|
float ylen = rootBox.max.y - rootBox.min.y;
|
|
|
|
|
float zlen = rootBox.max.z - rootBox.min.z;
|
|
|
|
|
int32 longestAxis;
|
2024-06-09 12:20:04 +02:00
|
|
|
if (xlen >= ylen && xlen >= zlen) {
|
2022-11-29 16:34:42 +01:00
|
|
|
longestAxis = 0;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
if (ylen >= xlen && ylen >= zlen) {
|
2022-11-29 16:34:42 +01:00
|
|
|
longestAxis = 1;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
if (zlen >= xlen && zlen >= ylen) {
|
2022-11-29 16:34:42 +01:00
|
|
|
longestAxis = 2;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
struct {
|
|
|
|
|
bool operator()(const AABBCenter& lhs, const AABBCenter& rhs) { return lhs.center[longestAxis] < rhs.center[longestAxis]; }
|
2024-01-16 19:24:49 +01:00
|
|
|
int32 longestAxis;
|
|
|
|
|
} compare = {longestAxis};
|
2024-06-09 12:20:04 +02:00
|
|
|
|
2024-01-16 19:24:49 +01:00
|
|
|
std::sort(aabbs.begin(), aabbs.end(), compare);
|
2024-06-09 12:20:04 +02:00
|
|
|
Array<AABBCenter> left((aabbs.size() + 1) / 2);
|
|
|
|
|
Array<AABBCenter> right(aabbs.size() / 2);
|
|
|
|
|
std::copy(aabbs.begin(), aabbs.begin() + left.size(), left.begin());
|
|
|
|
|
std::copy(aabbs.begin() + left.size(), aabbs.end(), right.begin());
|
2022-11-29 16:34:42 +01:00
|
|
|
int32 rootIndex = static_cast<int32>(staticNodes.size());
|
|
|
|
|
Node& rootNode = staticNodes.add();
|
|
|
|
|
rootNode.box = rootBox;
|
|
|
|
|
rootNode.left = splitNode(std::move(left));
|
|
|
|
|
rootNode.right = splitNode(std::move(right));
|
|
|
|
|
return rootIndex;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
int32 BVH::allocateNode() {
|
|
|
|
|
for (size_t i = 0; i < dynamicNodes.size(); i++) {
|
|
|
|
|
if (!dynamicNodes[i].isValid) {
|
2022-11-29 16:34:42 +01:00
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int32 newLeaf = static_cast<int32>(dynamicNodes.size());
|
|
|
|
|
dynamicNodes.add();
|
|
|
|
|
return newLeaf;
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::freeNode(int32 nodeIndex) {
|
2022-11-29 16:34:42 +01:00
|
|
|
dynamicNodes[nodeIndex].isValid = false;
|
2023-01-21 18:43:21 +01:00
|
|
|
dynamicNodes[nodeIndex].parentIndex = -1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:20:04 +02:00
|
|
|
void BVH::validateBVH() const {
|
|
|
|
|
if (dynamicRoot != -1) {
|
2023-01-21 18:43:21 +01:00
|
|
|
assert(dynamicNodes[dynamicRoot].parentIndex == -1);
|
|
|
|
|
}
|
2024-06-09 12:20:04 +02:00
|
|
|
for (size_t i = 0; i < dynamicNodes.size(); ++i) {
|
2023-01-21 18:43:21 +01:00
|
|
|
int32 nodeIdx = i;
|
|
|
|
|
size_t counter = dynamicNodes.size();
|
2024-06-09 12:20:04 +02:00
|
|
|
if (!dynamicNodes[nodeIdx].isValid)
|
2023-01-21 18:43:21 +01:00
|
|
|
continue;
|
2024-06-09 12:20:04 +02:00
|
|
|
while (dynamicNodes[nodeIdx].parentIndex != -1) {
|
2023-01-21 18:43:21 +01:00
|
|
|
nodeIdx = dynamicNodes[nodeIdx].parentIndex;
|
|
|
|
|
assert(counter-- > 0 && dynamicNodes[nodeIdx].isValid);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-29 16:34:42 +01:00
|
|
|
}
|