Cogs.Core
PotreeSubtree.cpp
1#include "Engine.h"
2
3#include "PotreeSystem.h"
4
5#include "Foundation/Logging/Logger.h"
6#include "Foundation/Platform/IO.h"
7
8namespace {
9 using namespace Cogs::Core;
11
12 PotreeCell* createCell(PotreeData* poData, PotreeSubtree* subtree)
13 {
14 auto * cell = poData->cellStore.create();
15 cell->owningSubtree = subtree;
16 subtree->cells.push(cell);
17 return cell;
18 }
19
20 void parseHrcFile1(PotreeData* poData, PotreeSubtree* subtree, uint32_t instanceId, uint32_t subtreeId, const std::string& url, Cogs::FileContents* data)
21 {
22 size_t n = data->size / 5; // number of regular nodes in .hrc file.
23
24 const glm::vec3& tbMinCenter = poData->octtreeFrame.tightBBoxMin;
25 const glm::vec3& tbMaxCenter = poData->octtreeFrame.tightBBoxMax;
26 const glm::vec3& shift = poData->octtreeFrame.toBBoxShift;
27 const glm::vec3& scale = poData->octtreeFrame.fullBBoxSize;
28
29 List<PotreeCell, offsetof(PotreeCell, tmpNode)> curr;
30 List<PotreeCell, offsetof(PotreeCell, tmpNode)> next;
31
32 auto* root = createCell(poData, subtree);
33 curr.push(root);
34 if (subtree->containingCell == nullptr) {
35 assert(poData->root == nullptr);
36 poData->root = root;
37 }
38 root->i = 0;
39 root->j = 0;
40 root->k = 0;
41 root->l = 0;
42 root->scaleFactor = subtree->containingCell ? subtree->containingCell->scaleFactor : 1;
43
44 for (size_t i = 0; i < n; i++) {
45
46 // We're done with a layer, start on next.
47 if (curr.empty()) {
48 curr.swap(next);
49 }
50 auto* cell = curr.shift();
51 //cell->owningSubtree = subtree;
52
53 // Calc temporarily bounding box, first by calculating extent of
54 // octree cell, and then clamping it to the tight bounding box
55 // of overall model.
56 // FIXME: Do this with proper rounding.
57
58 auto cellSizeAtLevel = std::exp2f(-float(subtree->l + cell->l));
59 auto pos = cellSizeAtLevel * glm::vec3(float((subtree->i << cell->l) + cell->i),
60 float((subtree->j << cell->l) + cell->j),
61 float((subtree->k << cell->l) + cell->k));
62
63 cell->tbmin = glm::max(tbMinCenter, shift + scale * pos);
64 cell->tbmax = glm::min(tbMaxCenter, shift + scale * (pos + glm::vec3(cellSizeAtLevel)));
65
66 cell->hierarchyNumPoints = ((data->ptr[5 * i + 1] << 0) |
67 (data->ptr[5 * i + 2] << 8) |
68 (data->ptr[5 * i + 3] << 16) |
69 (data->ptr[5 * i + 4] << 24));
70
71 if (cell->l < poData->hierarchyStepSize) {
72
73 // Cell is a regular cell. Set up links for children.
74
75 cell->kind = PotreeCell::Kind::RegularNode;
76
77 // Generate child cells.
78 auto mask = data->ptr[5 * i + 0];
79 for (unsigned b = 0; b < 8; b++) {
80 cell->children[b] = nullptr;
81 if ((mask >> b) & 1) {
82 auto* child = createCell(poData, subtree);
83 next.push(child);
84
85 child->i = (cell->i << 1) | ((b >> 2) & 1);
86 child->j = (cell->j << 1) | ((b >> 1) & 1);
87 child->k = (cell->k << 1) | ((b >> 0) & 1);
88 child->l = cell->l + 1;
89 child->prefix = cell->prefix | (b << (3 * cell->l));
90
91 cell->children[b] = child;
92 }
93 }
94 }
95 else if (cell->l == poData->hierarchyStepSize) {
96 cell->kind = PotreeCell::Kind::SubtreeLink;
97 cell->linkedSubtree = nullptr;
98 }
99 else {
100 LOG_ERROR(logger, "[instance=%u,tree=%u] Error in hierarchy file: %s", instanceId, subtreeId, url.c_str());
101 poData->state = PotreeState::Error;
102 subtree->state = PotreeSubtree::State::Failed;
103 if (subtree->containingCell) subtree->containingCell->state = PotreeCell::State::Failed;
104 return;
105 }
106 }
107 assert(curr.empty());
108 assert(next.empty());
109 }
110
111 uint32_t getUint32LE(const uint8_t* ptr)
112 {
113 uint32_t rv = ptr[0];
114 for (size_t i = 1; i < 4; i++) { rv = rv | (static_cast<uint32_t>(ptr[i]) << (8 * i)); }
115 return rv;
116 }
117
118 uint64_t getUint64LE(const uint8_t* ptr)
119 {
120 uint64_t rv = ptr[0];
121 for (size_t i = 1; i < 8; i++) { rv = rv | (static_cast<uint64_t>(ptr[i]) << (8 * i)); }
122 return rv;
123 }
124
125 void parseHrcFile2(PotreeData* poData, PotreeSubtree* subtree, uint32_t instanceId, uint32_t subtreeId, const std::string& url, Cogs::FileContents* data)
126 {
127 size_t bytesPerNode = 22;
128 size_t numNodes = data->size / bytesPerNode;
129 if (numNodes * bytesPerNode != data->size) {
130 LOG_ERROR(logger, "[instance=%u,tree=%u] Hierarchy file part size (%zu) not a multiple of node size (22): %s",
131 instanceId, subtreeId, data->size, url.c_str());
132 poData->state = PotreeState::Error;
133 subtree->state = PotreeSubtree::State::Failed;
134 if (subtree->containingCell) subtree->containingCell->state = PotreeCell::State::Failed;
135 return;
136 }
137
138 const glm::vec3& tbMinCenter = poData->octtreeFrame.tightBBoxMin;
139 const glm::vec3& tbMaxCenter = poData->octtreeFrame.tightBBoxMax;
140 const glm::vec3& shift = poData->octtreeFrame.toBBoxShift;
141 const glm::vec3& scale = poData->octtreeFrame.fullBBoxSize;
142
143 std::vector<PotreeCell*> cells(numNodes);
144 for (auto& ptr : cells) { ptr = createCell(poData, subtree); }
145 if (subtree->containingCell == nullptr) {
146 assert(poData->root == nullptr);
147 poData->root = cells[0];
148 }
149 cells[0]->i = 0;
150 cells[0]->j = 0;
151 cells[0]->k = 0;
152 cells[0]->l = 0;
153 cells[0]->scaleFactor = subtree->containingCell ? subtree->containingCell->scaleFactor : 1;
154
155 unsigned childOffset = 1;
156 for (size_t i = 0; i < numNodes; i++) {
157 const uint8_t* ptr = data->ptr + bytesPerNode * i;
158 uint8_t nodeType = ptr[0];
159 uint8_t childMask = ptr[1];
160 uint32_t numPoints = getUint32LE(ptr + 2);
161 uint64_t byteOffset = getUint64LE(ptr + 6);
162 uint64_t byteCount = getUint64LE(ptr + 14);
163
164 PotreeCell* current = cells[i];
165 auto cellSizeAtLevel = std::exp2f(-float(subtree->l + current->l));
166 auto pos = cellSizeAtLevel * glm::vec3(float((subtree->i << current->l) + current->i),
167 float((subtree->j << current->l) + current->j),
168 float((subtree->k << current->l) + current->k));
169
170 current->hierarchyNumPoints = byteCount ? numPoints : 0; // https://github.com/potree/potree/issues/1125
171 current->byteOffset = byteOffset;
172 current->byteCount = byteCount;
173 current->tbmin = glm::max(tbMinCenter, shift + scale * pos);
174 current->tbmax = glm::min(tbMaxCenter, shift + scale * (pos + glm::vec3(cellSizeAtLevel)));
175
176 if (nodeType == 2) {
177 // Link node
178 current->kind = PotreeCell::Kind::SubtreeLink;
179 current->linkedSubtree = nullptr;
180 continue;
181 }
182
183 for (unsigned childIndex = 0; childIndex < 8; childIndex++) {
184
185 if (((1u << childIndex) & childMask) == 0) {
186 continue; // child does not exist.
187 }
188
189 auto* child = cells[childOffset++];
190 child->i = (current->i << 1) | ((childIndex >> 2) & 1);
191 child->j = (current->j << 1) | ((childIndex >> 1) & 1);
192 child->k = (current->k << 1) | ((childIndex >> 0) & 1);
193 child->l = current->l + 1;
194 child->prefix = current->prefix | (childIndex << (3 * current->l)); // probably don't need this with V2
195
196 current->children[childIndex] = child;
197 }
198 }
199
200 }
201
202 void parseHrcFile(Context* context, uint32_t instanceId, uint32_t subtreeId, const std::string& url, Cogs::FileContents* data)
203 {
204 PotreeSystem* potreeSystem = nullptr;
205 PotreeComponent* poComp = nullptr;
206 PotreeData* poData = nullptr;
207 PotreeSubtree* subtree = nullptr;
208 bool ret = PotreeSystem::lookupAndCheckForStaleness(context, potreeSystem, poData, poComp, subtree, instanceId, subtreeId);
209 if(subtree) subtree->fetch_id = DataFetcherManager::NoFetchId;
210
211 if (!ret) {
212 if(data){
213 LOG_TRACE(logger, "[instance=%u,tree=%u] Received stale *.hrc file, ignoring: %.*s", instanceId, subtreeId, StringViewFormat(data->origin()));
214 }
215 return;
216 }
217 else {
218#if defined(COGS_PARANOIA)
219 assert(!subtree->containingCell || subtree->containingCell->state == PotreeCell::State::Issued);
220 assert(subtree->state == PotreeSubtree::State::Issued);
221#else
222 if (subtree->containingCell && subtree->containingCell->state != PotreeCell::State::Issued) {
223 LOG_FATAL(logger, "[instance=%u,tree=%u] Got hierarchy file, but containing cell is not in state Issued(=1) (state=%u), discarding",
224 instanceId, subtreeId, static_cast<unsigned>(subtree->containingCell->state));
225 return;
226 }
227 if (subtree->state != PotreeSubtree::State::Issued) {
228 LOG_FATAL(logger, "[instance=%u,tree=%u] Got hierarchy file, but subtree is not in state Issued(=1) (state=%u), discarding.",
229 instanceId, subtreeId, static_cast<unsigned>(subtree->state));
230 return;
231 }
232#endif
233
234 if (!data) {
235 LOG_ERROR(logger, "[instance=%u,tree=%u] Failed to get hierarchy file: %s", instanceId, subtreeId, url.c_str());
236 poData->state = PotreeState::Error;
237 subtree->state = PotreeSubtree::State::Failed;
238 if (subtree->containingCell) subtree->containingCell->state = PotreeCell::State::Failed;
239 }
240 else {
241 subtree->state = PotreeSubtree::State::Ready;
242 if (subtree->containingCell) subtree->containingCell->state = PotreeCell::State::Ready;
243
244 if (poData->versionMajor == 1) {
245 parseHrcFile1(poData, subtree, instanceId, subtreeId, url, data);
246 }
247 else {
248 parseHrcFile2(poData, subtree, instanceId, subtreeId, url, data);
249 }
250 }
251
252 }
253 }
254
255}
256
257void Cogs::Core::PotreeSubtree::fetchLayoutAndInitialize(Context* context, PotreeSystem* poSystem, const PotreeComponent* poComp, PotreeData* poData, PotreeCell* containingCell)
258{
259 assert(state == State::None);
260 state = State::Issued;
261
262 this->containingCell = containingCell;
263 if (containingCell == nullptr) {
264 // We are fetching the root, index is always [0,0,0,0]
265 i = 0;
266 j = 0;
267 k = 0;
268 l = 0;
269 }
270 else {
271 assert(containingCell->kind == PotreeCell::Kind::SubtreeLink);
272 assert(containingCell->state == PotreeCell::State::Issued);
273 i = (containingCell->owningSubtree->i << containingCell->l) + containingCell->i;
274 j = (containingCell->owningSubtree->j << containingCell->l) + containingCell->j;
275 k = (containingCell->owningSubtree->k << containingCell->l) + containingCell->k;
276 l = containingCell->owningSubtree->l + containingCell->l;
277 containingCell->linkedSubtree = this;
278 }
279
280 std::string url;
281 uint64_t fileOffset = 0u;
282 uint64_t fileByteSize = 0u;
283 if (poData->versionMajor == 1) {
284 // Build Potree 1.x URL
285 assert(poData->octreeDir.empty() || poData->octreeDir.back() == '/' || poData->octreeDir.back() == '\\');
286 if (containingCell == nullptr) {
287 path = "r/";
288 }
289 else {
290 std::string localPrefix;
291 containingCell->appendDescendSteps(localPrefix);
292 prefix = containingCell->owningSubtree->prefix + localPrefix;
293 path = containingCell->owningSubtree->path + localPrefix + "/";
294 }
295 url = poData->octreeDir + path + "r" + prefix + ".hrc";
296 }
297
298 else {
299 // Build Potree 2.x URL
300 assert(poData->rootPath.empty() || poData->rootPath.back() == '/' || poData->rootPath.back() == '\\');
301 if (containingCell == nullptr) {
302 fileOffset = 0;
303 fileByteSize = poData->firstChunkSize;
304 }
305 else {
306 fileOffset = containingCell->byteOffset;
307 fileByteSize = containingCell->byteCount;
308 }
309 url = poData->rootPath + "hierarchy.bin";
310 }
311
312 PotreeSystem::startFetch(context, poComp, poData);
313
314 poSystem->requestsInFlight++;
315
316 //LOG_DEBUG(logger, "Fetching subtree %s", url.c_str());
317 fetch_id = DataFetcherManager::fetchAsync(context, url, [context, url, instanceId = poData->instanceId, treeId = subtreeId](std::unique_ptr<Cogs::FileContents> data)
318 {
319 auto task = [context, url, instanceId, treeId, dataPtr = data.release()]() {
320 std::unique_ptr<Cogs::FileContents> data(dataPtr);
321 parseHrcFile(context, instanceId, treeId, url, data.get());
322
323 PotreeSystem* poSystem = nullptr;
324 PotreeComponent* poComp = nullptr;
325 PotreeData* poData = nullptr;
326 bool ret = PotreeSystem::lookupAndCheckForStaleness(context, poSystem, poData, poComp, instanceId);
327 poSystem->requestsInFlight--;
328 PotreeSystem::endFetch(context, poComp, poData);
329
330 if (!ret) {
331 if(data){
332 LOG_DEBUG(logger, "[instance=%u] Received *.hrc file, instance does not exist anymore, ignoring: %.*s", instanceId, StringViewFormat(data->origin()));
333 }
334 return;
335 }
336
337 context->engine->setDirty();
338 };
339 context->engine->runTaskInMainThread(std::move(task));
340 }, fileOffset, fileByteSize, true, Cogs::FileContentsHints::None);
341}
342
343void Cogs::Core::PotreeSubtree::release(Context* context, PotreeData* poData)
344{
345 while (!cells.empty()) {
346 auto* cell = cells.pop();
347 cell->releaseData(context, poData); // FIXME: shouldn't happen.
348 assert(cell->state == PotreeCell::State::None);
349 assert(!HandleIsValid(cell->boxParametersBuffer));
350 assert(!HandleIsValid(cell->pointData));
351 poData->cellStore.destroy(cell);
352 }
353 if (state == PotreeSubtree::State::Issued) {
354 DataFetcherManager::cancelAsyncFetch(context, fetch_id);
355 fetch_id = DataFetcherManager::NoFetchId;
356 }
357}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Engine > engine
Engine instance.
Definition: Context.h:222
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Component for Point Cloud Display.
std::string octreeDir
1.x only: subdir with data
Definition: PotreeSystem.h:213
std::string rootPath
URL to folder containing metadata file.
Definition: PotreeSystem.h:212
static void startFetch(Context *context, const PotreeComponent *poComp, PotreeData *poData)
Update Component request count and notify client when starting data fetch.
Abstract base class storing data read from a file.
Definition: FileContents.h:20