8 int maxDepth,
int maxPerNode)
9 : rootBounds_(
makeAABB3(minX, minY, minZ, maxX, maxY, maxZ)),
10 maxDepth_(std::max(1, maxDepth)),
11 maxPerNode_(std::max(1, maxPerNode)) {
12 if (!rootBounds_.
valid() || rootBounds_.
width() <= 0.f || rootBounds_.
height() <= 0.f ||
13 rootBounds_.
depth() <= 0.f) {
14 throw Exception(
"BSPTree3D: bounds must have positive volume");
19BSPTree3D::Node *BSPTree3D::ensureRoot() {
21 root_ = std::make_unique<Node>();
22 root_->bounds = rootBounds_;
41 if (!
b.valid())
return false;
44 if (!insertInto(*ensureRoot(),
id,
b)) {
45 ensureRoot()->itemIds.push_back(
id);
60 return insert(
id, minX, minY, minZ, maxX, maxY, maxZ);
63void BSPTree3D::rebuild() {
67 for (
const auto &kv : items_) {
68 if (!insertInto(*root_, kv.first, kv.second)) {
69 root_->itemIds.push_back(kv.first);
74bool BSPTree3D::insertInto(Node &node,
int id,
const AABB3 &bounds) {
75 if (!
node.bounds.containsAABB(bounds) &&
node.depth == 0) {
76 if (!
node.bounds.intersectsAABB(bounds))
return false;
77 }
else if (
node.depth > 0 && !
node.bounds.containsAABB(bounds)) {
82 node.itemIds.push_back(
id);
83 if (
static_cast<int>(
node.itemIds.size()) > maxPerNode_ &&
node.depth < maxDepth_) {
89 float bMin = 0.f, bMax = 0.f;
93 }
else if (
node.axis == 1) {
101 if (bMax <=
node.split &&
node.left) {
102 return insertInto(*
node.left,
id, bounds);
104 if (bMin >=
node.split &&
node.right) {
105 return insertInto(*
node.right,
id, bounds);
107 node.itemIds.push_back(
id);
111void BSPTree3D::split(Node &node) {
113 if (
node.axis == 0) {
115 }
else if (
node.axis == 1) {
121 node.left = std::make_unique<Node>();
122 node.right = std::make_unique<Node>();
125 node.left->axis =
node.left->depth % 3;
126 node.right->axis =
node.right->depth % 3;
128 const AABB3 &
b =
node.bounds;
129 if (
node.axis == 0) {
132 }
else if (
node.axis == 1) {
140 std::vector<int> remain;
141 remain.reserve(
node.itemIds.size());
142 for (
int id :
node.itemIds) {
143 auto it = items_.find(
id);
144 if (it == items_.end())
continue;
145 const AABB3 &ib = it->second;
146 float iMin = 0.f, iMax = 0.f;
147 if (
node.axis == 0) {
150 }
else if (
node.axis == 1) {
157 if (iMax <=
node.split) {
158 node.left->itemIds.push_back(
id);
159 }
else if (iMin >=
node.split) {
160 node.right->itemIds.push_back(
id);
162 remain.push_back(
id);
165 node.itemIds.swap(remain);
168void BSPTree3D::collect(Node &node,
const AABB3 *box,
float cx,
float cy,
float cz,
float radius,
170 if (box && !
node.bounds.intersectsAABB(*box))
return;
171 if (useSphere && !
node.bounds.intersectsSphere(
cx,
cy, cz, radius))
return;
173 for (
int id :
node.itemIds) {
174 auto it = items_.find(
id);
175 if (it == items_.end())
continue;
176 const AABB3 &
b = it->second;
179 hit =
b.intersectsAABB(*box);
180 }
else if (useSphere) {
181 hit =
b.intersectsSphere(
cx,
cy, cz, radius);
183 hit =
b.containsPoint(
cx,
cy, cz);
188 if (!
node.isLeaf()) {
189 if (
node.left) collect(*
node.left, box,
cx,
cy, cz, radius, useSphere);
190 if (
node.right) collect(*
node.right, box,
cx,
cy, cz, radius, useSphere);
196 collect(*ensureRoot(),
nullptr,
x,
y,
z, 0.f,
false);
203 collect(*ensureRoot(), &box, 0.f, 0.f, 0.f, 0.f,
false);
209 if (radius < 0.f) radius = 0.f;
210 collect(*ensureRoot(),
nullptr,
cx,
cy, cz, radius,
true);
bool contains(int id) const
int queryAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
int querySphere(float cx, float cy, float cz, float radius)
BSPTree3D(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int maxDepth=12, int maxPerNode=8)
int queryPoint(float x, float y, float z)
bool update(int id, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
bool insert(int id, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
NodeDesc node(std::string id, std::vector< NodeDesc > children, std::string name)
AABB3 makeAABB3(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)