Cogs.Core
PotreeSystem.cpp
1#include "Context.h"
2#include "ViewContext.h"
3#include "Resources/DataFetcherManager.h"
4#include "Resources/MaterialManager.h"
5#include "ExtensionRegistry.h"
6#include "Services/DPIService.h"
7
8#include "Services/Services.h"
9#include "Generators/TextureGenerator.h"
10
11#include "Systems/Core/TransformSystem.h"
12#include "Systems/Core/ClipShapeSystem.h"
13
14#include "PotreeSystem.h"
15#include "PotreeRenderer.h"
16
17#include "Rendering/IGraphicsDevice.h"
18#include "Rendering/ICapabilities.h"
19#include "Foundation/Logging/Logger.h"
20#include "Foundation/Platform/IO.h"
21
22namespace Cogs::Core {
23
24 const Cogs::StringView spacingGlobalScaleName = "potree.spacing.globalScale";
25 const Cogs::StringView pointSizeGlobalMinScaleName = "potree.pointSize.globalMinScale";
26 const Cogs::StringView pointSizeGlobalMaxScaleName = "potree.pointSize.globalMaxScale";
27 const Cogs::StringView pointSizeGlobalScaleName = "potree.pointSize.globalScale";
28
29 bool parseCloudJs(Context* context, PotreeData* poData, const Cogs::FileContents* data);
30
31}
32
33namespace {
34 using namespace Cogs::Core;
36
37 // Ids used to make sure that we're referencing the correct stuff in async callbacks.
38
39 uint32_t createId()
40 {
41 static uint32_t idCounter = 1;
42 if (idCounter == 0) idCounter++;
43 return idCounter++;
44 }
45
46
47
48 // --- Utils ---------------------------------------------------------------
49#if 0
50 unsigned popcount(uint8_t x)
51 {
52 x = (x & 0x55u) + ((x >> 1) & 0x55u);
53 x = (x & 0x33u) + ((x >> 2) & 0x33u);
54 x = (x & 0x0Fu) + ((x >> 4) & 0x0Fu);
55 return x;
56 }
57#endif
58
59 // --- Fetch and parse cloud.js ------------------------------------------
60
61
62
63 void issueCloudJSFetch(Context* context, PotreeSystem* poSystem, const PotreeComponent* poComp, PotreeData* poData)
64 {
65 PotreeSystem::startFetch(context, poComp, poData);
66 poSystem->requestsInFlight++;
67 poData->fetch_id = DataFetcherManager::fetchAsync(context, poData->jsonPath, [context, instanceId=poData->instanceId](std::unique_ptr<Cogs::FileContents> data)
68 {
69 auto task = [context, instanceId, dataPtr = data.release()]()
70 {
71 std::unique_ptr<Cogs::FileContents> data(dataPtr);
72 PotreeSystem* poSystem = nullptr;
73 PotreeComponent* poComp = nullptr;
74 PotreeData* poData = nullptr;
75 bool ret = PotreeSystem::lookupAndCheckForStaleness(context, poSystem, poData, poComp, instanceId);
76 poSystem->requestsInFlight--;
77 PotreeSystem::endFetch(context, poComp, poData);
78
79 if (!ret) {
80 if (data) {
81 LOG_TRACE(logger, "[instance=%u] Received stale metadata file, ignoring: %.*s", instanceId, StringViewFormat(data->origin()));
82 }
83 return;
84 }
85 else if (poData->state != PotreeState::RequestedCloudJS) {
86 if (data) {
87 LOG_ERROR(logger, "[instance=%u] Unexpected metadata file, ignoring: %.*s", instanceId, StringViewFormat(data->origin()));
88 }
89 else {
90 LOG_ERROR(logger, "[instance=%u] Unexpected metadata file", instanceId);
91 }
92 }
93 else if (!data) {
94 LOG_ERROR(logger, "[instance=%u] Failed to get metadata file: %s", instanceId, poData ? poData->jsonPath.c_str() : "");
95 poData->state = PotreeState::Error;
96 }
97 else if (!parseCloudJs(context, poData, data.get())) {
98 LOG_ERROR(logger, "[instance=%u] Error while parsing metadata file", instanceId);
99 poData->state = PotreeState::Error;
100 }
101 else {
102 LOG_TRACE(logger, "[instance=%u] metadata parsed successfully", instanceId);
103 poData->state = PotreeState::ParsedCloudJs;
104 }
105
106 poData->fetch_id = DataFetcherManager::NoFetchId;
107 context->engine->setDirty();
108 };
109
110 context->engine->runTaskInMainThread(std::move(task));
111 }, 0, 0, true, Cogs::FileContentsHints::None);
112
113 }
114
115 void setUpMaterial(Context* /*context*/, PotreeComponent* /*poComp*/, PotreeData* poData)
116 {
117 auto * PM = poData->pointMaterial.instance->material;
118 poData->pointMaterial.pointSize = PM->getFloatKey("pointSize");
119 poData->pointMaterial.shadingIntensity = PM->getFloatKey("shadingIntensity");
120 poData->pointMaterial.octTexScale = PM->getFloatKey("octTexScale");
121 poData->pointMaterial.minPointSize = PM->getFloatKey("minPointSize");
122 poData->pointMaterial.maxPointSize = PM->getFloatKey("maxPointSize");
123 poData->pointMaterial.outlineSize = PM->getFloatKey("outlineSize");
124 poData->pointMaterial.outlineColor = PM->getVec3Key("outlineColor");
125 poData->pointMaterial.baseColor = PM->getVec4Key("baseColor");
126 poData->pointMaterial.invShift = PM->getVec3Key("invShift");
127 poData->pointMaterial.invScale = PM->getVec3Key("invScale");
128 poData->pointMaterial.gradient = PM->getTextureKey("Gradient");
129
130 auto* BM = poData->boxMaterial.instance->material;
131 poData->boxMaterial.scale = BM->getVec3Key("scale");
132 poData->boxMaterial.offset = BM->getVec3Key("offset");
133 }
134
135 void refreshMaterialInstances(Context* context, PotreeSystem* system, PotreeComponent* poComp, PotreeData* poData)
136 {
137 float minPointSize = poData->pointSize.min;
138 float maxPointSize = poData->pointSize.max;
139 float pointSize = poData->pointSize.val;
140
141 // The pointSize has already been adjusted with the dpi scale.
142 // We require the unadjusted value to calculate modelSpaceRadius and avoid double adjustment.
143 float rawPointSize = pointSize / context->getDefaultView()->dpiService->getScaleFactor();
144 float pointSizeScale = glm::clamp(rawPointSize / 10.f, 1e-2f, 1e2f);
145 poData->modelSpaceRadius = 0.5f * pointSizeScale * poData->spacing;
146
147 auto& PM = poData->pointMaterial;
148 bool hasFragDepth = context->device->getCapabilities()->getDeviceCapabilities().FragDepth;
149 switch (poComp->pointShape) {
150 case PotreePointShape::Square:
151 PM.instance->setVariant("Shape", "Square");
152 hasFragDepth = false;
153 break;
154 case PotreePointShape::Disc:
155 PM.instance->setVariant("Shape", "Disc");
156 hasFragDepth = false;
157 break;
158 case PotreePointShape::Paraboloid:
159 PM.instance->setVariant("Shape", hasFragDepth ? "Paraboloid" : "Square");
160 break;
161 case PotreePointShape::Sphere:
162 PM.instance->setVariant("Shape", hasFragDepth ? "Sphere" : "Disc");
163 break;
164 default:
165 LOG_FATAL(logger, "Component member pointShape has illegal enum value: %u", unsigned(poComp->pointShape));
166 poComp->pointShape = PotreePointShape::Square;
167 PM.instance->setVariant("Shape", "Square");
168 hasFragDepth = false;
169 break;
170 }
171 // OpenGLES sets depth slightly differently to the HLSL shaders...
172 if ((context->device->getType() != Cogs::GraphicsDeviceType::OpenGLES30)) {
173 PM.instance->setVariant("CustomDepth", hasFragDepth);
174 }
175
176 switch (poComp->pointSizing) {
177 case PotreePointSizing::FixedSize:
178 PM.instance->setVariant("PointSizing", "FixedSize");
179 break;
180 case PotreePointSizing::DistanceScaled:
181 PM.instance->setVariant("PointSizing", "DistanceScaled");
182 break;
183 case PotreePointSizing::DistanceAndDensityScaled:
184 PM.instance->setVariant("PointSizing", "DistanceAndDensityScaled");
185 break;
186 default:
187 LOG_FATAL(logger, "Component member pointSizing has illegal enum value: %u", unsigned(poComp->pointSizing));
188 poComp->pointSizing = PotreePointSizing::FixedSize;
189 PM.instance->setVariant("PointSizing", "FixedSize");
190 break;
191 }
192
193 switch (poComp->shading)
194 {
195 case PotreeShading::Flat:
196 PM.instance->setVariant("BasicShading", false);
197 break;
198 case PotreeShading::Basic:
199 PM.instance->setVariant("BasicShading", true);
200 break;
201 default:
202 LOG_FATAL(logger, "Component member shading has illegal enum value: %u", unsigned(poComp->shading));
203 poComp->shading = PotreeShading::Flat;
204 PM.instance->setVariant("BasicShading", false);
205 break;
206 }
207
208 switch (poComp->coloring) {
209 case PotreeColoring::Base:
210 PM.instance->setVariant("HasColor", false);
211 PM.instance->setVariant("HasIntensity", false);
212 PM.instance->setVariant("Coloring", "Base");
213 break;
214 case PotreeColoring::Color:
215 PM.instance->setVariant("HasColor", poData->hasColor);
216 PM.instance->setVariant("HasIntensity", false);
217 PM.instance->setVariant("Coloring", poData->hasColor ? "Color" : "Base");
218 break;
219 case PotreeColoring::Intensity:
220 PM.instance->setVariant("HasColor", false);
221 PM.instance->setVariant("HasIntensity", poData->hasIntensity);
222 PM.instance->setVariant("Coloring", poData->hasIntensity ? "Intensity" : "Base");
223 break;
224 case PotreeColoring::Level:
225 PM.instance->setVariant("HasColor", false);
226 PM.instance->setVariant("HasIntensity", false);
227 PM.instance->setVariant("Coloring", "Level");
228 break;
229 default:
230 LOG_FATAL(logger, "Component member coloring has illegal enum value: %u", unsigned(poComp->coloring));
231 poComp->coloring = PotreeColoring::Base;
232 PM.instance->setVariant("Coloring", "Base");
233 break;
234 }
235 PM.instance->setVariant("Outline", poComp->outlineSize != 0.f);
236 PM.instance->setVariant("ClipPlanes", static_cast<int>(poData->clipPlaneCount));
237
238 if (PM.pointSize != NoProperty) PM.instance->setFloatProperty(PM.pointSize, pointSize);
239 if (PM.shadingIntensity != NoProperty) PM.instance->setFloatProperty(PM.shadingIntensity, poComp->shadingIntensity);
240 if (PM.octTexScale != NoProperty) PM.instance->setFloatProperty(PM.octTexScale, 1.f / poData->octtreeTextureData.size());
241 if (PM.minPointSize != NoProperty) PM.instance->setFloatProperty(PM.minPointSize, minPointSize);
242 if (PM.maxPointSize != NoProperty) PM.instance->setFloatProperty(PM.maxPointSize, maxPointSize);
243 if (PM.baseColor != NoProperty) PM.instance->setVec4Property(PM.baseColor, poComp->baseColor);
244 if (PM.outlineColor != NoProperty) PM.instance->setVec3Property(PM.outlineColor, poComp->outlineColor);
245 if (PM.outlineSize != NoProperty) PM.instance->setFloatProperty(PM.outlineSize, poComp->outlineSize);
246 if (PM.gradient != NoProperty) PM.instance->setTextureProperty(PM.gradient, HandleIsValid(poComp->gradient) ? poComp->gradient : system->defaultGradient);
247 if (PM.gradient != NoProperty) PM.instance->setTextureAddressMode(PM.gradient, Cogs::SamplerState::Clamp);
248 if (PM.invShift != NoProperty) PM.instance->setVec3Property(PM.invShift, 0.5f * (poData->octtreeFrame.tightBBoxMax - poData->octtreeFrame.tightBBoxMin));
249 if (PM.invScale != NoProperty) PM.instance->setVec3Property(PM.invScale, glm::vec3(1.f) / (poData->octtreeFrame.fullBBoxMax - poData->octtreeFrame.fullBBoxMin));
250
251 auto& BM = poData->boxMaterial;
252 if (BM.scale != NoProperty) BM.instance->setVec3Property(BM.scale, poData->octtreeFrame.fullBBoxSize);
253 if (BM.offset != NoProperty) BM.instance->setVec3Property(BM.offset,-poData->octtreeFrame.currentPositionInEntityFrame);
254 }
255
256 void initComponentData(Context* context, PotreeSystem* system, PotreeComponent* poComp, PotreeData* poData)
257 {
258 poData->instanceId = createId();
259 poData->pointMaterial.instance = context->materialInstanceManager->createMaterialInstance(system->defaultPointMaterial);
260 poData->boxMaterial.instance = context->materialInstanceManager->createMaterialInstance(system->defaultBoxMaterial);
261 setUpMaterial(context, poComp, poData);
262 }
263
264 void resetComponentData(Context* context, PotreeSystem* system, PotreeComponent* poComp, PotreeData* poData)
265 {
266 LOG_DEBUG(logger, "[instance=%u] Releasing resources (%s)", poData->instanceId, poData->jsonPath.c_str());
267 while (!poData->subtrees.empty()) {
268 auto* subtree = poData->subtrees.shift();
269 subtree->release(context, poData);
270 poData->subtreeStore.destroy(subtree);
271 }
272 DataFetcherManager::cancelAsyncFetch(context, poData->fetch_id);
273 poData->fetch_id = DataFetcherManager::NoFetchId;
274 assert(poData->subtrees.empty());
275 assert(poData->cellStore.size() == 0);
276 assert(poData->subtreeStore.size() == 0);
277 poData->~PotreeData();
278 poData = new (poData) PotreeData();
279 initComponentData(context, system, poComp, poData);
280 }
281
282}
283
284PotreeVertexLayoutInfo Cogs::Core::PotreeSystem::vertexLayoutInfo[static_cast<size_t>(PotreeVertexLayout::COUNT)] =
285{
286 //POSITION:
287 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
288 //POSITION_COLOR:
289 { 0, 3 * 4, ~0u, ~0u, ~0u, 4 * 4, {
290 {0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::Position, 0, Cogs::InputType::VertexData, 0 },
291 {3 * 4, Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::Color, 0, Cogs::InputType::VertexData, 0 }
292 }},
293 //POSITION_NORMAL:
294 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
295 //POSITION_COLOR_NORMAL:
296 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
297 //POSITION_INTENSITY:
298 { 0, ~0u, ~0u, 3 * 4, ~0u, 4 * 4, {
299 {0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::Position, 0, Cogs::InputType::VertexData, 0 },
300 {3 * 4, Cogs::Format::R32_FLOAT, Cogs::ElementSemantic::TextureCoordinate, 0, Cogs::InputType::VertexData, 0 }
301 }},
302 //POSITION_COLOR_INTENSITY:
303 { 0, 4 * 4, ~0u, 3 * 4, ~0u, 5 * 4, {
304 {0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::Position, 0, Cogs::InputType::VertexData, 0 },
305 {3 * 4, Cogs::Format::R32_FLOAT, Cogs::ElementSemantic::TextureCoordinate, 0, Cogs::InputType::VertexData, 0 },
306 {4 * 4, Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::Color, 0, Cogs::InputType::VertexData, 0 }
307 }},
308 //POSITION_NORMAL_INTENSITY:
309 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
310 //POSITION_COLOR_NORMAL_INTENSITY:
311 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
312 //POSITION_CLASSIFICATION:
313 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
314 //POSITION_COLOR_CLASSIFICATION:
315 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
316 //POSITION_NORMAL_CLASSIFICATION:
317 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
318 //POSITION_COLOR_NORMAL_CLASSIFICATION:
319 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
320 //POSITION_INTENSITY_CLASSIFICATION:
321 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
322 //POSITION_COLOR_INTENSITY_CLASSIFICATION:
323 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
324 //POSITION_NORMAL_INTENSITY_CLASSIFICATION:
325 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
326 //POSITION_COLOR_NORMAL_INTENSITY_CLASSIFICATION:
327 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
328};
329
330// --- Fetch and parse *.hrc -----------------------------------------------
331
332void Cogs::Core::PotreeSystem::issueSubtreeFetch(Context* context, const PotreeComponent* poComp, PotreeData* poData, PotreeCell* parent)
333{
334 PotreeSubtree* subtree = nullptr;
335 if (parent == nullptr && !poData->subtrees.empty()) {
336 LOG_FATAL(logger, "[instanceId=%u] Fetching root node but subtree array is not empty.", poData->instanceId);
337 assert(parent != nullptr || poData->subtrees.empty());
338 return;
339 }
340 subtree = poData->subtrees.push(poData->subtreeStore.create());
341 subtree->subtreeId = createId();
342 subtree->fetchLayoutAndInitialize(context, this, poComp, poData, parent);
343}
344
345bool Cogs::Core::PotreeSystem::lookupAndCheckForStaleness(Context* context, PotreeSystem*& potreeSystem, PotreeData*& poData, PotreeComponent*& poComp, const uint32_t instanceId)
346{
347 potreeSystem = ExtensionRegistry::getExtensionSystem<PotreeSystem>(context);
348 for (auto& poComp_ : potreeSystem->pool) {
349
350 PotreeDataHolder& poDataHolder = potreeSystem->getData(&poComp_);
351 PotreeData* poData_ = poDataHolder.poData.get();
352 assert(poData_);
353
354 if (poData_->instanceId == instanceId) {
355 poComp = &poComp_;
356 poData = poData_;
357 return true;
358 }
359 }
360 LOG_TRACE(logger, "[instance=%u] Failed to lookup instance, doesn't exist anymore.", instanceId);
361 return false;
362}
363
364bool Cogs::Core::PotreeSystem::lookupAndCheckForStaleness(Context* context, PotreeSystem*& potreeSystem, PotreeData*& poData, PotreeComponent*& poComp, PotreeSubtree*& subtree, const uint32_t instanceId, const uint32_t subtreeId)
365{
366 if (lookupAndCheckForStaleness(context, potreeSystem, poData, poComp, instanceId)) {
367 for (auto* item : poData->subtrees) {
368 if (item->subtreeId == subtreeId) {
369 subtree = item;
370 return true;
371 }
372 }
373 }
374 LOG_TRACE(logger, "[instance=%u,tree=%u] Failed to lookup subtree, doesn't exist anymore.", instanceId, subtreeId);
375 return false;
376}
377
379{
381
382 renderer = new PotreeRenderer();
383 context->renderer->registerExtension(renderer);
384
385 auto* texGen = context->services->getService<TextureGenerator>();
386 defaultGradient = texGen->getTexture(Cogs::Core::ImageType::GradientHeat);
387
388 if (!context->variables->exist(spacingGlobalScaleName)) {
389 context->variables->set(spacingGlobalScaleName, 1.f);
390 }
391
392 if (!context->variables->exist(pointSizeGlobalScaleName)) {
393 context->variables->set(pointSizeGlobalScaleName, 1.f);
394 }
395
396 if (!context->variables->exist(pointSizeGlobalMinScaleName)) {
397 context->variables->set(pointSizeGlobalMinScaleName, 1.f);
398 }
399
400 if (!context->variables->exist(pointSizeGlobalMaxScaleName)) {
401 context->variables->set(pointSizeGlobalMaxScaleName, 1.f);
402 }
403}
404
406{
407 context->renderer->unregisterExtension(renderer);
408 delete renderer;
409}
410
412{
413 // Load it here where base materials etc. should have been loaded.
414 if (!materialLoaded) {
415 context->materialManager->loadMaterial("Materials/PotreeBoxCore.material");
416 context->materialManager->loadMaterial("Materials/PotreePointCore.material");
417 defaultBoxMaterial = context->materialManager->loadMaterial("Materials/PotreeBoxDefault.material");
418 defaultPointMaterial = context->materialManager->loadMaterial("Materials/PotreePointDefault.material");
419 context->materialManager->processLoading();
420
421 materialLoaded = true;
422 }
423
425 auto* poComp = static_cast<PotreeComponent*>(handle.resolve());
426 auto& poDataHolder = getData(poComp);
427 poDataHolder.poData = std::make_unique<PotreeData>();
428 initComponentData(context, this, poComp, poDataHolder.poData.get());
429
430 if(pool.size() == 1) {
431 assert(bounds == nullptr);
432 bounds = new PotreeBounds(this);
433 context->bounds->addBoundsExtension(bounds);
434
435 assert(picker == nullptr);
436 picker = new PotreePicker(this);
437 context->rayPicking->addPickable(picker);
438
439 LOG_DEBUG(logger, "Registered potree bounds and picker extensions");
440 }
441
442 return handle;
443}
444
445
446
448{
450 if (pool.size() == 0) {
451 assert(bounds);
452 context->bounds->removeBoundsExtension(bounds);
453 delete bounds;
454 bounds = nullptr;
455
456 assert(picker);
457 context->rayPicking->removePickable(picker);
458 delete picker;
459 picker = nullptr;
460
461 LOG_DEBUG(logger, "Last component destroyed, unregistered potree bounds and picker extensions");
462 }
463}
464
466{
468 CpuInstrumentationScope(SCOPE_POTREE, "PotreeSystem::preUpdate");
469
470 for (auto& poComp : pool) {
472 assert(trComp);
473
474 PotreeDataHolder& poDataHolder = getData(&poComp);
475 PotreeData* poData = poDataHolder.poData.get();
476
477 switch (poComp.originPolicy) {
479 poData->octtreeFrame.currentPositionInEntityFrame = poData->octtreeFrame.positionInEntityFrame;
480 break;
482 poData->octtreeFrame.currentPositionInEntityFrame = glm::vec3(0.f);
483 break;
485 poData->octtreeFrame.currentPositionInEntityFrame = glm::vec3(0.f);
486 if (trComp->coordinates != poData->octtreeFrame.positionInEntityFrame) {
487 trComp->coordinates = poData->octtreeFrame.positionInEntityFrame;
488 trComp->setChanged();
489 }
490 break;
491 default:
493 }
494 poData->originPolicy = poComp.originPolicy;
495 }
496}
497
498void Cogs::Core::PotreeSystem::updateOcttreeFrame(Context* context, PotreeData& poData, const PotreeComponent& poComp)
499{
500 const glm::vec3& o = poData.octtreeFrame.currentPositionInEntityFrame;
501 const TransformComponent* transformComp = poComp.getComponent<TransformComponent>();
502 poData.worldFromOcttreeFrame = context->transformSystem->getLocalToWorld(transformComp) * glm::mat4(1.f, 0.f, 0.f, 0.f,
503 0.f, 1.f, 0.f, 0.f,
504 0.f, 0.f, 1.f, 0.f,
505 o.x, o.y, o.z, 1.f);
506
507}
508
509
511{
512 CpuInstrumentationScope(SCOPE_POTREE, "PotreeSystem::update");
513
514 const float spacingGlobalScale = context->variables->get(spacingGlobalScaleName, 1.f);
515 const float pointSizeGlobalMinScale = context->variables->get(pointSizeGlobalMinScaleName, 1.f);
516 const float pointSizeGlobalMaxScale = context->variables->get(pointSizeGlobalMaxScaleName, 1.f);
517 const float pointSizeGlobalScale = context->variables->get(pointSizeGlobalScaleName, 1.f);
518
519 for (auto& poComp : pool) {
520
521 PotreeDataHolder& poDataHolder = getData(&poComp);
522 PotreeData* poData = poDataHolder.poData.get();
523 assert(poData);
524 if (!poComp.isVisible()) {
525 if (poData->state != PotreeState::Uninitialized) {
526 resetComponentData(context, this, &poComp, poData);
527 assert(poData->state == PotreeState::Uninitialized);
528 }
529 continue; // Don't fetch anything while it is not visible.
530 }
531
532
533 if (poData->state != PotreeState::Uninitialized &&
534 ((poData->jsonPath != poComp.source) ||
535 (poData->supportPicking != poComp.supportPicking)))
536 {
537 resetComponentData(context, this, &poComp, poData);
538 assert(poData->state == PotreeState::Uninitialized);
539 }
540
541 if (poData->state == PotreeState::Uninitialized) {
542
543 if (!poComp.source.empty() && (poData->jsonPath != poComp.source)) {
544 poData->jsonPath = poComp.source;
545 if (size_t o = poData->jsonPath.find_last_of("\\/"); o != std::string::npos) {
546 poData->rootPath = poData->jsonPath.substr(0, o + 1);
547 }
548 else {
549 poData->rootPath.clear();
550 }
551 poData->supportPicking = poComp.supportPicking;
552 poData->instanceId = createId();
553 poData->state = PotreeState::RequestedCloudJS;
554 issueCloudJSFetch(context, this, &poComp, poData);
555 continue;
556 }
557 }
558
559 if (poData->state == PotreeState::ParsedCloudJs) {
560 poData->state = PotreeState::Running;
561 issueSubtreeFetch(context, &poComp, poData, nullptr); // fetch root subtree
562 }
563
564 // Sanity checks
565 bool materialChanged = false;
566
567 if (poComp.clipPlanes.empty()) {
568 materialChanged = materialChanged || (poData->clipPlaneCount != 0);
569
570 // Check if we have a clip shape
571 if (const ClipShapeRefComponent* clipRefComp = poComp.getComponent<ClipShapeRefComponent>(); clipRefComp) {
572 if (EntityPtr clipCompEntity = clipRefComp->clipShape.lock(); clipCompEntity) {
573 if (ComponentHandle clipComp = clipCompEntity->getComponentHandle<ClipShapeComponent>(); clipComp) {
574 poComp.clipShapeComponent = clipComp;
575 }
576 }
577 }
578
579 }
580 else {
581
582 // Do not use clipshapes
584
585 const size_t clipPlaneCount = std::min(poComp.disableCustomClipping ? size_t(0) : PotreeData::MaxClipPlanes, poComp.clipPlanes.size());
586 for (size_t i = 0; i < clipPlaneCount; i++) {
587 poData->clipPlanes[i] = poComp.clipPlanes[i];
588 }
589
590 materialChanged = materialChanged || (poData->clipPlaneCount != clipPlaneCount);
591 poData->clipPlaneCount = clipPlaneCount;
592 }
593
594
595 if (poComp.pointMaterial) {
596 if (poData->pointMaterial.instance != poComp.pointMaterial) {
597 LOG_DEBUG(logger, "Using point material instance from component, state=%u", uint32_t(poData->state));
598 poData->pointMaterial.instance = poComp.pointMaterial;
599 materialChanged = true;
600 }
601 }
602 else {
603 if (!poData->pointMaterial.instance || poData->pointMaterial.instance->material != defaultPointMaterial.resolve()) {
604 LOG_DEBUG(logger, "Creating default point material instance, state=%u", uint32_t(poData->state));
605 poData->pointMaterial.instance = context->materialInstanceManager->createMaterialInstance(defaultPointMaterial);
606 materialChanged = true;
607 }
608 }
609
610 if (poComp.boxMaterial) {
611 if (poData->boxMaterial.instance != poComp.boxMaterial) {
612 LOG_DEBUG(logger, "Using box material instance from component, state=%u", uint32_t(poData->state));
613 poData->boxMaterial.instance = poComp.boxMaterial;
614 materialChanged = true;
615 }
616 }
617 else {
618 if (!poData->boxMaterial.instance || poData->boxMaterial.instance->material != defaultBoxMaterial.resolve()) {
619 LOG_DEBUG(logger, "Creating default box material instance, state=%u", uint32_t(poData->state));
620 poData->boxMaterial.instance = context->materialInstanceManager->createMaterialInstance(defaultBoxMaterial);
621 materialChanged = true;
622 }
623 }
624 if (materialChanged) setUpMaterial(context, &poComp, poData);
625
626
627 updateOcttreeFrame(context, *poData, poComp);
628
629 float gridSize = std::max(1.f, poData->octtreeFrame.fullBBoxSize.x / poData->spacing);
630 float dpiScale = context->getDefaultView()->dpiService->getScaleFactor();
631 poData->tolerance = std::max(1.f, dpiScale * gridSize * spacingGlobalScale * poComp.pointScreenSpacing);
632 poData->rootTolerance = std::max(1.f, dpiScale * poComp.rootNodeMinScreenSize);
633
634 poComp.minPointSize = glm::clamp(poComp.minPointSize, 1.f, 100.f);
635 poComp.maxPointSize = glm::clamp(poComp.maxPointSize, 1.f, 100.f);
636 poComp.minPointSize = glm::min(poComp.minPointSize, poComp.maxPointSize);
637
638 poData->pointSize.min = pointSizeGlobalMinScale * dpiScale * poComp.minPointSize;
639 poData->pointSize.max = pointSizeGlobalMaxScale * dpiScale * poComp.maxPointSize;
640 poData->pointSize.val = glm::clamp(pointSizeGlobalScale * dpiScale * poComp.pointSize, poData->pointSize.min, poData->pointSize.max);
641
642 poData->densitySized.levelScale = poComp.densityLevelScale;
643 poData->densitySized.scale = poComp.densityScale;
644 poData->densitySized.bias = poComp.densityBias;
645
646 poData->debugBoxes = poComp.debugBoxes;
647 refreshMaterialInstances(context, this, &poComp, poData);
648 }
649 updateLodLevels(context);
650
651}
652
654{
655 if (poData->requestsInFlight == 0) {
658 context->engine->invokeComponentNotifyCallback(*poComp, (int)PotreeNotification::FetchingBegin, nullptr, 0);
659 }
661 LOG_ERROR(logger, "startFetch: Internal Error in NotificationState %d", int(poData->potreeNotificationState));
662 }
663
664 poData->requestsInFlight++;
665}
666
668{
669 if (poComp && poData) {
670 if (poData->requestsInFlight > 0) {
671 poData->requestsInFlight--;
672 }
673 else {
674 LOG_ERROR(logger, "endFetch: Too many endFetch received");
675 return;
676 }
679 context->engine->invokeComponentNotifyCallback(*poComp, (int)PotreeNotification::FetchingEnd, nullptr, 0);
680 }
682 // Close to assert(false).
683 LOG_ERROR(logger, "endFetch: Internal Error in NotificationState %d", int(poData->potreeNotificationState));
684 }
685 }
686}
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
Definition: Component.h:202
ComponentType * getComponent() const
Definition: Component.h:159
Sets up a clipping shape that can be used by multiple entities.
Component that attaches a ClipShape to an entity.
virtual ComponentHandle createComponent()
Create a new component instance.
Context * context
Pointer to the Context instance the system lives in.
virtual void initialize(Context *context)
Initialize the system.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
void update()
Updates the system state to that of the current frame.
void preUpdate()
Run the pre-update method of the system.
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Services > services
Services.
Definition: Context.h:174
class IRenderer * renderer
Renderer.
Definition: Context.h:228
std::unique_ptr< class Bounds > bounds
Bounds service instance.
Definition: Context.h:216
std::unique_ptr< class RayPicking > rayPicking
RayPicking service instance.
Definition: Context.h:213
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
std::unique_ptr< class Engine > engine
Engine instance.
Definition: Context.h:222
virtual void registerExtension(IRendererExtension *extension)=0
Register an extension with the renderer.
virtual void unregisterExtension(IRendererExtension *extension)=0
Unregister an extension with the renderer.
ComponentModel::ComponentHandle clipShapeComponent
Handle to the currently active clip component, if any.
constexpr bool isVisible() const
Check if the entity is visible or not.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
glm::dvec3 coordinates
Global coordinates.
std::unique_ptr< class DPIService > dpiService
DPI service instance.
Definition: ViewContext.h:71
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
Definition: EntityPtr.h:12
@ FetchingBegin
Loading queue transitions from empty to non-empty, that is, instance needs data.
@ FetchingEnd
Loading queue transitions from non-empty to empty, that is, instance has all data it currently needs.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
@ CenterOnOrigin
Adjust offset s.t. point cloud is centered around entity origin.
@ None
Just use scale and offset as is.
@ CenterOnOriginAdjustCoordinate
Adjust offset s.t. point cloud is centered around enttiy origin and adjust transformcomponent coordin...
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ OpenGLES30
Graphics device using the OpenGLES 3.0 API.
@ VertexData
Per vertex data.
@ Position
Position semantic.
@ Color
Color semantic.
@ TextureCoordinate
Texture coordinate semantic.
Handle to a Component instance.
Definition: Component.h:67
COGSFOUNDATION_API class Component * resolve() const
Resolve the handle, returning a pointer to the held Component instance.
Definition: Component.cpp:65
static ComponentHandle Empty()
Returns an empty, invalid handle. Will evaluate to false if tested against using operator bool().
Definition: Component.h:119
void setVec3Property(const VariableKey key, glm::vec3 value)
Set the vec3 property with the given key to value.
Material * material
Material resource this MaterialInstance is created from.
Component for Point Cloud Display.
float maxPointSize
Maximum point size in pixels regardless of point size strategy, gets DPI scaled.
std::vector< glm::vec4 > clipPlanes
Deprecated, use clip shapes instead.
MaterialInstanceHandle boxMaterial
Optional debug material override, material should inherit from PotreeBoxCore.material.
float shadingIntensity
Intensity of point shading effect, if enabled.
float pointSize
Base point size in pixels, gets DPI scaled.
float outlineSize
Size of point outlines, set to 0.0 to disable point outlines.
PotreeColoring coloring
Specify coloring strategy.
TextureHandle gradient
Optional gradient texture to colorize scalar values.
std::string source
URL of path where metadata.js(OLD format: cloud.js) resides.
MaterialInstanceHandle pointMaterial
Optional point material override, material should inherit from PotreePointCore.material.
glm::vec3 outlineColor
Color of point outlines if enabled.
float rootNodeMinScreenSize
Minimal screen-space size root node can have before being eligble for distance-based culling.
PotreeShading shading
Specify optional shading.
PotreePointShape pointShape
Specify point shape.
glm::vec4 baseColor
Base color to use when Base coloring strategy is used.
float densityBias
DistanceAndDensityScaled: Offset where depth and density starts to affectg point size calc.
PotreePointSizing pointSizing
Specify point sizing strategy.
bool disableCustomClipping
Debug switch to disable clipping of points against custom clipping planes.
float densityLevelScale
DistanceAndDensityScaled: Strength of level depth in point size calc.
bool supportPicking
If true, an extra CPU side copy of the point positions are maintained to enable CPU picking.
float pointScreenSpacing
Maximal screen space distance between points before refining, gets adjusted by DPI scaling.
float densityScale
DistanceAndDensityScaled: Strength of local point density in point size calc.
PotreeOriginPolicy originPolicy
Specify origin policy, see enum for details.
PotreeDebugBoxes debugBoxes
Specify optional debug graphics.
float minPointSize
Minimum point size in pixels regardless of point size strategy, gets DPI scaled.
uint32_t requestsInFlight
Number of outstanding data fetch requests.
Definition: PotreeSystem.h:224
float val
Current dpi-scaled and min-max clamped point-size.
Definition: PotreeSystem.h:227
float min
Current dpi-scaled minimum point-size.
Definition: PotreeSystem.h:228
float max
Current dpi-scaled maximum point-size.
Definition: PotreeSystem.h:229
static constexpr size_t MaxClipPlanes
Max available in shader.
Definition: PotreeSystem.h:216
std::string jsonPath
URL to metadata file.
Definition: PotreeSystem.h:211
PotreeNotification potreeNotificationState
Current notification state for this PoTree Component.
Definition: PotreeSystem.h:221
std::string rootPath
URL to folder containing metadata file.
Definition: PotreeSystem.h:212
ComponentHandle createComponent() override
static void startFetch(Context *context, const PotreeComponent *poComp, PotreeData *poData)
Update Component request count and notify client when starting data fetch.
void destroyComponent(ComponentHandle component) override
void cleanup(Context *context) override
Provided for custom cleanup logic in derived systems.
void initialize(Context *context) override
Initialize the system.
static void endFetch(Context *context, const PotreeComponent *poComp, PotreeData *poData)
Abstract base class storing data read from a file.
Definition: FileContents.h:20
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
Definition: SamplerState.h:17