2#include "Foundation/Logging/Logger.h"
4#include "Rendering/ICapabilities.h"
6#include "Utilities/Math.h"
7#include "Systems/Core/CameraSystem.h"
8#include "Systems/Core/TransformSystem.h"
9#include "Resources/Material.h"
10#include "Resources/MaterialInstance.h"
11#include "Resources/TextureManager.h"
12#include "Services/Time.h"
13#include "TexAtlasSystem.h"
14#include "TexAtlasRenderer.h"
16using namespace Cogs::Core::TexAtlas;
26 const size_t slotCount = 4;
28 Layout getLayout(
const glm::dvec2& domainMin,
const glm::dvec2& domainMax,
const uint32_t minLevel,
const uint32_t maxLevel)
31 double domainSize = std::max(domainMax.x - domainMin.x,
32 domainMax.y - domainMax.y);
33 uint32_t level =
static_cast<uint32_t
>(std::max(0.0, std::round(-std::log2(domainSize))));
34 if (level < minLevel) level = minLevel;
35 if (maxLevel < level) level = maxLevel;
37 const uint32_t tileScale = 1<<level;
42 glm::ivec2 maxGridSizeAtLevel = glm::ivec2(1 << level);
43 glm::ivec2 minGridCell = glm::ivec2(glm::floor(
static_cast<float>(tileScale) * glm::vec2(domainMin)));
44 glm::ivec2 maxGridCell = glm::ivec2(glm::floor(
static_cast<float>(tileScale) * glm::vec2(domainMax))) + glm::ivec2(1);
45 glm::uvec2 size = glm::max(glm::ivec2(0), glm::min(maxGridSizeAtLevel, maxGridCell - minGridCell));
50 .tileScale = tileScale,
61 materialKeys.resize(slotCount);
62 for (
size_t slot = 0; slot < slotCount; slot++) {
63 const std::string indexString = std::to_string(slot);
64 materialKeys[slot].level =
"TexAtlasLevels" + indexString;
65 materialKeys[slot].floatCoeffs =
"texAtlasFloatCoeffs" + indexString;
66 materialKeys[slot].intCoeffs =
"texAtlasIntCoeffs" + indexString;
67 materialKeys[slot].tile =
"texAtlasTiles" + indexString;
68 materialKeys[slot].tree =
"texAtlasTree" + indexString;
74 if (pool.size() == 0) {
75 LOG_DEBUG(logger,
"Creating first component, creating renderer.");
76 assert(renderer ==
nullptr);
78 context->renderer->registerExtension(renderer);
80 return base::createComponent();
85 base::destroyComponent(component);
86 if (pool.size() == 0) {
87 LOG_DEBUG(logger,
"Destroying last component, destroying renderer.");
88 trackedMaterials.clear();
90 context->renderer->unregisterExtension(renderer);
99 if (pool.size() == 0)
return;
103 const uint32_t currentFrame = context->
time->getFrame();
104 const uint32_t
currentTime =
static_cast<uint32_t
>(std::floor(context->
time->getAnimationTime()));
107 bool userLodFreeze = context->
variables->getOrAdd(lodFreezeName,
false);
108 renderer->renderFrustum = userLodFreeze;
113 bool lodFreeze = userLodFreeze || !renderer->isReady();
115 bool atlasLayoutLog = context->
variables->getOrAdd(atlasLayoutLogName,
false);
118 const glm::vec3 p[8] = {
119 glm::vec3(-1.f, -1.f, -1.f),
120 glm::vec3( 1.f, -1.f, -1.f),
121 glm::vec3( 1.f, 1.f, -1.f),
122 glm::vec3(-1.f, 1.f, -1.f),
123 glm::vec3(-1.f, -1.f, 1.f),
124 glm::vec3( 1.f, -1.f, 1.f),
125 glm::vec3( 1.f, 1.f, 1.f),
126 glm::vec3(-1.f, 1.f, 1.f)
129 const CameraData& mainCamData = context->cameraSystem->getMainCameraData();
130 glm::mat4 PVinv = glm::inverse(mainCamData.rawViewProjection);
131 for (
size_t i = 0; i < 8; i++) {
132 renderer->frustumCorners[i] = glm::vec4(euclidean(PVinv * glm::vec4(p[i], 1.f)), 1.f);
136 for (
auto& item : trackedMaterials) {
148 texAtlasData.levels = 0;
150 if (texAtlasData.inUse) {
151 texAtlasData.inUse =
false;
153 else if (texAtlasComp.materials.empty())
continue;
155 if (4 <= texAtlasComp.index)
continue;
156 if ((texAtlasComp.domainMax.x <= texAtlasComp.domainMin.x) ||
157 (texAtlasComp.domainMax.y <= texAtlasComp.domainMin.y))
continue;
161 texAtlasComp.maxLevel = std::min(28u, texAtlasComp.maxLevel);
162 texAtlasComp.minLevel = std::min(texAtlasComp.minLevel, texAtlasComp.maxLevel);
164 geo.domain[0] = texAtlasComp.domainMin;
165 geo.domain[1] = texAtlasComp.domainMax;
166 geo.invDomainMapShift = -glm::vec2(geo.domain[0]);
167 geo.invDomainMapScale = glm::vec2(1.f) / glm::vec2(geo.domain[1] - geo.domain[0]);
170 texAtlasData.layout = getLayout(texAtlasData.geometry.domain[0],
171 texAtlasData.geometry.domain[1],
172 texAtlasComp.minLevel,
173 texAtlasComp.maxLevel);
175 texAtlasData.fetcher.datasetExtentMin = texAtlasComp.domainExtentsMin;
176 texAtlasData.fetcher.datasetExtentMax = texAtlasComp.domainExtentsMax;
178 texAtlasData.fetcher.
update(context, currentFrame,
currentTime, texAtlasComp.timeout, texAtlasComp.path);
179 texAtlasData.cache.update(currentFrame,
currentTime, texAtlasComp.minRetryDelay, std::min(texAtlasComp.maxItemCount, maxItemCount));
180 texAtlasData.tree.update(context, texAtlasData.geometry, texAtlasData.cache, texAtlasData.fetcher, texAtlasData.layout, std::max(1.f / 8192.f, texAtlasComp.tolerance), texAtlasComp.restrictBetweenNearAndFar, lodFreeze);
185 std::vector<uint16_t>& encoded = texAtlasData.tree.encoded;
186 size_t treeHashValue =
Cogs::hash(encoded.data(),
sizeof(encoded[0]) * encoded.size());
187 if (!texAtlasData.treeTex ||
188 (texAtlasData.treeTex->description.width != encoded.size()) ||
189 (texAtlasData.treeHashValue != treeHashValue))
191 texAtlasData.treeTex = context->textureManager->loadTexture(texAtlasData.tree.encoded.data(),
192 ResourceDimensions::Texture2D,
193 static_cast<int>(texAtlasData.tree.encoded.size()), 1, 1, 1,
194 TextureFormat::R16_UINT, 0, texAtlasData.treeTex,
196 texAtlasData.treeHashValue = treeHashValue;
201 if (texAtlasData.fetcher.tileWidth != 0 && texAtlasData.fetcher.tileHeight != 0) {
203 uint32_t currentSlotCount = texAtlasData.tilesTex ? texAtlasData.tilesTex->description.layers : 0;
204 uint32_t newSlotCount = currentSlotCount;
206 if (currentSlotCount < texAtlasData.cache.slots.size()) {
207 newSlotCount = std::min(texAtlasData.cache.
maxItemCount, std::max(
static_cast<uint32_t
>(texAtlasData.cache.slots.size()), (currentSlotCount * 3 + 1) / 2));
208 if (atlasLayoutLog) {
209 LOG_DEBUG(logger,
"Growing to %u tiles", newSlotCount);
213 if (texAtlasData.cache.slots.size() < currentSlotCount / 2) {
214 newSlotCount = std::max(
static_cast<uint32_t
>(1), currentSlotCount / 2);
215 if (atlasLayoutLog) {
216 LOG_DEBUG(logger,
"Shrinking to %u tiles", newSlotCount);
220 if (currentSlotCount != newSlotCount) {
222 if (texAtlasData.tilesOldTex) {
228 texAtlasData.tilesOldTex = texAtlasData.tilesTex;
230 texAtlasData.tilesTex = context->textureManager->loadTexture(
nullptr,
231 ResourceDimensions::Texture2DArray,
232 texAtlasData.fetcher.tileWidth, texAtlasData.fetcher.tileHeight, 1, newSlotCount,
238 switch (texAtlasComp.projection) {
240 if (texAtlasComp.coefficients.size() != 4)
continue;
242 for (
size_t i = 0; i < 4; i++) {
243 texAtlasData.geometry.
coefficients[i] = transformSystem->engineFromWorldCoords(glm::dvec3(texAtlasComp.coefficients[i], 0.f));
245 texAtlasData.geometry.elevationMin = transformSystem->engineFromWorldCoords(glm::dvec3(0.0, 0.0,
double(texAtlasComp.elevation))).z;
246 texAtlasData.geometry.elevationMax = transformSystem->engineFromWorldCoords(glm::dvec3(0.0, 0.0,
double(texAtlasComp.elevation))).z;
254 uint32_t tileScale = (1 << texAtlasData.layout.
level);
255 glm::vec2 gridFromUnitScale = glm::vec2(
double(tileScale) * (texAtlasData.geometry.domain[1] - texAtlasData.geometry.domain[0]));
256 glm::vec2 gridFromUnitOffset = glm::vec2(
double(tileScale) * texAtlasData.geometry.domain[0] - glm::dvec2(texAtlasData.layout.
offset));
257 texAtlasData.floatCoefficients = glm::mat4(glm::vec4(texAtlasData.geometry.
coefficients[0], texAtlasData.geometry.
coefficients[1]),
259 glm::vec4(gridFromUnitScale, gridFromUnitOffset),
260 glm::vec4(tileScale, 0.f, 0.f, 0.f));
261 texAtlasData.intCoefficients = glm::ivec4(
int(tileScale - 1u), texAtlasData.layout.
size.x, 0, 0);
262 texAtlasData.levels =
static_cast<int>(texAtlasData.layout.
maxLevel + 1 - texAtlasData.layout.
level);
264 const std::string indexString = std::to_string(texAtlasComp.index);
266 if (!handle)
continue;
268 if (texAtlasComp.index < slotCount) {
270 tracked.material = handle;
271 tracked.lastSeen = currentFrame;
272 tracked.slots[texAtlasComp.index] = &texAtlasComp;
276 if (texAtlasComp.index == 0) {
277 LOG_DEBUG(logger,
"level=%u off=[%d %d], size=[%d %d]",
278 texAtlasData.layout.
level,
279 texAtlasData.layout.
offset.x,
280 texAtlasData.layout.
offset.y,
281 texAtlasData.layout.
size.x,
282 texAtlasData.layout.
size.y);
290 for (
auto& item : trackedMaterials) {
295 for (
size_t slotIndex = 0; slotIndex < slotCount; slotIndex++) {
298 if (
TexAtlasComponent* texAtlasComp = trackedMaterial.slots[slotIndex]; texAtlasComp) {
301 mat->setVariant(keys.level, texAtlasData.levels);
303 if (
VariableKey transformKey = mat->getMat4Key(keys.floatCoeffs); transformKey != NoProperty) {
307 if (
VariableKey key = mat->getInt4Key(keys.intCoeffs); key != NoProperty) {
311 if (
VariableKey key = mat->getTextureKey(keys.tile); key != NoProperty) {
317 if (
VariableKey key = mat->getTextureKey(keys.tree); key != NoProperty) {
325 mat->setVariant(keys.level, 0);
331 std::erase_if(trackedMaterials, [currentFrame](
auto& item) {
return item.second.lastSeen != currentFrame; });
Context * context
Pointer to the Context instance the system lives in.
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 Variables > variables
Variables service instance.
std::unique_ptr< class Time > time
Time service instance.
virtual IGraphicsDevice * getDevice()=0
Get the graphics device used by the renderer.
virtual ICapabilities * getCapabilities()=0
Get a pointer to the capability management interface used to query the graphics device capability fla...
Log implementation class.
Provides a weakly referenced view over the contents of a string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ RenderTarget
Set the usage flag of the texture to RenderTarget.
@ NoMipMaps
Do not generate mipmaps.
@ ForceSynchronous
Force loading the resource synchronously.
uint16_t VariableKey
Used to lookup material properties.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
COGSFOUNDATION_API Time currentTime()
High resolution clock time (NTP / UTC time). Returns an implementation defined absolute timestamp,...
Handle to a Component instance.
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Material resources define the how of geometry rendering (the what is defined by Mesh and Texture reso...
void setMat4Property(const VariableKey key, glm::mat4 value)
Set the mat4 property with the given key to value.
void setTextureFilterMode(const VariableKey key, SamplerState::FilterMode filterMode)
Set filtermode used for the texture property.
void setInt4Property(const VariableKey key, glm::ivec4 value)
Set the ivec4 property with the given key to value.
void setTextureProperty(const VariableKey key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
void setTextureAddressMode(const VariableKey key, SamplerState::AddressMode mode)
Set the address mode used for the texture property with the given key to mode.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
void destroyComponent(ComponentHandle component) override
void initialize(Context *context) override
Initialize the system.
ComponentHandle createComponent() override
uint32_t maxItemCount
Number of tiles in cache.
void update(Context *context, uint32_t currentFrame, uint32_t currentTime, uint32_t timeout, std::string_view urlTemplate)
void processLoadQueue(Context *context, Cache &cache)
glm::vec2 coefficients[4]
Coefficients wrt engine origin.
uint32_t level
Base level.
uint32_t maxLevel
Maximum level.
glm::uvec2 size
Size of grid.
glm::ivec2 offset
Indices of the grid cell with smallest indices.
uint32_t MaxTextureArrayLayers
Using D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as default.
virtual const GraphicsDeviceCapabilities & getDeviceCapabilities() const
Gets the device capabilities in a structure.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ MinMagMipPoint
Point sampling for both minification and magnification.
@ MinMagMipLinear
Linear sampling for both minification and magnification.