Cogs.Core
PotreeCell.cpp
1#include "PotreeSystem.h"
2
3#include "Context.h"
4#include "Engine.h"
5
6#include "Rendering/IGraphicsDevice.h"
7#include "Rendering/IBuffers.h"
8
9#include "Foundation/Logging/Logger.h"
10#include "Foundation/Platform/IO.h"
11
12namespace {
13 using namespace Cogs::Core;
15
16}
17
18void Cogs::Core::PotreeCell::appendDescendSteps(std::string& path) const
19{
20 auto steps = prefix;
21 for (unsigned i = 0; i < l; i++) {
22 path.push_back('0' + (steps & 0x7));
23 steps = steps >> 3;
24 }
25}
26
27void Cogs::Core::PotreeCell::fetchData(Context* context, PotreeSystem* poSystem, const PotreeComponent* poComp, PotreeData* poData)
28{
29 // Change state
30 assert(state == PotreeCell::State::None);
31 state = PotreeCell::State::Issued;
32
33 std::string url;
34 uint64_t fileOffset = 0u;
35 uint64_t fileByteSize = 0u;
36 if (poData->versionMajor == 1) {
37 url = poData->octreeDir;
38 url.append(owningSubtree->path);
39 url.push_back('r');
40 url.append(owningSubtree->prefix);
41 appendDescendSteps(url);
42 url.append(poData->suffix);
43 }
44 else {
45 url = poData->rootPath + "octree.bin";
46 fileOffset = byteOffset;
47 fileByteSize = byteCount;
48 }
49
50 // Either decompress during transfer or handle this when receiving result.
51 Cogs::FileContentsHints hints = Cogs::FileContentsHints::None;
52 switch (poData->encoding)
53 {
54 case PotreeEnconding::ZStd:
56 break;
57 case PotreeEnconding::Brotli:
59 break;
60 default:
61 break;
62 }
63
64 // Issue fetch
65 PotreeSystem::startFetch(context, poComp, poData);
66
67 PotreeDecodeInData decodeArgs{
68 .layoutInfo = PotreeSystem::vertexLayoutInfo[static_cast<size_t>(poData->layout)],
69 .attributes = poData->attributes,
70 .cellPos = glm::uvec4((owningSubtree->i << this->l) + this->i,
71 (owningSubtree->j << this->l) + this->j,
72 (owningSubtree->k << this->l) + this->k,
73 owningSubtree->l + this->l),
74 .posScale = poData->positionDecode.scale,
75 .posShift = poData->positionDecode.shift,
76 .encoding = poData->encoding,
77 .occGridDim = poData->occupancyGridDim,
78 .fullBBoxMin = poData->octtreeFrame.fullBBoxMin,
79 .fullBBoxSize = poData->octtreeFrame.fullBBoxSize
80 };
81 if (poData->versionMajor == 1) {
82 // Points in data file has the minimum corner of the oct-tree cell as origin
83 //auto shift = poData->active.bbmin - poData->active.tbCenter;
84 const glm::vec3& bbsize = poData->octtreeFrame.fullBBoxSize;
85 float cellSizeAtLevel = std::exp2f(-float(decodeArgs.cellPos.w));
86 glm::vec3 octCellMinCorner = /*shift +*/ bbsize * cellSizeAtLevel * glm::vec3(decodeArgs.cellPos);
87 decodeArgs.posShift += octCellMinCorner;
88 }
89
90
91 poSystem->requestsInFlight++;
92 fetch_id = DataFetcherManager::fetchAsync(context, url, [context, url, decodeArgs, cell = this, instanceId = poData->instanceId, treeId = owningSubtree->subtreeId](std::unique_ptr<Cogs::FileContents>&& data)
93 {
94 // This might run in a resource worker thread.
95 std::unique_ptr<PotreeDecodeOutData> decoded = std::make_unique<PotreeDecodeOutData>();
96
97 if (!data) {
98 decoded->state = PotreeDecodeOutData::State::NoData;
99 }
100 else {
101 decodePotreeBin(*decoded, decodeArgs, *data.get());
102 }
103
104 auto task = [context, decodedPtr = decoded.release(), url, cell, instanceId, treeId]()
105 {
106 // This should run in the main thread.
107 std::unique_ptr<PotreeDecodeOutData> decoded(decodedPtr);
108 PotreeSystem* poSystem = nullptr;
109 PotreeComponent* poComp = nullptr;
110 PotreeData* poData = nullptr;
111 PotreeSubtree* subtree = nullptr;
112 bool ret = PotreeSystem::lookupAndCheckForStaleness(context, poSystem, poData, poComp, subtree, instanceId, treeId);
113 poSystem->requestsInFlight--;
114 if(subtree) cell->fetch_id = DataFetcherManager::NoFetchId;
115 PotreeSystem::endFetch(context, poComp, poData);
116
117 if (!ret) {
118 return;
119 }
120 else if (cell->state != PotreeCell::State::Issued) {
121 LOG_TRACE(logger, "[instance=%u,tree=%u] Got data for cell, but cell has been deactivated (state=%u), discarding data: %s", instanceId, treeId, static_cast<uint32_t>(cell->state), url.c_str());
122 }
123 else if (decoded->state == PotreeDecodeOutData::State::NoData) {
124 LOG_ERROR(logger, "[instance=%u,tree=%u] Failed to get .bin-file: %s", instanceId, treeId, url.c_str());
125 cell->state = PotreeCell::State::Failed;
126 }
127 else if (decoded->state != PotreeDecodeOutData::State::Success) {
128 LOG_ERROR(logger, "[instance=%u,tree=%u] Failed to parse .bin-file: %s", instanceId, treeId, url.c_str());
129 cell->state = PotreeCell::State::Failed;
130 }
131 else {
132
133 // Check if number of points is as expected
134 switch (poData->encoding) {
135 case PotreeEnconding::Default:
136 case PotreeEnconding::ZStd:
137 if (decoded->pointCount != cell->hierarchyNumPoints) {
138 // This happens a lot for potree 1.x data
139 // LOG_WARNING(logger, "Mismatch between number of points in hierarchy (%u) and number of points in data (%zu)", cell->hierarchyNumPoints, pointCount);
140 }
141 break;
142
143 case PotreeEnconding::Brotli:
144 if (decoded->pointCount != cell->hierarchyNumPoints) {
145 LOG_WARNING(logger, "Mismatch between number of points in hierarchy (%u) and number of points in data (%zu)", cell->hierarchyNumPoints, decoded->pointCount);
146 }
147 break;
148 default:
149 assert(false && "Invalid encoding enum value");
150 }
151
152 if (decoded->lo.x <= decoded->hi.x) {
153 cell->tbmin = decoded->lo;
154 cell->tbmax = decoded->hi;
155 }
156
157 if (poData->supportPicking) {
158 cell->points.swap(decoded->points);
159 }
160
161 cell->pointCount = static_cast<uint32_t>(decoded->pointCount);
162 if (cell->pointCount) {
163 Cogs::IBuffers* buffers = context->device->getBuffers();
164 cell->pointData = buffers->loadVertexBuffer(decoded->vertexData.data(), cell->pointCount, poData->streamsLayout.vertexFormats[0]);
165 }
166
167 // Value of scale-factor will probably be something more clever in the future.
168#if 0
169 // Density-based scale is calculated in parseBin
170 float scale = 255.f * std::pow(0.65f, static_cast<float>(cell->owningSubtree->l + cell->l));
171 scale = scale <= 255.f ? scale : 255.f;
172 scale = 1.f <= scale ? scale : 1.f;
173 cell->scaleFactor = static_cast<uint8_t>(scale);
174#endif
175
176 // Update cell scale factor
177 float density = static_cast<float>(decoded->pointCount) / static_cast<float>(decoded->nonEmptyCells);
178 float offset = 10.f * (std::log2(density) / 2.f + 8.5f);
179 cell->scaleFactor = static_cast<uint8_t>(glm::clamp(offset, 0.f, 255.f));
180
181 for (auto* child : cell->children) {
182 if (child && child->state != PotreeCell::State::Ready) {
183 // Forward parent's scale factor to child until child has calculated its own factor.
184 child->scaleFactor = cell->scaleFactor;
185 }
186 }
187
188 cell->state = PotreeCell::State::Ready;
189 }
190
191 context->engine->setDirty();
192 };
193
194 context->engine->runTaskInMainThread(std::move(task));
195
196 }, fileOffset, fileByteSize, true, hints);
197}
198
199
200void Cogs::Core::PotreeCell::releaseData(Context* context, PotreeData* /*poData*/)
201{
202 PotreeCell::State old_state = state;
203 state = PotreeCell::State::None;
204 if (old_state == PotreeCell::State::Issued) {
205 LOG_TRACE(logger, "Deactivating cell with data still in transit.");
206 DataFetcherManager::cancelAsyncFetch(context, fetch_id);
207 fetch_id = DataFetcherManager::NoFetchId;
208 }
209 if (HandleIsValid(boxParametersBuffer)) {
210 auto* buffers = context->device->getBuffers();
211 buffers->releaseBuffer(boxParametersBuffer);
212 boxParametersBuffer = Cogs::BufferHandle::NoHandle;
213 }
214 if (HandleIsValid(pointData)) {
215 auto* buffers = context->device->getBuffers();
216 buffers->releaseVertexBuffer(pointData);
218 }
219}
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
FileContentsHints
Definition: FileContents.h:11
@ BrotliDecompress
A hint that the contents are Brotli (Google) compressed and is allowed to be decompressed during tran...
@ ZStdDecompress
A hint that the contents are Zstandard (Facebook) compressed and is allowed to be decompressed during...
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.
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:77