From 425e8eed7326449d599a235a6ae2d8994b2ee8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 15 Sep 2024 11:45:39 +0300 Subject: [PATCH 01/19] TerrainQuadTree produces real quad-tree --- engine/src/Graphics/TerrainQuadTree.cpp | 64 +++++++++++++++---------- engine/src/Graphics/TerrainQuadTree.hpp | 34 ++++++++----- engine/src/Graphics/TerrainSystem.cpp | 6 +-- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index e961e3d8..dd492d60 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -22,7 +22,9 @@ namespace kokko { -TerrainQuadTree::TerrainQuadTree() : +TerrainQuadTree::TerrainQuadTree(Allocator* allocator) : + allocator(allocator), + nodes(allocator), tiles(nullptr), tileTextureIds(nullptr), treeLevels(0), @@ -33,7 +35,7 @@ TerrainQuadTree::TerrainQuadTree() : { } -void TerrainQuadTree::CreateResources(Allocator* allocator, kokko::render::Device* renderDevice, int levels, +void TerrainQuadTree::CreateResources(render::Device* renderDevice, uint8_t levels, const TerrainParameters& params) { constexpr int tileResolution = TerrainTile::Resolution; @@ -70,7 +72,7 @@ void TerrainQuadTree::CreateResources(Allocator* allocator, kokko::render::Devic allocator->Allocate(tileCount * sizeof(uint32_t), "TerrainQuadTree.tileTextureIds")); renderDevice->CreateTextures(RenderTextureTarget::Texture2d, tileCount, tileTextureIds); - for (int levelIdx = 0; levelIdx < treeLevels; ++levelIdx) + for (uint8_t levelIdx = 0; levelIdx < treeLevels; ++levelIdx) { int tilesPerDimension = GetTilesPerDimension(levelIdx); @@ -91,7 +93,7 @@ void TerrainQuadTree::CreateResources(Allocator* allocator, kokko::render::Devic } } -void TerrainQuadTree::DestroyResources(Allocator* allocator, kokko::render::Device* renderDevice) +void TerrainQuadTree::DestroyResources(render::Device* renderDevice) { if (tileTextureIds != nullptr) renderDevice->DestroyTextures(static_cast(tileCount), tileTextureIds); @@ -104,11 +106,14 @@ void TerrainQuadTree::GetTilesToRender(const FrustumPlanes& frustum, const Vec3f const RenderDebugSettings& renderDebug, Array& resultOut) { KOKKO_PROFILE_SCOPE("TerrainQuadTree::GetTilesToRender()"); + + nodes.Clear(); + GetRenderTilesParams params{ frustum, cameraPos, renderDebug, resultOut }; RenderTile(TerrainTileId{}, params); } -void TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& params) +int TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& params) { float tileScale = GetTileScale(id.level); @@ -124,24 +129,23 @@ void TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& if (Intersect::FrustumAabb(params.frustum, tileBounds) == false) { - return; - } - - if (id.level + 1 == treeLevels) - { - params.resultOut.PushBack(id); - - return; + return -1; } constexpr float sizeFactor = 0.5f; - float distance = (tileBounds.center - params.cameraPos).Magnitude(); - bool tileIsSmallEnough = tileWidth < distance * sizeFactor; + bool lastLevel = id.level + 1 == treeLevels; + bool tileIsSmallEnough = lastLevel || (tileWidth < (tileBounds.center - params.cameraPos).Magnitude() * sizeFactor); - auto vr = Debug::Get()->GetVectorRenderer(); - auto tr = Debug::Get()->GetTextRenderer(); - Color col(1.0f, 0.0f, 1.0f); + int nodeIndex = static_cast(nodes.GetCount()); + assert(nodeIndex <= UINT16_MAX); + { + TerrainQuadTreeNode& node = nodes.PushBack(); + node.x = id.x; + node.y = id.y; + node.level = id.level; + node.numChildren = 0; + } if (tileIsSmallEnough) { @@ -154,7 +158,7 @@ void TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& Mat4x4f transform = Mat4x4f::Translate(tileBounds.center) * Mat4x4f::Scale(scale); - vr->DrawWireCube(transform, col); + Debug::Get()->GetVectorRenderer()->DrawWireCube(transform, Color(1.0f, 0.0f, 1.0f)); } } else @@ -168,10 +172,18 @@ void TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& tileId.x = id.x * 2 + x; tileId.y = id.y * 2 + y; - RenderTile(tileId, params); + int childIndex = RenderTile(tileId, params); + if (childIndex >= 0) + { + TerrainQuadTreeNode& node = nodes[nodeIndex]; + node.children[node.numChildren] = static_cast(childIndex); + node.numChildren += 1; + } } } } + + return nodeIndex; } int TerrainQuadTree::GetLevelCount() const @@ -179,17 +191,17 @@ int TerrainQuadTree::GetLevelCount() const return treeLevels; } -const TerrainTile* TerrainQuadTree::GetTile(int level, int x, int y) +const TerrainTile* TerrainQuadTree::GetTile(uint8_t level, int x, int y) { return &tiles[GetTileIndex(level, x, y)]; } -render::TextureId TerrainQuadTree::GetTileHeightTexture(int level, int x, int y) +render::TextureId TerrainQuadTree::GetTileHeightTexture(uint8_t level, int x, int y) { return tileTextureIds[GetTileIndex(level, x, y)]; } -int TerrainQuadTree::GetTilesPerDimension(int level) +int TerrainQuadTree::GetTilesPerDimension(uint8_t level) { assert(level >= 0); assert(level < 31); @@ -204,7 +216,7 @@ TEST_CASE("TerrainQuadTree.GetTilesPerDimension") CHECK(TerrainQuadTree::GetTilesPerDimension(3) == 8); } -int TerrainQuadTree::GetTileIndex(int level, int x, int y) +int TerrainQuadTree::GetTileIndex(uint8_t level, int x, int y) { int levelStart = 0; int levelSize = 1; @@ -231,7 +243,7 @@ TEST_CASE("TerrainQuadTree.GetTileIndex") CHECK(TerrainQuadTree::GetTileIndex(3, 0, 0) == 21); } -int TerrainQuadTree::GetTileCountForLevelCount(int levelCount) +int TerrainQuadTree::GetTileCountForLevelCount(uint8_t levelCount) { return GetTileIndex(levelCount, 0, 0); } @@ -244,7 +256,7 @@ TEST_CASE("TerrainQuadTree.GetTileCountForLevelCount") CHECK(TerrainQuadTree::GetTileCountForLevelCount(3) == 21); } -float TerrainQuadTree::GetTileScale(int level) +float TerrainQuadTree::GetTileScale(uint8_t level) { return 1.0f / (1 << level); } diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 3a97b5dd..6e5d05be 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -35,14 +35,23 @@ struct TerrainTile uint16_t heightData[ResolutionWithBorder * ResolutionWithBorder]; }; +struct TerrainQuadTreeNode +{ + uint32_t x; + uint32_t y; + uint16_t children[4]; + uint8_t numChildren; + uint8_t level; +}; + class TerrainQuadTree { public: - TerrainQuadTree(); + TerrainQuadTree(Allocator* allocator); - void CreateResources(Allocator* allocator, kokko::render::Device* renderDevice, int levels, + void CreateResources(kokko::render::Device* renderDevice, uint8_t levels, const TerrainParameters& params); - void DestroyResources(Allocator* allocator, kokko::render::Device* renderDevice); + void DestroyResources(kokko::render::Device* renderDevice); void GetTilesToRender(const FrustumPlanes& frustum, const Vec3f& cameraPos, const RenderDebugSettings& renderDebug, Array& resultOut); @@ -58,13 +67,13 @@ class TerrainQuadTree float GetHeight() const { return terrainHeight; } void SetHeight(float height) { terrainHeight = height; } - const TerrainTile* GetTile(int level, int x, int y); - render::TextureId GetTileHeightTexture(int level, int x, int y); + const TerrainTile* GetTile(uint8_t level, int x, int y); + render::TextureId GetTileHeightTexture(uint8_t level, int x, int y); - static int GetTilesPerDimension(int level); - static int GetTileIndex(int level, int x, int y); - static int GetTileCountForLevelCount(int levelCount); - static float GetTileScale(int level); + static int GetTilesPerDimension(uint8_t level); + static int GetTileIndex(uint8_t level, int x, int y); + static int GetTileCountForLevelCount(uint8_t levelCount); + static float GetTileScale(uint8_t level); private: struct GetRenderTilesParams @@ -75,16 +84,19 @@ class TerrainQuadTree Array& resultOut; }; - void RenderTile(const TerrainTileId& id, GetRenderTilesParams& params); + // Returns inserted node index (points to nodes array), or -1 if no insertion + int RenderTile(const TerrainTileId& id, GetRenderTilesParams& params); static void CreateTileTestData(TerrainTile& tile, int tileX, int tileY, float tileScale); static uint16_t TestData(float x, float y); + Allocator* allocator; + Array nodes; TerrainTile* tiles; render::TextureId* tileTextureIds; - int treeLevels; + uint8_t treeLevels; int tileCount; float terrainWidth; float terrainBottom; diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index 465095e4..46ad463a 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -160,7 +160,7 @@ TerrainId TerrainSystem::AddTerrain(Entity entity, const TerrainParameters& para data.entity[id] = entity; data.textureScale[id] = params.textureScale; data.textures[id] = TerrainTextures{}; - data.quadTree[id] = TerrainQuadTree(); + data.quadTree[id] = TerrainQuadTree(allocator); data.count += 1; @@ -322,7 +322,7 @@ void TerrainSystem::InitializeTerrain(TerrainId id, const TerrainParameters& par kokko::TerrainQuadTree& quadTree = data.quadTree[id.i]; constexpr int treeLevels = 7; - quadTree.CreateResources(allocator, renderDevice, treeLevels, params); + quadTree.CreateResources(renderDevice, treeLevels, params); } void TerrainSystem::DeinitializeTerrain(TerrainId id) @@ -331,7 +331,7 @@ void TerrainSystem::DeinitializeTerrain(TerrainId id) auto scope = renderDevice->CreateDebugScope(0, ConstStringView("TerrainSys_DestroyInstanceResources")); - data.quadTree[id.i].DestroyResources(allocator, renderDevice); + data.quadTree[id.i].DestroyResources(renderDevice); } void TerrainSystem::Reallocate(size_t required) From 3f8cfff846f2959095f4c66a22ac71b604d8c981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 15 Sep 2024 12:01:39 +0300 Subject: [PATCH 02/19] A bit of code structure for restricted quad tree algorithm --- engine/src/Graphics/TerrainQuadTree.cpp | 20 +++++++++++++++++--- engine/src/Graphics/TerrainQuadTree.hpp | 3 ++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index dd492d60..b364ef22 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -102,15 +102,24 @@ void TerrainQuadTree::DestroyResources(render::Device* renderDevice) allocator->Deallocate(tileTextureIds); } -void TerrainQuadTree::GetTilesToRender(const FrustumPlanes& frustum, const Vec3f& cameraPos, - const RenderDebugSettings& renderDebug, Array& resultOut) +void TerrainQuadTree::UpdateTilesToRender( + const FrustumPlanes& frustum, + const Vec3f& cameraPos, + const RenderDebugSettings& renderDebug, + Array& resultOut) { - KOKKO_PROFILE_SCOPE("TerrainQuadTree::GetTilesToRender()"); + KOKKO_PROFILE_SCOPE("TerrainQuadTree::UpdateTilesToRender()"); nodes.Clear(); + // Calculates optimal set of tiles to render and updates quad tree GetRenderTilesParams params{ frustum, cameraPos, renderDebug, resultOut }; RenderTile(TerrainTileId{}, params); + + // Next we need to update the quad tree so that it forms a restricted quad tree + RestrictQuadTree(); + + // Then we create the final render tiles from the leaf nodes of the quad tree } int TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& params) @@ -186,6 +195,11 @@ int TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& p return nodeIndex; } +void TerrainQuadTree::RestrictQuadTree() +{ + +} + int TerrainQuadTree::GetLevelCount() const { return treeLevels; diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 6e5d05be..24bb7ba6 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -53,7 +53,7 @@ class TerrainQuadTree const TerrainParameters& params); void DestroyResources(kokko::render::Device* renderDevice); - void GetTilesToRender(const FrustumPlanes& frustum, const Vec3f& cameraPos, + void UpdateTilesToRender(const FrustumPlanes& frustum, const Vec3f& cameraPos, const RenderDebugSettings& renderDebug, Array& resultOut); int GetLevelCount() const; @@ -86,6 +86,7 @@ class TerrainQuadTree // Returns inserted node index (points to nodes array), or -1 if no insertion int RenderTile(const TerrainTileId& id, GetRenderTilesParams& params); + void RestrictQuadTree(); static void CreateTileTestData(TerrainTile& tile, int tileX, int tileY, float tileScale); From c026e521170014618443f7b21f0bfda61d929ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Tue, 17 Sep 2024 23:10:15 +0300 Subject: [PATCH 03/19] Fix SortedArray to only need < and == operators --- engine/src/Core/SortedArray.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/Core/SortedArray.hpp b/engine/src/Core/SortedArray.hpp index 8ba66ec8..e8882370 100644 --- a/engine/src/Core/SortedArray.hpp +++ b/engine/src/Core/SortedArray.hpp @@ -29,7 +29,7 @@ class SortedArray { size_t m = (l + r + 1) / 2; - if (data[m] > value) + if (value < data[m]) r = m - 1; else l = m; From 2ca740b0d0f1a2eca699d388f4065395ade3fc2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Tue, 17 Sep 2024 23:36:01 +0300 Subject: [PATCH 04/19] Work on converting any quadtree to restricted quadtree --- engine/src/Graphics/TerrainQuadTree.cpp | 90 ++++++++++++++++++++----- engine/src/Graphics/TerrainQuadTree.hpp | 32 +++++++-- engine/src/Graphics/TerrainSystem.cpp | 2 +- 3 files changed, 100 insertions(+), 24 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index b364ef22..ec2b6114 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -4,6 +4,8 @@ #include "doctest/doctest.h" +#include "Core/SortedArray.hpp" + #include "Debug/Debug.hpp" #include "Debug/DebugTextRenderer.hpp" #include "Debug/DebugVectorRenderer.hpp" @@ -111,18 +113,20 @@ void TerrainQuadTree::UpdateTilesToRender( KOKKO_PROFILE_SCOPE("TerrainQuadTree::UpdateTilesToRender()"); nodes.Clear(); + maxNodeLevel = 0; // Calculates optimal set of tiles to render and updates quad tree - GetRenderTilesParams params{ frustum, cameraPos, renderDebug, resultOut }; - RenderTile(TerrainTileId{}, params); + UpdateTilesToRenderParams params{ frustum, cameraPos, renderDebug, resultOut }; + BuildQuadTree(QuadTreeNodeId{}, params); // Next we need to update the quad tree so that it forms a restricted quad tree RestrictQuadTree(); // Then we create the final render tiles from the leaf nodes of the quad tree + QuadTreeToTiles(); } -int TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& params) +int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRenderParams& params) { float tileScale = GetTileScale(id.level); @@ -150,15 +154,16 @@ int TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& p assert(nodeIndex <= UINT16_MAX); { TerrainQuadTreeNode& node = nodes.PushBack(); - node.x = id.x; - node.y = id.y; - node.level = id.level; - node.numChildren = 0; + node.id = id; + for (int i = 0; i < 4; ++i) + node.children[i] = 0; + + maxNodeLevel = std::max(maxNodeLevel, id.level); } if (tileIsSmallEnough) { - params.resultOut.PushBack(id); + //params.resultOut.PushBack(id); if (params.renderDebug.IsFeatureEnabled(RenderDebugFeatureFlag::DrawTerrainTiles)) { @@ -176,18 +181,11 @@ int TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& p { for (int x = 0; x < 2; ++x) { - TerrainTileId tileId; - tileId.level = id.level + 1; - tileId.x = id.x * 2 + x; - tileId.y = id.y * 2 + y; + QuadTreeNodeId tileId{ id.x * 2 + x, id.y * 2 + y, id.level + 1 }; - int childIndex = RenderTile(tileId, params); + int childIndex = BuildQuadTree(tileId, params); if (childIndex >= 0) - { - TerrainQuadTreeNode& node = nodes[nodeIndex]; - node.children[node.numChildren] = static_cast(childIndex); - node.numChildren += 1; - } + nodes[nodeIndex].children[y * 2 + x] = static_cast(childIndex); } } } @@ -196,6 +194,54 @@ int TerrainQuadTree::RenderTile(const TerrainTileId& id, GetRenderTilesParams& p } void TerrainQuadTree::RestrictQuadTree() +{ + SortedArray parentsToCheck(allocator); + SortedArray neighborsToCheck(allocator); + + uint8_t currentLevel = maxNodeLevel; + while (currentLevel > 1) + { + for (const auto& node : nodes) + if (node.id.level == currentLevel) + parentsToCheck.InsertUnique(GetParentId(node.id)); + + // Check + int tilesPerDim = GetTilesPerDimension(currentLevel - 1); + for (const auto& id : parentsToCheck) + { + // Bitwise AND is used to check if the potential tile has a different parent from current tile + + if ((id.x & 1) == 0 && id.x > 0) + neighborsToCheck.InsertUnique(QuadTreeNodeId{ id.x - 1, id.y, id.level }); + if ((id.x & 1) == 1 && id.x + 1 < tilesPerDim) + neighborsToCheck.InsertUnique(QuadTreeNodeId{ id.x + 1, id.y, id.level }); + if ((id.y & 1) == 0 && id.y > 0) + neighborsToCheck.InsertUnique(QuadTreeNodeId{ id.x, id.y - 1, id.level }); + if ((id.y & 1) == 1 && id.y + 1 < tilesPerDim) + neighborsToCheck.InsertUnique(QuadTreeNodeId{ id.x, id.y + 1, id.level }); + } + + for (const auto& id : neighborsToCheck) + { + // Try to find node in nodes + // If not, split parent tiles until we have the node + + TerrainQuadTreeNode* currentNode = &nodes[0]; + for (uint8_t level = 0; level <= id.level; ++level) + { + // Verify next level towards exists + // If so, update currentNode and continue + // Otherwise, split + } + } + + parentsToCheck.Clear(); + neighborsToCheck.Clear(); + currentLevel -= 1; + } +} + +void TerrainQuadTree::QuadTreeToTiles() { } @@ -314,4 +360,12 @@ uint16_t TerrainQuadTree::TestData(float x, float y) return static_cast(sum * UINT16_MAX); } +QuadTreeNodeId TerrainQuadTree::GetParentId(const QuadTreeNodeId& id) +{ + if (id.level == 0) + return id; + + return QuadTreeNodeId{ id.x / 2, id.y / 2, static_cast(id.level - 1) }; +} + } diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 24bb7ba6..504a940d 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -35,13 +35,31 @@ struct TerrainTile uint16_t heightData[ResolutionWithBorder * ResolutionWithBorder]; }; -struct TerrainQuadTreeNode +struct QuadTreeNodeId { uint32_t x; uint32_t y; - uint16_t children[4]; - uint8_t numChildren; uint8_t level; + + bool operator==(const QuadTreeNodeId& other) const + { + return x == other.x && y == other.y && level == other.level; + } + + bool operator<(const QuadTreeNodeId& other) const + { + if (level < other.level) return true; + if (level > other.level) return false; + if (x < other.level) return true; + if (x > other.level) return false; + return y < other.y; + } +}; + +struct TerrainQuadTreeNode +{ + QuadTreeNodeId id; + uint16_t children[4]; }; class TerrainQuadTree @@ -76,7 +94,7 @@ class TerrainQuadTree static float GetTileScale(uint8_t level); private: - struct GetRenderTilesParams + struct UpdateTilesToRenderParams { const FrustumPlanes& frustum; const Vec3f& cameraPos; @@ -85,19 +103,23 @@ class TerrainQuadTree }; // Returns inserted node index (points to nodes array), or -1 if no insertion - int RenderTile(const TerrainTileId& id, GetRenderTilesParams& params); + int BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRenderParams& params); void RestrictQuadTree(); + void QuadTreeToTiles(); static void CreateTileTestData(TerrainTile& tile, int tileX, int tileY, float tileScale); static uint16_t TestData(float x, float y); + static QuadTreeNodeId GetParentId(const QuadTreeNodeId& id); + Allocator* allocator; Array nodes; TerrainTile* tiles; render::TextureId* tileTextureIds; uint8_t treeLevels; + uint8_t maxNodeLevel; int tileCount; float terrainWidth; float terrainBottom; diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index 46ad463a..a22170a9 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -380,7 +380,7 @@ void TerrainSystem::Render(const RenderParameters& parameters) // Select tiles to render TerrainQuadTree& quadTree = data.quadTree[id.i]; - quadTree.GetTilesToRender(viewport.frustum, viewport.position, parameters.renderDebug, tilesToRender); + quadTree.UpdateTilesToRender(viewport.frustum, viewport.position, parameters.renderDebug, tilesToRender); // Update uniform buffer From fda314f6b77c27fc2459ebadf52b0ab0c9d0a5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Wed, 18 Sep 2024 18:56:45 +0300 Subject: [PATCH 05/19] First pass implementation for making restricted quad tree --- engine/src/Graphics/TerrainQuadTree.cpp | 60 +++++++++++++++++++++---- engine/src/Graphics/TerrainQuadTree.hpp | 8 ++-- engine/src/Graphics/TerrainSystem.hpp | 2 +- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index ec2b6114..caeec991 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -108,7 +108,7 @@ void TerrainQuadTree::UpdateTilesToRender( const FrustumPlanes& frustum, const Vec3f& cameraPos, const RenderDebugSettings& renderDebug, - Array& resultOut) + Array& resultOut) { KOKKO_PROFILE_SCOPE("TerrainQuadTree::UpdateTilesToRender()"); @@ -119,11 +119,15 @@ void TerrainQuadTree::UpdateTilesToRender( UpdateTilesToRenderParams params{ frustum, cameraPos, renderDebug, resultOut }; BuildQuadTree(QuadTreeNodeId{}, params); + size_t oldNodeCount = nodes.GetCount(); + // Next we need to update the quad tree so that it forms a restricted quad tree RestrictQuadTree(); + //KK_LOG_DEBUG("Nodes, original: {}, after restriction: {}", oldNodeCount, nodes.GetCount()); + // Then we create the final render tiles from the leaf nodes of the quad tree - QuadTreeToTiles(); + QuadTreeToTiles(0, params); } int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRenderParams& params) @@ -155,16 +159,12 @@ int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRender { TerrainQuadTreeNode& node = nodes.PushBack(); node.id = id; - for (int i = 0; i < 4; ++i) - node.children[i] = 0; maxNodeLevel = std::max(maxNodeLevel, id.level); } if (tileIsSmallEnough) { - //params.resultOut.PushBack(id); - if (params.renderDebug.IsFeatureEnabled(RenderDebugFeatureFlag::DrawTerrainTiles)) { Vec3f scale = tileSize; @@ -229,9 +229,42 @@ void TerrainQuadTree::RestrictQuadTree() TerrainQuadTreeNode* currentNode = &nodes[0]; for (uint8_t level = 0; level <= id.level; ++level) { + if (level == id.level) + break; + + uint8_t childLevel = static_cast(level + 1); + int levelDiff = id.level - childLevel; + int childX = id.x >> levelDiff; + int childY = id.y >> levelDiff; + int childIndex = (childY & 1) * 2 + (childX & 1); + // Verify next level towards exists - // If so, update currentNode and continue - // Otherwise, split + // Node always has all the children or none of them + // If currentNode doesn't have children, split + if (currentNode->children[0] == 0) + { + for (int y = 0; y < 2; ++y) + { + for (int x = 0; x < 2; ++x) + { + // Create child node + size_t newNodeIndex = nodes.GetCount(); + assert(newNodeIndex <= UINT16_MAX); + TerrainQuadTreeNode& newNode = nodes.PushBack(); + const QuadTreeNodeId& curId = currentNode->id; + newNode.id = QuadTreeNodeId{ curId.x * 2 + x, curId.y * 2 + y, childLevel }; + currentNode->children[y * 2 + x] = static_cast(newNodeIndex); + } + } + } + + // Update currentNode and continue + int childNodeIndex = currentNode->children[childIndex]; + + assert(childNodeIndex != 0); + + currentNode = &nodes[childNodeIndex]; + continue; } } @@ -241,9 +274,18 @@ void TerrainQuadTree::RestrictQuadTree() } } -void TerrainQuadTree::QuadTreeToTiles() +void TerrainQuadTree::QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params) { + const TerrainQuadTreeNode& node = nodes[nodeIndex]; + if (node.children[0] == 0) // Node has no children + { + params.resultOut.PushBack(node.id); + return; + } + for (int y = 0; y < 2; ++y) + for (int x = 0; x < 2; ++x) + QuadTreeToTiles(node.children[y * 2 + x], params); } int TerrainQuadTree::GetLevelCount() const diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 504a940d..dc099882 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -59,7 +59,7 @@ struct QuadTreeNodeId struct TerrainQuadTreeNode { QuadTreeNodeId id; - uint16_t children[4]; + uint16_t children[4] = { 0, 0, 0, 0 }; }; class TerrainQuadTree @@ -72,7 +72,7 @@ class TerrainQuadTree void DestroyResources(kokko::render::Device* renderDevice); void UpdateTilesToRender(const FrustumPlanes& frustum, const Vec3f& cameraPos, - const RenderDebugSettings& renderDebug, Array& resultOut); + const RenderDebugSettings& renderDebug, Array& resultOut); int GetLevelCount() const; @@ -99,13 +99,13 @@ class TerrainQuadTree const FrustumPlanes& frustum; const Vec3f& cameraPos; const RenderDebugSettings& renderDebug; - Array& resultOut; + Array& resultOut; }; // Returns inserted node index (points to nodes array), or -1 if no insertion int BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRenderParams& params); void RestrictQuadTree(); - void QuadTreeToTiles(); + void QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params); static void CreateTileTestData(TerrainTile& tile, int tileX, int tileY, float tileScale); diff --git a/engine/src/Graphics/TerrainSystem.hpp b/engine/src/Graphics/TerrainSystem.hpp index 9eb0a066..8c4789a3 100644 --- a/engine/src/Graphics/TerrainSystem.hpp +++ b/engine/src/Graphics/TerrainSystem.hpp @@ -135,7 +135,7 @@ class TerrainSystem : public GraphicsFeature } data; - Array tilesToRender; + Array tilesToRender; Array uniformStagingBuffer; From 9e5c85aaa8d7bc65e1cc5f333eeb5c253179ba83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Wed, 18 Sep 2024 22:22:39 +0300 Subject: [PATCH 06/19] Terrain quad tree is now restricted --- engine/src/Graphics/TerrainQuadTree.cpp | 37 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index caeec991..7c952b72 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -30,6 +30,7 @@ TerrainQuadTree::TerrainQuadTree(Allocator* allocator) : tiles(nullptr), tileTextureIds(nullptr), treeLevels(0), + maxNodeLevel(0), tileCount(0), terrainWidth(0.0f), terrainBottom(0.0f), @@ -124,8 +125,6 @@ void TerrainQuadTree::UpdateTilesToRender( // Next we need to update the quad tree so that it forms a restricted quad tree RestrictQuadTree(); - //KK_LOG_DEBUG("Nodes, original: {}, after restriction: {}", oldNodeCount, nodes.GetCount()); - // Then we create the final render tiles from the leaf nodes of the quad tree QuadTreeToTiles(0, params); } @@ -145,9 +144,7 @@ int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRender tileBounds.center = tileMin + tileBounds.extents; if (Intersect::FrustumAabb(params.frustum, tileBounds) == false) - { return -1; - } constexpr float sizeFactor = 0.5f; @@ -205,7 +202,6 @@ void TerrainQuadTree::RestrictQuadTree() if (node.id.level == currentLevel) parentsToCheck.InsertUnique(GetParentId(node.id)); - // Check int tilesPerDim = GetTilesPerDimension(currentLevel - 1); for (const auto& id : parentsToCheck) { @@ -239,9 +235,13 @@ void TerrainQuadTree::RestrictQuadTree() int childIndex = (childY & 1) * 2 + (childX & 1); // Verify next level towards exists - // Node always has all the children or none of them - // If currentNode doesn't have children, split - if (currentNode->children[0] == 0) + // If currentNode has no children, split + bool hasChildren = false; + for (int i = 0; i < 4; ++i) + if (currentNode->children[i] != 0) + hasChildren = true; + + if (hasChildren == false) { for (int y = 0; y < 2; ++y) { @@ -257,12 +257,13 @@ void TerrainQuadTree::RestrictQuadTree() } } } + // If it has some children, but not our target tile, skip work on it + else if (currentNode->children[childIndex] == 0) + break; // Update currentNode and continue int childNodeIndex = currentNode->children[childIndex]; - assert(childNodeIndex != 0); - currentNode = &nodes[childNodeIndex]; continue; } @@ -277,15 +278,27 @@ void TerrainQuadTree::RestrictQuadTree() void TerrainQuadTree::QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params) { const TerrainQuadTreeNode& node = nodes[nodeIndex]; - if (node.children[0] == 0) // Node has no children + + bool hasChildren = false; + for (int i = 0; i < 4; ++i) + if (node.children[i] != 0) + hasChildren = true; + + if (hasChildren == false) { params.resultOut.PushBack(node.id); return; } for (int y = 0; y < 2; ++y) + { for (int x = 0; x < 2; ++x) - QuadTreeToTiles(node.children[y * 2 + x], params); + { + uint16_t childIndex = node.children[y * 2 + x]; + if (childIndex != 0) + QuadTreeToTiles(childIndex, params); + } + } } int TerrainQuadTree::GetLevelCount() const From d346655ea08cb7ceafc253d4f6b7c1dd0911af63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Wed, 18 Sep 2024 22:24:24 +0300 Subject: [PATCH 07/19] Remove unused TerrainTileId.hpp --- engine/CMakeLists.txt | 1 - engine/src/Graphics/TerrainSystem.hpp | 1 - engine/src/Graphics/TerrainTileId.hpp | 8 -------- 3 files changed, 10 deletions(-) delete mode 100644 engine/src/Graphics/TerrainTileId.hpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index f654d8b0..fda73342 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -155,7 +155,6 @@ set (KOKKO_SOURCES src/Graphics/TerrainSerializer.hpp src/Graphics/TerrainSystem.cpp src/Graphics/TerrainSystem.hpp - src/Graphics/TerrainTileId.hpp src/Graphics/TerrainQuadTree.cpp src/Graphics/TerrainQuadTree.hpp src/Graphics/TransformSerializer.hpp diff --git a/engine/src/Graphics/TerrainSystem.hpp b/engine/src/Graphics/TerrainSystem.hpp index 8c4789a3..b94fff94 100644 --- a/engine/src/Graphics/TerrainSystem.hpp +++ b/engine/src/Graphics/TerrainSystem.hpp @@ -8,7 +8,6 @@ #include "Core/StringView.hpp" #include "Graphics/TerrainQuadTree.hpp" -#include "Graphics/TerrainTileId.hpp" #include "Graphics/GraphicsFeature.hpp" diff --git a/engine/src/Graphics/TerrainTileId.hpp b/engine/src/Graphics/TerrainTileId.hpp deleted file mode 100644 index b0405327..00000000 --- a/engine/src/Graphics/TerrainTileId.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -struct TerrainTileId -{ - int level; - int x; - int y; -}; From 17ab66b63925dafb4da8f79abf7acb01a4b5eb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Wed, 18 Sep 2024 22:36:12 +0300 Subject: [PATCH 08/19] Calculate terrain normals smoothly --- .../shaders/deferred_geometry/terrain.glsl | 12 ++++++++---- .../deferred_geometry/terrain.glsl.meta | 2 +- .../deferred_geometry/terrain_uniform.glsl | 2 +- .../terrain_uniform.glsl.meta | 2 +- engine/src/Graphics/TerrainQuadTree.cpp | 19 +++++++++---------- engine/src/Graphics/TerrainQuadTree.hpp | 8 +++++--- engine/src/Graphics/TerrainSystem.cpp | 12 ++++++------ 7 files changed, 31 insertions(+), 26 deletions(-) diff --git a/engine/res/shaders/deferred_geometry/terrain.glsl b/engine/res/shaders/deferred_geometry/terrain.glsl index c3faf5f7..14127355 100644 --- a/engine/res/shaders/deferred_geometry/terrain.glsl +++ b/engine/res/shaders/deferred_geometry/terrain.glsl @@ -17,15 +17,19 @@ uniform sampler2D height_map; vec3 calc_position(vec2 offset) { - float height_sample = texture(height_map, position + offset).r; - vec2 xy_pos = (position + offset + uniforms.tile_offset) * uniforms.terrain_size * uniforms.tile_scale; + const float texel_size = 1.0 / (uniforms.terrain_side_verts + 2); + const vec2 origin = vec2(1.5) * texel_size; + const float border_scale_factor = (uniforms.terrain_side_verts - 1) / (uniforms.terrain_side_verts + 2); + vec2 pos_tile_space = position + offset; + vec2 tex_coord = origin + pos_tile_space * border_scale_factor; + float height_sample = texture(height_map, tex_coord).r; + vec2 xy_pos = (pos_tile_space + uniforms.tile_offset) * uniforms.terrain_size * uniforms.tile_scale; return vec3(xy_pos.x, uniforms.height_origin + height_sample * uniforms.height_range, xy_pos.y); } void main() { - float offset_amount = 1.0 / uniforms.terrain_resolution; - float w_offset = uniforms.terrain_size / uniforms.terrain_resolution * 2.0; + float offset_amount = 1.0 / (uniforms.terrain_side_verts - 1); vec3 p_0 = calc_position(vec2(0.0, 0.0)); vec3 p_right = calc_position(vec2(offset_amount, 0.0)); diff --git a/engine/res/shaders/deferred_geometry/terrain.glsl.meta b/engine/res/shaders/deferred_geometry/terrain.glsl.meta index 2da6414c..6e2bb4f1 100644 --- a/engine/res/shaders/deferred_geometry/terrain.glsl.meta +++ b/engine/res/shaders/deferred_geometry/terrain.glsl.meta @@ -1 +1 @@ -{"hash":13725136470591927459,"uid":"62205cec36c6413a9e0603081a64a160"} \ No newline at end of file +{"hash":16143719169263984309,"uid":"62205cec36c6413a9e0603081a64a160"} \ No newline at end of file diff --git a/engine/res/shaders/deferred_geometry/terrain_uniform.glsl b/engine/res/shaders/deferred_geometry/terrain_uniform.glsl index 779880c1..a8bae339 100644 --- a/engine/res/shaders/deferred_geometry/terrain_uniform.glsl +++ b/engine/res/shaders/deferred_geometry/terrain_uniform.glsl @@ -8,7 +8,7 @@ layout(std140, binding = BLOCK_BINDING_OBJECT) uniform TerrainBlock vec2 tile_offset; float tile_scale; float terrain_size; - float terrain_resolution; + float terrain_side_verts; float height_origin; float height_range; float metalness; diff --git a/engine/res/shaders/deferred_geometry/terrain_uniform.glsl.meta b/engine/res/shaders/deferred_geometry/terrain_uniform.glsl.meta index 02ec6274..32900f6a 100644 --- a/engine/res/shaders/deferred_geometry/terrain_uniform.glsl.meta +++ b/engine/res/shaders/deferred_geometry/terrain_uniform.glsl.meta @@ -1 +1 @@ -{"hash":11585041967935328408,"uid":"a789c0f8fe97e8496859bb5c841bb5db"} \ No newline at end of file +{"hash":15716276276840922323,"uid":"a789c0f8fe97e8496859bb5c841bb5db"} \ No newline at end of file diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index 7c952b72..5ff88c00 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -41,8 +41,7 @@ TerrainQuadTree::TerrainQuadTree(Allocator* allocator) : void TerrainQuadTree::CreateResources(render::Device* renderDevice, uint8_t levels, const TerrainParameters& params) { - constexpr int tileResolution = TerrainTile::Resolution; - constexpr int texResolution = TerrainTile::ResolutionWithBorder; + constexpr int texResolution = TerrainTile::TexelsPerSide; treeLevels = levels; tileCount = GetTileCountForLevelCount(levels); @@ -386,18 +385,18 @@ TEST_CASE("TerrainQuadTree.GetTileScale") void TerrainQuadTree::CreateTileTestData(TerrainTile& tile, int tileX, int tileY, float tileScale) { - constexpr int texResolution = TerrainTile::ResolutionWithBorder; - float tileRes = static_cast(TerrainTile::Resolution); + const float quadScale = 1.0f / TerrainTile::QuadsPerSide; - for (int pixY = 0; pixY < texResolution; ++pixY) + for (int texY = 0; texY < TerrainTile::TexelsPerSide; ++texY) { - for (int pixX = 0; pixX < texResolution; ++pixX) + for (int texX = 0; texX < TerrainTile::TexelsPerSide; ++texX) { - float cx = (pixX / tileRes + tileX) * tileScale; - float cy = (pixY / tileRes + tileY) * tileScale; + float cx = ((texX - 1) * quadScale + tileX) * tileScale; + float cy = ((texY - 1) * quadScale + tileY) * tileScale; - size_t pixelIndex = pixY * texResolution + pixX; - tile.heightData[pixelIndex] = TestData(cx, cy); + int pixelIndex = texY * TerrainTile::TexelsPerTextureRow + texX; + uint16_t value = TestData(cx, cy); + tile.heightData[pixelIndex] = value; } } } diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index dc099882..08318fc3 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -29,10 +29,12 @@ class Device; struct TerrainTile { - static constexpr int Resolution = 31; - static constexpr int ResolutionWithBorder = Resolution + 1; + static constexpr int QuadsPerSide = 32; + static constexpr int VerticesPerSide = QuadsPerSide + 1; + static constexpr int TexelsPerSide = VerticesPerSide + 2; + static constexpr int TexelsPerTextureRow = TexelsPerSide + 1; // Texel data rows need a stride of 4 - uint16_t heightData[ResolutionWithBorder * ResolutionWithBorder]; + uint16_t heightData[TexelsPerTextureRow * TexelsPerSide]; }; struct QuadTreeNodeId diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index a22170a9..10a97de8 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -46,7 +46,7 @@ struct TerrainUniformBlock alignas(4) float tileScale; alignas(4) float terrainSize; - alignas(4) float terrainResolution; + alignas(4) float terrainSideVerts; alignas(4) float heightOrigin; alignas(4) float heightRange; @@ -244,20 +244,20 @@ void TerrainSystem::CreateVertexData() { // Create vertex data - int sideVerts = TerrainTile::Resolution + 1; + constexpr int sideVerts = TerrainTile::VerticesPerSide; unsigned int vertCount = sideVerts * sideVerts; size_t vertexComponents = 2; size_t vertSize = sizeof(float) * vertexComponents; unsigned int vertBytes = static_cast(vertSize * vertCount); float* vertexBuf = static_cast(allocator->Allocate(vertBytes, "TerrainSystem.CreateVertexData() vertexBuf")); - int sideQuads = TerrainTile::Resolution; + constexpr int sideQuads = TerrainTile::QuadsPerSide; int quadIndices = 3 * 2; // 3 indices per triangle, 2 triangles per quad unsigned int indexCount = sideQuads * sideQuads * quadIndices; unsigned int indexBytes = indexCount * sizeof(uint16_t); uint16_t* indexBuf = static_cast(allocator->Allocate(indexBytes, "TerrainSystem.CreateVertexData() indexBuf")); - - float quadSize = 1.0f / TerrainTile::Resolution; + + float quadSize = 1.0f / sideQuads; // Set vertex data for (size_t y = 0; y < sideVerts; ++y) @@ -394,7 +394,7 @@ void TerrainSystem::Render(const RenderParameters& parameters) uniforms.tileOffset = Vec2f(); uniforms.tileScale = 1.0f; uniforms.terrainSize = terrainWidth; - uniforms.terrainResolution = static_cast(TerrainTile::Resolution); + uniforms.terrainSideVerts = static_cast(TerrainTile::VerticesPerSide); uniforms.heightOrigin = quadTree.GetBottom(); uniforms.heightRange = terrainHeight; uniforms.metalness = 0.0f; From ff73e007002d03c9952f698fce7fc5b29a3e2c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Fri, 20 Sep 2024 19:24:07 +0300 Subject: [PATCH 09/19] Two terrain grid connectivity variants added --- engine/src/Graphics/TerrainSystem.cpp | 203 ++++++++++++++++---------- engine/src/Graphics/TerrainSystem.hpp | 26 +++- 2 files changed, 142 insertions(+), 87 deletions(-) diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index 10a97de8..7f1284ba 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -82,14 +82,14 @@ TerrainSystem::~TerrainSystem() { RemoveAll(); - if (vertexData.indexBuffer != 0) - renderDevice->DestroyBuffers(1, &vertexData.indexBuffer); + if (vertexData.indexBuffers[0] != 0) + renderDevice->DestroyBuffers(MeshType_COUNT, vertexData.indexBuffers); if (vertexData.vertexBuffer != 0) renderDevice->DestroyBuffers(1, &vertexData.vertexBuffer); - if (vertexData.vertexArray != 0) - renderDevice->DestroyVertexArrays(1, &vertexData.vertexArray); + if (vertexData.vertexArrays[0] != 0) + renderDevice->DestroyVertexArrays(MeshType_COUNT, vertexData.vertexArrays); if (uniformBufferId != 0) renderDevice->DestroyBuffers(1, &uniformBufferId); @@ -138,7 +138,7 @@ void TerrainSystem::Initialize() // Vertex data - CreateVertexData(); + CreateVertexAndIndexData(); } TerrainId TerrainSystem::Lookup(Entity e) @@ -240,79 +240,6 @@ void TerrainSystem::Submit(const SubmitParameters& parameters) } } -void TerrainSystem::CreateVertexData() -{ - // Create vertex data - - constexpr int sideVerts = TerrainTile::VerticesPerSide; - unsigned int vertCount = sideVerts * sideVerts; - size_t vertexComponents = 2; - size_t vertSize = sizeof(float) * vertexComponents; - unsigned int vertBytes = static_cast(vertSize * vertCount); - float* vertexBuf = static_cast(allocator->Allocate(vertBytes, "TerrainSystem.CreateVertexData() vertexBuf")); - - constexpr int sideQuads = TerrainTile::QuadsPerSide; - int quadIndices = 3 * 2; // 3 indices per triangle, 2 triangles per quad - unsigned int indexCount = sideQuads * sideQuads * quadIndices; - unsigned int indexBytes = indexCount * sizeof(uint16_t); - uint16_t* indexBuf = static_cast(allocator->Allocate(indexBytes, "TerrainSystem.CreateVertexData() indexBuf")); - - float quadSize = 1.0f / sideQuads; - - // Set vertex data - for (size_t y = 0; y < sideVerts; ++y) - { - for (size_t x = 0; x < sideVerts; ++x) - { - size_t vertIndex = y * sideVerts + x; - vertexBuf[vertIndex * vertexComponents + 0] = x * quadSize; - vertexBuf[vertIndex * vertexComponents + 1] = y * quadSize; - } - } - - // Set index data - for (size_t y = 0; y < sideQuads; ++y) - { - for (size_t x = 0; x < sideQuads; ++x) - { - size_t quadStart = (y * sideQuads + x) * quadIndices; - indexBuf[quadStart + 0] = static_cast(y * sideVerts + x); - indexBuf[quadStart + 1] = static_cast((y + 1) * sideVerts + x); - indexBuf[quadStart + 2] = static_cast(y * sideVerts + (x + 1)); - indexBuf[quadStart + 3] = static_cast((y + 1) * sideVerts + x); - indexBuf[quadStart + 4] = static_cast((y + 1) * sideVerts + (x + 1)); - indexBuf[quadStart + 5] = static_cast(y * sideVerts + (x + 1)); - } - } - - renderDevice->CreateVertexArrays(1, &vertexData.vertexArray); - renderDevice->CreateBuffers(1, &vertexData.vertexBuffer); - renderDevice->CreateBuffers(1, &vertexData.indexBuffer); - vertexData.indexCount = indexCount; - - // Bind and upload index buffer - renderDevice->SetBufferStorage(vertexData.indexBuffer, indexBytes, indexBuf, BufferStorageFlags::None); - - // Bind and upload vertex buffer - renderDevice->SetBufferStorage(vertexData.vertexBuffer, vertBytes, vertexBuf, BufferStorageFlags::None); - - VertexAttribute vertexAttributes[] = { VertexAttribute::pos2 }; - VertexFormat vertexFormatPos(vertexAttributes, KOKKO_ARRAY_ITEMS(vertexAttributes)); - vertexFormatPos.CalcOffsetsAndSizeInterleaved(); - const VertexAttribute& attr = vertexAttributes[0]; - - renderDevice->SetVertexArrayVertexBuffer(vertexData.vertexArray, 0, vertexData.vertexBuffer, 0, attr.stride); - renderDevice->SetVertexArrayIndexBuffer(vertexData.vertexArray, vertexData.indexBuffer); - - renderDevice->EnableVertexAttribute(vertexData.vertexArray, attr.attrIndex); - renderDevice->SetVertexAttribFormat( - vertexData.vertexArray, attr.attrIndex, attr.elemCount, attr.elemType, attr.offset); - renderDevice->SetVertexAttribBinding(vertexData.vertexArray, attr.attrIndex, 0); - - allocator->Deallocate(indexBuf); - allocator->Deallocate(vertexBuf); -} - void TerrainSystem::InitializeTerrain(TerrainId id, const TerrainParameters& params) { KOKKO_PROFILE_FUNCTION(); @@ -447,7 +374,6 @@ void TerrainSystem::Render(const RenderParameters& parameters) encoder->BindTextureToShader(albedoMap->uniformLocation, 1, textureObjectId); } - encoder->BindVertexArray(vertexData.vertexArray); // For height texture @@ -459,6 +385,10 @@ void TerrainSystem::Render(const RenderParameters& parameters) int blocksUsed = 0; for (const auto& tile : tilesToRender) { + int tileMeshTypeIndex = MeshType_TopSparse; + + encoder->BindVertexArray(vertexData.vertexArrays[tileMeshTypeIndex]); + intptr_t rangeOffset = blocksUsed * uniformBlockStride; blocksUsed += 1; @@ -472,12 +402,123 @@ void TerrainSystem::Render(const RenderParameters& parameters) encoder->BindTextureToShader(heightMap->uniformLocation, 0, heightTex); } - encoder->DrawIndexed( - RenderPrimitiveMode::Triangles, RenderIndexType::UnsignedShort, vertexData.indexCount, 0, 0); + encoder->DrawIndexed(RenderPrimitiveMode::Triangles, RenderIndexType::UnsignedShort, + vertexData.indexCounts[tileMeshTypeIndex], 0, 0); } } tilesToRender.Clear(); } +void TerrainSystem::CreateVertexAndIndexData() +{ + // Create vertex data + + constexpr int sideVerts = TerrainTile::VerticesPerSide; + unsigned int vertCount = sideVerts * sideVerts; + size_t vertexComponents = 2; + size_t vertSize = sizeof(float) * vertexComponents; + unsigned int vertBytes = static_cast(vertSize * vertCount); + float* vertexBuf = static_cast(allocator->Allocate(vertBytes, "TerrainSystem.CreateVertexData() vertexBuf")); + + constexpr int sideQuads = TerrainTile::QuadsPerSide; + int quadIndices = 3 * 2; // 3 indices per triangle, 2 triangles per quad + unsigned int maxIndexCount = sideQuads * sideQuads * quadIndices; + unsigned int maxIndexBytes = maxIndexCount * sizeof(uint16_t); + uint16_t* indexBuf = static_cast(allocator->Allocate(maxIndexBytes, "TerrainSystem.CreateVertexData() indexBuf")); + + float quadSize = 1.0f / sideQuads; + + renderDevice->CreateVertexArrays(MeshType_COUNT, vertexData.vertexArrays); + renderDevice->CreateBuffers(1, &vertexData.vertexBuffer); + renderDevice->CreateBuffers(MeshType_COUNT, vertexData.indexBuffers); + + // Set vertex data + for (size_t y = 0; y < sideVerts; ++y) + { + for (size_t x = 0; x < sideVerts; ++x) + { + size_t vertIndex = y * sideVerts + x; + vertexBuf[vertIndex * vertexComponents + 0] = x * quadSize; + vertexBuf[vertIndex * vertexComponents + 1] = y * quadSize; + } + } + + // Upload vertex buffer + renderDevice->SetBufferStorage(vertexData.vertexBuffer, vertBytes, vertexBuf, BufferStorageFlags::None); + + { // MeshType_Regular + for (int y = 0; y < sideQuads; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + int quadStart = (y * sideQuads + x) * quadIndices; + indexBuf[quadStart + 0] = static_cast(y * sideVerts + x); + indexBuf[quadStart + 1] = static_cast((y + 1) * sideVerts + x); + indexBuf[quadStart + 2] = static_cast(y * sideVerts + (x + 1)); + indexBuf[quadStart + 3] = static_cast((y + 1) * sideVerts + x); + indexBuf[quadStart + 4] = static_cast((y + 1) * sideVerts + (x + 1)); + indexBuf[quadStart + 5] = static_cast(y * sideVerts + (x + 1)); + } + } + + vertexData.indexCounts[MeshType_Regular] = maxIndexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_Regular], maxIndexBytes, indexBuf, BufferStorageFlags::None); + } + + { // MeshType_TopSparse + int indexCount = 0; + + for (int y = 0, x = 0; x < sideQuads; x += 2) + { + indexBuf[indexCount + 0] = static_cast(y * sideVerts + x); + indexBuf[indexCount + 1] = static_cast((y + 1) * sideVerts + x); + indexBuf[indexCount + 2] = static_cast((y + 1) * sideVerts + (x + 1)); + indexBuf[indexCount + 3] = static_cast(y * sideVerts + x); + indexBuf[indexCount + 4] = static_cast((y + 1) * sideVerts + (x + 1)); + indexBuf[indexCount + 5] = static_cast(y * sideVerts + (x + 2)); + indexBuf[indexCount + 6] = static_cast(y * sideVerts + (x + 2)); + indexBuf[indexCount + 7] = static_cast((y + 1) * sideVerts + (x + 1)); + indexBuf[indexCount + 8] = static_cast((y + 1) * sideVerts + (x + 2)); + indexCount += 9; + } + + for (int y = 1; y < sideQuads; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + indexBuf[indexCount + 0] = static_cast(y * sideVerts + x); + indexBuf[indexCount + 1] = static_cast((y + 1) * sideVerts + x); + indexBuf[indexCount + 2] = static_cast(y * sideVerts + (x + 1)); + indexBuf[indexCount + 3] = static_cast((y + 1) * sideVerts + x); + indexBuf[indexCount + 4] = static_cast((y + 1) * sideVerts + (x + 1)); + indexBuf[indexCount + 5] = static_cast(y * sideVerts + (x + 1)); + indexCount += 6; + } + } + + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_TopSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_TopSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + + VertexAttribute vertexAttributes[] = { VertexAttribute::pos2 }; + VertexFormat vertexFormatPos(vertexAttributes, KOKKO_ARRAY_ITEMS(vertexAttributes)); + vertexFormatPos.CalcOffsetsAndSizeInterleaved(); + const VertexAttribute& attr = vertexAttributes[0]; + + render::VertexArrayId vertexArray = vertexData.vertexArrays[MeshType_TopSparse]; + renderDevice->SetVertexArrayVertexBuffer(vertexArray, 0, vertexData.vertexBuffer, 0, attr.stride); + renderDevice->SetVertexArrayIndexBuffer(vertexArray, vertexData.indexBuffers[MeshType_TopSparse]); + + renderDevice->EnableVertexAttribute(vertexArray, attr.attrIndex); + renderDevice->SetVertexAttribFormat( + vertexArray, attr.attrIndex, attr.elemCount, attr.elemType, attr.offset); + renderDevice->SetVertexAttribBinding(vertexArray, attr.attrIndex, 0); + + allocator->Deallocate(indexBuf); + allocator->Deallocate(vertexBuf); +} } diff --git a/engine/src/Graphics/TerrainSystem.hpp b/engine/src/Graphics/TerrainSystem.hpp index b94fff94..711d5672 100644 --- a/engine/src/Graphics/TerrainSystem.hpp +++ b/engine/src/Graphics/TerrainSystem.hpp @@ -97,13 +97,27 @@ class TerrainSystem : public GraphicsFeature HashMap entityMap; + enum MeshType + { + MeshType_Regular, + MeshType_TopSparse, + MeshType_TopRightSparse, + MeshType_RightSparse, + MeshType_RightBottomSparse, + MeshType_BottomSparse, + MeshType_BottomLeftSparse, + MeshType_LeftSparse, + MeshType_LeftTopSparse, + + MeshType_COUNT + }; + struct VertexData { - render::VertexArrayId vertexArray; + render::VertexArrayId vertexArrays[MeshType_COUNT]; render::BufferId vertexBuffer; - render::BufferId indexBuffer; - - int indexCount; + render::BufferId indexBuffers[MeshType_COUNT]; + int indexCounts[MeshType_COUNT]; }; VertexData vertexData; @@ -138,12 +152,12 @@ class TerrainSystem : public GraphicsFeature Array uniformStagingBuffer; - void CreateVertexData(); - void InitializeTerrain(TerrainId id, const TerrainParameters& params); void DeinitializeTerrain(TerrainId id); void Reallocate(size_t required); + + void CreateVertexAndIndexData(); }; } From afd81f0c22ab6d99f184fdae954fcdfaf8410ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sat, 21 Sep 2024 00:52:24 +0300 Subject: [PATCH 10/19] Five terrain grid variants added --- engine/src/Graphics/TerrainSystem.cpp | 263 +++++++++++++++++++++++--- 1 file changed, 237 insertions(+), 26 deletions(-) diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index 7f1284ba..aaa2ce4e 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -385,7 +385,7 @@ void TerrainSystem::Render(const RenderParameters& parameters) int blocksUsed = 0; for (const auto& tile : tilesToRender) { - int tileMeshTypeIndex = MeshType_TopSparse; + int tileMeshTypeIndex = MeshType_Regular; encoder->BindVertexArray(vertexData.vertexArrays[tileMeshTypeIndex]); @@ -447,18 +447,21 @@ void TerrainSystem::CreateVertexAndIndexData() // Upload vertex buffer renderDevice->SetBufferStorage(vertexData.vertexBuffer, vertBytes, vertexBuf, BufferStorageFlags::None); - { // MeshType_Regular + auto vertexIndex = [](int x, int y) { return static_cast(y * TerrainTile::VerticesPerSide + x); }; + + // MeshType_Regular + { for (int y = 0; y < sideQuads; ++y) { for (int x = 0; x < sideQuads; ++x) { int quadStart = (y * sideQuads + x) * quadIndices; - indexBuf[quadStart + 0] = static_cast(y * sideVerts + x); - indexBuf[quadStart + 1] = static_cast((y + 1) * sideVerts + x); - indexBuf[quadStart + 2] = static_cast(y * sideVerts + (x + 1)); - indexBuf[quadStart + 3] = static_cast((y + 1) * sideVerts + x); - indexBuf[quadStart + 4] = static_cast((y + 1) * sideVerts + (x + 1)); - indexBuf[quadStart + 5] = static_cast(y * sideVerts + (x + 1)); + indexBuf[quadStart + 0] = vertexIndex(x, y); + indexBuf[quadStart + 1] = vertexIndex(x, y + 1); + indexBuf[quadStart + 2] = vertexIndex(x + 1, y); + indexBuf[quadStart + 3] = vertexIndex(x, y + 1); + indexBuf[quadStart + 4] = vertexIndex(x + 1, y + 1); + indexBuf[quadStart + 5] = vertexIndex(x + 1, y); } } @@ -467,20 +470,21 @@ void TerrainSystem::CreateVertexAndIndexData() vertexData.indexBuffers[MeshType_Regular], maxIndexBytes, indexBuf, BufferStorageFlags::None); } - { // MeshType_TopSparse + // MeshType_TopSparse + { int indexCount = 0; for (int y = 0, x = 0; x < sideQuads; x += 2) { - indexBuf[indexCount + 0] = static_cast(y * sideVerts + x); - indexBuf[indexCount + 1] = static_cast((y + 1) * sideVerts + x); - indexBuf[indexCount + 2] = static_cast((y + 1) * sideVerts + (x + 1)); - indexBuf[indexCount + 3] = static_cast(y * sideVerts + x); - indexBuf[indexCount + 4] = static_cast((y + 1) * sideVerts + (x + 1)); - indexBuf[indexCount + 5] = static_cast(y * sideVerts + (x + 2)); - indexBuf[indexCount + 6] = static_cast(y * sideVerts + (x + 2)); - indexBuf[indexCount + 7] = static_cast((y + 1) * sideVerts + (x + 1)); - indexBuf[indexCount + 8] = static_cast((y + 1) * sideVerts + (x + 2)); + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 3] = vertexIndex(x, y); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 2, y); + indexBuf[indexCount + 6] = vertexIndex(x + 2, y); + indexBuf[indexCount + 7] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 8] = vertexIndex(x + 2, y + 1); indexCount += 9; } @@ -488,30 +492,237 @@ void TerrainSystem::CreateVertexAndIndexData() { for (int x = 0; x < sideQuads; ++x) { - indexBuf[indexCount + 0] = static_cast(y * sideVerts + x); - indexBuf[indexCount + 1] = static_cast((y + 1) * sideVerts + x); - indexBuf[indexCount + 2] = static_cast(y * sideVerts + (x + 1)); - indexBuf[indexCount + 3] = static_cast((y + 1) * sideVerts + x); - indexBuf[indexCount + 4] = static_cast((y + 1) * sideVerts + (x + 1)); - indexBuf[indexCount + 5] = static_cast(y * sideVerts + (x + 1)); + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x, y + 1); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y); indexCount += 6; } } + assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); vertexData.indexCounts[MeshType_TopSparse] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[MeshType_TopSparse], indexBytes, indexBuf, BufferStorageFlags::None); } + // MeshType_TopRightSparse + { + int indexCount = 0; + + for (int y = 0, x = 0; x < sideQuads; x += 2) + { + // +---+ + // |\ / + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 3] = vertexIndex(x, y); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 2, y); + indexCount += 6; + + if (x < sideQuads - 2) + { + // + + // /| + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x + 2, y); + indexBuf[indexCount + 1] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 2, y + 1); + indexCount += 3; + } + else + { + // + + // /| + // + | + // |\| + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x + 2, y); + indexBuf[indexCount + 1] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 2, y + 2); + indexBuf[indexCount + 3] = vertexIndex(x + 2, y + 2); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 2); + indexCount += 6; + } + } + + for (int y = 1; y < sideQuads; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + if (x < sideQuads - 1) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x, y + 1); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y); + indexCount += 6; + } + else if ((y & 1) == 0) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 2); + indexBuf[indexCount + 6] = vertexIndex(x + 1, y + 2); + indexBuf[indexCount + 7] = vertexIndex(x, y + 1); + indexBuf[indexCount + 8] = vertexIndex(x, y + 2); + indexCount += 9; + } + } + } + + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_TopRightSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_TopRightSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + + // MeshType_RightSparse + { + int indexCount = 0; + + for (int y = 0; y < sideQuads; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + if (x < sideQuads - 1) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x, y + 1); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y); + indexCount += 6; + } + else if ((y & 1) == 0) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 2); + indexBuf[indexCount + 6] = vertexIndex(x + 1, y + 2); + indexBuf[indexCount + 7] = vertexIndex(x, y + 1); + indexBuf[indexCount + 8] = vertexIndex(x, y + 2); + indexCount += 9; + } + } + } + + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_RightSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_RightSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + + // MeshType_RightBottomSparse + { + int indexCount = 0; + + for (int y = 0; y < sideQuads - 1; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + if (x < sideQuads - 1) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x, y + 1); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y); + indexCount += 6; + } + else if ((y & 1) == 0) + { + // +-+ + // |/| + // + | + // \| + // + + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 2); + indexCount += 6; + + if (y < sideQuads - 2) + { + // + + // |\ + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x + 1, y + 2); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x, y + 2); + indexCount += 3; + } + } + } + } + + for (int y = sideQuads - 1, x = 0; x < sideQuads; x += 2) + { + if (x < sideQuads - 2) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 2, y + 1); + indexBuf[indexCount + 6] = vertexIndex(x + 1, y); + indexBuf[indexCount + 7] = vertexIndex(x + 2, y + 1); + indexBuf[indexCount + 8] = vertexIndex(x + 2, y); + indexCount += 9; + } + else + { + // +-+ + // |/ \ + // +---+ + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 2, y + 1); + indexCount += 6; + } + } + + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_RightBottomSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_RightBottomSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + VertexAttribute vertexAttributes[] = { VertexAttribute::pos2 }; VertexFormat vertexFormatPos(vertexAttributes, KOKKO_ARRAY_ITEMS(vertexAttributes)); vertexFormatPos.CalcOffsetsAndSizeInterleaved(); const VertexAttribute& attr = vertexAttributes[0]; - render::VertexArrayId vertexArray = vertexData.vertexArrays[MeshType_TopSparse]; + render::VertexArrayId vertexArray = vertexData.vertexArrays[MeshType_Regular]; renderDevice->SetVertexArrayVertexBuffer(vertexArray, 0, vertexData.vertexBuffer, 0, attr.stride); - renderDevice->SetVertexArrayIndexBuffer(vertexArray, vertexData.indexBuffers[MeshType_TopSparse]); + renderDevice->SetVertexArrayIndexBuffer(vertexArray, vertexData.indexBuffers[MeshType_RightBottomSparse]); renderDevice->EnableVertexAttribute(vertexArray, attr.attrIndex); renderDevice->SetVertexAttribFormat( From 4e0a9223b85101e471c9253ed7b1e31f76945d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sat, 21 Sep 2024 01:31:09 +0300 Subject: [PATCH 11/19] All nine terrain grid variants added --- engine/src/Graphics/TerrainSystem.cpp | 299 ++++++++++++++++++++------ 1 file changed, 239 insertions(+), 60 deletions(-) diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index aaa2ce4e..803a1b8f 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -402,8 +402,8 @@ void TerrainSystem::Render(const RenderParameters& parameters) encoder->BindTextureToShader(heightMap->uniformLocation, 0, heightTex); } - encoder->DrawIndexed(RenderPrimitiveMode::Triangles, RenderIndexType::UnsignedShort, - vertexData.indexCounts[tileMeshTypeIndex], 0, 0); + int count = vertexData.indexCounts[tileMeshTypeIndex]; + encoder->DrawIndexed(RenderPrimitiveMode::Triangles, RenderIndexType::UnsignedShort, count, 0, 0); } } @@ -448,26 +448,30 @@ void TerrainSystem::CreateVertexAndIndexData() renderDevice->SetBufferStorage(vertexData.vertexBuffer, vertBytes, vertexBuf, BufferStorageFlags::None); auto vertexIndex = [](int x, int y) { return static_cast(y * TerrainTile::VerticesPerSide + x); }; + auto basicQuad = [&indexBuf, &vertexIndex](int x, int y, int& count) + { + indexBuf[count + 0] = vertexIndex(x, y); + indexBuf[count + 1] = vertexIndex(x, y + 1); + indexBuf[count + 2] = vertexIndex(x + 1, y); + indexBuf[count + 3] = vertexIndex(x, y + 1); + indexBuf[count + 4] = vertexIndex(x + 1, y + 1); + indexBuf[count + 5] = vertexIndex(x + 1, y); + count += 6; + }; // MeshType_Regular { + int indexCount = 0; + for (int y = 0; y < sideQuads; ++y) - { for (int x = 0; x < sideQuads; ++x) - { - int quadStart = (y * sideQuads + x) * quadIndices; - indexBuf[quadStart + 0] = vertexIndex(x, y); - indexBuf[quadStart + 1] = vertexIndex(x, y + 1); - indexBuf[quadStart + 2] = vertexIndex(x + 1, y); - indexBuf[quadStart + 3] = vertexIndex(x, y + 1); - indexBuf[quadStart + 4] = vertexIndex(x + 1, y + 1); - indexBuf[quadStart + 5] = vertexIndex(x + 1, y); - } - } + basicQuad(x, y, indexCount); - vertexData.indexCounts[MeshType_Regular] = maxIndexCount; + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_Regular] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_Regular], maxIndexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[MeshType_Regular], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_TopSparse @@ -489,18 +493,8 @@ void TerrainSystem::CreateVertexAndIndexData() } for (int y = 1; y < sideQuads; ++y) - { for (int x = 0; x < sideQuads; ++x) - { - indexBuf[indexCount + 0] = vertexIndex(x, y); - indexBuf[indexCount + 1] = vertexIndex(x, y + 1); - indexBuf[indexCount + 2] = vertexIndex(x + 1, y); - indexBuf[indexCount + 3] = vertexIndex(x, y + 1); - indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); - indexBuf[indexCount + 5] = vertexIndex(x + 1, y); - indexCount += 6; - } - } + basicQuad(x, y, indexCount); assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); @@ -558,15 +552,7 @@ void TerrainSystem::CreateVertexAndIndexData() for (int x = 0; x < sideQuads; ++x) { if (x < sideQuads - 1) - { - indexBuf[indexCount + 0] = vertexIndex(x, y); - indexBuf[indexCount + 1] = vertexIndex(x, y + 1); - indexBuf[indexCount + 2] = vertexIndex(x + 1, y); - indexBuf[indexCount + 3] = vertexIndex(x, y + 1); - indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); - indexBuf[indexCount + 5] = vertexIndex(x + 1, y); - indexCount += 6; - } + basicQuad(x, y, indexCount); else if ((y & 1) == 0) { indexBuf[indexCount + 0] = vertexIndex(x, y); @@ -599,15 +585,7 @@ void TerrainSystem::CreateVertexAndIndexData() for (int x = 0; x < sideQuads; ++x) { if (x < sideQuads - 1) - { - indexBuf[indexCount + 0] = vertexIndex(x, y); - indexBuf[indexCount + 1] = vertexIndex(x, y + 1); - indexBuf[indexCount + 2] = vertexIndex(x + 1, y); - indexBuf[indexCount + 3] = vertexIndex(x, y + 1); - indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); - indexBuf[indexCount + 5] = vertexIndex(x + 1, y); - indexCount += 6; - } + basicQuad(x, y, indexCount); else if ((y & 1) == 0) { indexBuf[indexCount + 0] = vertexIndex(x, y); @@ -640,15 +618,7 @@ void TerrainSystem::CreateVertexAndIndexData() for (int x = 0; x < sideQuads; ++x) { if (x < sideQuads - 1) - { - indexBuf[indexCount + 0] = vertexIndex(x, y); - indexBuf[indexCount + 1] = vertexIndex(x, y + 1); - indexBuf[indexCount + 2] = vertexIndex(x + 1, y); - indexBuf[indexCount + 3] = vertexIndex(x, y + 1); - indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); - indexBuf[indexCount + 5] = vertexIndex(x + 1, y); - indexCount += 6; - } + basicQuad(x, y, indexCount); else if ((y & 1) == 0) { // +-+ @@ -715,19 +685,228 @@ void TerrainSystem::CreateVertexAndIndexData() vertexData.indexBuffers[MeshType_RightBottomSparse], indexBytes, indexBuf, BufferStorageFlags::None); } + // MeshType_TopSparse + { + int indexCount = 0; + + for (int y = 0; y < sideQuads - 1; ++y) + for (int x = 0; x < sideQuads; ++x) + basicQuad(x, y, indexCount); + + for (int y = sideQuads - 1, x = 0; x < sideQuads; x += 2) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 2, y + 1); + indexBuf[indexCount + 6] = vertexIndex(x + 1, y); + indexBuf[indexCount + 7] = vertexIndex(x + 2, y + 1); + indexBuf[indexCount + 8] = vertexIndex(x + 2, y); + indexCount += 9; + } + + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_BottomSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_BottomSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + + // MeshType_BottomLeftSparse + { + int indexCount = 0; + + for (int y = 0; y < sideQuads - 1; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + if (x > 0) + basicQuad(x, y, indexCount); + else if ((y & 1) == 0) + { + // +-+ + // |\| + // | + + // |/ + // + + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 2); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 1); + indexCount += 6; + + if (y < sideQuads - 2) + { + // + + // /| + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 1] = vertexIndex(x, y + 2); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y + 2); + indexCount += 3; + } + } + } + } + + for (int y = sideQuads - 1, x = 0; x < sideQuads; x += 2) + { + if (x > 0) + { + // +-+ + // |/ + // + + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexCount += 3; + } + + // +-+ + // / \| + // +---+ + indexBuf[indexCount + 0] = vertexIndex(x, y + 1); + indexBuf[indexCount + 1] = vertexIndex(x + 2, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y); + indexBuf[indexCount + 4] = vertexIndex(x + 2, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 2, y); + indexCount += 6; + } + + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_BottomLeftSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_BottomLeftSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + + // MeshType_LeftSparse + { + int indexCount = 0; + + for (int y = 0; y < sideQuads; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + if (x > 0) + basicQuad(x, y, indexCount); + else if ((y & 1) == 0) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 2); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 6] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 7] = vertexIndex(x, y + 2); + indexBuf[indexCount + 8] = vertexIndex(x + 1, y + 2); + indexCount += 9; + } + } + } + + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_LeftSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_LeftSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + + // MeshType_LeftTopSparse + { + int indexCount = 0; + + for (int y = 0, x = 0; x < sideQuads; x += 2) + { + if (x > 0) + { + // + + // |\ + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y + 1); + indexCount += 3; + } + else + { + // + + // |\ + // | + + // |/| + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x, y + 2); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 3] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 4] = vertexIndex(x, y + 2); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 2); + indexCount += 6; + } + + // +---+ + // \ /| + // +-+ + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 2, y); + indexBuf[indexCount + 3] = vertexIndex(x + 2, y); + indexBuf[indexCount + 4] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 5] = vertexIndex(x + 2, y + 1); + indexCount += 6; + } + + for (int y = 1; y < sideQuads; ++y) + { + for (int x = 0; x < sideQuads; ++x) + { + if (x > 0) + basicQuad(x, y, indexCount); + else if ((y & 1) == 0) + { + indexBuf[indexCount + 0] = vertexIndex(x, y); + indexBuf[indexCount + 1] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 2] = vertexIndex(x + 1, y); + indexBuf[indexCount + 3] = vertexIndex(x, y); + indexBuf[indexCount + 4] = vertexIndex(x, y + 2); + indexBuf[indexCount + 5] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 6] = vertexIndex(x + 1, y + 1); + indexBuf[indexCount + 7] = vertexIndex(x, y + 2); + indexBuf[indexCount + 8] = vertexIndex(x + 1, y + 2); + indexCount += 9; + } + } + } + + assert(indexCount <= maxIndexCount); + int indexBytes = indexCount * sizeof(uint16_t); + vertexData.indexCounts[MeshType_LeftTopSparse] = indexCount; + renderDevice->SetBufferStorage( + vertexData.indexBuffers[MeshType_LeftTopSparse], indexBytes, indexBuf, BufferStorageFlags::None); + } + VertexAttribute vertexAttributes[] = { VertexAttribute::pos2 }; VertexFormat vertexFormatPos(vertexAttributes, KOKKO_ARRAY_ITEMS(vertexAttributes)); vertexFormatPos.CalcOffsetsAndSizeInterleaved(); const VertexAttribute& attr = vertexAttributes[0]; - render::VertexArrayId vertexArray = vertexData.vertexArrays[MeshType_Regular]; - renderDevice->SetVertexArrayVertexBuffer(vertexArray, 0, vertexData.vertexBuffer, 0, attr.stride); - renderDevice->SetVertexArrayIndexBuffer(vertexArray, vertexData.indexBuffers[MeshType_RightBottomSparse]); + // Initialize vertex buffers + for (int i = 0; i < MeshType_COUNT; ++i) + { + render::VertexArrayId vertexArray = vertexData.vertexArrays[i]; + renderDevice->SetVertexArrayVertexBuffer(vertexArray, 0, vertexData.vertexBuffer, 0, attr.stride); + renderDevice->SetVertexArrayIndexBuffer(vertexArray, vertexData.indexBuffers[i]); - renderDevice->EnableVertexAttribute(vertexArray, attr.attrIndex); - renderDevice->SetVertexAttribFormat( - vertexArray, attr.attrIndex, attr.elemCount, attr.elemType, attr.offset); - renderDevice->SetVertexAttribBinding(vertexArray, attr.attrIndex, 0); + renderDevice->EnableVertexAttribute(vertexArray, attr.attrIndex); + renderDevice->SetVertexAttribFormat(vertexArray, attr.attrIndex, attr.elemCount, attr.elemType, attr.offset); + renderDevice->SetVertexAttribBinding(vertexArray, attr.attrIndex, 0); + } allocator->Deallocate(indexBuf); allocator->Deallocate(vertexBuf); From cd67b82cb98544711323ce924296549917bf23f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sat, 21 Sep 2024 12:32:42 +0300 Subject: [PATCH 12/19] Some code structure to for different mesh type rendering --- engine/src/Graphics/TerrainQuadTree.cpp | 6 +- engine/src/Graphics/TerrainQuadTree.hpp | 25 ++++++++- engine/src/Graphics/TerrainSystem.cpp | 74 ++++++++++++++----------- engine/src/Graphics/TerrainSystem.hpp | 23 ++------ 4 files changed, 73 insertions(+), 55 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index 5ff88c00..b8fa151a 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -108,7 +108,7 @@ void TerrainQuadTree::UpdateTilesToRender( const FrustumPlanes& frustum, const Vec3f& cameraPos, const RenderDebugSettings& renderDebug, - Array& resultOut) + Array& resultOut) { KOKKO_PROFILE_SCOPE("TerrainQuadTree::UpdateTilesToRender()"); @@ -128,7 +128,7 @@ void TerrainQuadTree::UpdateTilesToRender( QuadTreeToTiles(0, params); } -int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRenderParams& params) +int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, const UpdateTilesToRenderParams& params) { float tileScale = GetTileScale(id.level); @@ -285,7 +285,7 @@ void TerrainQuadTree::QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderPar if (hasChildren == false) { - params.resultOut.PushBack(node.id); + params.resultOut.PushBack(TerrainTileDrawInfo{ node.id, TerrainMeshType::Regular }); return; } diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 08318fc3..32034eec 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -64,6 +64,25 @@ struct TerrainQuadTreeNode uint16_t children[4] = { 0, 0, 0, 0 }; }; +enum class TerrainMeshType : uint8_t +{ + Regular, + TopSparse, + TopRightSparse, + RightSparse, + RightBottomSparse, + BottomSparse, + BottomLeftSparse, + LeftSparse, + LeftTopSparse +}; + +struct TerrainTileDrawInfo +{ + QuadTreeNodeId id; + TerrainMeshType meshType; +}; + class TerrainQuadTree { public: @@ -74,7 +93,7 @@ class TerrainQuadTree void DestroyResources(kokko::render::Device* renderDevice); void UpdateTilesToRender(const FrustumPlanes& frustum, const Vec3f& cameraPos, - const RenderDebugSettings& renderDebug, Array& resultOut); + const RenderDebugSettings& renderDebug, Array& resultOut); int GetLevelCount() const; @@ -101,11 +120,11 @@ class TerrainQuadTree const FrustumPlanes& frustum; const Vec3f& cameraPos; const RenderDebugSettings& renderDebug; - Array& resultOut; + Array& resultOut; }; // Returns inserted node index (points to nodes array), or -1 if no insertion - int BuildQuadTree(const QuadTreeNodeId& id, UpdateTilesToRenderParams& params); + int BuildQuadTree(const QuadTreeNodeId& id, const UpdateTilesToRenderParams& params); void RestrictQuadTree(); void QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params); diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index 803a1b8f..c2ffda92 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -83,13 +83,13 @@ TerrainSystem::~TerrainSystem() RemoveAll(); if (vertexData.indexBuffers[0] != 0) - renderDevice->DestroyBuffers(MeshType_COUNT, vertexData.indexBuffers); + renderDevice->DestroyBuffers(MeshTypeCount, vertexData.indexBuffers); if (vertexData.vertexBuffer != 0) renderDevice->DestroyBuffers(1, &vertexData.vertexBuffer); if (vertexData.vertexArrays[0] != 0) - renderDevice->DestroyVertexArrays(MeshType_COUNT, vertexData.vertexArrays); + renderDevice->DestroyVertexArrays(MeshTypeCount, vertexData.vertexArrays); if (uniformBufferId != 0) renderDevice->DestroyBuffers(1, &uniformBufferId); @@ -335,11 +335,11 @@ void TerrainSystem::Render(const RenderParameters& parameters) int blocksWritten = 0; for (const auto& tile : tilesToRender) { - const float halfTileCount = 0.5f * TerrainQuadTree::GetTilesPerDimension(tile.level); + const float halfTileCount = 0.5f * TerrainQuadTree::GetTilesPerDimension(tile.id.level); const Vec2f levelOrigin(-halfTileCount, -halfTileCount); - uniforms.tileOffset = levelOrigin + Vec2f(static_cast(tile.x), static_cast(tile.y)); - uniforms.tileScale = TerrainQuadTree::GetTileScale(tile.level); + uniforms.tileOffset = levelOrigin + Vec2f(static_cast(tile.id.x), static_cast(tile.id.y)); + uniforms.tileScale = TerrainQuadTree::GetTileScale(tile.id.level); uint8_t* dest = &uniformStagingBuffer[blocksWritten * uniformBlockStride]; std::memcpy(dest, &uniforms, sizeof(uniforms)); @@ -374,7 +374,6 @@ void TerrainSystem::Render(const RenderParameters& parameters) encoder->BindTextureToShader(albedoMap->uniformLocation, 1, textureObjectId); } - // For height texture encoder->BindSampler(0, textureSampler); @@ -382,12 +381,16 @@ void TerrainSystem::Render(const RenderParameters& parameters) { KOKKO_PROFILE_SCOPE("Render tiles"); + uint8_t prevMeshType = 255; int blocksUsed = 0; for (const auto& tile : tilesToRender) { - int tileMeshTypeIndex = MeshType_Regular; - - encoder->BindVertexArray(vertexData.vertexArrays[tileMeshTypeIndex]); + uint8_t tileMeshTypeIndex = static_cast(tile.meshType); + if (tileMeshTypeIndex != prevMeshType) + { + encoder->BindVertexArray(vertexData.vertexArrays[tileMeshTypeIndex]); + prevMeshType = tileMeshTypeIndex; + } intptr_t rangeOffset = blocksUsed * uniformBlockStride; blocksUsed += 1; @@ -397,7 +400,7 @@ void TerrainSystem::Render(const RenderParameters& parameters) if (heightMap != nullptr) { - render::TextureId heightTex = quadTree.GetTileHeightTexture(tile.level, tile.x, tile.y); + render::TextureId heightTex = quadTree.GetTileHeightTexture(tile.id.level, tile.id.x, tile.id.y); encoder->BindTextureToShader(heightMap->uniformLocation, 0, heightTex); } @@ -429,9 +432,9 @@ void TerrainSystem::CreateVertexAndIndexData() float quadSize = 1.0f / sideQuads; - renderDevice->CreateVertexArrays(MeshType_COUNT, vertexData.vertexArrays); + renderDevice->CreateVertexArrays(MeshTypeCount, vertexData.vertexArrays); renderDevice->CreateBuffers(1, &vertexData.vertexBuffer); - renderDevice->CreateBuffers(MeshType_COUNT, vertexData.indexBuffers); + renderDevice->CreateBuffers(MeshTypeCount, vertexData.indexBuffers); // Set vertex data for (size_t y = 0; y < sideVerts; ++y) @@ -469,9 +472,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_Regular] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::Regular); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_Regular], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_TopSparse @@ -498,9 +502,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_TopSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::TopSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_TopSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_TopRightSparse @@ -571,9 +576,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_TopRightSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::TopRightSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_TopRightSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_RightSparse @@ -604,9 +610,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_RightSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::RightSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_RightSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_RightBottomSparse @@ -680,9 +687,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_RightBottomSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::RightBottomSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_RightBottomSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_TopSparse @@ -709,9 +717,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_BottomSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::BottomSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_BottomSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_BottomLeftSparse @@ -780,9 +789,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_BottomLeftSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::BottomLeftSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_BottomLeftSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_LeftSparse @@ -813,9 +823,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_LeftSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::LeftSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_LeftSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } // MeshType_LeftTopSparse @@ -886,9 +897,10 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - vertexData.indexCounts[MeshType_LeftTopSparse] = indexCount; + uint8_t meshTypeIndex = static_cast(TerrainMeshType::LeftTopSparse); + vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( - vertexData.indexBuffers[MeshType_LeftTopSparse], indexBytes, indexBuf, BufferStorageFlags::None); + vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); } VertexAttribute vertexAttributes[] = { VertexAttribute::pos2 }; @@ -897,7 +909,7 @@ void TerrainSystem::CreateVertexAndIndexData() const VertexAttribute& attr = vertexAttributes[0]; // Initialize vertex buffers - for (int i = 0; i < MeshType_COUNT; ++i) + for (int i = 0; i < MeshTypeCount; ++i) { render::VertexArrayId vertexArray = vertexData.vertexArrays[i]; renderDevice->SetVertexArrayVertexBuffer(vertexArray, 0, vertexData.vertexBuffer, 0, attr.stride); diff --git a/engine/src/Graphics/TerrainSystem.hpp b/engine/src/Graphics/TerrainSystem.hpp index 711d5672..c410dae5 100644 --- a/engine/src/Graphics/TerrainSystem.hpp +++ b/engine/src/Graphics/TerrainSystem.hpp @@ -97,27 +97,14 @@ class TerrainSystem : public GraphicsFeature HashMap entityMap; - enum MeshType - { - MeshType_Regular, - MeshType_TopSparse, - MeshType_TopRightSparse, - MeshType_RightSparse, - MeshType_RightBottomSparse, - MeshType_BottomSparse, - MeshType_BottomLeftSparse, - MeshType_LeftSparse, - MeshType_LeftTopSparse, - - MeshType_COUNT - }; + static constexpr size_t MeshTypeCount = 9; struct VertexData { - render::VertexArrayId vertexArrays[MeshType_COUNT]; + render::VertexArrayId vertexArrays[MeshTypeCount]; render::BufferId vertexBuffer; - render::BufferId indexBuffers[MeshType_COUNT]; - int indexCounts[MeshType_COUNT]; + render::BufferId indexBuffers[MeshTypeCount]; + int indexCounts[MeshTypeCount]; }; VertexData vertexData; @@ -148,7 +135,7 @@ class TerrainSystem : public GraphicsFeature } data; - Array tilesToRender; + Array tilesToRender; Array uniformStagingBuffer; From 1c6d601d8d318bfde43d16970302b45caee22892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sat, 21 Sep 2024 15:39:47 +0300 Subject: [PATCH 13/19] Work on terrain tile edge level calculation --- engine/src/Graphics/TerrainQuadTree.cpp | 99 ++++++++++++++++++++++++- engine/src/Graphics/TerrainQuadTree.hpp | 23 ++++-- engine/src/Graphics/TerrainSystem.cpp | 20 ++--- 3 files changed, 120 insertions(+), 22 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index b8fa151a..b636f886 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -24,6 +24,28 @@ namespace kokko { +namespace +{ + +struct EdgeTypeDependents +{ + uint16_t dependentNodeIndices[2]; + uint32_t numDependents; + bool dependeeIsSparse; + + EdgeTypeDependents() : numDependents(0), dependeeIsSparse(false) {} + + void AddDependent(uint16_t dependent) + { + uint32_t idx = numDependents; + assert(idx < KOKKO_ARRAY_ITEMS(dependentNodeIndices)); + dependentNodeIndices[idx] = dependent; + numDependents += 1; + } +}; + +} // namespace + TerrainQuadTree::TerrainQuadTree(Allocator* allocator) : allocator(allocator), nodes(allocator), @@ -117,7 +139,7 @@ void TerrainQuadTree::UpdateTilesToRender( // Calculates optimal set of tiles to render and updates quad tree UpdateTilesToRenderParams params{ frustum, cameraPos, renderDebug, resultOut }; - BuildQuadTree(QuadTreeNodeId{}, params); + int rootNodeIndex = BuildQuadTree(QuadTreeNodeId{}, params); size_t oldNodeCount = nodes.GetCount(); @@ -125,7 +147,7 @@ void TerrainQuadTree::UpdateTilesToRender( RestrictQuadTree(); // Then we create the final render tiles from the leaf nodes of the quad tree - QuadTreeToTiles(0, params); + QuadTreeToTiles(rootNodeIndex, params); } int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, const UpdateTilesToRenderParams& params) @@ -274,6 +296,75 @@ void TerrainQuadTree::RestrictQuadTree() } } +void TerrainQuadTree::CalculateEdgeTypes() +{ + // Key = dependee node id, Value = dependent node ids + HashMap edgeDependencies(allocator); + + auto addDependency = [&edgeDependencies](const QuadTreeNodeId& dependee, uint16_t dependentNodeIndex) + { + auto pair = edgeDependencies.Lookup(dependee); + if (pair == nullptr) + pair = edgeDependencies.Insert(dependee); + + pair->second.AddDependent(dependentNodeIndex); + }; + + for (uint16_t nodeIndex = 0, nodeCount = nodes.GetCount(); nodeIndex != nodeCount; ++nodeIndex) + { + const TerrainQuadTreeNode& node = nodes[nodeIndex]; + if (node.HasChildren() == false) + { + QuadTreeNodeId id = node.id; + int tilesPerDim = GetTilesPerDimension(id.level); + if ((id.x & 1) == 0 && id.x > 0) + addDependency(QuadTreeNodeId{ id.x - 1, id.y, id.level }, nodeIndex); + if ((id.x & 1) == 1 && id.x + 1 < tilesPerDim) + addDependency(QuadTreeNodeId{ id.x + 1, id.y, id.level }, nodeIndex); + if ((id.y & 1) == 0 && id.y > 0) + addDependency(QuadTreeNodeId{ id.x, id.y - 1, id.level }, nodeIndex); + if ((id.y & 1) == 1 && id.y + 1 < tilesPerDim) + addDependency(QuadTreeNodeId{ id.x, id.y + 1, id.level }, nodeIndex); + } + } + + for (const auto& pair : edgeDependencies) + { + const QuadTreeNodeId& dependee = pair.first; + // Try to find node in nodes + // If not, mark edge status as sparse + + TerrainQuadTreeNode* currentNode = &nodes[0]; + for (uint8_t level = 0; level <= dependee.level; ++level) + { + if (level == dependee.level) + break; + + uint8_t childLevel = static_cast(level + 1); + int levelDiff = dependee.level - childLevel; + int childX = dependee.x >> levelDiff; + int childY = dependee.y >> levelDiff; + int childIndex = (childY & 1) * 2 + (childX & 1); + + // Verify next level towards exists + if (currentNode->HasChildren() == false) + { + // TODO: Mark dependent edge as sparse + } + // If it has some children, but not our target tile, skip work on it + else if (currentNode->children[childIndex] == 0) + break; + + // Update currentNode and continue + int childNodeIndex = currentNode->children[childIndex]; + + currentNode = &nodes[childNodeIndex]; + } + } + + edgeDependencies.Clear(); +} + void TerrainQuadTree::QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params) { const TerrainQuadTreeNode& node = nodes[nodeIndex]; @@ -285,7 +376,7 @@ void TerrainQuadTree::QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderPar if (hasChildren == false) { - params.resultOut.PushBack(TerrainTileDrawInfo{ node.id, TerrainMeshType::Regular }); + params.resultOut.PushBack(TerrainTileDrawInfo{ node.id, node.edgeType }); return; } @@ -422,4 +513,4 @@ QuadTreeNodeId TerrainQuadTree::GetParentId(const QuadTreeNodeId& id) return QuadTreeNodeId{ id.x / 2, id.y / 2, static_cast(id.level - 1) }; } -} +} // namespace kokko diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 32034eec..d2a46f30 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -58,13 +58,7 @@ struct QuadTreeNodeId } }; -struct TerrainQuadTreeNode -{ - QuadTreeNodeId id; - uint16_t children[4] = { 0, 0, 0, 0 }; -}; - -enum class TerrainMeshType : uint8_t +enum class TerrainEdgeType : uint8_t { Regular, TopSparse, @@ -77,10 +71,22 @@ enum class TerrainMeshType : uint8_t LeftTopSparse }; +struct TerrainQuadTreeNode +{ + QuadTreeNodeId id; + uint16_t children[4] = { 0, 0, 0, 0 }; + TerrainEdgeType edgeType = TerrainEdgeType::Regular; + + uint16_t HasChildren() const + { + return children[0] != 0 || children[1] != 0 || children[2] != 0 || children[3] != 0; + } +}; + struct TerrainTileDrawInfo { QuadTreeNodeId id; - TerrainMeshType meshType; + TerrainEdgeType edgeType; }; class TerrainQuadTree @@ -126,6 +132,7 @@ class TerrainQuadTree // Returns inserted node index (points to nodes array), or -1 if no insertion int BuildQuadTree(const QuadTreeNodeId& id, const UpdateTilesToRenderParams& params); void RestrictQuadTree(); + void CalculateEdgeTypes(); void QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params); static void CreateTileTestData(TerrainTile& tile, int tileX, int tileY, float tileScale); diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index c2ffda92..20d92c96 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -385,7 +385,7 @@ void TerrainSystem::Render(const RenderParameters& parameters) int blocksUsed = 0; for (const auto& tile : tilesToRender) { - uint8_t tileMeshTypeIndex = static_cast(tile.meshType); + uint8_t tileMeshTypeIndex = static_cast(tile.edgeType); if (tileMeshTypeIndex != prevMeshType) { encoder->BindVertexArray(vertexData.vertexArrays[tileMeshTypeIndex]); @@ -472,7 +472,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::Regular); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::Regular); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -502,7 +502,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::TopSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::TopSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -576,7 +576,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::TopRightSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::TopRightSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -610,7 +610,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::RightSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::RightSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -687,7 +687,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::RightBottomSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::RightBottomSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -717,7 +717,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::BottomSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::BottomSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -789,7 +789,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::BottomLeftSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::BottomLeftSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -823,7 +823,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::LeftSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::LeftSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); @@ -897,7 +897,7 @@ void TerrainSystem::CreateVertexAndIndexData() assert(indexCount <= maxIndexCount); int indexBytes = indexCount * sizeof(uint16_t); - uint8_t meshTypeIndex = static_cast(TerrainMeshType::LeftTopSparse); + uint8_t meshTypeIndex = static_cast(TerrainEdgeType::LeftTopSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( vertexData.indexBuffers[meshTypeIndex], indexBytes, indexBuf, BufferStorageFlags::None); From 90c4b1cfab2fd52c76037b22ae367a0656ef2de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 22 Sep 2024 10:40:45 +0300 Subject: [PATCH 14/19] Change HashMap to only require operator== and hash function on KeyType --- engine/src/Core/HashMap.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/src/Core/HashMap.hpp b/engine/src/Core/HashMap.hpp index ff471f1a..fa11e548 100644 --- a/engine/src/Core/HashMap.hpp +++ b/engine/src/Core/HashMap.hpp @@ -49,7 +49,7 @@ class HashMap { ++current; - if (current == end || current->first) + if (current == end || !(current->first == KeyType{})) { break; } @@ -102,7 +102,7 @@ class HashMap for (; existing != end; ++existing) { - if (existing->first != KeyType{}) // Pair has value + if (!(existing->first == KeyType{})) // Pair has value { for (size_t i = GetIndex(kokko::Hash32(existing->first, 0u));; i = GetIndex(i + 1)) { @@ -178,7 +178,7 @@ class HashMap KeyValuePair* itr = data; KeyValuePair* end = data + allocated; for (; itr != end; ++itr) - if (itr->first != KeyType{}) + if (!(itr->first == KeyType{})) itr->second.~ValueType(); if (zeroUsed) @@ -240,7 +240,7 @@ class HashMap KeyValuePair* end = data + allocated; for (; itr != end; ++itr) { - if (itr->first != KeyType{}) + if (!(itr->first == KeyType{})) { itr->second.~ValueType(); itr->first = KeyType{}; @@ -304,7 +304,7 @@ class HashMap KeyValuePair* Lookup(const KeyType& key) { - if (key != KeyType{}) + if (!(key == KeyType{})) { if (data != nullptr) { @@ -355,7 +355,7 @@ class HashMap if (data == nullptr) ReserveInternal(16); - if (key != KeyType{}) + if (!(key == KeyType{})) { for (;;) for (size_t i = GetIndex(kokko::Hash32(key, 0u));; i = GetIndex(i + 1)) From e1b2002e4c9a64d89867ba0c4ae2366c39e89cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 22 Sep 2024 10:41:10 +0300 Subject: [PATCH 15/19] Add Hash32 function for QuadTreeNodeId --- engine/src/Graphics/TerrainQuadTree.cpp | 7 +++++++ engine/src/Graphics/TerrainQuadTree.hpp | 2 ++ 2 files changed, 9 insertions(+) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index b636f886..5bc5a170 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -46,6 +46,13 @@ struct EdgeTypeDependents } // namespace +uint32_t Hash32(const QuadTreeNodeId& value, uint32_t seed) +{ + uint32_t hash = Hash32(&value.x, sizeof(value.x), seed); + hash = Hash32(&value.y, sizeof(value.y), hash); + return Hash32(&value.level, sizeof(value.level), hash); +} + TerrainQuadTree::TerrainQuadTree(Allocator* allocator) : allocator(allocator), nodes(allocator), diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index d2a46f30..8f6958b4 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -58,6 +58,8 @@ struct QuadTreeNodeId } }; +uint32_t Hash32(const QuadTreeNodeId& value, uint32_t seed); + enum class TerrainEdgeType : uint8_t { Regular, From 6cda479698940ff10ff0b8472aa789e259cdbb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 22 Sep 2024 11:37:20 +0300 Subject: [PATCH 16/19] Terrain tile edges have matched vertex density to avoid cracks --- engine/src/Graphics/TerrainQuadTree.cpp | 89 ++++++++++++++++++++++++- engine/src/Graphics/TerrainQuadTree.hpp | 6 +- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index 5bc5a170..f83b3943 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -44,6 +44,73 @@ struct EdgeTypeDependents } }; +enum TerrainEdgeMask +{ + TerrainEdgeMask_Regular = 0, + TerrainEdgeMask_TopSparse = 1 << 0, + TerrainEdgeMask_RightSparse = 1 << 1, + TerrainEdgeMask_BottomSparse = 1 << 2, + TerrainEdgeMask_LeftSparse = 1 << 3, + TerrainEdgeMask_TopRightSparse = TerrainEdgeMask_TopSparse | TerrainEdgeMask_RightSparse, + TerrainEdgeMask_RightBottomSparse = TerrainEdgeMask_RightSparse | TerrainEdgeMask_BottomSparse, + TerrainEdgeMask_BottomLeftSparse = TerrainEdgeMask_BottomSparse | TerrainEdgeMask_LeftSparse, + TerrainEdgeMask_LeftTopSparse = TerrainEdgeMask_LeftSparse | TerrainEdgeMask_TopSparse +}; + +TerrainEdgeMask EdgeTypeToMask(TerrainEdgeType type) +{ + switch (type) + { + case kokko::TerrainEdgeType::Regular: + return TerrainEdgeMask_Regular; + case kokko::TerrainEdgeType::TopSparse: + return TerrainEdgeMask_TopSparse; + case kokko::TerrainEdgeType::TopRightSparse: + return TerrainEdgeMask_TopRightSparse; + case kokko::TerrainEdgeType::RightSparse: + return TerrainEdgeMask_RightSparse; + case kokko::TerrainEdgeType::RightBottomSparse: + return TerrainEdgeMask_RightBottomSparse; + case kokko::TerrainEdgeType::BottomSparse: + return TerrainEdgeMask_BottomSparse; + case kokko::TerrainEdgeType::BottomLeftSparse: + return TerrainEdgeMask_BottomLeftSparse; + case kokko::TerrainEdgeType::LeftSparse: + return TerrainEdgeMask_LeftSparse; + case kokko::TerrainEdgeType::LeftTopSparse: + return TerrainEdgeMask_LeftTopSparse; + default: + return TerrainEdgeMask_Regular; + } +} + +TerrainEdgeType EdgeMaskToType(TerrainEdgeMask mask) +{ + switch (mask) + { + case TerrainEdgeMask_Regular: + return TerrainEdgeType::Regular; + case TerrainEdgeMask_TopSparse: + return TerrainEdgeType::TopSparse; + case TerrainEdgeMask_TopRightSparse: + return TerrainEdgeType::TopRightSparse; + case TerrainEdgeMask_RightSparse: + return TerrainEdgeType::RightSparse; + case TerrainEdgeMask_RightBottomSparse: + return TerrainEdgeType::RightBottomSparse; + case TerrainEdgeMask_BottomSparse: + return TerrainEdgeType::BottomSparse; + case TerrainEdgeMask_BottomLeftSparse: + return TerrainEdgeType::BottomLeftSparse; + case TerrainEdgeMask_LeftSparse: + return TerrainEdgeType::LeftSparse; + case TerrainEdgeMask_LeftTopSparse: + return TerrainEdgeType::LeftTopSparse; + default: + return TerrainEdgeType::Regular; + } +} + } // namespace uint32_t Hash32(const QuadTreeNodeId& value, uint32_t seed) @@ -153,6 +220,8 @@ void TerrainQuadTree::UpdateTilesToRender( // Next we need to update the quad tree so that it forms a restricted quad tree RestrictQuadTree(); + CalculateEdgeTypes(); + // Then we create the final render tiles from the leaf nodes of the quad tree QuadTreeToTiles(rootNodeIndex, params); } @@ -335,7 +404,7 @@ void TerrainQuadTree::CalculateEdgeTypes() } } - for (const auto& pair : edgeDependencies) + for (auto& pair : edgeDependencies) { const QuadTreeNodeId& dependee = pair.first; // Try to find node in nodes @@ -356,7 +425,23 @@ void TerrainQuadTree::CalculateEdgeTypes() // Verify next level towards exists if (currentNode->HasChildren() == false) { - // TODO: Mark dependent edge as sparse + // Mark dependent edge as sparse + EdgeTypeDependents& dependents = pair.second; + dependents.dependeeIsSparse = true; + for (uint32_t i = 0; i < dependents.numDependents; ++i) + { + TerrainQuadTreeNode& dependentNode = nodes[dependents.dependentNodeIndices[i]]; + int edgeMask = EdgeTypeToMask(dependentNode.edgeType); + int diffX = dependee.x - dependentNode.id.x; + int diffY = dependee.y - dependentNode.id.y; + + if (diffX != 0) + edgeMask |= diffX < 0 ? TerrainEdgeMask_LeftSparse : TerrainEdgeMask_RightSparse; + else + edgeMask |= diffY < 0 ? TerrainEdgeMask_TopSparse : TerrainEdgeMask_BottomSparse; + + dependentNode.edgeType = EdgeMaskToType(static_cast(edgeMask)); + } } // If it has some children, but not our target tile, skip work on it else if (currentNode->children[childIndex] == 0) diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 8f6958b4..41685834 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -39,9 +39,9 @@ struct TerrainTile struct QuadTreeNodeId { - uint32_t x; - uint32_t y; - uint8_t level; + uint32_t x = 0; + uint32_t y = 0; + uint8_t level = 0; bool operator==(const QuadTreeNodeId& other) const { From 13576edb05f931e2d2cd5c39be270295fd8ea11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 22 Sep 2024 22:16:23 +0300 Subject: [PATCH 17/19] Fix compiler warnings and do minor optimization --- engine/src/Graphics/TerrainQuadTree.cpp | 97 +++++++++++-------------- engine/src/Graphics/TerrainQuadTree.hpp | 25 +++++-- engine/src/Graphics/TerrainSystem.cpp | 57 ++++++++------- 3 files changed, 90 insertions(+), 89 deletions(-) diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index f83b3943..dbc316f8 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -27,23 +27,6 @@ namespace kokko namespace { -struct EdgeTypeDependents -{ - uint16_t dependentNodeIndices[2]; - uint32_t numDependents; - bool dependeeIsSparse; - - EdgeTypeDependents() : numDependents(0), dependeeIsSparse(false) {} - - void AddDependent(uint16_t dependent) - { - uint32_t idx = numDependents; - assert(idx < KOKKO_ARRAY_ITEMS(dependentNodeIndices)); - dependentNodeIndices[idx] = dependent; - numDependents += 1; - } -}; - enum TerrainEdgeMask { TerrainEdgeMask_Regular = 0, @@ -120,9 +103,21 @@ uint32_t Hash32(const QuadTreeNodeId& value, uint32_t seed) return Hash32(&value.level, sizeof(value.level), hash); } -TerrainQuadTree::TerrainQuadTree(Allocator* allocator) : +void TerrainQuadTree::EdgeTypeDependents::AddDependent(uint16_t dependent) +{ + uint32_t idx = numDependents; + assert(idx < KOKKO_ARRAY_ITEMS(dependentNodeIndices)); + dependentNodeIndices[idx] = dependent; + numDependents += 1; +} + +TerrainQuadTree::TerrainQuadTree(Allocator* allocator, render::Device* renderDevice) : allocator(allocator), + renderDevice(renderDevice), nodes(allocator), + parentsToCheck(allocator), + neighborsToCheck(allocator), + edgeDependencies(allocator), tiles(nullptr), tileTextureIds(nullptr), treeLevels(0), @@ -134,8 +129,16 @@ TerrainQuadTree::TerrainQuadTree(Allocator* allocator) : { } -void TerrainQuadTree::CreateResources(render::Device* renderDevice, uint8_t levels, - const TerrainParameters& params) +TerrainQuadTree::~TerrainQuadTree() +{ + if (tileTextureIds != nullptr) + renderDevice->DestroyTextures(static_cast(tileCount), tileTextureIds); + + allocator->Deallocate(tiles); + allocator->Deallocate(tileTextureIds); +} + +void TerrainQuadTree::CreateResources(uint8_t levels, const TerrainParameters& params) { constexpr int texResolution = TerrainTile::TexelsPerSide; @@ -191,15 +194,6 @@ void TerrainQuadTree::CreateResources(render::Device* renderDevice, uint8_t leve } } -void TerrainQuadTree::DestroyResources(render::Device* renderDevice) -{ - if (tileTextureIds != nullptr) - renderDevice->DestroyTextures(static_cast(tileCount), tileTextureIds); - - allocator->Deallocate(tiles); - allocator->Deallocate(tileTextureIds); -} - void TerrainQuadTree::UpdateTilesToRender( const FrustumPlanes& frustum, const Vec3f& cameraPos, @@ -275,7 +269,7 @@ int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, const UpdateTilesTo { for (int x = 0; x < 2; ++x) { - QuadTreeNodeId tileId{ id.x * 2 + x, id.y * 2 + y, id.level + 1 }; + QuadTreeNodeId tileId{ id.x * 2 + x, id.y * 2 + y, static_cast(id.level + 1) }; int childIndex = BuildQuadTree(tileId, params); if (childIndex >= 0) @@ -289,17 +283,13 @@ int TerrainQuadTree::BuildQuadTree(const QuadTreeNodeId& id, const UpdateTilesTo void TerrainQuadTree::RestrictQuadTree() { - SortedArray parentsToCheck(allocator); - SortedArray neighborsToCheck(allocator); - - uint8_t currentLevel = maxNodeLevel; - while (currentLevel > 1) + for (uint8_t currentLevel = maxNodeLevel; currentLevel > 1; --currentLevel) { for (const auto& node : nodes) if (node.id.level == currentLevel) parentsToCheck.InsertUnique(GetParentId(node.id)); - int tilesPerDim = GetTilesPerDimension(currentLevel - 1); + uint32_t tilesPerDim = GetTilesPerDimension(currentLevel - 1); for (const auto& id : parentsToCheck) { // Bitwise AND is used to check if the potential tile has a different parent from current tile @@ -368,39 +358,26 @@ void TerrainQuadTree::RestrictQuadTree() parentsToCheck.Clear(); neighborsToCheck.Clear(); - currentLevel -= 1; } } void TerrainQuadTree::CalculateEdgeTypes() { - // Key = dependee node id, Value = dependent node ids - HashMap edgeDependencies(allocator); - - auto addDependency = [&edgeDependencies](const QuadTreeNodeId& dependee, uint16_t dependentNodeIndex) - { - auto pair = edgeDependencies.Lookup(dependee); - if (pair == nullptr) - pair = edgeDependencies.Insert(dependee); - - pair->second.AddDependent(dependentNodeIndex); - }; - for (uint16_t nodeIndex = 0, nodeCount = nodes.GetCount(); nodeIndex != nodeCount; ++nodeIndex) { const TerrainQuadTreeNode& node = nodes[nodeIndex]; if (node.HasChildren() == false) { QuadTreeNodeId id = node.id; - int tilesPerDim = GetTilesPerDimension(id.level); + uint32_t tilesPerDim = GetTilesPerDimension(id.level); if ((id.x & 1) == 0 && id.x > 0) - addDependency(QuadTreeNodeId{ id.x - 1, id.y, id.level }, nodeIndex); + AddEdgeDependency(QuadTreeNodeId{ id.x - 1, id.y, id.level }, nodeIndex); if ((id.x & 1) == 1 && id.x + 1 < tilesPerDim) - addDependency(QuadTreeNodeId{ id.x + 1, id.y, id.level }, nodeIndex); + AddEdgeDependency(QuadTreeNodeId{ id.x + 1, id.y, id.level }, nodeIndex); if ((id.y & 1) == 0 && id.y > 0) - addDependency(QuadTreeNodeId{ id.x, id.y - 1, id.level }, nodeIndex); + AddEdgeDependency(QuadTreeNodeId{ id.x, id.y - 1, id.level }, nodeIndex); if ((id.y & 1) == 1 && id.y + 1 < tilesPerDim) - addDependency(QuadTreeNodeId{ id.x, id.y + 1, id.level }, nodeIndex); + AddEdgeDependency(QuadTreeNodeId{ id.x, id.y + 1, id.level }, nodeIndex); } } @@ -427,7 +404,6 @@ void TerrainQuadTree::CalculateEdgeTypes() { // Mark dependent edge as sparse EdgeTypeDependents& dependents = pair.second; - dependents.dependeeIsSparse = true; for (uint32_t i = 0; i < dependents.numDependents; ++i) { TerrainQuadTreeNode& dependentNode = nodes[dependents.dependentNodeIndices[i]]; @@ -457,6 +433,15 @@ void TerrainQuadTree::CalculateEdgeTypes() edgeDependencies.Clear(); } +void TerrainQuadTree::AddEdgeDependency(const QuadTreeNodeId& dependee, uint16_t dependentNodeIndex) +{ + auto pair = edgeDependencies.Lookup(dependee); + if (pair == nullptr) + pair = edgeDependencies.Insert(dependee); + + pair->second.AddDependent(dependentNodeIndex); +} + void TerrainQuadTree::QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params) { const TerrainQuadTreeNode& node = nodes[nodeIndex]; @@ -498,7 +483,7 @@ render::TextureId TerrainQuadTree::GetTileHeightTexture(uint8_t level, int x, in return tileTextureIds[GetTileIndex(level, x, y)]; } -int TerrainQuadTree::GetTilesPerDimension(uint8_t level) +uint32_t TerrainQuadTree::GetTilesPerDimension(uint8_t level) { assert(level >= 0); assert(level < 31); diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 41685834..90226585 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -4,6 +4,8 @@ #include #include "Core/Array.hpp" +#include "Core/HashMap.hpp" +#include "Core/SortedArray.hpp" #include "Math/Vec3.hpp" @@ -94,11 +96,11 @@ struct TerrainTileDrawInfo class TerrainQuadTree { public: - TerrainQuadTree(Allocator* allocator); + TerrainQuadTree(Allocator* allocator, render::Device* renderDevice); + TerrainQuadTree(const TerrainQuadTree&) = delete; + ~TerrainQuadTree(); - void CreateResources(kokko::render::Device* renderDevice, uint8_t levels, - const TerrainParameters& params); - void DestroyResources(kokko::render::Device* renderDevice); + void CreateResources(uint8_t levels, const TerrainParameters& params); void UpdateTilesToRender(const FrustumPlanes& frustum, const Vec3f& cameraPos, const RenderDebugSettings& renderDebug, Array& resultOut); @@ -117,7 +119,7 @@ class TerrainQuadTree const TerrainTile* GetTile(uint8_t level, int x, int y); render::TextureId GetTileHeightTexture(uint8_t level, int x, int y); - static int GetTilesPerDimension(uint8_t level); + static uint32_t GetTilesPerDimension(uint8_t level); static int GetTileIndex(uint8_t level, int x, int y); static int GetTileCountForLevelCount(uint8_t levelCount); static float GetTileScale(uint8_t level); @@ -131,10 +133,19 @@ class TerrainQuadTree Array& resultOut; }; + struct EdgeTypeDependents + { + uint16_t dependentNodeIndices[2]; + uint32_t numDependents = 0; + + void AddDependent(uint16_t dependent); + }; + // Returns inserted node index (points to nodes array), or -1 if no insertion int BuildQuadTree(const QuadTreeNodeId& id, const UpdateTilesToRenderParams& params); void RestrictQuadTree(); void CalculateEdgeTypes(); + void AddEdgeDependency(const QuadTreeNodeId& dependee, uint16_t dependentNodeIndex); void QuadTreeToTiles(uint16_t nodeIndex, UpdateTilesToRenderParams& params); static void CreateTileTestData(TerrainTile& tile, int tileX, int tileY, float tileScale); @@ -144,7 +155,11 @@ class TerrainQuadTree static QuadTreeNodeId GetParentId(const QuadTreeNodeId& id); Allocator* allocator; + render::Device* renderDevice; Array nodes; + SortedArray parentsToCheck; + SortedArray neighborsToCheck; + HashMap edgeDependencies; // Key = dependee node, Value = dependent nodes TerrainTile* tiles; render::TextureId* tileTextureIds; diff --git a/engine/src/Graphics/TerrainSystem.cpp b/engine/src/Graphics/TerrainSystem.cpp index 20d92c96..46a269dd 100644 --- a/engine/src/Graphics/TerrainSystem.cpp +++ b/engine/src/Graphics/TerrainSystem.cpp @@ -160,7 +160,7 @@ TerrainId TerrainSystem::AddTerrain(Entity entity, const TerrainParameters& para data.entity[id] = entity; data.textureScale[id] = params.textureScale; data.textures[id] = TerrainTextures{}; - data.quadTree[id] = TerrainQuadTree(allocator); + new (&data.quadTree[id]) TerrainQuadTree(allocator, renderDevice); data.count += 1; @@ -249,7 +249,7 @@ void TerrainSystem::InitializeTerrain(TerrainId id, const TerrainParameters& par kokko::TerrainQuadTree& quadTree = data.quadTree[id.i]; constexpr int treeLevels = 7; - quadTree.CreateResources(renderDevice, treeLevels, params); + quadTree.CreateResources(treeLevels, params); } void TerrainSystem::DeinitializeTerrain(TerrainId id) @@ -258,7 +258,7 @@ void TerrainSystem::DeinitializeTerrain(TerrainId id) auto scope = renderDevice->CreateDebugScope(0, ConstStringView("TerrainSys_DestroyInstanceResources")); - data.quadTree[id.i].DestroyResources(renderDevice); + data.quadTree[id.i].~TerrainQuadTree(); } void TerrainSystem::Reallocate(size_t required) @@ -417,17 +417,17 @@ void TerrainSystem::CreateVertexAndIndexData() { // Create vertex data - constexpr int sideVerts = TerrainTile::VerticesPerSide; - unsigned int vertCount = sideVerts * sideVerts; + constexpr uint32_t sideVerts = TerrainTile::VerticesPerSide; + uint32_t vertCount = sideVerts * sideVerts; size_t vertexComponents = 2; size_t vertSize = sizeof(float) * vertexComponents; - unsigned int vertBytes = static_cast(vertSize * vertCount); + uint32_t vertBytes = static_cast(vertSize * vertCount); float* vertexBuf = static_cast(allocator->Allocate(vertBytes, "TerrainSystem.CreateVertexData() vertexBuf")); constexpr int sideQuads = TerrainTile::QuadsPerSide; - int quadIndices = 3 * 2; // 3 indices per triangle, 2 triangles per quad - unsigned int maxIndexCount = sideQuads * sideQuads * quadIndices; - unsigned int maxIndexBytes = maxIndexCount * sizeof(uint16_t); + uint32_t quadIndices = 3 * 2; // 3 indices per triangle, 2 triangles per quad + uint32_t maxIndexCount = sideQuads * sideQuads * quadIndices; + uint32_t maxIndexBytes = maxIndexCount * sizeof(uint16_t); uint16_t* indexBuf = static_cast(allocator->Allocate(maxIndexBytes, "TerrainSystem.CreateVertexData() indexBuf")); float quadSize = 1.0f / sideQuads; @@ -451,7 +451,7 @@ void TerrainSystem::CreateVertexAndIndexData() renderDevice->SetBufferStorage(vertexData.vertexBuffer, vertBytes, vertexBuf, BufferStorageFlags::None); auto vertexIndex = [](int x, int y) { return static_cast(y * TerrainTile::VerticesPerSide + x); }; - auto basicQuad = [&indexBuf, &vertexIndex](int x, int y, int& count) + auto basicQuad = [&indexBuf, &vertexIndex](int x, int y, uint32_t& count) { indexBuf[count + 0] = vertexIndex(x, y); indexBuf[count + 1] = vertexIndex(x, y + 1); @@ -464,14 +464,14 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_Regular { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0; y < sideQuads; ++y) for (int x = 0; x < sideQuads; ++x) basicQuad(x, y, indexCount); assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::Regular); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -480,7 +480,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_TopSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0, x = 0; x < sideQuads; x += 2) { @@ -501,7 +501,7 @@ void TerrainSystem::CreateVertexAndIndexData() basicQuad(x, y, indexCount); assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::TopSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -510,7 +510,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_TopRightSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0, x = 0; x < sideQuads; x += 2) { @@ -575,7 +575,7 @@ void TerrainSystem::CreateVertexAndIndexData() } assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::TopRightSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -584,7 +584,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_RightSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0; y < sideQuads; ++y) { @@ -609,7 +609,7 @@ void TerrainSystem::CreateVertexAndIndexData() } assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::RightSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -618,7 +618,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_RightBottomSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0; y < sideQuads - 1; ++y) { @@ -686,7 +686,7 @@ void TerrainSystem::CreateVertexAndIndexData() } assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::RightBottomSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -695,7 +695,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_TopSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0; y < sideQuads - 1; ++y) for (int x = 0; x < sideQuads; ++x) @@ -716,7 +716,7 @@ void TerrainSystem::CreateVertexAndIndexData() } assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::BottomSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -725,7 +725,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_BottomLeftSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0; y < sideQuads - 1; ++y) { @@ -797,7 +797,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_LeftSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0; y < sideQuads; ++y) { @@ -822,7 +822,7 @@ void TerrainSystem::CreateVertexAndIndexData() } assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::LeftSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -831,7 +831,7 @@ void TerrainSystem::CreateVertexAndIndexData() // MeshType_LeftTopSparse { - int indexCount = 0; + uint32_t indexCount = 0; for (int y = 0, x = 0; x < sideQuads; x += 2) { @@ -896,7 +896,7 @@ void TerrainSystem::CreateVertexAndIndexData() } assert(indexCount <= maxIndexCount); - int indexBytes = indexCount * sizeof(uint16_t); + uint32_t indexBytes = indexCount * sizeof(uint16_t); uint8_t meshTypeIndex = static_cast(TerrainEdgeType::LeftTopSparse); vertexData.indexCounts[meshTypeIndex] = indexCount; renderDevice->SetBufferStorage( @@ -923,4 +923,5 @@ void TerrainSystem::CreateVertexAndIndexData() allocator->Deallocate(indexBuf); allocator->Deallocate(vertexBuf); } -} + +} // namespace kokko From 3bcd2fd60d23a752571b127d40974ebbf330e3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 22 Sep 2024 22:57:28 +0300 Subject: [PATCH 18/19] Try fix hash function overload problem --- editor/src/Views/FilePickerDialog.cpp | 2 +- engine/src/Core/Hash.cpp | 36 ++++++++++++------------- engine/src/Core/Hash.hpp | 32 +++++++++++----------- engine/src/Core/HashMap.hpp | 10 +++---- engine/src/Core/String.cpp | 4 +-- engine/src/Core/String.hpp | 2 ++ engine/src/Core/StringView.cpp | 4 +-- engine/src/Core/StringView.hpp | 2 +- engine/src/Core/Uid.cpp | 4 +-- engine/src/Core/Uid.hpp | 2 ++ engine/src/Graphics/TerrainQuadTree.cpp | 8 +++--- engine/src/Graphics/TerrainQuadTree.hpp | 3 +-- engine/src/Resources/AssetLibrary.cpp | 4 +-- 13 files changed, 58 insertions(+), 55 deletions(-) diff --git a/editor/src/Views/FilePickerDialog.cpp b/editor/src/Views/FilePickerDialog.cpp index d51f3887..de443efe 100644 --- a/editor/src/Views/FilePickerDialog.cpp +++ b/editor/src/Views/FilePickerDialog.cpp @@ -204,7 +204,7 @@ uint64_t FilePickerDialog::StartDialog(const Descriptor& descriptor) selectedFilePath = std::filesystem::path(); this->descriptor = descriptor; - currentTitleHash = kokko::Hash64(descriptor.popupTitle, std::strlen(descriptor.popupTitle), 0); + currentTitleHash = kokko::HashValue64(descriptor.popupTitle, std::strlen(descriptor.popupTitle), 0); ImGui::OpenPopup(descriptor.popupTitle); diff --git a/engine/src/Core/Hash.cpp b/engine/src/Core/Hash.cpp index 57ce51b6..28dba7c3 100644 --- a/engine/src/Core/Hash.cpp +++ b/engine/src/Core/Hash.cpp @@ -4,52 +4,52 @@ namespace kokko { -uint32_t Hash32(const void* data, size_t length, uint32_t seed) +uint32_t HashValue32(const void* data, size_t length, uint32_t seed) { return XXH32(data, length, seed); } -uint32_t Hash32(uint8_t data, uint32_t seed) +uint32_t HashValue32(uint8_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint32_t Hash32(int8_t data, uint32_t seed) +uint32_t HashValue32(int8_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint32_t Hash32(uint16_t data, uint32_t seed) +uint32_t HashValue32(uint16_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint32_t Hash32(int16_t data, uint32_t seed) +uint32_t HashValue32(int16_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint32_t Hash32(uint32_t data, uint32_t seed) +uint32_t HashValue32(uint32_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint32_t Hash32(int32_t data, uint32_t seed) +uint32_t HashValue32(int32_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint32_t Hash32(uint64_t data, uint32_t seed) +uint32_t HashValue32(uint64_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint32_t Hash32(int64_t data, uint32_t seed) +uint32_t HashValue32(int64_t data, uint32_t seed) { - return Hash32(&data, sizeof(data), seed); + return HashValue32(&data, sizeof(data), seed); } -uint64_t Hash64(const void* data, size_t length, uint64_t seed) +uint64_t HashValue64(const void* data, size_t length, uint64_t seed) { return XXH64(data, length, seed); } diff --git a/engine/src/Core/Hash.hpp b/engine/src/Core/Hash.hpp index b1a80497..00c49718 100644 --- a/engine/src/Core/Hash.hpp +++ b/engine/src/Core/Hash.hpp @@ -5,29 +5,29 @@ namespace kokko { -uint64_t Hash64(const void* data, size_t length, uint64_t seed); -uint32_t Hash32(const void* data, size_t length, uint32_t seed); +template +struct Hash32 { + uint32_t operator()(const T& val, uint8_t seed) const { return HashValue32(val, seed); } +}; -uint32_t Hash32(uint8_t data, uint32_t seed); -uint32_t Hash32(int8_t data, uint32_t seed); -uint32_t Hash32(uint16_t data, uint32_t seed); -uint32_t Hash32(int16_t data, uint32_t seed); -uint32_t Hash32(uint32_t data, uint32_t seed); -uint32_t Hash32(int32_t data, uint32_t seed); -uint32_t Hash32(uint64_t data, uint32_t seed); -uint32_t Hash32(int64_t data, uint32_t seed); +uint64_t HashValue64(const void* data, size_t length, uint64_t seed); + +uint32_t HashValue32(const void* data, size_t length, uint32_t seed); + +uint32_t HashValue32(uint8_t data, uint32_t seed); +uint32_t HashValue32(int8_t data, uint32_t seed); +uint32_t HashValue32(uint16_t data, uint32_t seed); +uint32_t HashValue32(int16_t data, uint32_t seed); +uint32_t HashValue32(uint32_t data, uint32_t seed); +uint32_t HashValue32(int32_t data, uint32_t seed); +uint32_t HashValue32(uint64_t data, uint32_t seed); +uint32_t HashValue32(int64_t data, uint32_t seed); // TODO: Figure out a way to declare the overloads in the types' own header file // Currently GCC would fail compilation because it doesn't consider those declarations // to be overloads of the original hash functions. -struct Uid; -uint32_t Hash32(const Uid& data, uint32_t seed); - -class String; -uint32_t Hash32(const String& data, uint32_t seed); - constexpr uint32_t HashString(const char* string, size_t length) { constexpr uint32_t FNV1a_32Basis = 0x811c9dc5; diff --git a/engine/src/Core/HashMap.hpp b/engine/src/Core/HashMap.hpp index fa11e548..19d88664 100644 --- a/engine/src/Core/HashMap.hpp +++ b/engine/src/Core/HashMap.hpp @@ -104,7 +104,7 @@ class HashMap { if (!(existing->first == KeyType{})) // Pair has value { - for (size_t i = GetIndex(kokko::Hash32(existing->first, 0u));; i = GetIndex(i + 1)) + for (size_t i = GetIndex(kokko::Hash32()(existing->first, 0u));; i = GetIndex(i + 1)) { if (newData[i].first == KeyType{}) // Insert here { @@ -308,7 +308,7 @@ class HashMap { if (data != nullptr) { - for (size_t i = GetIndex(kokko::Hash32(key, 0u));; i = GetIndex(i + 1)) + for (size_t i = GetIndex(kokko::Hash32()(key, 0u));; i = GetIndex(i + 1)) { KeyValuePair* pair = data + i; @@ -332,7 +332,7 @@ class HashMap { if (data != nullptr) { - for (size_t i = GetIndex(kokko::Hash32(key, 0u));; i = GetIndex(i + 1)) + for (size_t i = GetIndex(kokko::Hash32()(key, 0u));; i = GetIndex(i + 1)) { KeyValuePair* pair = data + i; @@ -358,7 +358,7 @@ class HashMap if (!(key == KeyType{})) { for (;;) - for (size_t i = GetIndex(kokko::Hash32(key, 0u));; i = GetIndex(i + 1)) + for (size_t i = GetIndex(kokko::Hash32()(key, 0u));; i = GetIndex(i + 1)) { KeyValuePair* pair = data + i; @@ -422,7 +422,7 @@ class HashMap return; } - KeyValuePair* ideal = data + GetIndex(kokko::Hash32(neighbor->first, 0u)); + KeyValuePair* ideal = data + GetIndex(kokko::Hash32()(neighbor->first, 0u)); if (GetOffset(ideal, pair) < GetOffset(ideal, neighbor)) { // Swap with neighbor, then make neighbor the new cell to remove. diff --git a/engine/src/Core/String.cpp b/engine/src/Core/String.cpp index d6fe9840..96848b14 100644 --- a/engine/src/Core/String.cpp +++ b/engine/src/Core/String.cpp @@ -518,9 +518,9 @@ bool operator!=(ConstStringView lhs, const String& rhs) return operator==(lhs, rhs) == false; } -uint32_t Hash32(const String& value, uint32_t seed) +uint32_t HashValue32(const String& value, uint32_t seed) { - return Hash32(value.GetData(), value.GetLength(), seed); + return HashValue32(value.GetData(), value.GetLength(), seed); } } diff --git a/engine/src/Core/String.hpp b/engine/src/Core/String.hpp index 9da4b95a..2f1451f8 100644 --- a/engine/src/Core/String.hpp +++ b/engine/src/Core/String.hpp @@ -102,4 +102,6 @@ bool operator!=(const char* lhs, const String& rhs); bool operator!=(const String& lhs, ConstStringView rhs); bool operator!=(ConstStringView lhs, const String& rhs); +uint32_t HashValue32(const String& data, uint32_t seed); + } diff --git a/engine/src/Core/StringView.cpp b/engine/src/Core/StringView.cpp index fbce19a6..c30fe780 100644 --- a/engine/src/Core/StringView.cpp +++ b/engine/src/Core/StringView.cpp @@ -332,9 +332,9 @@ TEST_CASE("StringView can find last substring") template struct StringView; template struct StringView; -uint32_t Hash32(const ConstStringView& value, uint32_t seed) +uint32_t HashValue32(const ConstStringView& value, uint32_t seed) { - return Hash32(value.str, value.len, seed); + return HashValue32(value.str, value.len, seed); } } diff --git a/engine/src/Core/StringView.hpp b/engine/src/Core/StringView.hpp index 3280f545..6dfb6cec 100644 --- a/engine/src/Core/StringView.hpp +++ b/engine/src/Core/StringView.hpp @@ -54,6 +54,6 @@ struct StringView using MutableStringView = StringView; using ConstStringView = StringView; -uint32_t Hash32(const ConstStringView& value, uint32_t seed); +uint32_t HashValue32(const ConstStringView& value, uint32_t seed); } diff --git a/engine/src/Core/Uid.cpp b/engine/src/Core/Uid.cpp index 4bd3c518..70f91397 100644 --- a/engine/src/Core/Uid.cpp +++ b/engine/src/Core/Uid.cpp @@ -55,9 +55,9 @@ void Uid::WriteTo(ArrayView out) const IntegerToHexadecimal(raw[1], out.GetSubView(charsPerRaw, charsPerRaw * 2)); } -uint32_t Hash32(const Uid& uid, uint32_t seed) +uint32_t HashValue32(const Uid& uid, uint32_t seed) { - return Hash32(&uid, sizeof(Uid), seed); + return HashValue32(&uid, sizeof(Uid), seed); } TEST_CASE("Uid.Serialization") diff --git a/engine/src/Core/Uid.hpp b/engine/src/Core/Uid.hpp index 8337b631..39e21991 100644 --- a/engine/src/Core/Uid.hpp +++ b/engine/src/Core/Uid.hpp @@ -34,4 +34,6 @@ struct Uid void WriteTo(ArrayView out) const; }; +uint32_t HashValue32(const Uid& data, uint32_t seed); + } diff --git a/engine/src/Graphics/TerrainQuadTree.cpp b/engine/src/Graphics/TerrainQuadTree.cpp index dbc316f8..1a78e2fe 100644 --- a/engine/src/Graphics/TerrainQuadTree.cpp +++ b/engine/src/Graphics/TerrainQuadTree.cpp @@ -96,11 +96,11 @@ TerrainEdgeType EdgeMaskToType(TerrainEdgeMask mask) } // namespace -uint32_t Hash32(const QuadTreeNodeId& value, uint32_t seed) +uint32_t HashValue32(const QuadTreeNodeId& value, uint32_t seed) { - uint32_t hash = Hash32(&value.x, sizeof(value.x), seed); - hash = Hash32(&value.y, sizeof(value.y), hash); - return Hash32(&value.level, sizeof(value.level), hash); + uint32_t hash = HashValue32(&value.x, sizeof(value.x), seed); + hash = HashValue32(&value.y, sizeof(value.y), hash); + return HashValue32(&value.level, sizeof(value.level), hash); } void TerrainQuadTree::EdgeTypeDependents::AddDependent(uint16_t dependent) diff --git a/engine/src/Graphics/TerrainQuadTree.hpp b/engine/src/Graphics/TerrainQuadTree.hpp index 90226585..b244bdc3 100644 --- a/engine/src/Graphics/TerrainQuadTree.hpp +++ b/engine/src/Graphics/TerrainQuadTree.hpp @@ -14,7 +14,6 @@ class Allocator; struct FrustumPlanes; struct Mat4x4f; struct CameraParameters; -struct TerrainTileId; namespace kokko { @@ -60,7 +59,7 @@ struct QuadTreeNodeId } }; -uint32_t Hash32(const QuadTreeNodeId& value, uint32_t seed); +uint32_t HashValue32(const QuadTreeNodeId& value, uint32_t seed); enum class TerrainEdgeType : uint8_t { diff --git a/engine/src/Resources/AssetLibrary.cpp b/engine/src/Resources/AssetLibrary.cpp index 3691e594..8f7a1818 100644 --- a/engine/src/Resources/AssetLibrary.cpp +++ b/engine/src/Resources/AssetLibrary.cpp @@ -528,11 +528,11 @@ uint64_t AssetLibrary::CalculateHash(AssetType type, ArrayView co { String normalized(allocator); NormalizeLineEndings(content, normalized); - hash = Hash64(normalized.GetData(), normalized.GetLength(), 0); + hash = HashValue64(normalized.GetData(), normalized.GetLength(), 0); } else { - hash = Hash64(content.GetData(), content.GetCount(), 0); + hash = HashValue64(content.GetData(), content.GetCount(), 0); } return hash; } From cd71c22be4041a46892bbefb2184446e31b55d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksi=20Gr=C3=B6n?= Date: Sun, 22 Sep 2024 23:15:36 +0300 Subject: [PATCH 19/19] Try fix hash overload problem --- engine/src/Core/Hash.hpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/engine/src/Core/Hash.hpp b/engine/src/Core/Hash.hpp index 00c49718..350b17b9 100644 --- a/engine/src/Core/Hash.hpp +++ b/engine/src/Core/Hash.hpp @@ -6,11 +6,6 @@ namespace kokko { -template -struct Hash32 { - uint32_t operator()(const T& val, uint8_t seed) const { return HashValue32(val, seed); } -}; - uint64_t HashValue64(const void* data, size_t length, uint64_t seed); uint32_t HashValue32(const void* data, size_t length, uint32_t seed); @@ -24,9 +19,10 @@ uint32_t HashValue32(int32_t data, uint32_t seed); uint32_t HashValue32(uint64_t data, uint32_t seed); uint32_t HashValue32(int64_t data, uint32_t seed); -// TODO: Figure out a way to declare the overloads in the types' own header file -// Currently GCC would fail compilation because it doesn't consider those declarations -// to be overloads of the original hash functions. +template +struct Hash32 { + uint32_t operator()(const T& val, uint32_t seed) const { return HashValue32(val, seed); } +}; constexpr uint32_t HashString(const char* string, size_t length) { @@ -44,7 +40,7 @@ constexpr uint32_t HashString(const char* string, size_t length) return hash; } -} +} // namespace kokko constexpr uint32_t operator ""_hash(const char* string, size_t size) {