Kelsidavis-WoWee/include/rendering/world_map/composite_renderer.hpp

158 lines
5.7 KiB
C++
Raw Normal View History

refactor: decompose world map into modular component architecture Break the monolithic 1360-line world_map.cpp into 16 focused modules under src/rendering/world_map/: Architecture: - world_map_facade: public API composing all components (PIMPL) - world_map_types: Vulkan-free domain types (Zone, ViewLevel, etc.) - data_repository: DBC zone loading, ZMP pixel map, POI/overlay storage - coordinate_projection: UV projection, zone/continent lookups - composite_renderer: Vulkan tile pipeline + off-screen compositing - exploration_state: server mask + local exploration tracking - view_state_machine: COSMIC→WORLD→CONTINENT→ZONE navigation - input_handler: keyboard/mouse input → InputAction mapping - overlay_renderer: layer-based ImGui overlay system (OCP) - map_resolver: cross-map navigation (Outland, Northrend, etc.) - zone_metadata: level ranges and faction data Overlay layers (each an IOverlayLayer): - player_marker, party_dot, taxi_node, poi_marker, quest_poi, corpse_marker, zone_highlight, coordinate_display, subzone_tooltip Fixes: - Player marker no longer bleeds across continents (only shown when player is in a zone belonging to the displayed continent) - Zone hover uses DBC-projected AABB rectangles (restored from original working behavior) - Exploration overlay rendering for zone view subzones Tests: - 6 new test files covering coordinate projection, exploration state, map resolver, view state machine, zone metadata, and integration Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
2026-04-12 09:52:51 +03:00
// composite_renderer.hpp — Vulkan off-screen composite rendering for the world map.
// Extracted from WorldMap (Phase 7 of refactoring plan).
// SRP — all GPU resource management separated from domain logic.
#pragma once
#include "rendering/world_map/world_map_types.hpp"
#include <vulkan/vulkan.h>
#include <vk_mem_alloc.h>
#include <glm/glm.hpp>
#include <memory>
#include <vector>
#include <unordered_set>
namespace wowee {
namespace rendering {
class VkContext;
class VkTexture;
class VkRenderTarget;
}
namespace pipeline { class AssetManager; }
namespace rendering {
namespace world_map {
/// Push constant for world map tile composite vertex shader.
struct WorldMapTilePush {
glm::vec2 gridOffset; // 8 bytes
float gridCols; // 4 bytes
float gridRows; // 4 bytes
}; // 16 bytes
/// Push constant for the overlay/fog pipeline (vertex + fragment stages).
struct OverlayPush {
glm::vec2 gridOffset; // 8 bytes (vertex)
float gridCols; // 4 bytes (vertex)
float gridRows; // 4 bytes (vertex)
glm::vec4 tintColor; // 16 bytes (fragment)
}; // 32 bytes
class CompositeRenderer {
public:
CompositeRenderer();
~CompositeRenderer();
bool initialize(VkContext* ctx, pipeline::AssetManager* am);
void shutdown();
/// Load base tile textures for a zone.
void loadZoneTextures(int zoneIdx, std::vector<Zone>& zones, const std::string& mapName);
/// Load exploration overlay textures for a zone.
void loadOverlayTextures(int zoneIdx, std::vector<Zone>& zones);
/// Request a composite for the given zone (deferred to compositePass).
void requestComposite(int zoneIdx);
/// Execute the off-screen composite pass.
void compositePass(VkCommandBuffer cmd,
const std::vector<Zone>& zones,
const std::unordered_set<int>& exploredOverlays,
bool hasServerMask);
/// Descriptor set for ImGui display of the composite.
VkDescriptorSet displayDescriptorSet() const { return imguiDisplaySet; }
/// Destroy all loaded zone textures (on map change).
void destroyZoneTextures(std::vector<Zone>& zones);
/// Detach zone textures for deferred GPU destruction.
/// Clears CPU tracking immediately but moves GPU texture objects to a stale
/// list so they can be freed later when no in-flight frames reference them.
void detachZoneTextures();
/// Free any GPU textures previously moved to the stale list by detachZoneTextures.
/// Calls vkDeviceWaitIdle internally to ensure no in-flight work references them.
void flushStaleTextures();
/// Index of the zone currently composited (-1 if none).
int compositedIdx() const { return compositedIdx_; }
/// Reset composited index to force re-composite.
void invalidateComposite() { compositedIdx_ = -1; }
/// Check whether a zone has any loaded tile textures.
bool hasAnyTile(int zoneIdx) const;
// FBO dimensions (public for overlay coordinate math)
static constexpr int GRID_COLS = 4;
static constexpr int GRID_ROWS = 3;
static constexpr int TILE_PX = 256;
static constexpr int FBO_W = GRID_COLS * TILE_PX;
static constexpr int FBO_H = GRID_ROWS * TILE_PX;
// WoW's WorldMapDetailFrame is 1002x668 — the visible map content area.
// The FBO is 1024x768 so we crop UVs to show only the actual map region.
static constexpr int MAP_W = 1002;
static constexpr int MAP_H = 668;
static constexpr float MAP_U_MAX = static_cast<float>(MAP_W) / static_cast<float>(FBO_W);
static constexpr float MAP_V_MAX = static_cast<float>(MAP_H) / static_cast<float>(FBO_H);
private:
VkContext* vkCtx = nullptr;
pipeline::AssetManager* assetManager = nullptr;
bool initialized = false;
std::unique_ptr<VkRenderTarget> compositeTarget;
// Quad vertex buffer (pos2 + uv2)
::VkBuffer quadVB = VK_NULL_HANDLE;
VmaAllocation quadVBAlloc = VK_NULL_HANDLE;
// Descriptor resources
VkDescriptorSetLayout samplerSetLayout = VK_NULL_HANDLE;
VkDescriptorPool descPool = VK_NULL_HANDLE;
static constexpr uint32_t MAX_DESC_SETS = 192;
static constexpr uint32_t MAX_OVERLAY_TILES = 48;
// Tile composite pipeline
VkPipeline tilePipeline = VK_NULL_HANDLE;
VkPipelineLayout tilePipelineLayout = VK_NULL_HANDLE;
VkDescriptorSet tileDescSets[2][12] = {}; // [frameInFlight][tileSlot]
// Alpha-blended overlay pipeline (fog + explored area overlays)
VkPipeline overlayPipeline_ = VK_NULL_HANDLE;
VkPipelineLayout overlayPipelineLayout_ = VK_NULL_HANDLE;
std::unique_ptr<VkTexture> fogTexture_; // 1×1 white pixel for fog quad
VkDescriptorSet fogDescSet_ = VK_NULL_HANDLE;
VkDescriptorSet overlayDescSets_[2][MAX_OVERLAY_TILES] = {};
// ImGui display descriptor set (points to composite render target)
VkDescriptorSet imguiDisplaySet = VK_NULL_HANDLE;
// Texture storage (owns all VkTexture objects for zone tiles)
std::vector<std::unique_ptr<VkTexture>> zoneTextures;
int compositedIdx_ = -1;
int pendingCompositeIdx_ = -1;
// Per-zone tile texture pointers (indexed by zone, then by tile slot)
// Stored separately since Zone struct is now Vulkan-free
struct ZoneTextureSlots {
VkTexture* tileTextures[12] = {};
bool tilesLoaded = false;
// Per-overlay tile textures
struct OverlaySlots {
std::vector<VkTexture*> tiles;
bool tilesLoaded = false;
};
std::vector<OverlaySlots> overlays;
};
std::vector<ZoneTextureSlots> zoneTextureSlots_;
void ensureTextureSlots(size_t zoneCount, const std::vector<Zone>& zones);
};
} // namespace world_map
} // namespace rendering
} // namespace wowee