Cogs.Core
OctSystem.cpp
1#include "Context.h"
2#include "ExtensionRegistry.h"
3#include "Systems/Core/CameraSystem.h"
4#include "Systems/Core/TransformSystem.h"
5#include "Resources/MaterialManager.h"
6
7#include "../OctBounds.h"
8#include "OctSystem.h"
9#include "../Renderers/OctRenderer.h"
10
11#include "Rendering/ICapabilities.h"
12#include "Rendering/IBuffers.h"
13#include "Rendering/IEffects.h"
14
15#include "Foundation/BitTwiddling/MortonCode.h"
16#include "Foundation/BitTwiddling/PowerOfTwo.h"
17#include "Foundation/Geometry/Glm.hpp"
18#include "Foundation/Logging/Logger.h"
19#include "Foundation/HashSequence.h"
20
21
22namespace {
23 using namespace Cogs::Core;
24
25 Cogs::Logging::Log logger = Cogs::Logging::getLogger("OctSystem");
26
27 void updateMaterialVariant(Volumetric::OctData& octData)
28 {
29 switch (octData.source)
30 {
31 case Volumetric::OctSource::Value:
32 octData.materialInstance->setVariant("Source", "Value");
33 break;
34 case Volumetric::OctSource::ValueAge:
35 octData.materialInstance->setVariant("Source", "ValueAge");
36 break;
37 }
38 octData.materialInstance->setVariant("AlphaTest", "Discard");
39 octData.materialInstance->setFloatProperty(octData.materialInstance->material->getFloatKey("alphaThreshold"), 0.0f);
40 }
41
42
43
44 bool forgetRegion(Volumetric::OctData& octData, const uint64_t regionKey)
45 {
46 auto jt = octData.knownRegions.find(regionKey);
47 if (jt != octData.knownRegions.end()) {
48 auto & regData = jt->second;
49 for (const auto blockKey : regData->baseBlocks) {
50
51 // Find block and erase.
52 auto lt = octData.baseBlocks.find(blockKey);
53 lt->second->regionKeys.erase(regData->regionKey);
54
55 // If block has no regions left, delete it.
56 if (lt->second->regionKeys.empty()) {
57 octData.baseBlockPool.destroy(lt->second);
58 octData.baseBlocks.erase(lt);
59 }
60 }
61
62 octData.knownRegionPool.destroy(jt->second);
63 octData.knownRegions.erase(jt);
64 return true;
65 }
66 return false;
67 }
68
69 bool removeRegions(Volumetric::OctComponent& octComp, Volumetric::OctData& octData)
70 {
71 for (const auto regionKey : octComp.regionsToRemove) {
72 forgetRegion(octData, regionKey);
73 }
74 auto rv = !octComp.regionsToRemove.empty();
75 octComp.regionsToRemove.clear();
76 return rv;
77 }
78
79 bool addRegions(Volumetric::OctComponent& octComp, Volumetric::OctData& octData, std::vector<Volumetric::Region>& regionsToAdd)
80 {
81 const glm::vec3 blockScale(1.f / octComp.blockExtent.x,
82 1.f / octComp.blockExtent.y,
83 1.f / octComp.blockExtent.z);
84
85 const auto skirtExpand = float(Volumetric::OctSystem::skirtSize) / float(octComp.tileSize - Volumetric::OctSystem::skirtSize);
86
87 const glm::vec3 skirtExtent = octComp.blockExtent * skirtExpand;
88
89 const auto M = glm::scale(blockScale) * glm::translate(-octComp.blockShift);
90
91
92 for (auto & region : regionsToAdd) {
93
94 Volumetric::OctRegionData* regData = octData.knownRegionPool.create();
95 regData->regionKey = region.regionKey;
96 regData->min = region.min;
97 regData->max = region.max;
98
99 // Determine range of base blocks intersecting the region.
100 glm::ivec3 i;
101 const auto A4 = M * glm::vec4(region.min - skirtExtent, 1.f);
102 const auto A = glm::ivec3(glm::floor((1.f / A4.w)*glm::vec3(A4)));
103
104 const auto B4 = M * glm::vec4(region.max + skirtExtent, 1.f);
105 const auto B = glm::ivec3(glm::floor((1.f / B4.w)*glm::vec3(B4)));
106 regData->baseBlocks.reserve((B.x - A.x + 1)*(B.y - A.y + 1)*(B.z - A.z + 1));
107 for (i.z = A.z; i.z <= B.z; i.z++) {
108 for (i.y = A.y; i.y <= B.y; i.y++) {
109 for (i.x = A.x; i.x <= B.x; i.x++) {
110 const auto baseBlockKey = Volumetric::createBaseBlockKey(i.x, i.y, i.z);
111
112 Volumetric::OctBaseBlock* baseBlock;
113 auto blockIt = octData.baseBlocks.find(baseBlockKey);
114 if (blockIt == octData.baseBlocks.end()) { // New base block
115 baseBlock = octData.baseBlockPool.create();
116 baseBlock->ix3 = i;
117 octData.baseBlocks[baseBlockKey] = baseBlock;
118 }
119 else {
120 baseBlock = blockIt->second;
121 }
122
123 baseBlock->timestamp = octData.currentTimestamp;
124 baseBlock->regionKeys.insert(region.regionKey);
125 regData->baseBlocks.push_back(baseBlockKey);
126 }
127 }
128 }
129
130 if (forgetRegion(octData, region.regionKey)) {
131 LOG_DEBUG(logger, "Region %PRIu64 overwritten", region.regionKey);
132 }
133 octData.knownRegions[region.regionKey] = std::move(regData);
134 }
135 auto rv = !regionsToAdd.empty();
136 regionsToAdd.clear();
137 return rv;
138 }
139
140 void wipe(Volumetric::OctData* octData)
141 {
142 LOG_DEBUG(logger, "Wiped %d baseblocks and %d regions.", unsigned(octData->baseBlocks.size()), unsigned(octData->knownRegions.size()));
143 for (auto it : octData->baseBlocks) {
144 octData->baseBlockPool.destroy(it.second);
145 }
146 octData->baseBlocks.clear();
147
148 for (auto it : octData->knownRegions) {
149 octData->knownRegionPool.destroy(it.second);
150 }
151 octData->knownRegions.clear();
152 }
153
154}
155
156
157Cogs::Core::Volumetric::OctData::~OctData()
158{
159 // Make sure that destructors for pool contents get called.
160 wipe(this);
161}
162
164{
166
167 renderer = new OctRenderer();
168 context->renderer->registerExtension(renderer);
169
170 material = context->materialManager->loadMaterial("OcttreeRaycastMaterial.material");
171 context->materialManager->processLoading();
172 transferTexKey = material->getTextureKey("transferTexture");
173 volumeTexKey = material->getTextureKey("volumeTexture");
174
175 bounds = new OctBounds();
176 bounds->octSystem = this;
177 context->bounds->addBoundsExtension(bounds);
178
179 auto device = context->device;
180 auto buffers = device->getBuffers();
181
183 desc.name = "DebugEffect";
184 desc.type = EffectDescriptionType::File;
185 switch (device->getType()) {
187 desc.vertexShader = "Engine/DebugVS.es30.glsl";
188 desc.pixelShader = "Engine/DebugPS.es30.glsl";
189 desc.flags = static_cast<EffectFlags::EEffectFlags>(desc.flags | EffectFlags::GLSL);
190 break;
191 default:
192 desc.vertexShader = "Engine/DebugVS.hlsl";
193 desc.pixelShader = "Engine/DebugPS.hlsl";
194 break;
195 }
196 effectHandle = device->getEffects()->loadEffect(desc);
197
198 VertexElement elements[] = {
199 { 0, DataFormat::X32Y32Z32_FLOAT, ElementSemantic::Position, 0, InputType::VertexData, {} }
200 };
201 debugVertexFormat = buffers->createVertexFormat(elements, 1);
202}
203
205{
206 Cogs::IEffects* effects = context->device->getEffects();
207 Cogs::ResourceStatus status = effects->checkEffect(effectHandle);
208
209 if (status == Cogs::ResourceStatus::Ready && effectStatus != Cogs::ResourceStatus::Ready) {
210 Cogs::IBuffers* buffers = context->device->getBuffers();
211 inputLayoutHandle = buffers->loadInputLayout(&debugVertexFormat, 1, effectHandle);
212 }
213
214 if (status == Cogs::ResourceStatus::Error && effectStatus != Cogs::ResourceStatus::Error) {
215 LOG_ERROR(logger, "OctSystem: Failed to build debug wireframe effect.");
216 }
217
218 effectStatus = status;
219 return status == Cogs::ResourceStatus::Ready;
220}
221
223{
225 auto * comp = component.resolveComponent<OctComponent>();
226 comp->system = this;
227 auto & data = getData(comp);
228 data.comp = comp;
229 for (unsigned i = 0; i < 32; i++) {
230 data.tileResponsesStash.push_back(new TileResponse());
231 }
232 data.materialInstance = context->materialInstanceManager->createMaterialInstance(material);
233 updateMaterialVariant(data);
234 return component;
235}
236
238{
239 for (auto & octComp : pool) {
240 auto & octData = getData(&octComp);
241
242 if (octData.source != octComp.source) {
243 octData.source = octComp.source;
244 updateMaterialVariant(octData);
245 octComp.forceWipe = true;
246 }
247
248 octData.currentTimestamp++;
249 bool baseBlocksModified = false;
250
251 // Sanity checks on octComp properties, force to sane ranges.
252 octComp.tileSize = std::max(8u, std::min(256u, octComp.tileSize));
253 octComp.gpuCacheSize = std::max(1u, std::min((2048 / octComp.tileSize), octComp.gpuCacheSize));
254
255 // Check if basic assumptions used by the system has changed, if so, flush everything.
256 const size_t layoutHash = hashSequence(octComp.blockExtent,
257 octComp.blockShift,
258 octComp.tileSize,
259 octComp.gpuCacheSize);
260 if (octComp.forceWipe || octComp.clearAllRegions || octData.layoutHash != layoutHash) {
261 octComp.forceWipe = false;
262 octComp.clearAllRegions = false;
263 octData.gpuCacheWipe = true;
264 octData.layoutHash = layoutHash;
265 octData.maxFrontSize = octComp.gpuCacheSize * octComp.gpuCacheSize * octComp.gpuCacheSize - 1u;
266
267 std::vector<Region> knownRegions;
268 if (!octComp.clearAllRegions) {
269 // Wipe and re-insert regions.
270 knownRegions.reserve(octData.knownRegions.size());
271 for (auto & it : octData.knownRegions) {
272 const auto * regData = it.second;
273 knownRegions.push_back(Region{ regData->regionKey, regData->min, regData->max });
274 }
275 }
276 wipe(&octData);
277 addRegions(octComp, octData, knownRegions);
278 baseBlocksModified = true;
279 }
280
281 baseBlocksModified = addRegions(octComp, octData, octComp.regionsToAdd) || baseBlocksModified;
282 baseBlocksModified = removeRegions(octComp, octData) || baseBlocksModified;
283
284 if(baseBlocksModified) {
285 buildTree(context, octComp, octData);
286 }
287
288 // Update current subset of oct-tree nodes based on viewports.
289 const auto * camComp = context->cameraSystem->getMainCamera();
290 const auto & camData = context->cameraSystem->getMainCameraData();
291 const auto * transComp = octComp.getComponent<TransformComponent>();
292 adaptiveSubset(context, octComp, octData, *transComp, *camComp, camData);
293
294 // Build list of tile requests, update timestamps
295 octComp.tileRequests.clear();
296 for (auto & item : octData.front) {
297 const auto & node = octData.nodes[item];
298 const auto tileKey = createTileKey(node.ix4, octData.alignMinToZeroShift);
299 auto staleness = octData.atlas.checkTile(tileKey, node.timestamp, octData.currentTimestamp);
300 if (staleness == 0) {
301 continue;
302 }
303 octComp.tileRequests.emplace_back(TileRequest{ tileKey, staleness });
304 }
305 std::stable_sort(octComp.tileRequests.begin(), octComp.tileRequests.end(), [](const auto &a, const auto & b) {return a.staleness > b.staleness; });
306 }
307
308}
309
310
311void Cogs::Core::Volumetric::OctSystem::buildTree(Context* /*context*/, OctComponent& /*octComp*/, OctData& octData)
312{
313 auto & base = octData.baseBlocks;
314 auto & nodes = octData.nodes;
315
316 if (base.empty()) return;
317
318 // Find min/max index.
319 glm::i16vec3 baseBlockMinIndex = base.begin()->second->ix3;
320 glm::i16vec3 baseBlockMaxIndex = base.begin()->second->ix3;
321 for (auto & it : base) {
322 baseBlockMinIndex = glm::min(baseBlockMinIndex, it.second->ix3);
323 baseBlockMaxIndex = glm::max(baseBlockMaxIndex, it.second->ix3);
324 }
325
326 // An invariant of the oct-tree buildup is that all indices are non-negative.
327
328
329
330 // Realign pyramid if we have new data on the negative sides. Try to preserve nodes by
331 // moving in steps proportional to size of top node.
332 if ((baseBlockMinIndex.x < octData.alignMinToZeroShift.x) || (baseBlockMinIndex.y < octData.alignMinToZeroShift.y) || (baseBlockMinIndex.z < octData.alignMinToZeroShift.z))
333 {
334 auto d = std::max(std::max((baseBlockMaxIndex.x - baseBlockMinIndex.x), (baseBlockMaxIndex.y - baseBlockMinIndex.y)), (baseBlockMaxIndex.z - baseBlockMinIndex.z));
335 auto l = roundUpToPowerOfTwoShift((unsigned)d);
336 auto m = -1 << l;
337 octData.alignMinToZeroShift.x = baseBlockMinIndex.x & m;
338 octData.alignMinToZeroShift.y = baseBlockMinIndex.y & m;
339 octData.alignMinToZeroShift.z = baseBlockMinIndex.z & m;
340 LOG_DEBUG(logger, "alignMinToZeroShift=[%d, %d, %d], l=%d", octData.alignMinToZeroShift.x, octData.alignMinToZeroShift.y, octData.alignMinToZeroShift.z, l);
341 }
342
343 // Build base layer
344 nodes.clear();
345 for (auto & it : octData.baseBlocks) {
346 glm::uvec4 ix4 = glm::uvec4(it.second->ix3 - octData.alignMinToZeroShift, 0);
347 nodes.emplace_back(NodeBlock{});
348 nodes.back().ix = mortonCode(static_cast<uint16_t>(ix4.x),
349 static_cast<uint16_t>(ix4.y),
350 static_cast<uint16_t>(ix4.z));
351 nodes.back().timestamp = it.second->timestamp;
352 nodes.back().ix4 = ix4;
353 nodes.back().extentMin = glm::uvec3(ix4);
354 nodes.back().extentMax = glm::uvec3(ix4) + glm::uvec3(1);
355 nodes.back().baseBlock = it.second;
356 }
357
358 // Sort by Morton code, which linearizes the 3D structure of the oct-tree.
359 std::sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) -> bool { return a.ix < b.ix; });
360
361 // And build upper layers until we have a single apex node.
362 size_t offset = 0;
363 for (uint16_t l = 1; l < 16 && (1 < nodes.size() - offset); l++) {
364 size_t nextOffset = nodes.size();
365
366 NodeBlock * parent = nullptr;
367 for (size_t i = offset; i < nextOffset; i++) {
368 const auto child = nodes[i];
369 // Upper bits of morton code give parent index.
370 const auto parentIx = child.ix >> 3;
371
372 // Sorted by morton code, so when parent index changes, so the childs of a parent lies successively.
373 if (!parent || parent->ix != parentIx) {
374 nodes.emplace_back(NodeBlock{});
375 parent = &octData.nodes.back();
376 parent->ix = parentIx;
377 parent->timestamp = child.timestamp;
378 parent->ix4 = glm::u16vec4(child.ix4.x >> 1, child.ix4.y >> 1, child.ix4.z >> 1, l);
379 parent->extentMin = child.extentMin;
380 parent->extentMax = child.extentMax;
381 for (unsigned k = 0; k < 8; k++) parent->children[k] = ~0u;
382 }
383 // Some trickery comparing age instead of timestamps directly handle wrap-around of timestamps.
384 parent->timestamp = octData.currentTimestamp - std::min(octData.currentTimestamp - parent->timestamp,
385 octData.currentTimestamp - child.timestamp);
386 // Grow parent to include child node.
387 parent->extentMin = glm::min(parent->extentMin, child.extentMin);
388 parent->extentMax = glm::max(parent->extentMax, child.extentMax);
389 // Lower three bits of morton code give child index.
390 parent->children[child.ix & 7] = static_cast<uint32_t>(i);
391 }
392 offset = nextOffset;
393 }
394}
395
396glm::mat4 Cogs::Core::Volumetric::OctSystem::LocalFromIndexSpaceTransform(const OctComponent& octComp, const OctData& octData)
397{
398 return
399 glm::translate(glm::mat4(), octComp.blockShift) *
400 glm::scale(octComp.blockExtent) *
401 glm::translate(glm::mat4(), glm::vec3(octData.alignMinToZeroShift));
402}
403
404glm::mat4 Cogs::Core::Volumetric::OctSystem::IndexSpaceFromLocalTransform(const OctComponent& octComp, const OctData& octData)
405{
406 return
407 glm::translate(glm::mat4(), -glm::vec3(octData.alignMinToZeroShift)) *
408 glm::scale(glm::vec3(1.f / octComp.blockExtent.x,
409 1.f / octComp.blockExtent.y,
410 1.f / octComp.blockExtent.z)) *
411 glm::translate(glm::mat4(), -octComp.blockShift);
412}
413
414
415void Cogs::Core::Volumetric::OctSystem::adaptiveSubset(Context * context,
416 OctComponent& octComp, OctData& octData,
417 const TransformComponent& transComp,
418 const CameraComponent& /*camComp*/, const CameraData& camData)
419{
420 const auto & nodes = octData.nodes;
421 auto & front = octData.front;
422 auto & stack = octData.stack;
423
424 if (nodes.empty()) return;
425
426 // Fixme: use LODreference
427 // Discard only nodes that is not in any frustums, i.e. process all frustums simultaneously.
428
429
430 const auto & worldFromLocal = context->transformSystem->getLocalToWorld(&transComp);
431 const auto localFromWorld = glm::inverse(worldFromLocal);
432 const auto & ViewFromWorld = camData.viewMatrix;
433 const auto & worldFromView = camData.inverseViewMatrix;
434 const auto & ClipFromWorld = camData.viewProjection;
435 const auto LocalFromIxspc = LocalFromIndexSpaceTransform(octComp, octData);
436 const auto IxspcFromLocal = IndexSpaceFromLocalTransform(octComp, octData);
437 const auto viewFromIxspc = ViewFromWorld * worldFromLocal * LocalFromIxspc;
438 const auto ixspcFromView = IxspcFromLocal * localFromWorld * worldFromView;
439 const auto clipFromIxspc = ClipFromWorld * worldFromLocal * LocalFromIxspc;
440 const auto camOriginIxspc = glm::vec3(ixspcFromView[3]);
441
442 auto buildFront =
443 [maxFrontSize = octData.maxFrontSize,
444 &nodes,
445 &stack,
446 &viewFromIxspc,
447 &clipFromIxspc,
448 &camOriginIxspc](auto & front, const auto threshold) -> bool
449 {
450 front.clear();
451 stack.clear();
452 stack.push_back(static_cast<uint32_t>(nodes.size() - 1));
453
454 while (!stack.empty()) {
455 const auto nodeIx = stack.back();
456 stack.pop_back();
457
458 const auto & n = nodes[nodeIx];
459
460 // Check if node is inside frustum.
461 unsigned planes = 0;
462 for (unsigned i = 0; i < 8; i++) {
463 const glm::vec4 p((i & 1) == 0 ? n.extentMin.x : n.extentMax.x,
464 (i & 2) == 0 ? n.extentMin.y : n.extentMax.y,
465 (i & 4) == 0 ? n.extentMin.z : n.extentMax.z,
466 1.f);
467 auto v = viewFromIxspc * p;
468 auto c = clipFromIxspc * p;
469 planes = planes |
470 (v.z < 0.f ? 1 : 0) |
471 (c.x <= c.w ? 2 : 0) | (-c.w <= c.x ? 4 : 0) |
472 (c.y <= c.w ? 8 : 0) | (-c.w <= c.y ? 16 : 0);
473 }
474
475 if (planes == 31) {
476 // Find point inside node nearest to camera, and determine camera distance.
477 glm::vec3 nearestIxspc = glm::clamp(camOriginIxspc, glm::vec3(n.extentMin), glm::vec3(n.extentMax));
478 glm::vec4 nearestView = viewFromIxspc * glm::vec4(nearestIxspc, 1.f);
479 float camDist = -nearestView.z / nearestView.w;
480
481 if ((0 < n.ix4.w) && (camDist*threshold < (1 << n.ix4.w))) {
482 for (auto childIx : n.children) {
483 if (childIx != ~0u) stack.push_back(childIx);
484 }
485 }
486 else {
487 front.push_back(nodeIx);
488 if (maxFrontSize < front.size()) {
489 return false;
490 }
491 }
492 }
493 }
494 return true;
495 };
496
497 float tolerance = std::max(0.01f, octData.tolerance);
498 //const auto maxFrontSize = octData.maxFrontSize;
499
500 // Try building front until it is smaller than the max size, doubling the tolerance for each try.
501 bool success = false;
502 for (unsigned i = 0; i < 8 && !success; i++) {
503 success = buildFront(front, tolerance);
504 if (!success) {
505 tolerance = 2 * tolerance;
506 }
507 }
508
509 const auto minFrontSize = (size_t)std::max(1.f, 0.9f*octData.maxFrontSize);
510 for (unsigned i = 0; i < 8 && octComp.tolerance < tolerance && front.size() < minFrontSize; i++) {
511 auto t = std::max(octComp.tolerance, 0.9f*tolerance);
512 if (buildFront(octData.frontTmp, t)) {
513 tolerance = t;
514 octData.front.swap(octData.frontTmp);
515 }
516 else {
517 break;
518 }
519 }
520
521 //LOG_DEBUG(logger, "front size=%d / %d", octData.front.size(), octData.maxFrontSize);
522
523 if (octData.tolerance != tolerance) {
524 LOG_DEBUG(logger, "Current tolerance set to %f", octData.tolerance);
525 }
526 octData.tolerance = tolerance;
527
528}
ComponentType * getComponent() const
Definition: Component.h:159
virtual ComponentHandle createComponent()
Create a new component instance.
virtual void initialize(Context *context)
Initialize the system.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
class IRenderer * renderer
Renderer.
Definition: Context.h:228
std::unique_ptr< class Bounds > bounds
Bounds service instance.
Definition: Context.h:216
virtual void registerExtension(IRendererExtension *extension)=0
Register an extension with the renderer.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
Log implementation class.
Definition: LogManager.h:140
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
@ OpenGLES30
Graphics device using the OpenGLES 3.0 API.
ResourceStatus
Status of an asynchronously loaded resource, such as an effect or a pipeline.
Definition: Common.h:198
@ Error
The resource failed to load.
@ Ready
The resource has loaded successfully and is ready for use.
constexpr size_t hashSequence(const T &t, const U &u)
Hash the last two items in a sequence of objects.
Definition: HashSequence.h:8
@ VertexData
Per vertex data.
@ Position
Position semantic.
constexpr uint64_t mortonCode(uint16_t i, uint16_t j, uint16_t k)
Interleave bits of 3 values to form the 3-way Morton code.
Definition: MortonCode.h:10
uint8_t roundUpToPowerOfTwoShift(uint8_t x)
Definition: PowerOfTwo.h:170
Handle to a Component instance.
Definition: Component.h:67
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
void setFloatProperty(const VariableKey key, float value)
Set the float property with the given key to value.
Material * material
Material resource this MaterialInstance is created from.
Represent the blocks at the oct-tree base level. Independent on current the particular oct-tree.
Definition: OctSystem.h:41
std::set< RegionKey > regionKeys
Regions intersecting this block.
Definition: OctSystem.h:44
glm::i16vec3 ix3
Index without block shift.
Definition: OctSystem.h:43
uint32_t timestamp
Timestamp last time a region was added to block.
Definition: OctSystem.h:42
bool clearAllRegions
If set to true, all current regions are discarded before regionsToAdd is processed.
Definition: OctComponent.h:95
std::vector< RegionKey > regionsToRemove
Set of regions to remove. Blocks with no regions are purged from the oct-tree.
Definition: OctComponent.h:103
glm::vec3 blockShift
Object space grid origin, tweak if block boundaries happen at unfortunate places.
Definition: OctComponent.h:73
bool forceWipe
Discard all processed data, but regions persist.
Definition: OctComponent.h:71
std::vector< TileRequest > tileRequests
Requests for tiles, populated by OctSystem::update, consumed by provider.
Definition: OctComponent.h:106
std::vector< Region > regionsToAdd
Regions to add this frame. It is OK to add a region multiple times, and this will invalidate regions ...
Definition: OctComponent.h:100
glm::i16vec3 alignMinToZeroShift
Shift value for baseBlock ix3 to get them non-negative.
Definition: OctSystem.h:86
std::vector< uint64_t > baseBlocks
Base blocks that intersects with this region.
Definition: OctSystem.h:66
glm::vec3 min
Object space min corner of region bounding box.
Definition: OctSystem.h:64
glm::vec3 max
Object space max corner of region bounding box.
Definition: OctSystem.h:65
void initialize(Context *context) override
Initialize the system.
Definition: OctSystem.cpp:163
bool isDebugEffectReady(Context *context)
Definition: OctSystem.cpp:204
ComponentModel::ComponentHandle createComponent() override
Definition: OctSystem.cpp:222
Contains an effect description used to load a single effect.
Definition: IEffects.h:62
EEffectFlags
Effect source flags.
Definition: IEffects.h:27
@ GLSL
Effect source is GLSL.
Definition: IEffects.h:33
Provides buffer management functionality.
Definition: IBuffers.h:13
virtual InputLayoutHandle loadInputLayout(const VertexFormatHandle *vertexFormats, const size_t count, EffectHandle effectHandle)=0
Loads a new input layout to map vertex flow between vertex buffers with the given vertexFormats to ef...
Provides effects and shader management functionality.
Definition: IEffects.h:158
virtual ResourceStatus checkEffect(EffectHandle effectHandle)=0
Check the load status of the effect with the given effectHandle.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38