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"
14#include "Foundation/BitTwiddling/MortonCode.h"
15#include "Foundation/BitTwiddling/PowerOfTwo.h"
16#include "Foundation/Geometry/Glm.hpp"
17#include "Foundation/Logging/Logger.h"
18#include "Foundation/HashSequence.h"
28 switch (octData.source)
30 case Volumetric::OctSource::Value:
31 octData.materialInstance->setVariant(
"Source",
"Value");
33 case Volumetric::OctSource::ValueAge:
34 octData.materialInstance->setVariant(
"Source",
"ValueAge");
37 octData.materialInstance->setVariant(
"AlphaTest",
"Discard");
45 auto jt = octData.knownRegions.find(regionKey);
46 if (jt != octData.knownRegions.end()) {
47 auto & regData = jt->second;
48 for (
const auto blockKey : regData->baseBlocks) {
51 auto lt = octData.baseBlocks.find(blockKey);
52 lt->second->regionKeys.erase(regData->regionKey);
55 if (lt->second->regionKeys.empty()) {
56 octData.baseBlockPool.destroy(lt->second);
57 octData.baseBlocks.erase(lt);
61 octData.knownRegionPool.destroy(jt->second);
62 octData.knownRegions.erase(jt);
71 forgetRegion(octData, regionKey);
80 const glm::vec3 blockScale(1.f / octComp.blockExtent.x,
81 1.f / octComp.blockExtent.y,
82 1.f / octComp.blockExtent.z);
84 const auto skirtExpand = float(Volumetric::OctSystem::skirtSize) / float(octComp.tileSize - Volumetric::OctSystem::skirtSize);
86 const glm::vec3 skirtExtent = octComp.blockExtent * skirtExpand;
88 const auto M = glm::scale(blockScale) * glm::translate(-octComp.
blockShift);
91 for (
auto & region : regionsToAdd) {
94 regData->regionKey = region.regionKey;
95 regData->
min = region.min;
96 regData->
max = region.max;
100 const auto A4 = M * glm::vec4(region.min - skirtExtent, 1.f);
101 const auto A = glm::ivec3(glm::floor((1.f / A4.w)*glm::vec3(A4)));
103 const auto B4 = M * glm::vec4(region.max + skirtExtent, 1.f);
104 const auto B = glm::ivec3(glm::floor((1.f / B4.w)*glm::vec3(B4)));
105 regData->
baseBlocks.reserve((B.x - A.x + 1)*(B.y - A.y + 1)*(B.z - A.z + 1));
106 for (i.z = A.z; i.z <= B.z; i.z++) {
107 for (i.y = A.y; i.y <= B.y; i.y++) {
108 for (i.x = A.x; i.x <= B.x; i.x++) {
109 const auto baseBlockKey = Volumetric::createBaseBlockKey(i.x, i.y, i.z);
112 auto blockIt = octData.baseBlocks.find(baseBlockKey);
113 if (blockIt == octData.baseBlocks.end()) {
114 baseBlock = octData.baseBlockPool.create();
116 octData.baseBlocks[baseBlockKey] = baseBlock;
119 baseBlock = blockIt->second;
122 baseBlock->
timestamp = octData.currentTimestamp;
123 baseBlock->
regionKeys.insert(region.regionKey);
129 if (forgetRegion(octData, region.regionKey)) {
130 LOG_DEBUG(logger,
"Region %PRIu64 overwritten", region.regionKey);
132 octData.knownRegions[region.regionKey] = std::move(regData);
134 auto rv = !regionsToAdd.empty();
135 regionsToAdd.clear();
141 LOG_DEBUG(logger,
"Wiped %d baseblocks and %d regions.",
unsigned(octData->baseBlocks.size()),
unsigned(octData->knownRegions.size()));
142 for (
auto it : octData->baseBlocks) {
143 octData->baseBlockPool.destroy(it.second);
145 octData->baseBlocks.clear();
147 for (
auto it : octData->knownRegions) {
148 octData->knownRegionPool.destroy(it.second);
150 octData->knownRegions.clear();
156Cogs::Core::Volumetric::OctData::~OctData()
169 material = context->materialManager->loadMaterial(
"OcttreeRaycastMaterial.material");
170 context->materialManager->processLoading();
171 transferTexKey = material->getTextureKey(
"transferTexture");
172 volumeTexKey = material->getTextureKey(
"volumeTexture");
175 bounds->octSystem =
this;
176 context->
bounds->addBoundsExtension(bounds);
178 auto device = context->device;
179 auto buffers = device->getBuffers();
182 desc.name =
"DebugEffect";
184 switch (device->getType()) {
186 desc.vertexShader =
"Engine/DebugVS.es30.glsl";
187 desc.pixelShader =
"Engine/DebugPS.es30.glsl";
191 desc.vertexShader =
"Engine/DebugVS.hlsl";
192 desc.pixelShader =
"Engine/DebugPS.hlsl";
195 effectHandle = device->getEffects()->loadEffect(desc);
200 auto vertexFormat = buffers->createVertexFormat(elements, 1);
201 inputLayoutHandle = buffers->loadInputLayout(&vertexFormat, 1, effectHandle);
207 auto * comp = component.resolveComponent<
OctComponent>();
209 auto & data = getData(comp);
211 for (
unsigned i = 0; i < 32; i++) {
214 data.materialInstance = context->materialInstanceManager->createMaterialInstance(material);
215 updateMaterialVariant(data);
221 for (
auto & octComp : pool) {
222 auto & octData = getData(&octComp);
224 if (octData.source != octComp.source) {
225 octData.source = octComp.source;
226 updateMaterialVariant(octData);
230 octData.currentTimestamp++;
231 bool baseBlocksModified =
false;
234 octComp.tileSize = std::max(8u, std::min(256u, octComp.tileSize));
235 octComp.gpuCacheSize = std::max(1u, std::min((2048 / octComp.tileSize), octComp.gpuCacheSize));
238 const size_t layoutHash =
hashSequence(octComp.blockExtent,
241 octComp.gpuCacheSize);
245 octData.gpuCacheWipe =
true;
246 octData.layoutHash = layoutHash;
247 octData.maxFrontSize = octComp.gpuCacheSize * octComp.gpuCacheSize * octComp.gpuCacheSize - 1u;
249 std::vector<Region> knownRegions;
252 knownRegions.reserve(octData.knownRegions.size());
253 for (
auto & it : octData.knownRegions) {
254 const auto * regData = it.second;
255 knownRegions.push_back(
Region{ regData->regionKey, regData->
min, regData->
max });
259 addRegions(octComp, octData, knownRegions);
260 baseBlocksModified =
true;
263 baseBlocksModified = addRegions(octComp, octData, octComp.
regionsToAdd) || baseBlocksModified;
264 baseBlocksModified = removeRegions(octComp, octData) || baseBlocksModified;
266 if(baseBlocksModified) {
267 buildTree(context, octComp, octData);
271 const auto * camComp = context->cameraSystem->getMainCamera();
272 const auto & camData = context->cameraSystem->getMainCameraData();
274 adaptiveSubset(context, octComp, octData, *transComp, *camComp, camData);
278 for (
auto & item : octData.front) {
279 const auto & node = octData.nodes[item];
281 auto staleness = octData.atlas.checkTile(tileKey, node.timestamp, octData.currentTimestamp);
282 if (staleness == 0) {
287 std::stable_sort(octComp.
tileRequests.begin(), octComp.
tileRequests.end(), [](
const auto &a,
const auto & b) {return a.staleness > b.staleness; });
295 auto & base = octData.baseBlocks;
296 auto & nodes = octData.nodes;
298 if (base.empty())
return;
301 glm::i16vec3 baseBlockMinIndex = base.begin()->second->ix3;
302 glm::i16vec3 baseBlockMaxIndex = base.begin()->second->ix3;
303 for (
auto & it : base) {
304 baseBlockMinIndex = glm::min(baseBlockMinIndex, it.second->ix3);
305 baseBlockMaxIndex = glm::max(baseBlockMaxIndex, it.second->ix3);
316 auto d = std::max(std::max((baseBlockMaxIndex.x - baseBlockMinIndex.x), (baseBlockMaxIndex.y - baseBlockMinIndex.y)), (baseBlockMaxIndex.z - baseBlockMinIndex.z));
327 for (
auto & it : octData.baseBlocks) {
329 nodes.emplace_back(NodeBlock{});
330 nodes.back().ix =
mortonCode(
static_cast<uint16_t
>(ix4.x),
331 static_cast<uint16_t
>(ix4.y),
332 static_cast<uint16_t
>(ix4.z));
333 nodes.back().timestamp = it.second->timestamp;
334 nodes.back().ix4 = ix4;
335 nodes.back().extentMin = glm::uvec3(ix4);
336 nodes.back().extentMax = glm::uvec3(ix4) + glm::uvec3(1);
337 nodes.back().baseBlock = it.second;
341 std::sort(nodes.begin(), nodes.end(), [](
const auto& a,
const auto& b) ->
bool { return a.ix < b.ix; });
345 for (uint16_t l = 1; l < 16 && (1 < nodes.size() - offset); l++) {
346 size_t nextOffset = nodes.size();
348 NodeBlock * parent =
nullptr;
349 for (
size_t i = offset; i < nextOffset; i++) {
350 const auto child = nodes[i];
352 const auto parentIx = child.ix >> 3;
355 if (!parent || parent->ix != parentIx) {
356 nodes.emplace_back(NodeBlock{});
357 parent = &octData.nodes.back();
358 parent->ix = parentIx;
359 parent->timestamp = child.timestamp;
360 parent->ix4 = glm::u16vec4(child.ix4.x >> 1, child.ix4.y >> 1, child.ix4.z >> 1, l);
361 parent->extentMin = child.extentMin;
362 parent->extentMax = child.extentMax;
363 for (
unsigned k = 0; k < 8; k++) parent->children[k] = ~0u;
366 parent->timestamp = octData.currentTimestamp - std::min(octData.currentTimestamp - parent->timestamp,
367 octData.currentTimestamp - child.timestamp);
369 parent->extentMin = glm::min(parent->extentMin, child.extentMin);
370 parent->extentMax = glm::max(parent->extentMax, child.extentMax);
372 parent->children[child.ix & 7] =
static_cast<uint32_t
>(i);
378glm::mat4 Cogs::Core::Volumetric::OctSystem::LocalFromIndexSpaceTransform(
const OctComponent& octComp,
const OctData& octData)
381 glm::translate(glm::mat4(), octComp.blockShift) *
382 glm::scale(octComp.blockExtent) *
383 glm::translate(glm::mat4(), glm::vec3(octData.alignMinToZeroShift));
386glm::mat4 Cogs::Core::Volumetric::OctSystem::IndexSpaceFromLocalTransform(
const OctComponent& octComp,
const OctData& octData)
389 glm::translate(glm::mat4(), -glm::vec3(octData.alignMinToZeroShift)) *
390 glm::scale(glm::vec3(1.f / octComp.blockExtent.x,
391 1.f / octComp.blockExtent.y,
392 1.f / octComp.blockExtent.z)) *
393 glm::translate(glm::mat4(), -octComp.blockShift);
397void Cogs::Core::Volumetric::OctSystem::adaptiveSubset(
Context * context,
398 OctComponent& octComp, OctData& octData,
402 const auto & nodes = octData.nodes;
403 auto & front = octData.front;
404 auto & stack = octData.stack;
406 if (nodes.empty())
return;
412 const auto & worldFromLocal = context->transformSystem->getLocalToWorld(&transComp);
413 const auto localFromWorld = glm::inverse(worldFromLocal);
414 const auto & ViewFromWorld = camData.viewMatrix;
415 const auto & worldFromView = camData.inverseViewMatrix;
416 const auto & ClipFromWorld = camData.viewProjection;
417 const auto LocalFromIxspc = LocalFromIndexSpaceTransform(octComp, octData);
418 const auto IxspcFromLocal = IndexSpaceFromLocalTransform(octComp, octData);
419 const auto viewFromIxspc = ViewFromWorld * worldFromLocal * LocalFromIxspc;
420 const auto ixspcFromView = IxspcFromLocal * localFromWorld * worldFromView;
421 const auto clipFromIxspc = ClipFromWorld * worldFromLocal * LocalFromIxspc;
422 const auto camOriginIxspc = glm::vec3(ixspcFromView[3]);
425 [maxFrontSize = octData.maxFrontSize,
430 &camOriginIxspc](
auto & front,
const auto threshold) ->
bool
434 stack.push_back(
static_cast<uint32_t
>(nodes.size() - 1));
436 while (!stack.empty()) {
437 const auto nodeIx = stack.back();
440 const auto & n = nodes[nodeIx];
444 for (
unsigned i = 0; i < 8; i++) {
445 const glm::vec4 p((i & 1) == 0 ? n.extentMin.x : n.extentMax.x,
446 (i & 2) == 0 ? n.extentMin.y : n.extentMax.y,
447 (i & 4) == 0 ? n.extentMin.z : n.extentMax.z,
449 auto v = viewFromIxspc * p;
450 auto c = clipFromIxspc * p;
452 (v.z < 0.f ? 1 : 0) |
453 (c.x <= c.w ? 2 : 0) | (-c.w <= c.x ? 4 : 0) |
454 (c.y <= c.w ? 8 : 0) | (-c.w <= c.y ? 16 : 0);
459 glm::vec3 nearestIxspc = glm::clamp(camOriginIxspc, glm::vec3(n.extentMin), glm::vec3(n.extentMax));
460 glm::vec4 nearestView = viewFromIxspc * glm::vec4(nearestIxspc, 1.f);
461 float camDist = -nearestView.z / nearestView.w;
463 if ((0 < n.ix4.w) && (camDist*threshold < (1 << n.ix4.w))) {
464 for (
auto childIx : n.children) {
465 if (childIx != ~0u) stack.push_back(childIx);
469 front.push_back(nodeIx);
470 if (maxFrontSize < front.size()) {
479 float tolerance = std::max(0.01f, octData.tolerance);
483 bool success =
false;
484 for (
unsigned i = 0; i < 8 && !success; i++) {
485 success = buildFront(front, tolerance);
487 tolerance = 2 * tolerance;
491 const auto minFrontSize = (size_t)std::max(1.f, 0.9f*octData.maxFrontSize);
492 for (
unsigned i = 0; i < 8 && octComp.tolerance < tolerance && front.size() < minFrontSize; i++) {
493 auto t = std::max(octComp.tolerance, 0.9f*tolerance);
494 if (buildFront(octData.frontTmp, t)) {
496 octData.front.swap(octData.frontTmp);
505 if (octData.tolerance != tolerance) {
506 LOG_DEBUG(logger,
"Current tolerance set to %f", octData.tolerance);
508 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.
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.
ComponentModel::ComponentHandle createComponent() override
Contains an effect description used to load a single effect.
EEffectFlags
Effect source flags.
@ GLSL
Effect source is GLSL.
Vertex element structure used to describe a single data element in a vertex for the input assembler.