2026-02-02 12:24:50 -08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "pipeline/terrain_mesh.hpp"
|
2026-02-08 01:16:23 -08:00
|
|
|
#include "pipeline/blp_loader.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
#include "rendering/camera.hpp"
|
2026-02-21 19:41:21 -08:00
|
|
|
#include <vulkan/vulkan.h>
|
|
|
|
|
#include <vk_mem_alloc.h>
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <glm/glm.hpp>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <unordered_map>
|
2026-02-22 07:26:54 -08:00
|
|
|
#include <unordered_set>
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
|
|
|
|
|
// Forward declarations
|
|
|
|
|
namespace pipeline { class AssetManager; }
|
|
|
|
|
|
|
|
|
|
namespace rendering {
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
class VkContext;
|
|
|
|
|
class VkTexture;
|
2026-02-02 12:24:50 -08:00
|
|
|
class Frustum;
|
|
|
|
|
|
|
|
|
|
/**
|
2026-02-21 19:41:21 -08:00
|
|
|
* GPU-side terrain chunk data (Vulkan)
|
2026-02-02 12:24:50 -08:00
|
|
|
*/
|
|
|
|
|
struct TerrainChunkGPU {
|
2026-02-21 19:41:21 -08:00
|
|
|
::VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
|
|
|
|
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
|
|
|
|
::VkBuffer indexBuffer = VK_NULL_HANDLE;
|
|
|
|
|
VmaAllocation indexAlloc = VK_NULL_HANDLE;
|
|
|
|
|
uint32_t indexCount = 0;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Material descriptor set (set 1: 7 samplers + params UBO)
|
|
|
|
|
VkDescriptorSet materialSet = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
// Per-chunk params UBO (hasLayer1/2/3)
|
|
|
|
|
::VkBuffer paramsUBO = VK_NULL_HANDLE;
|
|
|
|
|
VmaAllocation paramsAlloc = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
// Texture handles (owned by cache, NOT destroyed per-chunk)
|
|
|
|
|
VkTexture* baseTexture = nullptr;
|
|
|
|
|
VkTexture* layerTextures[3] = {nullptr, nullptr, nullptr};
|
|
|
|
|
VkTexture* alphaTextures[3] = {nullptr, nullptr, nullptr};
|
|
|
|
|
int layerCount = 0;
|
|
|
|
|
|
|
|
|
|
// Per-chunk alpha textures (owned by this chunk, destroyed on removal)
|
|
|
|
|
std::vector<std::unique_ptr<VkTexture>> ownedAlphaTextures;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// World position for culling
|
|
|
|
|
float worldX = 0.0f;
|
|
|
|
|
float worldY = 0.0f;
|
|
|
|
|
float worldZ = 0.0f;
|
|
|
|
|
|
|
|
|
|
// Owning tile coordinates (for per-tile removal)
|
|
|
|
|
int tileX = -1, tileY = -1;
|
|
|
|
|
|
|
|
|
|
// Bounding sphere for frustum culling
|
|
|
|
|
float boundingSphereRadius = 0.0f;
|
|
|
|
|
glm::vec3 boundingSphereCenter = glm::vec3(0.0f);
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
bool isValid() const { return vertexBuffer != VK_NULL_HANDLE && indexBuffer != VK_NULL_HANDLE; }
|
2026-02-02 12:24:50 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2026-02-21 19:41:21 -08:00
|
|
|
* Terrain renderer (Vulkan)
|
2026-02-02 12:24:50 -08:00
|
|
|
*/
|
|
|
|
|
class TerrainRenderer {
|
|
|
|
|
public:
|
|
|
|
|
TerrainRenderer();
|
|
|
|
|
~TerrainRenderer();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize terrain renderer
|
2026-02-21 19:41:21 -08:00
|
|
|
* @param ctx Vulkan context
|
|
|
|
|
* @param perFrameLayout Descriptor set layout for set 0 (per-frame UBO)
|
2026-02-02 12:24:50 -08:00
|
|
|
* @param assetManager Asset manager for loading textures
|
|
|
|
|
*/
|
2026-02-21 19:41:21 -08:00
|
|
|
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout,
|
|
|
|
|
pipeline::AssetManager* assetManager);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
void shutdown();
|
|
|
|
|
|
|
|
|
|
bool loadTerrain(const pipeline::TerrainMesh& mesh,
|
|
|
|
|
const std::vector<std::string>& texturePaths,
|
|
|
|
|
int tileX = -1, int tileY = -1);
|
|
|
|
|
|
2026-03-07 11:59:19 -08:00
|
|
|
/// Upload a batch of terrain chunks incrementally. Returns true when all chunks done.
|
|
|
|
|
/// chunkIndex is updated to the next chunk to process (0-255 row-major).
|
|
|
|
|
bool loadTerrainIncremental(const pipeline::TerrainMesh& mesh,
|
|
|
|
|
const std::vector<std::string>& texturePaths,
|
|
|
|
|
int tileX, int tileY,
|
|
|
|
|
int& chunkIndex, int maxChunksPerCall = 16);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
void removeTile(int tileX, int tileY);
|
|
|
|
|
|
2026-02-08 01:16:23 -08:00
|
|
|
void uploadPreloadedTextures(const std::unordered_map<std::string, pipeline::BLPImage>& textures);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
/**
|
2026-02-21 19:41:21 -08:00
|
|
|
* Render terrain
|
|
|
|
|
* @param cmd Command buffer to record into
|
|
|
|
|
* @param perFrameSet Per-frame descriptor set (set 0)
|
|
|
|
|
* @param camera Camera for frustum culling
|
2026-02-02 12:24:50 -08:00
|
|
|
*/
|
2026-02-21 19:41:21 -08:00
|
|
|
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
/**
|
2026-02-21 19:41:21 -08:00
|
|
|
* Render terrain into shadow depth map (Phase 6 stub)
|
2026-02-02 12:24:50 -08:00
|
|
|
*/
|
2026-02-21 19:41:21 -08:00
|
|
|
void renderShadow(VkCommandBuffer cmd, const glm::vec3& shadowCenter, float halfExtent);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
void clear();
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-22 02:59:24 -08:00
|
|
|
void recreatePipelines();
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
void setWireframe(bool enabled) { wireframe = enabled; }
|
|
|
|
|
void setFrustumCulling(bool enabled) { frustumCullingEnabled = enabled; }
|
|
|
|
|
void setFogEnabled(bool enabled) { fogEnabled = enabled; }
|
|
|
|
|
bool isFogEnabled() const { return fogEnabled; }
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Shadow mapping stubs (Phase 6)
|
|
|
|
|
void setShadowMap(VkDescriptorImageInfo /*depthInfo*/, const glm::mat4& /*lightSpaceMat*/) {}
|
|
|
|
|
void clearShadowMap() {}
|
2026-02-04 16:08:35 -08:00
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
int getChunkCount() const { return static_cast<int>(chunks.size()); }
|
|
|
|
|
int getRenderedChunkCount() const { return renderedChunks; }
|
|
|
|
|
int getCulledChunkCount() const { return culledChunks; }
|
|
|
|
|
int getTriangleCount() const;
|
2026-03-07 12:32:39 -08:00
|
|
|
VkContext* getVkContext() const { return vkCtx; }
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
TerrainChunkGPU uploadChunk(const pipeline::ChunkMesh& chunk);
|
2026-02-21 19:41:21 -08:00
|
|
|
VkTexture* loadTexture(const std::string& path);
|
|
|
|
|
VkTexture* createAlphaTexture(const std::vector<uint8_t>& alphaData);
|
2026-02-02 12:24:50 -08:00
|
|
|
bool isChunkVisible(const TerrainChunkGPU& chunk, const Frustum& frustum);
|
|
|
|
|
void calculateBoundingSphere(TerrainChunkGPU& chunk, const pipeline::ChunkMesh& meshChunk);
|
2026-02-21 19:41:21 -08:00
|
|
|
VkDescriptorSet allocateMaterialSet();
|
|
|
|
|
void writeMaterialDescriptors(VkDescriptorSet set, const TerrainChunkGPU& chunk);
|
|
|
|
|
void destroyChunkGPU(TerrainChunkGPU& chunk);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
VkContext* vkCtx = nullptr;
|
2026-02-02 12:24:50 -08:00
|
|
|
pipeline::AssetManager* assetManager = nullptr;
|
2026-02-21 19:41:21 -08:00
|
|
|
|
|
|
|
|
// Pipeline
|
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipeline wireframePipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSetLayout materialSetLayout = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
// Descriptor pool for material sets
|
|
|
|
|
VkDescriptorPool materialDescPool = VK_NULL_HANDLE;
|
2026-02-22 02:59:24 -08:00
|
|
|
static constexpr uint32_t MAX_MATERIAL_SETS = 16384;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Loaded terrain chunks
|
|
|
|
|
std::vector<TerrainChunkGPU> chunks;
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Texture cache (path -> VkTexture)
|
2026-02-12 16:29:36 -08:00
|
|
|
struct TextureCacheEntry {
|
2026-02-21 19:41:21 -08:00
|
|
|
std::unique_ptr<VkTexture> texture;
|
2026-02-12 16:29:36 -08:00
|
|
|
size_t approxBytes = 0;
|
|
|
|
|
uint64_t lastUse = 0;
|
|
|
|
|
};
|
|
|
|
|
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
|
|
|
|
size_t textureCacheBytes_ = 0;
|
|
|
|
|
uint64_t textureCacheCounter_ = 0;
|
2026-02-21 19:41:21 -08:00
|
|
|
size_t textureCacheBudgetBytes_ = 4096ull * 1024 * 1024;
|
2026-02-22 07:26:54 -08:00
|
|
|
std::unordered_set<std::string> failedTextureCache_;
|
|
|
|
|
std::unordered_set<std::string> loggedTextureLoadFails_;
|
|
|
|
|
uint32_t textureBudgetRejectWarnings_ = 0;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Fallback textures
|
|
|
|
|
std::unique_ptr<VkTexture> whiteTexture;
|
|
|
|
|
std::unique_ptr<VkTexture> opaqueAlphaTexture;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Rendering state
|
|
|
|
|
bool wireframe = false;
|
|
|
|
|
bool frustumCullingEnabled = true;
|
|
|
|
|
bool fogEnabled = true;
|
|
|
|
|
int renderedChunks = 0;
|
|
|
|
|
int culledChunks = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace rendering
|
|
|
|
|
} // namespace wowee
|