Kelsidavis-WoWee/include/rendering/world_map/composite_renderer.hpp
Pavel Okhlopkov fff06fc932 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

157 lines
5.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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