TDME2  1.9.200
OctTreePartition.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <bitset>
4 #include <list>
5 #include <string>
6 #include <tuple>
7 #include <unordered_map>
8 #include <unordered_set>
9 #include <variant>
10 #include <vector>
11 
12 #include <tdme/tdme.h>
13 #include <tdme/engine/fwd-tdme.h>
15 #include <tdme/engine/Entity.h>
16 #include <tdme/engine/Frustum.h>
17 #include <tdme/engine/Partition.h>
18 #include <tdme/math/fwd-tdme.h>
20 #include <tdme/utilities/Console.h>
21 
22 using std::bitset;
23 using std::get;
24 using std::list;
25 using std::string;
26 using std::to_string;
27 using std::tuple;
28 using std::unordered_map;
29 using std::unordered_set;
30 using std::vector;
31 
37 
38 /**
39  * Oct tree partition implementation
40  * @author Andreas Drewke
41  */
43 {
44 private:
46  std::size_t operator()(const tuple<int32_t, int32_t, int32_t>& k) const {
47  std::hash<uint64_t> hashVal;
48  return hashVal(get<0>(k) ^ get<1>(k) ^ get<2>(k));
49  }
50  };
51 
53  {
54  // partition size
56 
57  // x, y, z position
58  int32_t x;
59  int32_t y;
60  int32_t z;
61 
62  // parent node
63  PartitionTreeNode* parent { nullptr };
64 
65  // node bounding volume
67 
68  // sub nodes of oct tree nodes
69  list<PartitionTreeNode> subNodes;
70 
71  // sub nodes of oct tree nodes by partition coordinate, only used in root node
72  unordered_map<tuple<int32_t, int32_t, int32_t>, PartitionTreeNode*, PartitionTreeNodeId_Hash> subNodesByCoordinate;
73 
74  // or finally our partition entities
75  vector<Entity*> partitionEntities;
76  };
77 
78  static constexpr float PARTITION_SIZE_MIN { 64.0f };
79  static constexpr float PARTITION_SIZE_MAX { 512.0f };
80 
81  unordered_map<Entity*, vector<PartitionTreeNode*>> entityPartitionNodes;
82  vector<Entity*> visibleEntities;
84 
85  // TODO: put those limits into tdme.h or use dynamic bitset here
86  bitset<1024 * 512> visibleEntitiesBitSet;
87  unordered_map<Entity*, int> entityUniquePartitionIdMapping;
89 
90  // overridden methods
91  void reset() override;
92  void addEntity(Entity* entity) override;
93  inline void updateEntity(Entity* entity) override {
94  addEntity(entity);
95  }
96  void removeEntity(Entity* entity) override;
97 
98  /**
99  * Update partition tree
100  * @param parent parent
101  * @param x x
102  * @param y y
103  * @param z z
104  * @param partitionSize partition size
105  * @param entity entity
106  */
107  inline void updatePartitionTree(PartitionTreeNode* parent, int32_t x, int32_t y, int32_t z, float partitionSize, Entity* entity) {
108  // key
109  tuple<int32_t, int32_t, int32_t> key = { x, y, z };
110  auto storedNodeIt = parent->subNodesByCoordinate.find(key);
111  auto storedNode = storedNodeIt != parent->subNodesByCoordinate.end()?storedNodeIt->second:nullptr;
112 
113  // check if exists
114  if (storedNode == nullptr) {
115  // if not add
116  PartitionTreeNode node;
117  node.partitionSize = partitionSize;
118  node.x = x;
119  node.y = y;
120  node.z = z;
121  node.parent = parent;
122  node.bv.getMin().set(
123  x * partitionSize,
124  y * partitionSize,
125  z * partitionSize
126  );
127  node.bv.getMax().set(
128  x * partitionSize + partitionSize,
129  y * partitionSize + partitionSize,
130  z * partitionSize + partitionSize
131  );
132  node.bv.update();
133  // register in parent sub nodes
134  parent->subNodes.push_back(node);
135  storedNode = &parent->subNodes.back();
136  // register in parent sub nodes by coordinate, if root node
137  parent->subNodesByCoordinate[key] = storedNode;
138  }
139 
140  // create sub nodes
141  if (partitionSize > PARTITION_SIZE_MIN) {
142  // frustum bounding box
143  auto boundingBox = entity->getWorldBoundingBox();
144  // create partitions if not yet done
145  auto minXPartition = static_cast<int32_t>((Math::floor(boundingBox->getMin().getX() - x * partitionSize) / (partitionSize / 2.0f)));
146  auto minYPartition = static_cast<int32_t>((Math::floor(boundingBox->getMin().getY() - y * partitionSize) / (partitionSize / 2.0f)));
147  auto minZPartition = static_cast<int32_t>((Math::floor(boundingBox->getMin().getZ() - z * partitionSize) / (partitionSize / 2.0f)));
148  auto maxXPartition = static_cast<int32_t>((Math::floor(boundingBox->getMax().getX() - x * partitionSize) / (partitionSize / 2.0f)));
149  auto maxYPartition = static_cast<int32_t>((Math::floor(boundingBox->getMax().getY() - y * partitionSize) / (partitionSize / 2.0f)));
150  auto maxZPartition = static_cast<int32_t>((Math::floor(boundingBox->getMax().getZ() - z * partitionSize) / (partitionSize / 2.0f)));
151  minXPartition = Math::clamp(minXPartition, 0, 1);
152  minYPartition = Math::clamp(minYPartition, 0, 1);
153  minZPartition = Math::clamp(minZPartition, 0, 1);
154  maxXPartition = Math::clamp(maxXPartition, 0, 1);
155  maxYPartition = Math::clamp(maxYPartition, 0, 1);
156  maxZPartition = Math::clamp(maxZPartition, 0, 1);
157  for (auto yPartition = minYPartition; yPartition <= maxYPartition; yPartition++)
158  for (auto xPartition = minXPartition; xPartition <= maxXPartition; xPartition++)
159  for (auto zPartition = minZPartition; zPartition <= maxZPartition; zPartition++) {
161  storedNode,
162  static_cast<int32_t>((x * partitionSize) / (partitionSize / 2.0f) + xPartition),
163  static_cast<int32_t>((y * partitionSize) / (partitionSize / 2.0f) + yPartition),
164  static_cast<int32_t>((z * partitionSize) / (partitionSize / 2.0f) + zPartition),
165  partitionSize / 2.0f, entity
166  );
167  }
168  } else {
169  storedNode->partitionEntities.push_back(entity);
170  entityPartitionNodes[entity].push_back(storedNode);
171  }
172  }
173 
174  /**
175  * Is partition empty
176  * @param node node
177  * @return partition empty
178  */
180  // lowest level node has objects attached?
181  if (node->partitionEntities.size() > 0) {
182  return false;
183  } else {
184  for (auto& subNode: node->subNodes) {
185  if (isPartitionNodeEmpty(&subNode) == false)
186  return false;
187 
188  }
189  return true;
190  }
191  }
192 
193  /**
194  * Remove partition node, should be empty
195  * @param node node
196  */
198  // lowest level node has objects attached?
199  if (node->partitionEntities.size() > 0) {
200  Console::println("OctTreePartition::removePartitionNode(): partition has objects attached!!!");
201  node->partitionEntities.clear();
202  } else {
203  // otherwise check top level node sub nodes
204  for (auto& subNode: node->subNodes) {
205  removePartitionNode(&subNode);
206  }
207  node->subNodesByCoordinate.clear();
208  node->subNodes.clear();
209  }
210  }
211 
212  /**
213  * Do partition tree lookup
214  * @param frustum frustum
215  * @param node node
216  * @return number of look ups
217  */
219  auto lookUps = 1;
220  // check if given cbv collides with partition node bv
221  if (frustum->isVisible(&node->bv) == false) {
222  return lookUps;
223  } else
224  // otherwise check sub nodes
225  if (node->subNodes.size() > 0) {
226  for (auto& subNode: node->subNodes) {
227  lookUps += doPartitionTreeLookUpVisibleObjects(frustum, &subNode);
228  }
229  return lookUps;
230  } else
231  // last check if this node has partition entities
232  if (node->partitionEntities.size() > 0) {
233  for (auto i = 0; i < node->partitionEntities.size(); i++) {
234  auto entity = node->partitionEntities[i];
235 
236  // look up
237  lookUps++;
238  if (frustum->isVisible(entity->getWorldBoundingBox()) == false) continue;
239 
240  //
241  auto uniquePartitionId = entity->getUniquePartitionId();
242  // lets have this only once in result
243  if (visibleEntitiesBitSet.test(uniquePartitionId) == true) {
244  continue;
245  }
246  visibleEntitiesBitSet.set(uniquePartitionId);
247 
248  // done
249  visibleEntities.push_back(entity);
250  }
251  return lookUps;
252  }
253  return lookUps;
254  }
255 
256  /**
257  * Dump node to console
258  * @param node node
259  * @param indent indent
260  */
261  void dumpNode(PartitionTreeNode* node, int indent);
262 
263  /**
264  * Find entity
265  * @param node node
266  * @param entity entity
267  */
268  void findEntity(PartitionTreeNode* node, Entity* entity);
269 
270 public:
271  // forbid class copy
273 
274  /**
275  * Public constructor
276  */
278 
279  // overridden methods
280  const vector<Entity*>& getVisibleEntities(Frustum* frustum) override;
281  inline bool isVisibleEntity(Entity* entity) override {
282  auto uniquePartitionId = entity->getUniquePartitionId();
283  return uniquePartitionId != Entity::UNIQUEPARTITIONID_NONE && visibleEntitiesBitSet.test(uniquePartitionId) == true;
284  }
285 
286  /**
287  * Dump oct tree to console
288  */
289  void dump();
290 
291 };
Engine entity.
Definition: Entity.h:30
virtual BoundingBox * getWorldBoundingBox()=0
static constexpr int UNIQUEPARTITIONID_NONE
Definition: Entity.h:57
int getUniquePartitionId()
Definition: Entity.h:72
Frustum class.
Definition: Frustum.h:29
bool isVisible(const Vector3 &vertex)
Checks if given vertex is in frustum.
Definition: Frustum.h:73
Oct tree partition implementation.
unordered_map< Entity *, vector< PartitionTreeNode * > > entityPartitionNodes
bool isPartitionNodeEmpty(PartitionTreeNode *node)
Is partition empty.
void removeEntity(Entity *entity) override
Removes a entity.
void dumpNode(PartitionTreeNode *node, int indent)
Dump node to console.
const vector< Entity * > & getVisibleEntities(Frustum *frustum) override
Get visible entities.
vector< int > freeEntityUniquePartitionIds
unordered_map< Entity *, int > entityUniquePartitionIdMapping
void removePartitionNode(PartitionTreeNode *node)
Remove partition node, should be empty.
void updateEntity(Entity *entity) override
Updates a entity.
static constexpr float PARTITION_SIZE_MIN
void addEntity(Entity *entity) override
Adds a entity.
void dump()
Dump oct tree to console.
bitset< 1024 *512 > visibleEntitiesBitSet
void updatePartitionTree(PartitionTreeNode *parent, int32_t x, int32_t y, int32_t z, float partitionSize, Entity *entity)
Update partition tree.
int32_t doPartitionTreeLookUpVisibleObjects(Frustum *frustum, PartitionTreeNode *node)
Do partition tree lookup.
void findEntity(PartitionTreeNode *node, Entity *entity)
Find entity.
static constexpr float PARTITION_SIZE_MAX
bool isVisibleEntity(Entity *entity) override
Check if entity is visible.
OctTreePartition()
Public constructor.
vector< Entity * > visibleEntities
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
void update()
Updates this bounding box.
Vector3 & set(float x, float y, float z)
Sets this vector3 by its components.
Definition: Vector3.h:70
Console class.
Definition: Console.h:29
std::size_t operator()(const tuple< int32_t, int32_t, int32_t > &k) const
unordered_map< tuple< int32_t, int32_t, int32_t >, PartitionTreeNode *, PartitionTreeNodeId_Hash > subNodesByCoordinate
Partition interface.
Definition: Partition.h:18
#define FORBID_CLASS_COPY(CLASS)
Definition: tdme.h:6