载入中...
搜索中...
未找到
Octree.cpp
浏览该文件的文档.
1#include "spatial/Octree.h"
2
3#include "common/Exception.h"
4
5namespace eve::spatial {
6
7Octree::Octree(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("Octree: bounds must have positive volume");
15 }
16 ensureRoot();
17}
18
19Octree::Node *Octree::ensureRoot() {
20 if (!root_) {
21 root_ = std::make_unique<Node>();
22 root_->bounds = rootBounds_;
23 root_->depth = 0;
24 }
25 return root_.get();
26}
27
29 items_.clear();
30 results_.clear();
31 root_.reset();
32 ensureRoot();
33}
34
35bool Octree::contains(int id) const { return items_.find(id) != items_.end(); }
36
37bool Octree::insert(int id, float minX, float minY, float minZ, float maxX, float maxY,
38 float maxZ) {
39 AABB3 b = makeAABB3(minX, minY, minZ, maxX, maxY, maxZ);
40 if (!b.valid()) return false;
41 if (contains(id)) remove(id);
42 items_[id] = b;
43 if (!insertInto(*ensureRoot(), id, b)) {
44 ensureRoot()->itemIds.push_back(id);
45 }
46 return true;
47}
48
49bool Octree::remove(int id) {
50 if (!contains(id)) return false;
51 items_.erase(id);
52 rebuild();
53 return true;
54}
55
56bool Octree::update(int id, float minX, float minY, float minZ, float maxX, float maxY,
57 float maxZ) {
58 if (!contains(id)) return false;
59 return insert(id, minX, minY, minZ, maxX, maxY, maxZ);
60}
61
62void Octree::rebuild() {
63 results_.clear();
64 root_.reset();
65 ensureRoot();
66 for (const auto &kv : items_) {
67 if (!insertInto(*root_, kv.first, kv.second)) {
68 root_->itemIds.push_back(kv.first);
69 }
70 }
71}
72
73bool Octree::insertInto(Node &node, int id, const AABB3 &bounds) {
74 if (!node.bounds.containsAABB(bounds) && node.depth == 0) {
75 if (!node.bounds.intersectsAABB(bounds)) return false;
76 } else if (node.depth > 0 && !node.bounds.containsAABB(bounds)) {
77 return false;
78 }
79
80 if (node.isLeaf()) {
81 node.itemIds.push_back(id);
82 if (static_cast<int>(node.itemIds.size()) > maxPerNode_ && node.depth < maxDepth_) {
83 split(node);
84 }
85 return true;
86 }
87
88 for (int i = 0; i < 8; ++i) {
89 if (node.children[i] && node.children[i]->bounds.containsAABB(bounds)) {
90 return insertInto(*node.children[i], id, bounds);
91 }
92 }
93 node.itemIds.push_back(id);
94 return true;
95}
96
97void Octree::split(Node &node) {
98 const float mx = node.bounds.centerX();
99 const float my = node.bounds.centerY();
100 const float mz = node.bounds.centerZ();
101 const float x0 = node.bounds.minX, x1 = node.bounds.maxX;
102 const float y0 = node.bounds.minY, y1 = node.bounds.maxY;
103 const float z0 = node.bounds.minZ, z1 = node.bounds.maxZ;
104 const AABB3 octs[8] = {
105 makeAABB3(x0, y0, z0, mx, my, mz), makeAABB3(mx, y0, z0, x1, my, mz),
106 makeAABB3(x0, my, z0, mx, y1, mz), makeAABB3(mx, my, z0, x1, y1, mz),
107 makeAABB3(x0, y0, mz, mx, my, z1), makeAABB3(mx, y0, mz, x1, my, z1),
108 makeAABB3(x0, my, mz, mx, y1, z1), makeAABB3(mx, my, mz, x1, y1, z1),
109 };
110 for (int i = 0; i < 8; ++i) {
111 node.children[i] = std::make_unique<Node>();
112 node.children[i]->bounds = octs[i];
113 node.children[i]->depth = node.depth + 1;
114 }
115
116 std::vector<int> remain;
117 remain.reserve(node.itemIds.size());
118 for (int id : node.itemIds) {
119 auto it = items_.find(id);
120 if (it == items_.end()) continue;
121 bool placed = false;
122 for (int i = 0; i < 8; ++i) {
123 if (node.children[i]->bounds.containsAABB(it->second)) {
124 node.children[i]->itemIds.push_back(id);
125 placed = true;
126 break;
127 }
128 }
129 if (!placed) remain.push_back(id);
130 }
131 node.itemIds.swap(remain);
132}
133
134void Octree::collect(Node &node, const AABB3 *box, float cx, float cy, float cz, float radius,
135 bool useSphere) {
136 if (box && !node.bounds.intersectsAABB(*box)) return;
137 if (useSphere && !node.bounds.intersectsSphere(cx, cy, cz, radius)) return;
138
139 for (int id : node.itemIds) {
140 auto it = items_.find(id);
141 if (it == items_.end()) continue;
142 const AABB3 &b = it->second;
143 bool hit = false;
144 if (box) {
145 hit = b.intersectsAABB(*box);
146 } else if (useSphere) {
147 hit = b.intersectsSphere(cx, cy, cz, radius);
148 } else {
149 hit = b.containsPoint(cx, cy, cz);
150 }
151 if (hit) results_.addUnique(id);
152 }
153
154 if (!node.isLeaf()) {
155 for (int i = 0; i < 8; ++i) {
156 if (node.children[i]) collect(*node.children[i], box, cx, cy, cz, radius, useSphere);
157 }
158 }
159}
160
161int Octree::queryPoint(float x, float y, float z) {
162 results_.clear();
163 collect(*ensureRoot(), nullptr, x, y, z, 0.f, false);
164 return results_.getCount();
165}
166
167int Octree::queryAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
168 results_.clear();
169 AABB3 box = makeAABB3(minX, minY, minZ, maxX, maxY, maxZ);
170 collect(*ensureRoot(), &box, 0.f, 0.f, 0.f, 0.f, false);
171 return results_.getCount();
172}
173
174int Octree::querySphere(float cx, float cy, float cz, float radius) {
175 results_.clear();
176 if (radius < 0.f) radius = 0.f;
177 collect(*ensureRoot(), nullptr, cx, cy, cz, radius, true);
178 return results_.getCount();
179}
180
181} // 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
bool placed
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
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 Octree.cpp:37
int queryPoint(float x, float y, float z)
Finds items overlapping a point; fills the result buffer.
Definition Octree.cpp:161
int queryAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Finds items overlapping an AABB; fills the result buffer.
Definition Octree.cpp:167
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 Octree.cpp:56
int querySphere(float cx, float cy, float cz, float radius)
Finds items overlapping a sphere; fills the result buffer.
Definition Octree.cpp:174
bool contains(int id) const
True if the id is currently stored.
Definition Octree.cpp:35
void clear()
Removes all stored entries.
Definition Octree.cpp:28
Octree(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int maxDepth=8, int maxPerNode=8)
Creates an octree covering the given 3D bounds.
Definition Octree.cpp:7
bool remove(int id)
Removes an item by id; false if unknown.
Definition Octree.cpp:49
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