Cogs.Core
ParseBin.cpp
1#include "Context.h"
2#include "PotreeSystem.h"
3#include "Foundation/Logging/Logger.h"
4#include "Foundation/Platform/IO.h"
5
6namespace {
8 using namespace Cogs::Core;
9
10#if 0
11 float getFloat32(const uint8_t* ptr) // Handle un-aligned accesses
12 {
13 union {
14 uint32_t u;
15 float f;
16 } t;
17 t.u = uint32_t(ptr[0]) | (uint32_t(ptr[1]) << 8) | (uint32_t(ptr[2]) << 16) | (uint32_t(ptr[3]) << 24);
18 return t.f;
19 }
20#endif
21
22 uint32_t getUint32(const uint8_t* ptr) // Handle un-aligned accesses
23 {
24 return int32_t(ptr[0]) | (uint32_t(ptr[1]) << 8) | (uint32_t(ptr[2]) << 16) | (uint32_t(ptr[3]) << 24);
25 }
26
27 uint16_t getUint16(const uint8_t* ptr) // Handle un-aligned accesses
28 {
29 return uint16_t(ptr[0]) | (uint16_t(ptr[1]) << 8);
30 }
31
32 uint32_t unMorton3(uint32_t v)
33 {
34 // ---6-----4-----2-----0
35 uint32_t t0 = 0b0001000001000001000001 & v;
36 // 7-----5-----3-----1---
37 uint32_t t1 = 0b1000001000001000001000 & v;
38 // --76----54----32----10
39 uint32_t t2 = t0 | (t1 >> 2);
40 // --------54----------10
41 uint32_t t3 = 0b0000000011000000000011 & t2;
42 // --76----------32------
43 uint32_t t4 = 0b0011000000000011000000 & t2;
44 // ------7654--------3210
45 uint32_t t5 = t3 | (t4 >> 4);
46 // ------------------3210
47 uint32_t t6 = 0b0000000000000000001111 & t5;
48 // ------7654------------
49 uint32_t t7 = 0b0000001111000000000000 & t5;
50 // ------------------3210
51 // --------------7654----
52 uint32_t t8 = t6 | (t7 >> 8u);
53 return t8;
54 }
55
56 void postProcessPoints(PotreeDecodeOutData& out,
57 const PotreeDecodeInData& in)
58 {
59
60 // Set up scaling and bucket buffer for density estimate
61 // -----------------------------------------------------
62 float cellSizeAtLevel = std::exp2f(-float(in.cellPos.w));
63 glm::vec3 occupancyShift = (in.fullBBoxMin +
64 in.fullBBoxSize * cellSizeAtLevel * glm::vec3(in.cellPos));
65 glm::vec3 occupancyScale = cellSizeAtLevel * in.fullBBoxSize;
66 glm::vec3 occupancyMaxIndex = glm::vec3(in.occGridDim - 1.f);
67 out.occupancyTmpData.resize(in.occGridDim * in.occGridDim * in.occGridDim);
68 std::memset(out.occupancyTmpData.data(), 0, sizeof(out.occupancyTmpData[0]) * out.occupancyTmpData.size());
69
70 out.points.resize(out.pointCount);
71
72 out.nonEmptyCells = 0;
73 out.lo = glm::vec3(std::numeric_limits<float>::max());
74 out.hi = glm::vec3(-std::numeric_limits<float>::max());
75 for (size_t i = 0; i < out.pointCount; i++) {
76 const glm::vec3 p = *reinterpret_cast<const glm::vec3*>(reinterpret_cast<const uint8_t*>(out.vertexData.data()) + i * in.layoutInfo.stride);
77 out.lo = glm::min(out.lo, p);
78 out.hi = glm::max(out.hi, p);
79
80 { // Update number of occupied cells
81 glm::vec3 bucket = glm::clamp((p - occupancyShift), glm::vec3(0.0), occupancyMaxIndex);
82 size_t bucketIndex = (static_cast<size_t>(bucket.x) +
83 in.occGridDim * static_cast<size_t>(bucket.y) +
84 in.occGridDim * in.occGridDim * static_cast<size_t>(bucket.z));
85 if (out.occupancyTmpData[bucketIndex] == 0) {
86 out.occupancyTmpData[bucketIndex] = 1;
87 out.nonEmptyCells++;
88 }
89 }
90 out.points[i] = p;
91 }
92 }
93
94 bool decodeDefault(PotreeDecodeOutData& out,
95 const PotreeDecodeInData& in,
96 const std::span<const uint8_t> src)
97 {
98 struct DataChannel { PotreeAttributes kind = PotreeAttributes::COUNT; size_t sourceOffset = 0u; };
99
100 DataChannel position;
101 DataChannel color;
102 DataChannel normal;
103 DataChannel intensity;
104 DataChannel classification;
105
106 size_t size = 0;
107 for (const PotreeAttributes& attribute : in.attributes) {
108 switch (attribute) {
109 case PotreeAttributes::POSITION_CARTESIAN:
110 position.kind = attribute;
111 position.sourceOffset = size;
112 size += 3 * sizeof(uint32_t);
113 break;
114 case PotreeAttributes::RGBA_PACKED:
115 color.kind = attribute;
116 color.sourceOffset = size;
117 size += 4 * sizeof(uint8_t);
118 break;
119 case PotreeAttributes::RGB_U8_PACKED:
120 color.kind = attribute;
121 color.sourceOffset = size;
122 size += 3 * sizeof(uint8_t);
123 break;
124 case PotreeAttributes::RGB_U16_PACKED:
125 color.kind = attribute;
126 color.sourceOffset = size;
127 size += 3 * sizeof(uint16_t);
128 break;
129 case PotreeAttributes::NORMAL:
130 normal.kind = attribute;
131 normal.sourceOffset = size;
132 size += 3 * sizeof(uint32_t);
133 break;
134 case PotreeAttributes::FILLER_1B:
135 size += 1 * sizeof(uint8_t);
136 break;
137 case PotreeAttributes::NORMAL_SPHEREMAPPED:
138 case PotreeAttributes::NORMAL_OCT16:
139 normal.kind = attribute;
140 normal.sourceOffset = size;
141 size += 2 * sizeof(uint8_t);
142 break;
143 case PotreeAttributes::INTENSITY_U16:
144 intensity.kind = attribute;
145 intensity.sourceOffset = size;
146 size += 1 * sizeof(uint16_t);
147 break;
148 case PotreeAttributes::CLASSIFICATION:
149 classification.kind = attribute;
150 classification.sourceOffset = size;
151 size += 1 * sizeof(uint8_t);
152 break;
153 default:
154 assert(false && "Invalid attribute enum value");
155 break;
156 }
157 }
158 assert(position.kind != PotreeAttributes::COUNT && "We should never get this far without a position");
159
160
161 // Check if data size is a multiple of point size
162 out.pointCount = src.size_bytes() / size;
163 if (out.pointCount * size != src.size_bytes()) {
164 LOG_ERROR(logger, "Cell data size is not a multiple of deduced point data size");
165 return false;
166 }
167
168 assert(in.layoutInfo.stride != ~0u);
169
170 out.vertexData.resize(in.layoutInfo.stride * out.pointCount, false);
171
172
173 if (position.kind == PotreeAttributes::POSITION_CARTESIAN) {
174 assert(in.layoutInfo.positionOffset != ~0u);
175 auto* sptr = src.data() + position.sourceOffset;
176 auto* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data());
177
178 for (size_t i = 0; i < out.pointCount; i++) {
179 const glm::vec3 p = in.posScale * glm::vec3(getUint32(sptr + 0),
180 getUint32(sptr + 4),
181 getUint32(sptr + 8)) + in.posShift;
182 *reinterpret_cast<glm::vec3*>(dptr) = p;
183 sptr += size;
184 dptr += in.layoutInfo.stride;
185 }
186 }
187 else { assert(position.kind == PotreeAttributes::COUNT); }
188
189
190 if (color.kind == PotreeAttributes::RGB_U8_PACKED) {
191 assert(in.layoutInfo.colorOffset != ~0u);
192 const auto* sptr = src.data() + color.sourceOffset;
193 auto* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data()) + in.layoutInfo.colorOffset;
194 for (size_t i = 0; i < out.pointCount; i++) {
195 for (size_t k = 0; k < 3; k++) {
196 dptr[k] = sptr[k];
197 }
198 dptr[3] = 0xFF;
199 sptr += size;
200 dptr += in.layoutInfo.stride;
201 }
202 }
203 else if (color.kind == PotreeAttributes::RGB_U16_PACKED) {
204 assert(in.layoutInfo.colorOffset != ~0u);
205 const auto* sptr = src.data() + color.sourceOffset;
206 auto* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data()) + in.layoutInfo.colorOffset;
207 for (size_t i = 0; i < out.pointCount; i++) {
208 for (size_t k = 0; k < 3; k++) {
209 // FIXME: Don't throw away the 8 least significant bits.
210 dptr[k] = static_cast<uint8_t>(getUint16(sptr + sizeof(uint16_t) * k) >> 8);
211 }
212 dptr[3] = 0xFF;
213 sptr += size;
214 dptr += in.layoutInfo.stride;
215 }
216 }
217 else if (color.kind == PotreeAttributes::RGBA_PACKED) {
218 assert(in.layoutInfo.colorOffset != ~0u);
219 const auto* sptr = src.data() + color.sourceOffset;
220 auto* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data()) + in.layoutInfo.colorOffset;
221 for (size_t i = 0; i < out.pointCount; i++) {
222 for (size_t k = 0; k < 4; k++) {
223 dptr[k] = sptr[k];
224 }
225 sptr += size;
226 dptr += in.layoutInfo.stride;
227 }
228 }
229 else { assert(color.kind == PotreeAttributes::COUNT); }
230
231
232 if (intensity.kind == PotreeAttributes::INTENSITY_U16) {
233 assert(in.layoutInfo.intensityOffset != ~0u);
234 const auto* sptr = src.data() + intensity.sourceOffset;
235 auto* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data()) + in.layoutInfo.intensityOffset;
236 for (size_t i = 0; i < out.pointCount; i++) {
237 *reinterpret_cast<float*>(dptr) = (1.f / 65535.f) * (static_cast<float>(getUint16(sptr)) + 0.5f);
238 sptr += size;
239 dptr += in.layoutInfo.stride;
240 }
241 }
242 else { assert(intensity.kind == PotreeAttributes::COUNT); }
243
244 return true;
245 }
246
247
248 bool decodeMorton(PotreeDecodeOutData& out,
249 const PotreeDecodeInData& in,
250 const std::span<const uint8_t> src)
251 {
252 uint8_t attributeByteSize[static_cast<size_t>(PotreeAttributes::COUNT)] = {
253 16, // POSITION_CARTESIAN
254 8, // RGBA_PACKED
255 8, // RGB_U8_PACKED
256 8, // RGB_U16_PACKED
257 3, // NORMAL
258 2, // NORMAL_SPHEREMAPPED
259 2, // NORMAL_OCT16
260 2, // INTENSITY_U16
261 1, // CLASSIFICATION
262 1, // FILLER_1B
263 };
264
265 // Calculate number of bytes per point, to figure out number of points.
266 size_t pointByteSize = 0;
267 for (auto& attribute : in.attributes) {
268 assert(static_cast<size_t>(attribute) < static_cast<size_t>(PotreeAttributes::COUNT));
269 pointByteSize += attributeByteSize[static_cast<size_t>(attribute)];
270 }
271 out.pointCount = src.size_bytes() / pointByteSize;
272 if (out.pointCount * pointByteSize != src.size_bytes()) {
273 LOG_ERROR(logger, "Calculated byte size does not match actual size.");
274 return false;
275 }
276
277 assert(in.layoutInfo.stride != ~0u);
278 out.vertexData.resize(in.layoutInfo.stride * out.pointCount, false);
279
280 const uint8_t* ptr = src.data();
281 for (const PotreeAttributes& attribute : in.attributes) {
282 const size_t byteSize = attributeByteSize[static_cast<size_t>(attribute)];
283 switch (attribute) {
284 case PotreeAttributes::POSITION_CARTESIAN: {
285 assert(in.layoutInfo.positionOffset != ~0u);
286 uint8_t* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data());
287
288 glm::vec3 lo(std::numeric_limits<float>::max());
289 glm::vec3 hi(-std::numeric_limits<float>::max());
290 for (size_t i = 0; i < out.pointCount; i++) {
291 uint32_t t0 = ptr[ 8] | (ptr[ 9] << 8) | (ptr[10] << 16);
292 uint32_t t1 = ptr[11] | (ptr[12] << 8) | (ptr[13] << 16);
293 uint32_t t2 = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
294 uint32_t t3 = ptr[3] | (ptr[4] << 8) | (ptr[5] << 16);
295 uint32_t xl = unMorton3(t0 >> 0u) | (unMorton3(t1 >> 0u) << 8);
296 uint32_t yl = unMorton3(t0 >> 1u) | (unMorton3(t1 >> 1u) << 8);
297 uint32_t zl = unMorton3(t0 >> 2u) | (unMorton3(t1 >> 2u) << 8);
298 uint32_t xh = (unMorton3(t2 >> 0u) << 16) | (unMorton3(t3 >> 0u) << 24);
299 uint32_t yh = (unMorton3(t2 >> 1u) << 16) | (unMorton3(t3 >> 1u) << 24);
300 uint32_t zh = (unMorton3(t2 >> 2u) << 16) | (unMorton3(t3 >> 2u) << 24);
301 const glm::vec3 p = in.posScale * glm::vec3((xl + xh),
302 (yl + yh),
303 (zl + zh)) + in.posShift;
304 *reinterpret_cast<glm::vec3*>(dptr) = p;
305 dptr += in.layoutInfo.stride;
306 ptr += byteSize;
307 }
308 break;
309 }
310 case PotreeAttributes::RGBA_PACKED:
311 case PotreeAttributes::RGB_U8_PACKED:
312 case PotreeAttributes::RGB_U16_PACKED: {
313 assert(in.layoutInfo.colorOffset != ~0u);
314 auto* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data()) + in.layoutInfo.colorOffset;
315 for (size_t i = 0; i < out.pointCount; i++) {
316 uint32_t t0 = ptr[0] | (ptr[1] << 8u) | (ptr[2] << 16u);
317 uint32_t t1 = ptr[3] | (ptr[4] << 8u) | (ptr[5] << 16u);
318 uint32_t r = unMorton3(t0 >> 0u) | (unMorton3(t1 >> 0u) << 8u);
319 uint32_t g = unMorton3(t0 >> 1u) | (unMorton3(t1 >> 1u) << 8u);
320 uint32_t b = unMorton3(t0 >> 2u) | (unMorton3(t1 >> 2u) << 8u);
321 dptr[0] = static_cast<uint8_t>((r <= 0xff) ? r : (r >> 8u));
322 dptr[1] = static_cast<uint8_t>((g <= 0xff) ? g : (g >> 8u));
323 dptr[2] = static_cast<uint8_t>((b <= 0xff) ? b : (b >> 8u));
324 dptr[3] = 0xFF;
325 dptr += in.layoutInfo.stride;
326 ptr += byteSize;
327 }
328 break;
329 }
330 case PotreeAttributes::NORMAL:
331 case PotreeAttributes::FILLER_1B:
332 case PotreeAttributes::NORMAL_SPHEREMAPPED:
333 case PotreeAttributes::NORMAL_OCT16:
334 ptr += out.pointCount * byteSize;
335 break;
336 case PotreeAttributes::INTENSITY_U16: {
337 assert(in.layoutInfo.intensityOffset != ~0u);
338 auto* dptr = reinterpret_cast<uint8_t*>(out.vertexData.data()) + in.layoutInfo.intensityOffset;
339 for (size_t i = 0; i < out.pointCount; i++) {
340 *reinterpret_cast<float*>(dptr) = (1.f / 65535.f)* (static_cast<float>(getUint16(ptr)) + 0.5f);
341 dptr += in.layoutInfo.stride;
342 ptr += byteSize;
343 }
344 break;
345 }
346 case PotreeAttributes::CLASSIFICATION:
347 ptr += out.pointCount * byteSize;
348 break;
349 default:
350 assert(false && "Invalid attribute enum value");
351 break;
352 }
353 }
354 assert(ptr == src.data() + src.size());
355 return true;
356 }
357
358}
359
360void Cogs::Core::decodePotreeBin(PotreeDecodeOutData& out,
361 const PotreeDecodeInData& in,
362 const Cogs::FileContents& inData)
363{
364 switch (in.encoding) {
365 case PotreeEnconding::Default:
366 if (!decodeDefault(out, in, std::span < const uint8_t > (inData.ptr, inData.size))) {
367 out.state = PotreeDecodeOutData::State::DecodingFailed;
368 return;
369 }
370 break;
371 case PotreeEnconding::ZStd:
373 LOG_ERROR(logger, "Parsing bin: ZStd-compressed data was unexpectely not decompressed during transit");
374 out.state = PotreeDecodeOutData::State::DecodingFailed;
375 return;
376 }
377 if (!decodeDefault(out, in, std::span < const uint8_t > (inData.ptr, inData.size))) {
378 out.state = PotreeDecodeOutData::State::DecodingFailed;
379 return;
380 }
381 break;
382 case PotreeEnconding::Brotli:
384 LOG_ERROR(logger, "Parsing bin: Brotli-compressed data was unexpectely not decompressed during transit");
385 out.state = PotreeDecodeOutData::State::DecodingFailed;
386 return;
387 }
388 if (!decodeMorton(out, in, std::span < const uint8_t > (inData.ptr, inData.size))) {
389 out.state = PotreeDecodeOutData::State::DecodingFailed;
390 return;
391 }
392 break;
393 default:
394 assert(false && "Invalid encoding enum value");
395 }
396
397 postProcessPoints(out, in);
398}
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ 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...
Abstract base class storing data read from a file.
Definition: FileContents.h:20
size_t size
Number of data bytes.
Definition: FileContents.h:29
const uint8_t * ptr
Start of buffer storing file data. Use.
Definition: FileContents.h:27