2#include "ExtensionRegistry.h"
3#include "Systems/Core/CameraSystem.h"
4#include "Systems/Core/TransformSystem.h"
5#include "Resources/MaterialManager.h"
7#include "../OctBounds.h"
9#include "../Renderers/OctRenderer.h"
11#include "Rendering/ICapabilities.h"
12#include "Rendering/IBuffers.h"
13#include "Rendering/IEffects.h"
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"
29 switch (octData.source)
31 case Volumetric::OctSource::Value:
32 octData.materialInstance->setVariant(
"Source",
"Value");
34 case Volumetric::OctSource::ValueAge:
35 octData.materialInstance->setVariant(
"Source",
"ValueAge");
38 octData.materialInstance->setVariant(
"AlphaTest",
"Discard");
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) {
52 auto lt = octData.baseBlocks.find(blockKey);
53 lt->second->regionKeys.erase(regData->regionKey);
56 if (lt->second->regionKeys.empty()) {
57 octData.baseBlockPool.destroy(lt->second);
58 octData.baseBlocks.erase(lt);
62 octData.knownRegionPool.destroy(jt->second);
63 octData.knownRegions.erase(jt);
72 forgetRegion(octData, regionKey);
81 const glm::vec3 blockScale(1.f / octComp.blockExtent.x,
82 1.f / octComp.blockExtent.y,
83 1.f / octComp.blockExtent.z);
85 const auto skirtExpand = float(Volumetric::OctSystem::skirtSize) / float(octComp.tileSize - Volumetric::OctSystem::skirtSize);
87 const glm::vec3 skirtExtent = octComp.blockExtent * skirtExpand;
89 const auto M = glm::scale(blockScale) * glm::translate(-octComp.
blockShift);
92 for (
auto & region : regionsToAdd) {
95 regData->regionKey = region.regionKey;
96 regData->
min = region.min;
97 regData->
max = region.max;
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)));
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);
113 auto blockIt = octData.baseBlocks.find(baseBlockKey);
114 if (blockIt == octData.baseBlocks.end()) {
115 baseBlock = octData.baseBlockPool.create();
117 octData.baseBlocks[baseBlockKey] = baseBlock;
120 baseBlock = blockIt->second;
123 baseBlock->
timestamp = octData.currentTimestamp;
124 baseBlock->
regionKeys.insert(region.regionKey);
130 if (forgetRegion(octData, region.regionKey)) {
131 LOG_DEBUG(logger,
"Region %PRIu64 overwritten", region.regionKey);
133 octData.knownRegions[region.regionKey] = std::move(regData);
135 auto rv = !regionsToAdd.empty();
136 regionsToAdd.clear();
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);
146 octData->baseBlocks.clear();
148 for (
auto it : octData->knownRegions) {
149 octData->knownRegionPool.destroy(it.second);
151 octData->knownRegions.clear();
157Cogs::Core::Volumetric::OctData::~OctData()
170 material = context->materialManager->loadMaterial(
"OcttreeRaycastMaterial.material");
171 context->materialManager->processLoading();
172 transferTexKey = material->getTextureKey(
"transferTexture");
173 volumeTexKey = material->getTextureKey(
"volumeTexture");
176 bounds->octSystem =
this;
177 context->
bounds->addBoundsExtension(bounds);
179 auto device = context->device;
180 auto buffers = device->getBuffers();
183 desc.name =
"DebugEffect";
185 switch (device->getType()) {
187 desc.vertexShader =
"Engine/DebugVS.es30.glsl";
188 desc.pixelShader =
"Engine/DebugPS.es30.glsl";
192 desc.vertexShader =
"Engine/DebugVS.hlsl";
193 desc.pixelShader =
"Engine/DebugPS.hlsl";
196 effectHandle = device->getEffects()->loadEffect(desc);
201 debugVertexFormat = buffers->createVertexFormat(elements, 1);
211 inputLayoutHandle = buffers->
loadInputLayout(&debugVertexFormat, 1, effectHandle);
215 LOG_ERROR(logger,
"OctSystem: Failed to build debug wireframe effect.");
218 effectStatus = status;
225 auto * comp = component.resolveComponent<
OctComponent>();
227 auto & data = getData(comp);
229 for (
unsigned i = 0; i < 32; i++) {
232 data.materialInstance = context->materialInstanceManager->createMaterialInstance(material);
233 updateMaterialVariant(data);
239 for (
auto & octComp : pool) {
240 auto & octData = getData(&octComp);
242 if (octData.source != octComp.source) {
243 octData.source = octComp.source;
244 updateMaterialVariant(octData);
248 octData.currentTimestamp++;
249 bool baseBlocksModified =
false;
252 octComp.tileSize = std::max(8u, std::min(256u, octComp.tileSize));
253 octComp.gpuCacheSize = std::max(1u, std::min((2048 / octComp.tileSize), octComp.gpuCacheSize));
256 const size_t layoutHash =
hashSequence(octComp.blockExtent,
259 octComp.gpuCacheSize);
263 octData.gpuCacheWipe =
true;
264 octData.layoutHash = layoutHash;
265 octData.maxFrontSize = octComp.gpuCacheSize * octComp.gpuCacheSize * octComp.gpuCacheSize - 1u;
267 std::vector<Region> knownRegions;
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 });
277 addRegions(octComp, octData, knownRegions);
278 baseBlocksModified =
true;
281 baseBlocksModified = addRegions(octComp, octData, octComp.
regionsToAdd) || baseBlocksModified;
282 baseBlocksModified = removeRegions(octComp, octData) || baseBlocksModified;
284 if(baseBlocksModified) {
285 buildTree(context, octComp, octData);
289 const auto * camComp = context->cameraSystem->getMainCamera();
290 const auto & camData = context->cameraSystem->getMainCameraData();
292 adaptiveSubset(context, octComp, octData, *transComp, *camComp, camData);
296 for (
auto & item : octData.front) {
297 const auto & node = octData.nodes[item];
299 auto staleness = octData.atlas.checkTile(tileKey, node.timestamp, octData.currentTimestamp);
300 if (staleness == 0) {
305 std::stable_sort(octComp.
tileRequests.begin(), octComp.
tileRequests.end(), [](
const auto &a,
const auto & b) {return a.staleness > b.staleness; });
313 auto & base = octData.baseBlocks;
314 auto & nodes = octData.nodes;
316 if (base.empty())
return;
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);
334 auto d = std::max(std::max((baseBlockMaxIndex.x - baseBlockMinIndex.x), (baseBlockMaxIndex.y - baseBlockMinIndex.y)), (baseBlockMaxIndex.z - baseBlockMinIndex.z));
345 for (
auto & it : octData.baseBlocks) {
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;
359 std::sort(nodes.begin(), nodes.end(), [](
const auto& a,
const auto& b) ->
bool { return a.ix < b.ix; });
363 for (uint16_t l = 1; l < 16 && (1 < nodes.size() - offset); l++) {
364 size_t nextOffset = nodes.size();
366 NodeBlock * parent =
nullptr;
367 for (
size_t i = offset; i < nextOffset; i++) {
368 const auto child = nodes[i];
370 const auto parentIx = child.ix >> 3;
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;
384 parent->timestamp = octData.currentTimestamp - std::min(octData.currentTimestamp - parent->timestamp,
385 octData.currentTimestamp - child.timestamp);
387 parent->extentMin = glm::min(parent->extentMin, child.extentMin);
388 parent->extentMax = glm::max(parent->extentMax, child.extentMax);
390 parent->children[child.ix & 7] =
static_cast<uint32_t
>(i);
396glm::mat4 Cogs::Core::Volumetric::OctSystem::LocalFromIndexSpaceTransform(
const OctComponent& octComp,
const OctData& octData)
399 glm::translate(glm::mat4(), octComp.blockShift) *
400 glm::scale(octComp.blockExtent) *
401 glm::translate(glm::mat4(), glm::vec3(octData.alignMinToZeroShift));
404glm::mat4 Cogs::Core::Volumetric::OctSystem::IndexSpaceFromLocalTransform(
const OctComponent& octComp,
const OctData& octData)
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);
415void Cogs::Core::Volumetric::OctSystem::adaptiveSubset(
Context * context,
416 OctComponent& octComp, OctData& octData,
420 const auto & nodes = octData.nodes;
421 auto & front = octData.front;
422 auto & stack = octData.stack;
424 if (nodes.empty())
return;
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]);
443 [maxFrontSize = octData.maxFrontSize,
448 &camOriginIxspc](
auto & front,
const auto threshold) ->
bool
452 stack.push_back(
static_cast<uint32_t
>(nodes.size() - 1));
454 while (!stack.empty()) {
455 const auto nodeIx = stack.back();
458 const auto & n = nodes[nodeIx];
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,
467 auto v = viewFromIxspc * p;
468 auto c = clipFromIxspc * p;
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);
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;
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);
487 front.push_back(nodeIx);
488 if (maxFrontSize < front.size()) {
497 float tolerance = std::max(0.01f, octData.tolerance);
501 bool success =
false;
502 for (
unsigned i = 0; i < 8 && !success; i++) {
503 success = buildFront(front, tolerance);
505 tolerance = 2 * tolerance;
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)) {
514 octData.front.swap(octData.frontTmp);
523 if (octData.tolerance != tolerance) {
524 LOG_DEBUG(logger,
"Current tolerance set to %f", octData.tolerance);
526 octData.tolerance = tolerance;
ComponentType * getComponent() const
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.
class IRenderer * renderer
Renderer.
std::unique_ptr< class Bounds > bounds
Bounds service instance.
virtual void registerExtension(IRendererExtension *extension)=0
Register an extension with the renderer.
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
@ OpenGLES30
Graphics device using the OpenGLES 3.0 API.
ResourceStatus
Status of an asynchronously loaded resource, such as an effect or a pipeline.
@ 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.
@ 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.
uint8_t roundUpToPowerOfTwoShift(uint8_t x)
Handle to a Component instance.
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
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.
std::set< RegionKey > regionKeys
Regions intersecting this block.
glm::i16vec3 ix3
Index without block shift.
uint32_t timestamp
Timestamp last time a region was added to block.
bool clearAllRegions
If set to true, all current regions are discarded before regionsToAdd is processed.
std::vector< RegionKey > regionsToRemove
Set of regions to remove. Blocks with no regions are purged from the oct-tree.
glm::vec3 blockShift
Object space grid origin, tweak if block boundaries happen at unfortunate places.
bool forceWipe
Discard all processed data, but regions persist.
std::vector< TileRequest > tileRequests
Requests for tiles, populated by OctSystem::update, consumed by provider.
std::vector< Region > regionsToAdd
Regions to add this frame. It is OK to add a region multiple times, and this will invalidate regions ...
glm::i16vec3 alignMinToZeroShift
Shift value for baseBlock ix3 to get them non-negative.
std::vector< uint64_t > baseBlocks
Base blocks that intersects with this region.
glm::vec3 min
Object space min corner of region bounding box.
glm::vec3 max
Object space max corner of region bounding box.
void initialize(Context *context) override
Initialize the system.
bool isDebugEffectReady(Context *context)
ComponentModel::ComponentHandle createComponent() override
Contains an effect description used to load a single effect.
EEffectFlags
Effect source flags.
@ GLSL
Effect source is GLSL.
Provides buffer management functionality.
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.
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.