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) {
76 if (!
node.bounds.intersectsAABB(
bounds))
return false;
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) {
104 if (bMin >=
node.split &&
node.right) {
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) {
188 if (!
node.isLeaf()) {
196 collect(*ensureRoot(),
nullptr,
x,
y,
z, 0.f,
false);
203 collect(*ensureRoot(), &box, 0.f, 0.f, 0.f, 0.f,
false);
210 collect(*ensureRoot(),
nullptr,
cx,
cy, cz,
radius,
true);
EVENGINE_API_FOUNDATION public API.
bool contains(int id) const
True if the id is currently stored.
bool remove(int id)
Removes an item by id; false if unknown.
void clear()
Removes all stored entries.
int queryAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Finds items overlapping an AABB; fills the result buffer.
int querySphere(float cx, float cy, float cz, float radius)
Finds items overlapping a sphere; fills the result buffer.
BSPTree3D(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int maxDepth=12, int maxPerNode=8)
Creates a 3D BSP/kd tree covering the given bounds.
int queryPoint(float x, float y, float z)
Finds items overlapping a point; fills the result buffer.
bool update(int id, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Moves an existing item to a new AABB; false if unknown or out of bounds.
bool insert(int id, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Inserts an item AABB; false if out of bounds or id already present.
void clear()
Removes all stored entries.
int getCount() const
Number of stored ids.
void addUnique(int id)
Appends id if not already present.
AABB3 makeAABB3(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Builds a normalized 3D AABB (swaps inverted mins/maxes).
Axis-aligned 3D bounding box (min/max inclusive).
float height() const
Extent along Y (max - min).
bool valid() const
True when min <= max on every axis.
float width() const
Extent along X (max - min).
float depth() const
Extent along Z (max - min).