1#include "Serialization/JsonParser.h"
2#include "PotreeSystem.h"
3#include "PotreeRenderer.h"
5#include "Foundation/Logging/Logger.h"
6#include "Foundation/Platform/IO.h"
12 bool parseStringMemberNoLog(
const char*& value, rapidjson::Value& item,
const char* name)
14 if (
auto it = item.FindMember(name); it != item.MemberEnd() && it->value.IsString()) {
15 value = it->value.GetString();
21 bool parseUIntMemberNoLog(
unsigned& value, rapidjson::Value& item,
const char* name)
23 if (
auto it = item.FindMember(name); it != item.MemberEnd() && it->value.IsUint()) {
24 value = it->value.GetUint();
30 bool parseFloatMemberNoLog(
float& value, rapidjson::Value& item,
const char* name)
32 if (
auto it = item.FindMember(name); it != item.MemberEnd() && it->value.IsNumber()) {
33 value = it->value.GetFloat();
39 bool parseVec3NoLog(glm::vec3& value, rapidjson::Value& item,
const char* name)
41 if (
auto kt = item.FindMember(name); kt != item.MemberEnd()) {
42 if (!kt->value.IsArray()) {
return false; }
43 auto arr = kt->value.GetArray();
44 if (arr.Size() != 3) {
return false; }
45 for (
int i = 0; i < 3; i++) {
46 if (!arr[i].IsNumber()) {
return false; }
47 value[i] = arr[i].GetFloat();
54 bool parseVec3NoLog(glm::dvec3& value, rapidjson::Value& item,
const char* name)
56 if (
auto kt = item.FindMember(name); kt != item.MemberEnd()) {
57 if (!kt->value.IsArray()) {
return false; }
58 auto arr = kt->value.GetArray();
59 if (arr.Size() != 3) {
return false; }
60 for (
int i = 0; i < 3; i++) {
61 if (!arr[i].IsNumber()) {
return false; }
62 value[i] = arr[i].GetDouble();
69 bool parseBBox(
PotreeData* poData, glm::dvec3& bbmin, glm::dvec3& bbmax, Document& doc,
const std::string& name, uint32_t instanceId)
71 if (
auto jt = doc.FindMember(name.c_str()); jt != doc.MemberEnd()) {
72 if (jt->value.IsObject()) {
73 auto& parent = jt->value;
74 if (poData->versionMajor == 1) {
75 struct {
double* dst;
const char* name; }
77 {&bbmin.x,
"lx"}, {&bbmin.y,
"ly"},{&bbmin.z,
"lz"},
78 {&bbmax.x,
"ux"}, {&bbmax.y,
"uy"},{&bbmax.z,
"uz"},
80 for (
auto& component : components) {
81 if (
auto it = parent.FindMember(component.name); it != parent.MemberEnd() && it->value.IsNumber()) {
82 *component.dst = it->value.GetDouble();
85 LOG_ERROR(logger,
"[instance=%u] metadata file: %s element of %s is missing or not a numbers.", instanceId, component.name, name.c_str());
91 if (!parseVec3NoLog(bbmin, parent,
"min")) {
92 LOG_ERROR(logger,
"[instance=%u] metadata file: min element of %s is missing or not an array of three numbers.", instanceId, name.c_str());
95 if (!parseVec3NoLog(bbmax, parent,
"max")) {
96 LOG_ERROR(logger,
"[instance=%u] metadata file: min element of %s is missing or not an array of three numbers.", instanceId, name.c_str());
104 LOG_ERROR(logger,
"[instance=%u] metadata file: %s is not an object", instanceId, name.c_str());
109 LOG_ERROR(logger,
"[instance=%u] metadata file: missing %s", instanceId, name.c_str());
114 bool parsePointAttributeName(PotreeAttributes& attribute,
const char* str, uint32_t instanceId)
118 attribute = PotreeAttributes::POSITION_CARTESIAN;
121 case
Cogs::hash(
"RGBA_PACKED"):
122 case
Cogs::hash(
"COLOR_PACKED"):
123 attribute = PotreeAttributes::RGBA_PACKED;
126 attribute = PotreeAttributes::RGB_U8_PACKED;
129 case
Cogs::hash(
"NORMAL_FLOATS"):
130 attribute = PotreeAttributes::NORMAL;
133 attribute = PotreeAttributes::NORMAL_SPHEREMAPPED;
136 attribute = PotreeAttributes::NORMAL_OCT16;
139 case
Cogs::hash(
"intensity"):
140 attribute = PotreeAttributes::INTENSITY_U16;
143 attribute = PotreeAttributes::CLASSIFICATION;
146 attribute = PotreeAttributes::FILLER_1B;
149 LOG_WARNING(logger,
"[instance=%u] metadata file: unrecoginzed element name %s", instanceId, str);
155 bool parseAttributeItem17(
PotreeData* poData, PotreeAttributes& attribute,
unsigned& skipbytes, rapidjson::Value& item, uint32_t instanceId)
157 assert(poData->versionMajor == 1 && poData->versionMinor < 8);
159 attribute = PotreeAttributes::COUNT;
160 if (!item.IsString()) {
161 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttributes element is not a string", instanceId);
164 if (!parsePointAttributeName(attribute, item.GetString(), instanceId))
return false;
165 assert(attribute < PotreeAttributes::COUNT);
169 bool parseAttributeItem18(
PotreeData* poData, PotreeAttributes& attribute,
unsigned& skipbytes, rapidjson::Value& item, uint32_t instanceId)
171 assert(poData->versionMajor == 1 && 8 <= poData->versionMinor);
173 attribute = PotreeAttributes::COUNT;
175 if (!item.IsObject()) {
176 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttributes element is not an object", instanceId);
180 const char* name =
nullptr;
181 if (
auto nn = item.FindMember(
"name"); nn != item.MemberEnd()) {
182 if (nn->value.IsString()) {
183 name = nn->value.GetString();
184 if (!parsePointAttributeName(attribute, name, instanceId)) {
187 if (
auto it = item.FindMember(
"size"); it != item.MemberEnd()) {
188 if (it->value.IsUint()) {
189 skipbytes = it->value.GetUint();
197 LOG_ERROR(logger,
"[instance=%u] metadata file: attribute name field is not a string", instanceId);
202 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttribute element missing name", instanceId);
213 {
"elements", 0,
false},
214 {
"elementSize", 0,
false},
218 case PotreeAttributes::POSITION_CARTESIAN: expected[1].value = 3; expected[2].value = 4; expected[3].value =
Cogs::hash(
"int32");
break;
219 case PotreeAttributes::RGBA_PACKED: expected[1].value = 4; expected[2].value = 1; expected[3].value =
Cogs::hash(
"uint8");
break;
220 case PotreeAttributes::RGB_U8_PACKED: expected[1].value = 3; expected[2].value = 1; expected[3].value =
Cogs::hash(
"uint8");
break;
221 case PotreeAttributes::NORMAL: expected[1].value = 3; expected[2].value = 4; expected[3].value =
Cogs::hash(
"float");
break;
222 case PotreeAttributes::NORMAL_SPHEREMAPPED: expected[1].value = 2; expected[2].value = 1; expected[3].value =
Cogs::hash(
"uint8");
break;
223 case PotreeAttributes::NORMAL_OCT16: expected[1].value = 2; expected[2].value = 1; expected[3].value =
Cogs::hash(
"uint8");
break;
224 case PotreeAttributes::INTENSITY_U16: expected[1].value = 1; expected[2].value = 2; expected[3].value =
Cogs::hash(
"uint16");
break;
225 case PotreeAttributes::CLASSIFICATION: expected[1].value = 1; expected[2].value = 1; expected[3].value =
Cogs::hash(
"uint8");
break;
226 case PotreeAttributes::FILLER_1B: expected[1].value = 1; expected[2].value = 1; expected[3].value =
Cogs::hash(
"uint8");
break;
228 assert(
false &&
"Unhandled attribute enum value");
231 expected[0].value = expected[1].value * expected[2].value;
232 for (
const auto& e : expected) {
233 if (
auto spec = item.FindMember(e.key); spec != item.MemberEnd()) {
235 if (spec->value.IsString()) {
237 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttribute %s %s: unexpected type %s", instanceId, name, e.key, spec->value.GetString());
242 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttribute %s %s is not a string", instanceId, name, e.key);
247 if (spec->value.IsUint()) {
248 if (spec->value.GetUint() != e.value) {
249 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttribute %s %s: expected %u, got %u", instanceId, name, e.key,
unsigned(e.value), spec->value.GetUint());
254 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttribute %s %s is not an unsigned number", instanceId, name, e.key);
260 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttribute %s missing %s", instanceId, name, e.key);
265 assert(attribute < PotreeAttributes::COUNT);
270 bool parseAttributeItem20(
PotreeData* poData, PotreeAttributes& attribute,
unsigned& skipbytes, rapidjson::Value& item, uint32_t instanceId)
272 assert(poData->versionMajor == 2);
275 attribute = PotreeAttributes::COUNT;
276 if (!item.IsObject()) {
277 LOG_ERROR(logger,
"[instance=%u] metadata file: pointAttributes element is not an object", instanceId);
281 const char* name =
nullptr;
282 if (!parseStringMemberNoLog(name, item,
"name")) {
283 LOG_ERROR(logger,
"[instance=%u] metadata file: missing or malformed name element in attribute list", instanceId);
288 if (!parseUIntMemberNoLog(size, item,
"size")) {
289 LOG_ERROR(logger,
"[instance=%u] metadata file: missing or malformed size element in attribute %s", instanceId, name);
293 unsigned numElements = 0;
294 if (!parseUIntMemberNoLog(numElements, item,
"numElements")) {
295 LOG_ERROR(logger,
"[instance=%u] metadata file: missing or malformed numElements element in attribute %s", instanceId, name);
298 if (4 < numElements) {
299 LOG_ERROR(logger,
"[instance=%u] metadata file: attribute %s has %u elements, more than max supported of 4", instanceId, name, numElements);
303 PotreeeType type = PotreeeType::None;
304 unsigned typeSize = 0;
305 if (
auto it = item.FindMember(
"type"); it != item.MemberEnd() && it->value.IsString()) {
307 case Cogs::hash(
"double"): type = PotreeeType::Double; typeSize = 8;
break;
308 case Cogs::hash(
"float"): type = PotreeeType::Float; typeSize = 4;
break;
309 case Cogs::hash(
"int8"): type = PotreeeType::Int8; typeSize = 1;
break;
310 case Cogs::hash(
"uint8"): type = PotreeeType::UInt8; typeSize = 1;
break;
311 case Cogs::hash(
"int16"): type = PotreeeType::Int16; typeSize = 2;
break;
312 case Cogs::hash(
"uint16"): type = PotreeeType::UInt16; typeSize = 2;
break;
313 case Cogs::hash(
"int32"): type = PotreeeType::Int32; typeSize = 4;
break;
314 case Cogs::hash(
"uint32"): type = PotreeeType::UInt32; typeSize = 4;
break;
315 case Cogs::hash(
"int64"): type = PotreeeType::Int64; typeSize = 8;
break;
316 case Cogs::hash(
"uint64"): type = PotreeeType::UInt64; typeSize = 8;
break;
318 LOG_ERROR(logger,
"[instance=%u] metadata file: unrecognized attribute type '%s' attribute %s", instanceId, it->value.GetString(), name);
323 LOG_ERROR(logger,
"[instance=%u] metadata file: missing or malformed type element in attribute %s", instanceId, name);
327 unsigned elementSize = 0;
328 if (!parseUIntMemberNoLog(elementSize, item,
"elementSize")) {
329 LOG_ERROR(logger,
"[instance=%u] metadata file: missing or malformed elementSize element in attribute %s", instanceId, name);
332 if (typeSize != elementSize) {
333 LOG_ERROR(logger,
"[instance=%u] metadata file: mismatch type size in attribute %s, expected %u, but got %u", instanceId, name, typeSize, elementSize);
336 if (typeSize * numElements != size) {
337 LOG_ERROR(logger,
"[instance=%u] metadata file: attribute %s: unexpected element size %u, expected %u", instanceId, name, size, typeSize * numElements);
341 double attributeMin[4] = {};
342 if (
auto it = item.FindMember(
"min"); it != item.MemberEnd() && it->value.IsArray() && it->value.GetArray().Size() == numElements) {
343 for (
unsigned i = 0; i < numElements; i++) {
344 if (
auto& element = it->value.GetArray()[i]; element.IsNumber()) {
345 attributeMin[i] = element.GetDouble();
348 LOG_ERROR(logger,
"[instance=%u] metadata file: item %u in min element in attribute %s is not a number", instanceId, i, name);
354 LOG_ERROR(logger,
"[instance=%u] metadata file: missing or malformed min element in attribute %s", instanceId, name);
358 double attributeMax[4] = {};
359 if (
auto it = item.FindMember(
"max"); it != item.MemberEnd() && it->value.IsArray() && it->value.GetArray().Size() == numElements) {
360 for (
unsigned i = 0; i < numElements; i++) {
361 if (
auto& element = it->value.GetArray()[i]; element.IsNumber()) {
362 attributeMax[i] = element.GetDouble();
365 LOG_ERROR(logger,
"[instance=%u] metadata file: item %u in max element in attribute %s is not a number", instanceId, i, name);
371 LOG_ERROR(logger,
"[instance=%u] metadata file: missing or malformed max element in attribute %s", instanceId, name);
377 if (type == PotreeeType::Int32 && numElements == 3) {
378 attribute = PotreeAttributes::POSITION_CARTESIAN;
379 for (
unsigned i = 0; i < 3; i++) { poData->metadata.tbmin[i] = attributeMin[i]; }
380 for (
unsigned i = 0; i < 3; i++) { poData->metadata.tbmax[i] = attributeMax[i]; }
384 if (type == PotreeeType::UInt16 && numElements == 1) {
385 attribute = PotreeAttributes::INTENSITY_U16;
389 if (type == PotreeeType::UInt8 && numElements == 3) {
390 attribute = PotreeAttributes::RGB_U8_PACKED;
392 else if (type == PotreeeType::UInt16 && numElements == 3) {
393 attribute = PotreeAttributes::RGB_U16_PACKED;
400 if(attribute == PotreeAttributes::COUNT) {
401 LOG_WARNING(logger,
"[instance=%u] metadata file: Unrecognized attribute %s, skipping %u bytes", instanceId, name, size);
409 bool parseAttributeItem(
PotreeData* poData, PotreeAttributes& attribute,
unsigned& skipbytes, rapidjson::Value& item, uint32_t instanceId)
411 if (poData->versionMajor == 1 && poData->versionMinor < 8) {
412 return parseAttributeItem17(poData, attribute, skipbytes, item, instanceId);
414 else if (poData->versionMajor == 1) {
415 return parseAttributeItem18(poData, attribute, skipbytes, item, instanceId);
418 return parseAttributeItem20(poData, attribute, skipbytes, item, instanceId);
422 bool parsePointAttributes(
PotreeSystem* poSystem,
PotreeData* poData, rapidjson::Document& doc, uint32_t instanceId)
424 const char* attributeName = poData->versionMajor == 1 ?
"pointAttributes" :
"attributes";
426 if (
auto it = doc.FindMember(attributeName); it != doc.MemberEnd()) {
428 if (!it->value.IsArray()) {
429 LOG_ERROR(logger,
"[instance=%u] metadata file: %s is not an array.", instanceId, attributeName);
433 bool hasPosition =
false;
434 poData->hasColor =
false;
435 poData->hasNormal =
false;
436 poData->hasIntensity =
false;
437 poData->hasClassification =
false;
438 for (
auto& subitem : it->value.GetArray()) {
439 unsigned skipbytes = 0;
440 PotreeAttributes attribute = PotreeAttributes::COUNT;
441 if (!parseAttributeItem(poData, attribute, skipbytes, subitem, instanceId)) {
444 LOG_WARNING(logger,
"[instance=%u] Failed to parse attribute, skipping %u bytes", instanceId, skipbytes);
445 for (
unsigned i = 0; i < skipbytes; i++) {
446 poData->attributes.push_back(PotreeAttributes::FILLER_1B);
450 LOG_ERROR(logger,
"[instance=%u] Failed to parse attribute", instanceId);
456 case PotreeAttributes::POSITION_CARTESIAN:
458 LOG_ERROR(logger,
"[instance=%u] metadata file: Multiple position sources", instanceId);
463 case PotreeAttributes::RGBA_PACKED:
464 case PotreeAttributes::RGB_U8_PACKED:
465 case PotreeAttributes::RGB_U16_PACKED:
466 if (poData->hasColor) {
467 LOG_ERROR(logger,
"[instance=%u] metadata file: Multiple color sources", instanceId);
470 poData->hasColor =
true;
472 case PotreeAttributes::NORMAL:
473 poData->hasNormal =
true;
475 case PotreeAttributes::NORMAL_SPHEREMAPPED:
476 poData->hasNormal =
true;
478 case PotreeAttributes::NORMAL_OCT16:
479 poData->hasNormal =
true;
481 case PotreeAttributes::INTENSITY_U16:
482 if (poData->hasIntensity) {
483 LOG_ERROR(logger,
"[instance=%u] metadata file: Multiple intensity sources", instanceId);
486 poData->hasIntensity =
true;
488 case PotreeAttributes::CLASSIFICATION:
489 poData->hasClassification =
true;
491 case PotreeAttributes::FILLER_1B:
494 assert(
false &&
"Unhandled attribute enum value");
497 poData->attributes.push_back(attribute);
502 LOG_ERROR(logger,
"[instance=%u] metadata file: %s does not contain a position element", instanceId, attributeName);
507 poData->layout =
static_cast<PotreeVertexLayout
>((poData->hasColor ? 1 : 0) |
508 (poData->hasNormal ? 2 : 0) |
509 (poData->hasIntensity ? 4 : 0) |
510 (poData->hasClassification ? 8 : 0));
513 if (info.stride != ~0u) {
514 if (poSystem->useInstancing) {
515 poData->streamsLayout.
vertexFormats[0] = Cogs::VertexFormats::createVertexFormat(info.elements.data(), info.elements.size());
520 poData->streamsLayout.
vertexFormats[0] = Cogs::VertexFormats::createVertexFormat(info.elements.data(), info.elements.size());
526 LOG_ERROR(logger,
"[instance=%u] metadata file: Unimplemented vertex format: color=%s normal=%s intensity=%s classification=%s",
528 poData->hasColor ?
"yes" :
"no",
529 poData->hasNormal ?
"yes" :
"no",
530 poData->hasIntensity ?
"yes" :
"no",
531 poData->hasClassification ?
"yes" :
"no");
536 LOG_ERROR(logger,
"[instance=%u] metadata file: Missing %s", instanceId, attributeName);
551 Document doc = parseJson(
StringView((
const char*)data->ptr, data->size), JsonParseFlags::None);
553 if (doc.HasParseError()) {
554 LOG_ERROR(logger,
"[instance=%u] metadata file: Failed to parse JSON", poData->instanceId);
558 if (!doc.IsObject()) {
559 LOG_ERROR(logger,
"[instance=%u] metadata file: JSON root is not an object", poData->instanceId);
563 if (
auto it = doc.FindMember(
"version"); it != doc.MemberEnd()) {
564 unsigned versionMajor = 0;
565 unsigned versionMinor = 0;
566 if (it->value.IsString()) {
568 auto str_N =
static_cast<size_t>(it->value.GetStringLength());
569 auto * str = it->value.GetString();
573 for (j = i; j < str_N; j++) {
574 if (
'0' <= str[j] && str[j] <=
'9') versionMajor = 10 * versionMajor + (str[j] -
'0');
578 LOG_ERROR(logger,
"[instance=%u] metadata file: version \"%s\": missing major version number", poData->instanceId, str);
581 if (j == str_N || str[j] !=
'.') {
582 LOG_ERROR(logger,
"[instance=%u] metadata file: version \"%s\": no dot after major version number", poData->instanceId, str);
585 for (j = i; j < str_N; j++) {
586 if (
'0' <= str[j] && str[j] <=
'9') versionMinor = 10 * versionMinor + (str[j] -
'0');
590 LOG_ERROR(logger,
"[instance=%u] metadata file: version \"%s\": missing minor version number", poData->instanceId, str);
594 LOG_DEBUG(logger,
"[instance=%u] metadata file: detected version %u.%u", poData->instanceId, versionMajor, versionMinor);
595 poData->versionMajor = versionMajor;
596 poData->versionMinor = versionMinor;
599 LOG_ERROR(logger,
"[instance=%u] metadata file: version is not a string", poData->instanceId);
604 LOG_ERROR(logger,
"[instance=%u] metadata file: version is missing", poData->instanceId);
608 if (poData->versionMajor == 1) {
609 if (
auto it = doc.FindMember(
"octreeDir"); it != doc.MemberEnd()) {
610 if (it->value.IsString()) {
611 std::string path = poData->
rootPath;
612 if (!path.empty() && path.back() !=
'/') { path.push_back(
'/'); }
613 path.append(it->value.GetString());
614 if (!path.empty() && path.back() !=
'/') { path.push_back(
'/'); }
618 LOG_ERROR(logger,
"metadata file: octreeDir is not a string");
623 LOG_ERROR(logger,
"metadata file: Missing octreeDir");
628 if (
auto it = doc.FindMember(
"suffix"); it != doc.MemberEnd()) {
629 if (it->value.IsString()) {
630 poData->suffix = it->value.GetString();
633 poData->encoding = PotreeEnconding::Default;
637 poData->encoding = PotreeEnconding::ZStd;
640 poData->encoding = PotreeEnconding::Default;
641 LOG_WARNING(logger,
"Unrecognized suffix '%s', assuming non-compressed data.", it->value.GetString());
646 LOG_ERROR(logger,
"metadata file: suffix is not a string");
651 if (!parseBBox(poData, poData->metadata.bbmin, poData->metadata.bbmax, doc,
"boundingBox", poData->instanceId))
return false;
652 if (poData->versionMajor == 1) {
653 if (!parseBBox(poData, poData->metadata.tbmin, poData->metadata.tbmax, doc,
"tightBoundingBox", poData->instanceId))
return false;
657 poData->attributes.clear();
658 if (!parsePointAttributes(poSystem, poData, doc, poData->instanceId))
return false;
661 if (poData->versionMajor == 1) {
662 if (!parseUIntMemberNoLog(poData->hierarchyStepSize, doc,
"hierarchyStepSize")) {
663 LOG_ERROR(logger,
"metadata file: hierarchyStepSize is missing or not an unsigned integer");
667 if (!parseFloatMemberNoLog(scale, doc,
"scale")) {
668 LOG_ERROR(logger,
"metadata file: scale is missing or not a number");
671 poData->metadata.scale = glm::vec3(scale);
676 else if (poData->versionMajor == 2) {
679 if (
auto it = doc.FindMember(
"hierarchy"); it != doc.MemberEnd() && it->value.IsObject()) {
680 auto& item = it->value;
681 if (!parseUIntMemberNoLog(poData->firstChunkSize, item,
"firstChunkSize")) {
682 LOG_ERROR(logger,
"metadata file: missing or malformed firstChunkSize in hierarchy");
685 if (!parseUIntMemberNoLog(poData->hierarchyStepSize, item,
"stepSize")) {
686 LOG_ERROR(logger,
"metadata file: missing or malformed stepSize in hierarchy");
689 if (!parseUIntMemberNoLog(poData->hierarchyDepth, item,
"depth")) {
690 LOG_ERROR(logger,
"metadata file: missing or malformed depth in hierarchy");
695 LOG_ERROR(logger,
"metadata file: hierarchy is missing");
700 const char* encoding =
nullptr;
701 if (!parseStringMemberNoLog(encoding, doc,
"encoding")) {
702 LOG_ERROR(logger,
"metadata file: encoding is missing or not a string");
707 poData->encoding = PotreeEnconding::Default;
710 poData->encoding = PotreeEnconding::Brotli;
713 LOG_ERROR(logger,
"metadata file: unrecognized encodng '%s'", encoding);
717 if (!parseVec3NoLog(poData->metadata.offset, doc,
"offset")) {
718 LOG_ERROR(logger,
"metadata file: offset missing or not an array of 3 numbers");
721 if (!parseVec3NoLog(poData->metadata.scale, doc,
"scale")) {
722 LOG_ERROR(logger,
"metadata file: offset missing or not an array of 3 numbers");
729 assert(
false &&
"Unsupported version");
733 if (8 < poData->hierarchyStepSize) {
735 LOG_ERROR(logger,
"metadata file: hierarchyStepSize larger than 8 is not supported");
741 if (
auto it = doc.FindMember(
"spacing"); it != doc.MemberEnd()) {
743 if (it->value.IsNumber()) {
744 poData->spacing = it->value.GetFloat();
747 LOG_ERROR(logger,
"metadata file: spacing is not a number");
752 LOG_ERROR(logger,
"metadata file: spacing is missing");
757 poData->octtreeFrame.positionInEntityFrame = 0.5 * (poData->metadata.tbmin + poData->metadata.tbmax);
758 poData->octtreeFrame.toBBoxShift = glm::vec3(poData->metadata.bbmin - poData->octtreeFrame.positionInEntityFrame);
760 poData->octtreeFrame.fullBBoxSize = glm::vec3(poData->metadata.bbmax - poData->metadata.bbmin);
761 poData->octtreeFrame.fullBBoxDiagonal = glm::length(poData->octtreeFrame.fullBBoxSize);
763 poData->octtreeFrame.tightBBoxMin = glm::vec3(poData->metadata.tbmin - poData->octtreeFrame.positionInEntityFrame);
764 poData->octtreeFrame.tightBBoxMax = glm::vec3(poData->metadata.tbmax - poData->octtreeFrame.positionInEntityFrame);
766 poData->octtreeFrame.fullBBoxMin = glm::vec3(poData->metadata.bbmin - poData->octtreeFrame.positionInEntityFrame);
767 poData->octtreeFrame.fullBBoxMax = glm::vec3(poData->metadata.bbmax - poData->octtreeFrame.positionInEntityFrame);
770 if (poData->versionMajor == 1) {
771 poData->positionDecode.scale = poData->versionMinor <= 3 ? glm::vec3(1.f) : poData->metadata.scale;
772 poData->positionDecode.shift = glm::vec3(poData->metadata.bbmin - poData->octtreeFrame.positionInEntityFrame);
774 else if (poData->versionMajor == 2) {
775 poData->positionDecode.scale = poData->metadata.scale;
776 poData->positionDecode.shift = glm::vec3(poData->metadata.offset - poData->octtreeFrame.positionInEntityFrame);
779 LOG_FATAL(logger,
"Unsupported major version %d", poData->versionMajor);
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
size_t hashLowercase(size_t hashValue=Cogs::hash()) const noexcept
Get the hash code of the string converted to lowercase.
constexpr size_t hash() const noexcept
Get the hash code of the string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
VertexFormatHandle vertexFormats[maxStreams]
COGSCORE_DLL_API void updateHash()
std::string octreeDir
1.x only: subdir with data
std::string rootPath
URL to folder containing metadata file.
Abstract base class storing data read from a file.