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, PotreeSystem* poSystem, 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, poSystem, 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*/, PotreeSystem* poSystem, PotreeComponent* /*poComp*/, PotreeData* poData)
116 {
117 Material* PM = poData->pointMaterial.instance->material;
118
119 poData->pointMaterial.instance->setVariant("UseInstancing", poSystem->useInstancing);
120
121 poData->pointMaterial.pointSize = PM->getFloatKey("pointSize");
122 poData->pointMaterial.shadingIntensity = PM->getFloatKey("shadingIntensity");
123 poData->pointMaterial.octTexScale = PM->getFloatKey("octTexScale");
124 poData->pointMaterial.minPointSize = PM->getFloatKey("minPointSize");
125 poData->pointMaterial.maxPointSize = PM->getFloatKey("maxPointSize");
126 poData->pointMaterial.outlineSize = PM->getFloatKey("outlineSize");
127 poData->pointMaterial.outlineColor = PM->getVec3Key("outlineColor");
128 poData->pointMaterial.baseColor = PM->getVec4Key("baseColor");
129 poData->pointMaterial.invShift = PM->getVec3Key("invShift");
130 poData->pointMaterial.invScale = PM->getVec3Key("invScale");
131 poData->pointMaterial.gradient = PM->getTextureKey("Gradient");
132
133 auto* BM = poData->boxMaterial.instance->material;
134 poData->boxMaterial.scale = BM->getVec3Key("scale");
135 poData->boxMaterial.offset = BM->getVec3Key("offset");
136 }
137
138 void refreshMaterialInstances(Context* context, PotreeSystem* system, PotreeComponent* poComp, PotreeData* poData)
139 {
140 float minPointSize = poData->pointSize.min;
141 float maxPointSize = poData->pointSize.max;
142 float pointSize = poData->pointSize.val;
143
144 // The pointSize has already been adjusted with the dpi scale.
145 // We require the unadjusted value to calculate modelSpaceRadius and avoid double adjustment.
146 float rawPointSize = pointSize / context->getDefaultView()->dpiService->getScaleFactor();
147 float pointSizeScale = glm::clamp(rawPointSize / 10.f, 1e-2f, 1e2f);
148 poData->modelSpaceRadius = 0.5f * pointSizeScale * poData->spacing;
149
150 auto& PM = poData->pointMaterial;
151 bool hasFragDepth = context->device->getCapabilities()->getDeviceCapabilities().FragDepth;
152 switch (poComp->pointShape) {
153 case PotreePointShape::Square:
154 PM.instance->setVariant("Shape", "Square");
155 hasFragDepth = false;
156 break;
157 case PotreePointShape::Disc:
158 PM.instance->setVariant("Shape", "Disc");
159 hasFragDepth = false;
160 break;
161 case PotreePointShape::Paraboloid:
162 PM.instance->setVariant("Shape", hasFragDepth ? "Paraboloid" : "Square");
163 break;
164 case PotreePointShape::Sphere:
165 PM.instance->setVariant("Shape", hasFragDepth ? "Sphere" : "Disc");
166 break;
167 default:
168 LOG_FATAL(logger, "Component member pointShape has illegal enum value: %u", unsigned(poComp->pointShape));
169 poComp->pointShape = PotreePointShape::Square;
170 PM.instance->setVariant("Shape", "Square");
171 hasFragDepth = false;
172 break;
173 }
174 // OpenGLES sets depth slightly differently to the HLSL shaders...
175 if ((context->device->getType() != Cogs::GraphicsDeviceType::OpenGLES30)) {
176 PM.instance->setVariant("CustomDepth", hasFragDepth);
177 }
178
179 switch (poComp->pointSizing) {
180 case PotreePointSizing::FixedSize:
181 PM.instance->setVariant("PointSizing", "FixedSize");
182 break;
183 case PotreePointSizing::DistanceScaled:
184 PM.instance->setVariant("PointSizing", "DistanceScaled");
185 break;
186 case PotreePointSizing::DistanceAndDensityScaled:
187 PM.instance->setVariant("PointSizing", "DistanceAndDensityScaled");
188 break;
189 default:
190 LOG_FATAL(logger, "Component member pointSizing has illegal enum value: %u", unsigned(poComp->pointSizing));
191 poComp->pointSizing = PotreePointSizing::FixedSize;
192 PM.instance->setVariant("PointSizing", "FixedSize");
193 break;
194 }
195
196 switch (poComp->shading)
197 {
198 case PotreeShading::Flat:
199 PM.instance->setVariant("BasicShading", false);
200 break;
201 case PotreeShading::Basic:
202 PM.instance->setVariant("BasicShading", true);
203 break;
204 default:
205 LOG_FATAL(logger, "Component member shading has illegal enum value: %u", unsigned(poComp->shading));
206 poComp->shading = PotreeShading::Flat;
207 PM.instance->setVariant("BasicShading", false);
208 break;
209 }
210
211 switch (poComp->coloring) {
212 case PotreeColoring::Base:
213 PM.instance->setVariant("HasColor", false);
214 PM.instance->setVariant("HasIntensity", false);
215 PM.instance->setVariant("Coloring", "Base");
216 break;
217 case PotreeColoring::Color:
218 PM.instance->setVariant("HasColor", poData->hasColor);
219 PM.instance->setVariant("HasIntensity", false);
220 PM.instance->setVariant("Coloring", poData->hasColor ? "Color" : "Base");
221 break;
222 case PotreeColoring::Intensity:
223 PM.instance->setVariant("HasColor", false);
224 PM.instance->setVariant("HasIntensity", poData->hasIntensity);
225 PM.instance->setVariant("Coloring", poData->hasIntensity ? "Intensity" : "Base");
226 break;
227 case PotreeColoring::IntensitySmooth:
228 PM.instance->setVariant("HasColor", false);
229 PM.instance->setVariant("HasIntensity", poData->hasIntensity);
230 PM.instance->setVariant("Coloring", poData->hasIntensity ? "IntensitySmooth" : "Base");
231 break;
232 case PotreeColoring::Level:
233 PM.instance->setVariant("HasColor", false);
234 PM.instance->setVariant("HasIntensity", false);
235 PM.instance->setVariant("Coloring", "Level");
236 break;
237 default:
238 LOG_FATAL(logger, "Component member coloring has illegal enum value: %u", unsigned(poComp->coloring));
239 poComp->coloring = PotreeColoring::Base;
240 PM.instance->setVariant("Coloring", "Base");
241 break;
242 }
243 PM.instance->setVariant("Outline", poComp->outlineSize != 0.f);
244 PM.instance->setVariant("ClipPlanes", static_cast<int>(poData->clipPlaneCount));
245
246 if (PM.pointSize != NoProperty) PM.instance->setFloatProperty(PM.pointSize, pointSize);
247 if (PM.shadingIntensity != NoProperty) PM.instance->setFloatProperty(PM.shadingIntensity, poComp->shadingIntensity);
248 if (PM.octTexScale != NoProperty) PM.instance->setFloatProperty(PM.octTexScale, 1.f / poData->octtreeTextureData.size());
249 if (PM.minPointSize != NoProperty) PM.instance->setFloatProperty(PM.minPointSize, minPointSize);
250 if (PM.maxPointSize != NoProperty) PM.instance->setFloatProperty(PM.maxPointSize, maxPointSize);
251 if (PM.baseColor != NoProperty) PM.instance->setVec4Property(PM.baseColor, poComp->baseColor);
252 if (PM.outlineColor != NoProperty) PM.instance->setVec3Property(PM.outlineColor, poComp->outlineColor);
253 if (PM.outlineSize != NoProperty) PM.instance->setFloatProperty(PM.outlineSize, poComp->outlineSize);
254 if (PM.gradient != NoProperty) PM.instance->setTextureProperty(PM.gradient, HandleIsValid(poComp->gradient) ? poComp->gradient : system->defaultGradient);
255 if (PM.gradient != NoProperty) PM.instance->setTextureAddressMode(PM.gradient, Cogs::SamplerState::Clamp);
256 if (PM.invShift != NoProperty) PM.instance->setVec3Property(PM.invShift, 0.5f * (poData->octtreeFrame.tightBBoxMax - poData->octtreeFrame.tightBBoxMin));
257 if (PM.invScale != NoProperty) PM.instance->setVec3Property(PM.invScale, glm::vec3(1.f) / (poData->octtreeFrame.fullBBoxMax - poData->octtreeFrame.fullBBoxMin));
258
259 auto& BM = poData->boxMaterial;
260 if (BM.scale != NoProperty) BM.instance->setVec3Property(BM.scale, poData->octtreeFrame.fullBBoxSize);
261 if (BM.offset != NoProperty) BM.instance->setVec3Property(BM.offset,-poData->octtreeFrame.currentPositionInEntityFrame);
262 }
263
264 void initComponentData(Context* context, PotreeSystem* system, PotreeComponent* poComp, PotreeData* poData)
265 {
266 poData->instanceId = createId();
267 poData->pointMaterial.instance = context->materialInstanceManager->createMaterialInstance(system->defaultPointMaterial);
268 poData->boxMaterial.instance = context->materialInstanceManager->createMaterialInstance(system->defaultBoxMaterial);
269 setUpMaterial(context, system, poComp, poData);
270 }
271
272 void resetComponentData(Context* context, PotreeSystem* system, PotreeComponent* poComp, PotreeData* poData)
273 {
274 LOG_DEBUG(logger, "[instance=%u] Releasing resources (%s)", poData->instanceId, poData->jsonPath.c_str());
275 while (!poData->subtrees.empty()) {
276 auto* subtree = poData->subtrees.shift();
277 subtree->release(context, poData);
278 poData->subtreeStore.destroy(subtree);
279 }
280 DataFetcherManager::cancelAsyncFetch(context, poData->fetch_id);
281 poData->fetch_id = DataFetcherManager::NoFetchId;
282 assert(poData->subtrees.empty());
283 assert(poData->cellStore.size() == 0);
284 assert(poData->subtreeStore.size() == 0);
285 poData->~PotreeData();
286 poData = new (poData) PotreeData();
287 initComponentData(context, system, poComp, poData);
288 }
289
290}
291
292PotreeVertexLayoutInfo Cogs::Core::PotreeSystem::vertexLayoutInfo[static_cast<size_t>(PotreeVertexLayout::COUNT)] =
293{
294 //POSITION:
295 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
296 //POSITION_COLOR:
297 { 0, 3 * 4, ~0u, ~0u, ~0u, 4 * 4, {
298 {0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::Position, 0, Cogs::InputType::VertexData, 0 },
299 {3 * 4, Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::Color, 0, Cogs::InputType::VertexData, 0 }
300 }},
301 //POSITION_NORMAL:
302 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
303 //POSITION_COLOR_NORMAL:
304 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
305 //POSITION_INTENSITY:
306 { 0, ~0u, ~0u, 3 * 4, ~0u, 4 * 4, {
307 {0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::Position, 0, Cogs::InputType::VertexData, 0 },
308 {3 * 4, Cogs::Format::R32_FLOAT, Cogs::ElementSemantic::TextureCoordinate, 0, Cogs::InputType::VertexData, 0 }
309 }},
310 //POSITION_COLOR_INTENSITY:
311 { 0, 4 * 4, ~0u, 3 * 4, ~0u, 5 * 4, {
312 {0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::Position, 0, Cogs::InputType::VertexData, 0 },
313 {3 * 4, Cogs::Format::R32_FLOAT, Cogs::ElementSemantic::TextureCoordinate, 0, Cogs::InputType::VertexData, 0 },
314 {4 * 4, Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::Color, 0, Cogs::InputType::VertexData, 0 }
315 }},
316 //POSITION_NORMAL_INTENSITY:
317 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
318 //POSITION_COLOR_NORMAL_INTENSITY:
319 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
320 //POSITION_CLASSIFICATION:
321 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
322 //POSITION_COLOR_CLASSIFICATION:
323 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
324 //POSITION_NORMAL_CLASSIFICATION:
325 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
326 //POSITION_COLOR_NORMAL_CLASSIFICATION:
327 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
328 //POSITION_INTENSITY_CLASSIFICATION:
329 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
330 //POSITION_COLOR_INTENSITY_CLASSIFICATION:
331 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
332 //POSITION_NORMAL_INTENSITY_CLASSIFICATION:
333 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
334 //POSITION_COLOR_NORMAL_INTENSITY_CLASSIFICATION:
335 { ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, {}},
336};
337
338// --- Fetch and parse *.hrc -----------------------------------------------
339
340void Cogs::Core::PotreeSystem::issueSubtreeFetch(Context* context, const PotreeComponent* poComp, PotreeData* poData, PotreeCell* parent)
341{
342 PotreeSubtree* subtree = nullptr;
343 if (parent == nullptr && !poData->subtrees.empty()) {
344 LOG_FATAL(logger, "[instanceId=%u] Fetching root node but subtree array is not empty.", poData->instanceId);
345 assert(parent != nullptr || poData->subtrees.empty());
346 return;
347 }
348 subtree = poData->subtrees.push(poData->subtreeStore.create());
349 subtree->subtreeId = createId();
350 subtree->fetchLayoutAndInitialize(context, this, poComp, poData, parent);
351}
352
353bool Cogs::Core::PotreeSystem::lookupAndCheckForStaleness(Context* context, PotreeSystem*& potreeSystem, PotreeData*& poData, PotreeComponent*& poComp, const uint32_t instanceId)
354{
355 potreeSystem = ExtensionRegistry::getExtensionSystem<PotreeSystem>(context);
356 for (auto& poComp_ : potreeSystem->pool) {
357
358 PotreeDataHolder& poDataHolder = potreeSystem->getData(&poComp_);
359 PotreeData* poData_ = poDataHolder.poData.get();
360 assert(poData_);
361
362 if (poData_->instanceId == instanceId) {
363 poComp = &poComp_;
364 poData = poData_;
365 return true;
366 }
367 }
368 LOG_TRACE(logger, "[instance=%u] Failed to lookup instance, doesn't exist anymore.", instanceId);
369 return false;
370}
371
372bool Cogs::Core::PotreeSystem::lookupAndCheckForStaleness(Context* context, PotreeSystem*& potreeSystem, PotreeData*& poData, PotreeComponent*& poComp, PotreeSubtree*& subtree, const uint32_t instanceId, const uint32_t subtreeId)
373{
374 if (lookupAndCheckForStaleness(context, potreeSystem, poData, poComp, instanceId)) {
375 for (auto* item : poData->subtrees) {
376 if (item->subtreeId == subtreeId) {
377 subtree = item;
378 return true;
379 }
380 }
381 }
382 LOG_TRACE(logger, "[instance=%u,tree=%u] Failed to lookup subtree, doesn't exist anymore.", instanceId, subtreeId);
383 return false;
384}
385
387{
389
390 useInstancing = true;
391 if (context->device->getType() == Cogs::GraphicsDeviceType::OpenGLES30) {
392 if (Variable* var = context->variables->getIfExists("potree.renderAsPoints"); var) {
393 useInstancing = !var->getBool(useInstancing);
394 }
395 }
396 LOG_DEBUG(logger, "Render points as %s", useInstancing ? "instanced quads" : "points");
397
398 for (PotreeVertexLayoutInfo& info : vertexLayoutInfo) {
399 for (Cogs::VertexElement& element : info.elements) {
401 element.instanceStep = useInstancing ? 1 : 0;
402 }
403 }
404
405 renderer = new PotreeRenderer();
406 context->renderer->registerExtension(renderer);
407
408 auto* texGen = context->services->getService<TextureGenerator>();
409 defaultGradient = texGen->getTexture(Cogs::Core::ImageType::GradientHeat);
410
411 if (!context->variables->exist(spacingGlobalScaleName)) {
412 context->variables->set(spacingGlobalScaleName, 1.f);
413 }
414
415 if (!context->variables->exist(pointSizeGlobalScaleName)) {
416 context->variables->set(pointSizeGlobalScaleName, 1.f);
417 }
418
419 if (!context->variables->exist(pointSizeGlobalMinScaleName)) {
420 context->variables->set(pointSizeGlobalMinScaleName, 1.f);
421 }
422
423 if (!context->variables->exist(pointSizeGlobalMaxScaleName)) {
424 context->variables->set(pointSizeGlobalMaxScaleName, 1.f);
425 }
426}
427
429{
430 context->renderer->unregisterExtension(renderer);
431 delete renderer;
432}
433
435{
436 // Load it here where base materials etc. should have been loaded.
437 if (!materialLoaded) {
438 context->materialManager->loadMaterial("Materials/PotreeBoxCore.material");
439 context->materialManager->loadMaterial("Materials/PotreePointCore.material");
440 defaultBoxMaterial = context->materialManager->loadMaterial("Materials/PotreeBoxDefault.material");
441 defaultPointMaterial = context->materialManager->loadMaterial("Materials/PotreePointDefault.material");
442 context->materialManager->processLoading();
443
444 materialLoaded = true;
445 }
446
448 auto* poComp = static_cast<PotreeComponent*>(handle.resolve());
449 auto& poDataHolder = getData(poComp);
450 poDataHolder.poData = std::make_unique<PotreeData>();
451 initComponentData(context, this, poComp, poDataHolder.poData.get());
452
453 if(pool.size() == 1) {
454 assert(bounds == nullptr);
455 bounds = new PotreeBounds(this);
456 context->bounds->addBoundsExtension(bounds);
457
458 assert(picker == nullptr);
459 picker = new PotreePicker(this);
460 context->rayPicking->addPickable(picker);
461
462 LOG_DEBUG(logger, "Registered potree bounds and picker extensions");
463 }
464
465 return handle;
466}
467
468
469
471{
473 if (pool.size() == 0) {
474 assert(bounds);
475 context->bounds->removeBoundsExtension(bounds);
476 delete bounds;
477 bounds = nullptr;
478
479 assert(picker);
480 context->rayPicking->removePickable(picker);
481 delete picker;
482 picker = nullptr;
483
484 LOG_DEBUG(logger, "Last component destroyed, unregistered potree bounds and picker extensions");
485 }
486}
487
489{
491 CpuInstrumentationScope(SCOPE_POTREE, "PotreeSystem::preUpdate");
492
493 for (auto& poComp : pool) {
495 assert(trComp);
496
497 PotreeDataHolder& poDataHolder = getData(&poComp);
498 PotreeData* poData = poDataHolder.poData.get();
499
500 switch (poComp.originPolicy) {
502 poData->octtreeFrame.currentPositionInEntityFrame = poData->octtreeFrame.positionInEntityFrame;
503 break;
505 poData->octtreeFrame.currentPositionInEntityFrame = glm::vec3(0.f);
506 break;
508 poData->octtreeFrame.currentPositionInEntityFrame = glm::vec3(0.f);
509 if (trComp->coordinates != poData->octtreeFrame.positionInEntityFrame) {
510 trComp->coordinates = poData->octtreeFrame.positionInEntityFrame;
511 trComp->setChanged();
512 }
513 break;
514 default:
516 }
517 poData->originPolicy = poComp.originPolicy;
518 }
519}
520
521void Cogs::Core::PotreeSystem::updateOcttreeFrame(Context* context, PotreeData& poData, const PotreeComponent& poComp)
522{
523 const glm::vec3& o = poData.octtreeFrame.currentPositionInEntityFrame;
524 const TransformComponent* transformComp = poComp.getComponent<TransformComponent>();
525 poData.worldFromOcttreeFrame = context->transformSystem->getLocalToWorld(transformComp) * glm::mat4(1.f, 0.f, 0.f, 0.f,
526 0.f, 1.f, 0.f, 0.f,
527 0.f, 0.f, 1.f, 0.f,
528 o.x, o.y, o.z, 1.f);
529
530}
531
532
534{
535 CpuInstrumentationScope(SCOPE_POTREE, "PotreeSystem::update");
536
537 const float spacingGlobalScale = context->variables->get(spacingGlobalScaleName, 1.f);
538 const float pointSizeGlobalMinScale = context->variables->get(pointSizeGlobalMinScaleName, 1.f);
539 const float pointSizeGlobalMaxScale = context->variables->get(pointSizeGlobalMaxScaleName, 1.f);
540 const float pointSizeGlobalScale = context->variables->get(pointSizeGlobalScaleName, 1.f);
541
542 for (auto& poComp : pool) {
543
544 bool notifyRunning = false;
545 PotreeDataHolder& poDataHolder = getData(&poComp);
546 PotreeData* poData = poDataHolder.poData.get();
547 assert(poData);
548 if (!poComp.isVisible()) {
549 if (poData->state != PotreeState::Uninitialized) {
550 resetComponentData(context, this, &poComp, poData);
551 assert(poData->state == PotreeState::Uninitialized);
552 }
553 continue; // Don't fetch anything while it is not visible.
554 }
555
556
557 if (poData->state != PotreeState::Uninitialized &&
558 ((poData->jsonPath != poComp.source) ||
559 (poData->supportPicking != poComp.supportPicking)))
560 {
561 resetComponentData(context, this, &poComp, poData);
562 assert(poData->state == PotreeState::Uninitialized);
563 }
564
565 if (poData->state == PotreeState::Uninitialized) {
566
567 if (!poComp.source.empty() && (poData->jsonPath != poComp.source)) {
568 poData->jsonPath = poComp.source;
569 if (size_t o = poData->jsonPath.find_last_of("\\/"); o != std::string::npos) {
570 poData->rootPath = poData->jsonPath.substr(0, o + 1);
571 }
572 else {
573 poData->rootPath.clear();
574 }
575 poData->supportPicking = poComp.supportPicking;
576 poData->instanceId = createId();
577 poData->state = PotreeState::RequestedCloudJS;
578 issueCloudJSFetch(context, this, &poComp, poData);
579 continue;
580 }
581 }
582
583 if (poData->state == PotreeState::ParsedCloudJs) {
584 poData->state = PotreeState::Running;
585 notifyRunning = true;
586 issueSubtreeFetch(context, &poComp, poData, nullptr); // fetch root subtree
587 }
588
589 // Sanity checks
590 bool materialChanged = false;
591
592 if (poComp.clipPlanes.empty()) {
593 materialChanged = materialChanged || (poData->clipPlaneCount != 0);
594
595 // Check if we have a clip shape
596 if (const ClipShapeRefComponent* clipRefComp = poComp.getComponent<ClipShapeRefComponent>(); clipRefComp) {
597 if (EntityPtr clipCompEntity = clipRefComp->clipShape.lock(); clipCompEntity) {
598 if (ComponentHandle clipComp = clipCompEntity->getComponentHandle<ClipShapeComponent>(); clipComp) {
599 poComp.clipShapeComponent = clipComp;
600 }
601 }
602 }
603
604 }
605 else {
606
607 // Do not use clipshapes
609
610 const size_t clipPlaneCount = std::min(poComp.disableCustomClipping ? size_t(0) : PotreeData::MaxClipPlanes, poComp.clipPlanes.size());
611 for (size_t i = 0; i < clipPlaneCount; i++) {
612 poData->clipPlanes[i] = poComp.clipPlanes[i];
613 }
614
615 materialChanged = materialChanged || (poData->clipPlaneCount != clipPlaneCount);
616 poData->clipPlaneCount = clipPlaneCount;
617 }
618
619
620 if (poComp.pointMaterial) {
621 if (poData->pointMaterial.instance != poComp.pointMaterial) {
622 LOG_DEBUG(logger, "Using point material instance from component, state=%u", uint32_t(poData->state));
623 poData->pointMaterial.instance = poComp.pointMaterial;
624 materialChanged = true;
625 }
626 }
627 else {
628 if (!poData->pointMaterial.instance || poData->pointMaterial.instance->material != defaultPointMaterial.resolve()) {
629 LOG_DEBUG(logger, "Creating default point material instance, state=%u", uint32_t(poData->state));
630 poData->pointMaterial.instance = context->materialInstanceManager->createMaterialInstance(defaultPointMaterial);
631 materialChanged = true;
632 }
633 }
634
635 if (poComp.boxMaterial) {
636 if (poData->boxMaterial.instance != poComp.boxMaterial) {
637 LOG_DEBUG(logger, "Using box material instance from component, state=%u", uint32_t(poData->state));
638 poData->boxMaterial.instance = poComp.boxMaterial;
639 materialChanged = true;
640 }
641 }
642 else {
643 if (!poData->boxMaterial.instance || poData->boxMaterial.instance->material != defaultBoxMaterial.resolve()) {
644 LOG_DEBUG(logger, "Creating default box material instance, state=%u", uint32_t(poData->state));
645 poData->boxMaterial.instance = context->materialInstanceManager->createMaterialInstance(defaultBoxMaterial);
646 materialChanged = true;
647 }
648 }
649 if (materialChanged) setUpMaterial(context, this, &poComp, poData);
650
651
652 updateOcttreeFrame(context, *poData, poComp);
653
654 float gridSize = std::max(1.f, poData->octtreeFrame.fullBBoxSize.x / poData->spacing);
655 float dpiScale = context->getDefaultView()->dpiService->getScaleFactor();
656 poData->tolerance = std::max(1.f, dpiScale * gridSize * spacingGlobalScale * poComp.pointScreenSpacing);
657 poData->rootTolerance = std::max(1.f, dpiScale * poComp.rootNodeMinScreenSize);
658
659 poComp.minPointSize = glm::clamp(poComp.minPointSize, 1.f, 100.f);
660 poComp.maxPointSize = glm::clamp(poComp.maxPointSize, 1.f, 100.f);
661 poComp.minPointSize = glm::min(poComp.minPointSize, poComp.maxPointSize);
662
663 poData->pointSize.min = pointSizeGlobalMinScale * dpiScale * poComp.minPointSize;
664 poData->pointSize.max = pointSizeGlobalMaxScale * dpiScale * poComp.maxPointSize;
665 poData->pointSize.val = glm::clamp(pointSizeGlobalScale * dpiScale * poComp.pointSize, poData->pointSize.min, poData->pointSize.max);
666
667 poData->densitySized.levelScale = poComp.densityLevelScale;
668 poData->densitySized.scale = poComp.densityScale;
669 poData->densitySized.bias = poComp.densityBias;
670
671 poData->debugBoxes = poComp.debugBoxes;
672 refreshMaterialInstances(context, this, &poComp, poData);
673
674 if (notifyRunning) {
675 context->engine->invokeComponentNotifyCallback(poComp, (int)PotreeNotification::Ready, nullptr, 0);
676 }
677 }
678 updateLodLevels(context);
679
680}
681
683{
684 if (poData->requestsInFlight == 0) {
687 context->engine->invokeComponentNotifyCallback(*poComp, (int)PotreeNotification::FetchingBegin, nullptr, 0);
688 }
690 LOG_ERROR(logger, "startFetch: Internal Error in NotificationState %d", int(poData->potreeNotificationState));
691 }
692
693 poData->requestsInFlight++;
694}
695
697{
698 if (poComp && poData) {
699 if (poData->requestsInFlight > 0) {
700 poData->requestsInFlight--;
701 }
702 else {
703 LOG_ERROR(logger, "endFetch: Too many endFetch received");
704 return;
705 }
708 context->engine->invokeComponentNotifyCallback(*poComp, (int)PotreeNotification::FetchingEnd, nullptr, 0);
709 }
711 // Close to assert(false).
712 LOG_ERROR(logger, "endFetch: Internal Error in NotificationState %d", int(poData->potreeNotificationState));
713 }
714 }
715}
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:140
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:50
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.
@ Ready
CloudJS has been parsed and we know bounds etc.
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:181
@ OpenGLES30
Graphics device using the OpenGLES 3.0 API.
@ InstanceData
Per instance data.
@ 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.
Material resources define the how of geometry rendering (the what is defined by Mesh and Texture reso...
Definition: Material.h:82
void setFloatProperty(const VariableKey key, float value)
Set the float property with the given key to value.
Definition: Material.h:240
void setVec4Property(const VariableKey key, glm::vec4 value)
Set the vec4 property with the given key to value.
Definition: Material.h:207
void setTextureProperty(const VariableKey key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
Definition: Material.cpp:116
void setVec3Property(const VariableKey key, glm::vec3 value)
Set the vec3 property with the given key to value.
Definition: Material.h:196
void setTextureAddressMode(const VariableKey key, SamplerState::AddressMode mode)
Set the address mode used for the texture property with the given key to mode.
Definition: Material.cpp:142
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)
Runtime control variable.
Definition: Variables.h:27
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
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38
InputType inputType
Input type of the element, vertex or instance data.
Definition: VertexFormat.h:43
uint16_t instanceStep
Instance step factor.
Definition: VertexFormat.h:44