载入中...
搜索中...
未找到
BSPTree3D.cpp
浏览该文件的文档.
1#include "spatial/BSPTree3D.h"
2
3#include "common/Exception.h"
4
5namespace eve::spatial {
6
7BSPTree3D::BSPTree3D(float minX, float minY, float minZ, float maxX, float maxY, float maxZ,
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");
15 }
16 ensureRoot();
17}
18
19BSPTree3D::Node *BSPTree3D::ensureRoot() {
20 if (!root_) {
21 root_ = std::make_unique<Node>();
22 root_->bounds = rootBounds_;
23 root_->depth = 0;
24 root_->axis = 0;
25 }
26 return root_.get();
27}
28
30 items_.clear();
31 results_.clear();
32 root_.reset();
33 ensureRoot();
34}
35
36bool BSPTree3D::contains(int id) const { return items_.find(id) != items_.end(); }
37
38bool BSPTree3D::insert(int id, float minX, float minY, float minZ, float maxX, float maxY,
39 float maxZ) {
40 AABB3 b = makeAABB3(minX, minY, minZ, maxX, maxY, maxZ);
41 if (!b.valid()) return false;
42 if (contains(id)) remove(id);
43 items_[id] = b;
44 if (!insertInto(*ensureRoot(), id, b)) {
45 ensureRoot()->itemIds.push_back(id);
46 }
47 return true;
48}
49
50bool BSPTree3D::remove(int id) {
51 if (!contains(id)) return false;
52 items_.erase(id);
53 rebuild();
54 return true;
55}
56
57bool BSPTree3D::update(int id, float minX, float minY, float minZ, float maxX, float maxY,
58 float maxZ) {
59 if (!contains(id)) return false;
60 return insert(id, minX, minY, minZ, maxX, maxY, maxZ);
61}
62
63void BSPTree3D::rebuild() {
64 results_.clear();
65 root_.reset();
66 ensureRoot();
67 for (const auto &kv : items_) {
68 if (!insertInto(*root_, kv.first, kv.second)) {
69 root_->itemIds.push_back(kv.first);
70 }
71 }
72}
73
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)) {
78 return false;
79 }
80
81 if (node.isLeaf()) {
82 node.itemIds.push_back(id);
83 if (static_cast<int>(node.itemIds.size()) > maxPerNode_ && node.depth < maxDepth_) {
84 split(node);
85 }
86 return true;
87 }
88
89 float bMin = 0.f, bMax = 0.f;
90 if (node.axis == 0) {
91 bMin = bounds.minX;
92 bMax = bounds.maxX;
93 } else if (node.axis == 1) {
94 bMin = bounds.minY;
95 bMax = bounds.maxY;
96 } else {
97 bMin = bounds.minZ;
98 bMax = bounds.maxZ;
99 }
100
101 if (bMax <= node.split && node.left) {
102 return insertInto(*node.left, id, bounds);
103 }
104 if (bMin >= node.split && node.right) {
105 return insertInto(*node.right, id, bounds);
106 }
107 node.itemIds.push_back(id);
108 return true;
109}
110
111void BSPTree3D::split(Node &node) {
112 node.axis = node.depth % 3;
113 if (node.axis == 0) {
114 node.split = node.bounds.centerX();
115 } else if (node.axis == 1) {
116 node.split = node.bounds.centerY();
117 } else {
118 node.split = node.bounds.centerZ();
119 }
120
121 node.left = std::make_unique<Node>();
122 node.right = std::make_unique<Node>();
123 node.left->depth = node.depth + 1;
124 node.right->depth = node.depth + 1;
125 node.left->axis = node.left->depth % 3;
126 node.right->axis = node.right->depth % 3;
127
128 const AABB3 &b = node.bounds;
129 if (node.axis == 0) {
130 node.left->bounds = makeAABB3(b.minX, b.minY, b.minZ, node.split, b.maxY, b.maxZ);
131 node.right->bounds = makeAABB3(node.split, b.minY, b.minZ, b.maxX, b.maxY, b.maxZ);
132 } else if (node.axis == 1) {
133 node.left->bounds = makeAABB3(b.minX, b.minY, b.minZ, b.maxX, node.split, b.maxZ);
134 node.right->bounds = makeAABB3(b.minX, node.split, b.minZ, b.maxX, b.maxY, b.maxZ);
135 } else {
136 node.left->bounds = makeAABB3(b.minX, b.minY, b.minZ, b.maxX, b.maxY, node.split);
137 node.right->bounds = makeAABB3(b.minX, b.minY, node.split, b.maxX, b.maxY, b.maxZ);
138 }
139
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) {
148 iMin = ib.minX;
149 iMax = ib.maxX;
150 } else if (node.axis == 1) {
151 iMin = ib.minY;
152 iMax = ib.maxY;
153 } else {
154 iMin = ib.minZ;
155 iMax = ib.maxZ;
156 }
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);
161 } else {
162 remain.push_back(id);
163 }
164 }
165 node.itemIds.swap(remain);
166}
167
168void BSPTree3D::collect(Node &node, const AABB3 *box, float cx, float cy, float cz, float radius,
169 bool useSphere) {
170 if (box && !node.bounds.intersectsAABB(*box)) return;
171 if (useSphere && !node.bounds.intersectsSphere(cx, cy, cz, radius)) return;
172
173 for (int id : node.itemIds) {
174 auto it = items_.find(id);
175 if (it == items_.end()) continue;
176 const AABB3 &b = it->second;
177 bool hit = false;
178 if (box) {
179 hit = b.intersectsAABB(*box);
180 } else if (useSphere) {
181 hit = b.intersectsSphere(cx, cy, cz, radius);
182 } else {
183 hit = b.containsPoint(cx, cy, cz);
184 }
185 if (hit) results_.addUnique(id);
186 }
187
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);
191 }
192}
193
194int BSPTree3D::queryPoint(float x, float y, float z) {
195 results_.clear();
196 collect(*ensureRoot(), nullptr, x, y, z, 0.f, false);
197 return results_.getCount();
198}
199
200int BSPTree3D::queryAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
201 results_.clear();
202 AABB3 box = makeAABB3(minX, minY, minZ, maxX, maxY, maxZ);
203 collect(*ensureRoot(), &box, 0.f, 0.f, 0.f, 0.f, false);
204 return results_.getCount();
205}
206
207int BSPTree3D::querySphere(float cx, float cy, float cz, float radius) {
208 results_.clear();
209 if (radius < 0.f) radius = 0.f;
210 collect(*ensureRoot(), nullptr, cx, cy, cz, radius, true);
211 return results_.getCount();
212}
213
214} // namespace eve::spatial
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
bool split
Definition CaveMesh.cpp:123
MeleePoint3 b
Definition MeleeHit.cpp:41
float radius
std::string id
Definition PlayHost.cpp:108
bool hit
const RoadNode * node
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
bool contains(int id) const
True if the id is currently stored.
Definition BSPTree3D.cpp:36
bool remove(int id)
Removes an item by id; false if unknown.
Definition BSPTree3D.cpp:50
void clear()
Removes all stored entries.
Definition BSPTree3D.cpp:29
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.
Definition BSPTree3D.cpp:7
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.
Definition BSPTree3D.cpp:57
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.
Definition BSPTree3D.cpp:38
void clear()
Removes all stored entries.
Definition QueryIds.h:14
int getCount() const
Number of stored ids.
Definition QueryIds.h:28
void addUnique(int id)
Appends id if not already present.
Definition QueryIds.h:17
AABB3 makeAABB3(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Builds a normalized 3D AABB (swaps inverted mins/maxes).
Definition Bounds.h:115
Axis-aligned 3D bounding box (min/max inclusive).
Definition Bounds.h:54
float height() const
Extent along Y (max - min).
Definition Bounds.h:65
bool valid() const
True when min <= max on every axis.
Definition Bounds.h:76
float width() const
Extent along X (max - min).
Definition Bounds.h:63
float depth() const
Extent along Z (max - min).
Definition Bounds.h:67
glm::vec4 bounds