TDME2  1.9.200
OctTreePartition.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <bitset>
5 #include <list>
6 #include <string>
7 #include <tuple>
8 #include <unordered_map>
9 #include <unordered_set>
10 #include <vector>
11 
12 #include <tdme/tdme.h>
14 #include <tdme/engine/Entity.h>
15 #include <tdme/engine/Frustum.h>
16 #include <tdme/math/Math.h>
17 #include <tdme/utilities/Console.h>
18 
19 using std::bitset;
20 using std::list;
21 using std::remove;
22 using std::string;
23 using std::to_string;
24 using std::tuple;
25 using std::unordered_map;
26 using std::unordered_set;
27 using std::vector;
28 
33 using tdme::math::Math;
35 
36 OctTreePartition::OctTreePartition()
37 {
38  reset();
39 }
40 
42 {
43  this->entityPartitionNodes.clear();
44  this->visibleEntities.clear();
45  this->treeRoot.partitionSize = -1;
46  this->treeRoot.x = -1;
47  this->treeRoot.y = -1;
48  this->treeRoot.z = -1;
49  this->treeRoot.parent = nullptr;
50  this->entityUniquePartitionIdMapping.clear();
51  this->freeEntityUniquePartitionIds.clear();
52 }
53 
55 {
56  // update if already exists
57  vector<PartitionTreeNode*>* thisEntityPartitions = nullptr;
58  auto thisEntityPartitionsIt = entityPartitionNodes.find(entity);
59  if (thisEntityPartitionsIt != entityPartitionNodes.end()) {
60  thisEntityPartitions = &thisEntityPartitionsIt->second;
61  }
62  if (thisEntityPartitions != nullptr && thisEntityPartitions->empty() == false) {
63  removeEntity(entity);
64  }
65  // do we have a entity -> unique partition id mapping
66  auto entityUniquePartitionIdMappingIt = entityUniquePartitionIdMapping.find(entity);
67  // nope
68  if (entityUniquePartitionIdMappingIt == entityUniquePartitionIdMapping.end()) {
69  // reset
70  auto uniquePartitionId = Entity::UNIQUEPARTITIONID_NONE;
71  // reuse a unique partition id from freeEntityUniquePartitionIds
72  if (freeEntityUniquePartitionIds.empty() == false) {
73  // yet
74  uniquePartitionId = freeEntityUniquePartitionIds[freeEntityUniquePartitionIds.size() - 1];
76  } else {
77  // otherwise create a id
78  uniquePartitionId = entityUniquePartitionIdMapping.size() + 1;
79  if (uniquePartitionId >= visibleEntitiesBitSet.size()) {
80  Console::println("OctTreePartition::addEntity(): too many entities: " + to_string(uniquePartitionId) + " >= " + to_string(visibleEntitiesBitSet.size()));
81  return;
82  }
83  }
84  entity->setUniquePartitionId(uniquePartitionId);
85  entityUniquePartitionIdMapping[entity] = uniquePartitionId;
86  } else {
87  entity->setUniquePartitionId(entityUniquePartitionIdMappingIt->second);
88  }
89  // frustum bounding box
90  auto boundingBox = entity->getWorldBoundingBox();
91  // find, create root nodes if not exists
92  auto minXPartition = static_cast<int32_t>(Math::floor(boundingBox->getMin().getX() / PARTITION_SIZE_MAX));
93  auto minYPartition = static_cast<int32_t>(Math::floor(boundingBox->getMin().getY() / PARTITION_SIZE_MAX));
94  auto minZPartition = static_cast<int32_t>(Math::floor(boundingBox->getMin().getZ() / PARTITION_SIZE_MAX));
95  auto maxXPartition = static_cast<int32_t>(Math::floor(boundingBox->getMax().getX() / PARTITION_SIZE_MAX));
96  auto maxYPartition = static_cast<int32_t>(Math::floor(boundingBox->getMax().getY() / PARTITION_SIZE_MAX));
97  auto maxZPartition = static_cast<int32_t>(Math::floor(boundingBox->getMax().getZ() / PARTITION_SIZE_MAX));
98  for (auto yPartition = minYPartition; yPartition <= maxYPartition; yPartition++)
99  for (auto xPartition = minXPartition; xPartition <= maxXPartition; xPartition++)
100  for (auto zPartition = minZPartition; zPartition <= maxZPartition; zPartition++) {
101  updatePartitionTree(&treeRoot, xPartition, yPartition, zPartition, PARTITION_SIZE_MAX, entity);
102  }
103 }
104 
106 {
107  // check if we have entity in oct tree
108  vector<PartitionTreeNode*>* objectPartitionsVector = nullptr;
109  auto objectPartitionsVectorIt = entityPartitionNodes.find(entity);
110  if (objectPartitionsVectorIt != entityPartitionNodes.end()) {
111  objectPartitionsVector = &objectPartitionsVectorIt->second;
112  }
113  if (objectPartitionsVector == nullptr || objectPartitionsVector->empty() == true) {
114  Console::println(
115  "OctTreePartition::removeEntity(): '" +
116  entity->getId() +
117  "' not registered"
118  );
119  return;
120  }
121  //
122  auto uniquePartitionId = entity->getUniquePartitionId();
123  if (uniquePartitionId != Entity::UNIQUEPARTITIONID_NONE) {
125  entityUniquePartitionIdMapping.erase(entity);
126  freeEntityUniquePartitionIds.push_back(uniquePartitionId);
127  }
128  // remove object from assigned partitions
129  // TODO: remove tree root sub nodes as well not only empty root nodes
130  while (objectPartitionsVector->size() > 0) {
131  auto lastIdx = objectPartitionsVector->size() - 1;
132  auto partitionTreeNode = (*objectPartitionsVector)[lastIdx];
133  auto& partitionEntities = partitionTreeNode->partitionEntities;
134  partitionEntities.erase(remove(partitionEntities.begin(), partitionEntities.end(), entity), partitionEntities.end());
135  objectPartitionsVector->erase(objectPartitionsVector->begin() + lastIdx);
136  if (partitionEntities.empty() == true) {
137  auto rootPartitionTreeNode = partitionTreeNode;
138  while (rootPartitionTreeNode->parent != nullptr) rootPartitionTreeNode = rootPartitionTreeNode->parent;
139  // check if whole top level partition is empty
140  if (isPartitionNodeEmpty(rootPartitionTreeNode) == true) {
141  // yep, remove it
142  removePartitionNode(rootPartitionTreeNode);
143  //
144  for (auto treeRootSubNodeIt = treeRoot.subNodes.begin(); treeRootSubNodeIt != treeRoot.subNodes.end(); ++treeRootSubNodeIt) {
145  if ((void*)&treeRootSubNodeIt == (void*)rootPartitionTreeNode) {
146  treeRoot.subNodes.erase(treeRootSubNodeIt);
147  break;
148  }
149  }
150  tuple<int32_t, int32_t, int32_t> key = {
151  rootPartitionTreeNode->x,
152  rootPartitionTreeNode->y,
153  rootPartitionTreeNode->z
154  };
155  treeRoot.subNodesByCoordinate.erase(key);
156  }
157  }
158  }
159  entityPartitionNodes.erase(objectPartitionsVectorIt);
160 }
161 
162 const vector<Entity*>& OctTreePartition::getVisibleEntities(Frustum* frustum)
163 {
164  visibleEntities.clear();
165  visibleEntitiesBitSet.reset();
166  auto lookUps = 0;
167  for (auto& subNode: treeRoot.subNodes) {
168  lookUps += doPartitionTreeLookUpVisibleObjects(frustum, &subNode);
169  }
170  return visibleEntities;
171 }
172 
174  for (auto i = 0; i < indent; i++) Console::print("\t");
175  Console::println(to_string(node->x) + "/" + to_string(node->y) + "/" + to_string(node->z) + ": ");
176  for (auto entity: node->partitionEntities) {
177  for (auto i = 0; i < indent + 1; i++) Console::print("\t");
178  Console::println(entity->getId());
179  }
180  for (auto& subNode: node->subNodes) dumpNode(&subNode, indent + 1);
181 }
182 
184  for (auto nodeEntity: node->partitionEntities) {
185  if (nodeEntity == entity) Console::println("OctTreePartition::findEntity(): found entity: " + entity->getId());
186  }
187  for (auto& subNode: node->subNodes) findEntity(&subNode, entity);
188 }
189 
191  dumpNode(&treeRoot, 0);
192 }
Engine entity.
Definition: Entity.h:30
virtual const string & getId()=0
void setUniquePartitionId(int uniquePartitionId)
Set unique partition id.
Definition: Entity.h:65
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
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 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
vector< Entity * > visibleEntities
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
Standard math functions.
Definition: Math.h:19
Console class.
Definition: Console.h:29
unordered_map< tuple< int32_t, int32_t, int32_t >, PartitionTreeNode *, PartitionTreeNodeId_Hash > subNodesByCoordinate