载入中...
搜索中...
未找到
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 cx
Definition CardTypes.cpp:31
float cy
Definition CardTypes.cpp:32
std::string id
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
uint32_t b
bool contains(int id) const
Definition BSPTree3D.cpp:36
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)
Definition BSPTree3D.cpp:7
int queryPoint(float x, float y, float z)
bool update(int id, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Definition BSPTree3D.cpp:57
bool insert(int id, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Definition BSPTree3D.cpp:38
int getCount() const
Definition QueryIds.h:24
void addUnique(int id)
Definition QueryIds.h:15
NodeDesc node(std::string id, std::vector< NodeDesc > children, std::string name)
Definition NodeDesc.cpp:214
AABB3 makeAABB3(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Definition Bounds.h:91
float height() const
Definition Bounds.h:52
bool valid() const
Definition Bounds.h:58
float width() const
Definition Bounds.h:51
float depth() const
Definition Bounds.h:53