mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-16 09:13:50 +00:00
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>
This commit is contained in:
parent
db3f65a87e
commit
fff06fc932
55 changed files with 6335 additions and 1542 deletions
157
include/rendering/world_map/composite_renderer.hpp
Normal file
157
include/rendering/world_map/composite_renderer.hpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
// 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
|
||||
49
include/rendering/world_map/coordinate_projection.hpp
Normal file
49
include/rendering/world_map/coordinate_projection.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// coordinate_projection.hpp — Pure coordinate math for world map UV projection.
|
||||
// Extracted from WorldMap methods (Phase 2 of refactoring plan).
|
||||
// All functions are stateless free functions — trivially testable.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
/// Project render-space position to [0,1] UV on a zone or continent map.
|
||||
glm::vec2 renderPosToMapUV(const glm::vec3& renderPos,
|
||||
const ZoneBounds& bounds,
|
||||
bool isContinent);
|
||||
|
||||
/// Derive effective projection bounds for a continent from its child zones.
|
||||
/// Uses zoneBelongsToContinent() internally. Returns false if insufficient data.
|
||||
bool getContinentProjectionBounds(const std::vector<Zone>& zones,
|
||||
int contIdx,
|
||||
float& left, float& right,
|
||||
float& top, float& bottom);
|
||||
|
||||
/// Find the best-fit continent index for a player position.
|
||||
/// Prefers the smallest containing continent; falls back to nearest center.
|
||||
int findBestContinentForPlayer(const std::vector<Zone>& zones,
|
||||
const glm::vec3& playerRenderPos);
|
||||
|
||||
/// Find the smallest zone (areaID != 0) containing the player position.
|
||||
/// Returns -1 if no zone contains the position.
|
||||
int findZoneForPlayer(const std::vector<Zone>& zones,
|
||||
const glm::vec3& playerRenderPos);
|
||||
|
||||
/// Test if a zone spatially belongs to a given continent.
|
||||
/// Uses parentWorldMapID when available, falls back to overlap heuristic.
|
||||
bool zoneBelongsToContinent(const std::vector<Zone>& zones,
|
||||
int zoneIdx, int contIdx);
|
||||
|
||||
/// Check whether the zone at idx is a root continent (has leaf continents as children).
|
||||
bool isRootContinent(const std::vector<Zone>& zones, int idx);
|
||||
|
||||
/// Check whether the zone at idx is a leaf continent (parentWorldMapID != 0, areaID == 0).
|
||||
bool isLeafContinent(const std::vector<Zone>& zones, int idx);
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
95
include/rendering/world_map/data_repository.hpp
Normal file
95
include/rendering/world_map/data_repository.hpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// data_repository.hpp — DBC data loading, ZMP pixel map, and zone/POI/overlay storage.
|
||||
// Extracted from WorldMap::loadZonesFromDBC, loadPOIData, buildCosmicView
|
||||
// (Phase 5 of refactoring plan). SRP — all DBC parsing lives here.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class DataRepository {
|
||||
public:
|
||||
/// Load all zone data from DBC files for the given map name.
|
||||
void loadZones(const std::string& mapName, pipeline::AssetManager& assetManager);
|
||||
|
||||
/// Load area POI markers from AreaPOI.dbc.
|
||||
void loadPOIs(pipeline::AssetManager& assetManager);
|
||||
|
||||
/// Build cosmic view entries for the active expansion (uses isActiveExpansion).
|
||||
void buildCosmicView(int expansionLevel = 0);
|
||||
|
||||
/// Build Azeroth world-view continent regions for the active expansion.
|
||||
void buildAzerothView(int expansionLevel = 0);
|
||||
|
||||
/// Load ZMP pixel map for the given continent name (e.g. "Azeroth").
|
||||
/// The ZMP is a 128x128 grid of uint32 AreaTable IDs.
|
||||
void loadZmpPixelMap(const std::string& continentName,
|
||||
pipeline::AssetManager& assetManager);
|
||||
|
||||
/// Determine expansion level from the active expansion profile.
|
||||
static int getExpansionLevel();
|
||||
|
||||
// --- Accessors ---
|
||||
std::vector<Zone>& zones() { return zones_; }
|
||||
const std::vector<Zone>& zones() const { return zones_; }
|
||||
int cosmicIdx() const { return cosmicIdx_; }
|
||||
int worldIdx() const { return worldIdx_; }
|
||||
int currentMapId() const { return currentMapId_; }
|
||||
const std::vector<CosmicMapEntry>& cosmicMaps() const { return cosmicMaps_; }
|
||||
const std::vector<CosmicMapEntry>& azerothRegions() const { return azerothRegions_; }
|
||||
bool cosmicEnabled() const { return cosmicEnabled_; }
|
||||
const std::vector<POI>& poiMarkers() const { return poiMarkers_; }
|
||||
|
||||
const std::unordered_map<uint32_t, uint32_t>& exploreFlagByAreaId() const { return exploreFlagByAreaId_; }
|
||||
const std::unordered_map<uint32_t, std::string>& areaNameByAreaId() const { return areaNameByAreaId_; }
|
||||
|
||||
/// ZMP pixel map accessors.
|
||||
static constexpr int ZMP_SIZE = 128;
|
||||
const std::array<uint32_t, 128 * 128>& zmpGrid() const { return zmpGrid_; }
|
||||
bool hasZmpData() const { return zmpLoaded_; }
|
||||
|
||||
/// Look up zone index from an AreaTable ID (from ZMP). Returns -1 if not found.
|
||||
int zoneIndexForAreaId(uint32_t areaId) const;
|
||||
|
||||
/// ZMP-derived bounding rectangles per zone index (UV [0,1] on display).
|
||||
const std::unordered_map<int, ZmpRect>& zmpZoneBounds() const { return zmpZoneBounds_; }
|
||||
|
||||
/// Reset all data (called on map change).
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::vector<Zone> zones_;
|
||||
std::vector<POI> poiMarkers_;
|
||||
std::vector<CosmicMapEntry> cosmicMaps_;
|
||||
std::vector<CosmicMapEntry> azerothRegions_;
|
||||
std::unordered_map<uint32_t, uint32_t> exploreFlagByAreaId_;
|
||||
std::unordered_map<uint32_t, std::string> areaNameByAreaId_;
|
||||
int cosmicIdx_ = -1;
|
||||
int worldIdx_ = -1;
|
||||
int currentMapId_ = -1;
|
||||
bool cosmicEnabled_ = true;
|
||||
bool poisLoaded_ = false;
|
||||
|
||||
// ZMP pixel map: 128x128 grid of AreaTable IDs for continent-level hover
|
||||
std::array<uint32_t, 128 * 128> zmpGrid_{};
|
||||
bool zmpLoaded_ = false;
|
||||
// AreaID → zone index (zones_ vector) for quick resolution
|
||||
std::unordered_map<uint32_t, int> areaIdToZoneIdx_;
|
||||
// ZMP-derived bounding boxes per zone index (UV coords on display)
|
||||
std::unordered_map<int, ZmpRect> zmpZoneBounds_;
|
||||
|
||||
/// Scan ZMP grid and build bounding boxes for each zone.
|
||||
void buildZmpZoneBounds();
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
53
include/rendering/world_map/exploration_state.hpp
Normal file
53
include/rendering/world_map/exploration_state.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// exploration_state.hpp — Fog of war / exploration tracking (pure domain logic).
|
||||
// Extracted from WorldMap::updateExploration (Phase 3 of refactoring plan).
|
||||
// No rendering or GPU dependencies — fully testable standalone.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class ExplorationState {
|
||||
public:
|
||||
void setServerMask(const std::vector<uint32_t>& masks, bool hasData);
|
||||
bool hasServerMask() const { return hasServerMask_; }
|
||||
|
||||
/// Recompute explored zones and overlays for given player position.
|
||||
/// @param zones All loaded zones
|
||||
/// @param playerRenderPos Player position in render-space
|
||||
/// @param currentZoneIdx Currently viewed zone index
|
||||
/// @param exploreFlagByAreaId AreaID → ExploreFlag mapping from AreaTable.dbc
|
||||
void update(const std::vector<Zone>& zones,
|
||||
const glm::vec3& playerRenderPos,
|
||||
int currentZoneIdx,
|
||||
const std::unordered_map<uint32_t, uint32_t>& exploreFlagByAreaId);
|
||||
|
||||
const std::unordered_set<int>& exploredZones() const { return exploredZones_; }
|
||||
const std::unordered_set<int>& exploredOverlays() const { return exploredOverlays_; }
|
||||
|
||||
/// Returns true if the explored overlay set changed since last update.
|
||||
bool overlaysChanged() const { return overlaysChanged_; }
|
||||
|
||||
/// Clear accumulated local exploration data.
|
||||
void clearLocal() { locallyExploredZones_.clear(); }
|
||||
|
||||
private:
|
||||
bool isBitSet(uint32_t bitIndex) const;
|
||||
|
||||
std::vector<uint32_t> serverMask_;
|
||||
bool hasServerMask_ = false;
|
||||
std::unordered_set<int> exploredZones_;
|
||||
std::unordered_set<int> exploredOverlays_;
|
||||
std::unordered_set<int> locallyExploredZones_;
|
||||
bool overlaysChanged_ = false;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
37
include/rendering/world_map/input_handler.hpp
Normal file
37
include/rendering/world_map/input_handler.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// input_handler.hpp — Input processing for the world map.
|
||||
// Extracted from WorldMap::render (Phase 9 of refactoring plan).
|
||||
// SRP — input interpretation separated from state changes and rendering.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
enum class InputAction {
|
||||
NONE,
|
||||
CLOSE,
|
||||
ZOOM_IN,
|
||||
ZOOM_OUT,
|
||||
CLICK_ZONE, // left-click on continent view zone
|
||||
CLICK_COSMIC_REGION, // left-click on cosmic landmass
|
||||
RIGHT_CLICK_BACK, // right-click to go back
|
||||
};
|
||||
|
||||
struct InputResult {
|
||||
InputAction action = InputAction::NONE;
|
||||
int targetIdx = -1; // zone or cosmic region index
|
||||
};
|
||||
|
||||
class InputHandler {
|
||||
public:
|
||||
/// Process input for current frame. Returns the highest-priority action.
|
||||
InputResult process(ViewLevel currentLevel,
|
||||
int hoveredZoneIdx,
|
||||
bool cosmicEnabled);
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
16
include/rendering/world_map/layers/coordinate_display.hpp
Normal file
16
include/rendering/world_map/layers/coordinate_display.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// coordinate_display.hpp — WoW coordinates under cursor on the world map.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class CoordinateDisplay : public IOverlayLayer {
|
||||
public:
|
||||
void render(const LayerContext& ctx) override;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
24
include/rendering/world_map/layers/corpse_marker_layer.hpp
Normal file
24
include/rendering/world_map/layers/corpse_marker_layer.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// corpse_marker_layer.hpp — Death corpse X marker on the world map.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class CorpseMarkerLayer : public IOverlayLayer {
|
||||
public:
|
||||
void setCorpse(bool hasCorpse, glm::vec3 renderPos) {
|
||||
hasCorpse_ = hasCorpse;
|
||||
corpseRenderPos_ = renderPos;
|
||||
}
|
||||
void render(const LayerContext& ctx) override;
|
||||
private:
|
||||
bool hasCorpse_ = false;
|
||||
glm::vec3 corpseRenderPos_ = {};
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
21
include/rendering/world_map/layers/party_dot_layer.hpp
Normal file
21
include/rendering/world_map/layers/party_dot_layer.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// party_dot_layer.hpp — Party member position dots on the world map.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class PartyDotLayer : public IOverlayLayer {
|
||||
public:
|
||||
void setDots(const std::vector<PartyDot>& dots) { dots_ = &dots; }
|
||||
void render(const LayerContext& ctx) override;
|
||||
private:
|
||||
const std::vector<PartyDot>* dots_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
16
include/rendering/world_map/layers/player_marker_layer.hpp
Normal file
16
include/rendering/world_map/layers/player_marker_layer.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// player_marker_layer.hpp — Directional player arrow on the world map.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class PlayerMarkerLayer : public IOverlayLayer {
|
||||
public:
|
||||
void render(const LayerContext& ctx) override;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
21
include/rendering/world_map/layers/poi_marker_layer.hpp
Normal file
21
include/rendering/world_map/layers/poi_marker_layer.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// poi_marker_layer.hpp — Town/dungeon/capital POI icons on the world map.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class POIMarkerLayer : public IOverlayLayer {
|
||||
public:
|
||||
void setMarkers(const std::vector<POI>& markers) { markers_ = &markers; }
|
||||
void render(const LayerContext& ctx) override;
|
||||
private:
|
||||
const std::vector<POI>* markers_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
21
include/rendering/world_map/layers/quest_poi_layer.hpp
Normal file
21
include/rendering/world_map/layers/quest_poi_layer.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// quest_poi_layer.hpp — Quest objective markers on the world map.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class QuestPOILayer : public IOverlayLayer {
|
||||
public:
|
||||
void setPois(const std::vector<QuestPOI>& pois) { pois_ = &pois; }
|
||||
void render(const LayerContext& ctx) override;
|
||||
private:
|
||||
const std::vector<QuestPOI>* pois_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
16
include/rendering/world_map/layers/subzone_tooltip_layer.hpp
Normal file
16
include/rendering/world_map/layers/subzone_tooltip_layer.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// subzone_tooltip_layer.hpp — Overlay area hover labels in zone view.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class SubzoneTooltipLayer : public IOverlayLayer {
|
||||
public:
|
||||
void render(const LayerContext& ctx) override;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
21
include/rendering/world_map/layers/taxi_node_layer.hpp
Normal file
21
include/rendering/world_map/layers/taxi_node_layer.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// taxi_node_layer.hpp — Flight master diamond icons on the world map.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class TaxiNodeLayer : public IOverlayLayer {
|
||||
public:
|
||||
void setNodes(const std::vector<TaxiNode>& nodes) { nodes_ = &nodes; }
|
||||
void render(const LayerContext& ctx) override;
|
||||
private:
|
||||
const std::vector<TaxiNode>* nodes_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
55
include/rendering/world_map/layers/zone_highlight_layer.hpp
Normal file
55
include/rendering/world_map/layers/zone_highlight_layer.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// zone_highlight_layer.hpp — Continent view zone rectangles + hover effects.
|
||||
#pragma once
|
||||
#include "rendering/world_map/overlay_renderer.hpp"
|
||||
#include "rendering/world_map/zone_metadata.hpp"
|
||||
#include "rendering/vk_texture.hpp"
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
class VkContext;
|
||||
}
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class ZoneHighlightLayer : public IOverlayLayer {
|
||||
public:
|
||||
~ZoneHighlightLayer() override;
|
||||
|
||||
void setMetadata(const ZoneMetadata* metadata) { metadata_ = metadata; }
|
||||
void initialize(VkContext* ctx, pipeline::AssetManager* am);
|
||||
void clearTextures();
|
||||
void render(const LayerContext& ctx) override;
|
||||
int hoveredZone() const { return hoveredZone_; }
|
||||
|
||||
/// Get the ImGui texture ID for a highlight BLP, loading lazily.
|
||||
/// key is used as cache key; customPath overrides the default path if non-empty.
|
||||
ImTextureID getHighlightTexture(const std::string& key,
|
||||
const std::string& customPath = "");
|
||||
|
||||
private:
|
||||
/// Load the highlight BLP and register it with ImGui.
|
||||
void ensureHighlight(const std::string& key, const std::string& customPath);
|
||||
|
||||
const ZoneMetadata* metadata_ = nullptr;
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
pipeline::AssetManager* assetManager_ = nullptr;
|
||||
|
||||
struct HighlightEntry {
|
||||
std::unique_ptr<VkTexture> texture;
|
||||
VkDescriptorSet imguiDS = VK_NULL_HANDLE; // ImGui texture ID
|
||||
};
|
||||
std::unordered_map<std::string, HighlightEntry> highlights_;
|
||||
std::unordered_set<std::string> missingHighlights_; // areas with no highlight file
|
||||
|
||||
int hoveredZone_ = -1;
|
||||
int prevHoveredZone_ = -1;
|
||||
float hoverHighlightAlpha_ = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
75
include/rendering/world_map/map_resolver.hpp
Normal file
75
include/rendering/world_map/map_resolver.hpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// map_resolver.hpp — Centralized map navigation resolution for the world map.
|
||||
// Determines the correct action when clicking a region or zone at any view level.
|
||||
// All functions are stateless free functions — trivially testable.
|
||||
// Map folder names are resolved from a built-in table matching
|
||||
// Data/interface/worldmap/ rather than WorldLoader::mapIdToName.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
// ── Map folder lookup (replaces WorldLoader::mapIdToName for world map) ──
|
||||
|
||||
/// Map ID → worldmap folder name (e.g. 0 → "Azeroth", 571 → "Northrend").
|
||||
/// Returns empty string if unknown.
|
||||
const char* mapIdToFolder(uint32_t mapId);
|
||||
|
||||
/// Worldmap folder name → map ID (e.g. "Azeroth" → 0, "Northrend" → 571).
|
||||
/// Case-insensitive comparison. Returns -1 if unknown.
|
||||
int folderToMapId(const std::string& folder);
|
||||
|
||||
/// Map ID → display name for UI (e.g. 0 → "Eastern Kingdoms", 571 → "Northrend").
|
||||
/// Returns nullptr if unknown.
|
||||
const char* mapDisplayName(uint32_t mapId);
|
||||
|
||||
// ── Result types ─────────────────────────────────────────────
|
||||
|
||||
enum class MapResolveAction {
|
||||
NONE, ///< No valid navigation target
|
||||
NAVIGATE_CONTINENT, ///< Switch to continent view within current map data
|
||||
LOAD_MAP, ///< Load a different map entirely (switchToMap)
|
||||
ENTER_ZONE, ///< Enter zone view within current continent
|
||||
};
|
||||
|
||||
struct MapResolveResult {
|
||||
MapResolveAction action = MapResolveAction::NONE;
|
||||
int targetZoneIdx = -1; ///< Zone index for NAVIGATE_CONTINENT or ENTER_ZONE
|
||||
std::string targetMapName; ///< Map folder name for LOAD_MAP
|
||||
};
|
||||
|
||||
// ── Resolve functions ────────────────────────────────────────
|
||||
|
||||
/// Resolve WORLD view region click. Determines whether to navigate within
|
||||
/// the current map data (e.g. clicking EK when already on Azeroth) or load
|
||||
/// a new map (e.g. clicking Kalimdor or Northrend from Azeroth world view).
|
||||
MapResolveResult resolveWorldRegionClick(uint32_t regionMapId,
|
||||
const std::vector<Zone>& zones,
|
||||
int currentMapId,
|
||||
int cosmicIdx);
|
||||
|
||||
/// Resolve CONTINENT view zone click. Determines whether the clicked zone
|
||||
/// can be entered directly (same map) or requires loading a different map
|
||||
/// (zone's displayMapID differs from current).
|
||||
MapResolveResult resolveZoneClick(int zoneIdx,
|
||||
const std::vector<Zone>& zones,
|
||||
int currentMapId);
|
||||
|
||||
/// Resolve COSMIC view map click. Always returns LOAD_MAP for the target.
|
||||
MapResolveResult resolveCosmicClick(uint32_t targetMapId);
|
||||
|
||||
/// Find the best continent zone index to display for a given mapId within
|
||||
/// the currently loaded zones. Prefers leaf continents over root continents.
|
||||
/// Returns -1 if no suitable continent is found.
|
||||
int findContinentForMapId(const std::vector<Zone>& zones,
|
||||
uint32_t mapId,
|
||||
int cosmicIdx);
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
71
include/rendering/world_map/overlay_renderer.hpp
Normal file
71
include/rendering/world_map/overlay_renderer.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// overlay_renderer.hpp — ImGui overlay layer system for the world map.
|
||||
// Extracted from WorldMap::renderImGuiOverlay (Phase 8 of refactoring plan).
|
||||
// OCP — new marker types are added by implementing IOverlayLayer.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <imgui.h>
|
||||
|
||||
struct ImDrawList;
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
/// Context passed to each overlay layer during rendering.
|
||||
struct LayerContext {
|
||||
ImDrawList* drawList = nullptr;
|
||||
ImVec2 imgMin; // top-left of map image in screen space
|
||||
float displayW = 0, displayH = 0;
|
||||
glm::vec3 playerRenderPos;
|
||||
float playerYawDeg = 0;
|
||||
int currentZoneIdx = -1;
|
||||
int continentIdx = -1;
|
||||
int currentMapId = -1;
|
||||
ViewLevel viewLevel = ViewLevel::ZONE;
|
||||
const std::vector<Zone>* zones = nullptr;
|
||||
const std::unordered_set<int>* exploredZones = nullptr;
|
||||
const std::unordered_set<int>* exploredOverlays = nullptr;
|
||||
const std::unordered_map<uint32_t, std::string>* areaNameByAreaId = nullptr;
|
||||
// FBO dimensions for overlay coordinate math
|
||||
int fboW = 1024;
|
||||
int fboH = 768;
|
||||
|
||||
// ZMP pixel map for continent-view hover (128x128 grid of AreaTable IDs)
|
||||
const std::array<uint32_t, 128 * 128>* zmpGrid = nullptr;
|
||||
bool hasZmpData = false;
|
||||
// Function to resolve AreaTable ID → zone index (from DataRepository)
|
||||
int (*zmpResolveZoneIdx)(const void* repo, uint32_t areaId) = nullptr;
|
||||
const void* zmpRepoPtr = nullptr; // opaque DataRepository pointer
|
||||
// ZMP-derived zone bounding boxes (zone index → UV rect on display)
|
||||
const std::unordered_map<int, ZmpRect>* zmpZoneBounds = nullptr;
|
||||
};
|
||||
|
||||
/// Interface for an overlay layer rendered on top of the composite map.
|
||||
class IOverlayLayer {
|
||||
public:
|
||||
virtual ~IOverlayLayer() = default;
|
||||
virtual void render(const LayerContext& ctx) = 0;
|
||||
};
|
||||
|
||||
/// Orchestrates rendering of all registered overlay layers.
|
||||
class OverlayRenderer {
|
||||
public:
|
||||
void addLayer(std::unique_ptr<IOverlayLayer> layer);
|
||||
void render(const LayerContext& ctx);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<IOverlayLayer>> layers_;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
65
include/rendering/world_map/view_state_machine.hpp
Normal file
65
include/rendering/world_map/view_state_machine.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// view_state_machine.hpp — Navigation state and transitions for the world map.
|
||||
// Extracted from WorldMap zoom/enter methods (Phase 6 of refactoring plan).
|
||||
// SRP — pure state machine, no rendering or input code.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
/// Manages the current view level and transitions between views.
|
||||
class ViewStateMachine {
|
||||
public:
|
||||
ViewLevel currentLevel() const { return level_; }
|
||||
const TransitionState& transition() const { return transition_; }
|
||||
|
||||
int continentIdx() const { return continentIdx_; }
|
||||
int currentZoneIdx() const { return currentIdx_; }
|
||||
bool cosmicEnabled() const { return cosmicEnabled_; }
|
||||
|
||||
void setContinentIdx(int idx) { continentIdx_ = idx; }
|
||||
void setCurrentZoneIdx(int idx) { currentIdx_ = idx; }
|
||||
void setCosmicEnabled(bool enabled) { cosmicEnabled_ = enabled; }
|
||||
void setLevel(ViewLevel level) { level_ = level; }
|
||||
|
||||
/// Result of a zoom/navigate operation.
|
||||
struct ZoomResult {
|
||||
bool changed = false;
|
||||
ViewLevel newLevel = ViewLevel::ZONE;
|
||||
int targetIdx = -1; // zone index to load/composite
|
||||
};
|
||||
|
||||
/// Attempt to zoom in. hoveredZoneIdx is the zone under the cursor (-1 if none).
|
||||
/// playerZoneIdx is the zone the player is standing in (-1 if none).
|
||||
ZoomResult zoomIn(int hoveredZoneIdx, int playerZoneIdx);
|
||||
|
||||
/// Attempt to zoom out one level.
|
||||
ZoomResult zoomOut();
|
||||
|
||||
/// Navigate to world view. Returns the root/fallback continent index to composite.
|
||||
ZoomResult enterWorldView();
|
||||
|
||||
/// Navigate to cosmic view.
|
||||
ZoomResult enterCosmicView();
|
||||
|
||||
/// Navigate directly into a zone from continent view.
|
||||
ZoomResult enterZone(int zoneIdx);
|
||||
|
||||
/// Advance transition animation. Returns true while animating.
|
||||
bool updateTransition(float deltaTime);
|
||||
|
||||
private:
|
||||
void startTransition(ViewLevel from, ViewLevel to, float duration = 0.3f);
|
||||
|
||||
ViewLevel level_ = ViewLevel::CONTINENT;
|
||||
TransitionState transition_;
|
||||
int continentIdx_ = -1;
|
||||
int currentIdx_ = -1;
|
||||
bool cosmicEnabled_ = true;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
64
include/rendering/world_map/world_map_facade.hpp
Normal file
64
include/rendering/world_map/world_map_facade.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// world_map_facade.hpp — Public API for the world map system.
|
||||
// Drop-in replacement for the monolithic WorldMap class (Phase 10 of refactoring plan).
|
||||
// Facade pattern — hides internal complexity behind the same public interface.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
class VkContext;
|
||||
}
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class WorldMapFacade {
|
||||
public:
|
||||
/// Backward-compatible alias for old WorldMap::QuestPoi usage.
|
||||
using QuestPoi = QuestPOI;
|
||||
|
||||
WorldMapFacade();
|
||||
~WorldMapFacade();
|
||||
|
||||
bool initialize(VkContext* ctx, pipeline::AssetManager* am);
|
||||
void shutdown();
|
||||
|
||||
/// Off-screen composite pass — call BEFORE the main render pass begins.
|
||||
void compositePass(VkCommandBuffer cmd);
|
||||
|
||||
/// ImGui overlay — call INSIDE the main render pass (during ImGui frame).
|
||||
void render(const glm::vec3& playerRenderPos,
|
||||
int screenWidth, int screenHeight,
|
||||
float playerYawDeg = 0.0f);
|
||||
|
||||
void setMapName(const std::string& name);
|
||||
void setServerExplorationMask(const std::vector<uint32_t>& masks, bool hasData);
|
||||
void setPartyDots(std::vector<PartyDot> dots);
|
||||
void setTaxiNodes(std::vector<TaxiNode> nodes);
|
||||
void setQuestPois(std::vector<QuestPOI> pois);
|
||||
void setCorpsePos(bool hasCorpse, glm::vec3 renderPos);
|
||||
|
||||
bool isOpen() const;
|
||||
void close();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
||||
// Backward-compatible alias for gradual migration
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
using WorldMap = world_map::WorldMapFacade;
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
132
include/rendering/world_map/world_map_types.hpp
Normal file
132
include/rendering/world_map/world_map_types.hpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// world_map_types.hpp — Vulkan-free domain types for the world map system.
|
||||
// Extracted from rendering/world_map.hpp (Phase 1 of refactoring plan).
|
||||
// Consumers of these types do NOT need Vulkan/VMA headers.
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
// ── View hierarchy ───────────────────────────────────────────
|
||||
|
||||
enum class ViewLevel { COSMIC, WORLD, CONTINENT, ZONE };
|
||||
|
||||
// ── Transition animation ─────────────────────────────────────
|
||||
|
||||
struct TransitionState {
|
||||
bool active = false;
|
||||
float progress = 0.0f; // 0.0 → 1.0
|
||||
float duration = 0.2f; // seconds
|
||||
ViewLevel fromLevel = ViewLevel::ZONE;
|
||||
ViewLevel toLevel = ViewLevel::ZONE;
|
||||
};
|
||||
|
||||
// ── Zone faction & metadata ──────────────────────────────────
|
||||
|
||||
enum class ZoneFaction : uint8_t { Neutral, Alliance, Horde, Contested };
|
||||
|
||||
struct ZoneMeta {
|
||||
uint8_t minLevel = 0, maxLevel = 0;
|
||||
ZoneFaction faction = ZoneFaction::Neutral;
|
||||
};
|
||||
|
||||
// ── Cosmic view (cross-realm) ────────────────────────────────
|
||||
|
||||
struct CosmicMapEntry {
|
||||
int mapId = 0;
|
||||
std::string label;
|
||||
// Clickable region in UV space (0-1 range on the cosmic composite)
|
||||
float uvLeft = 0, uvTop = 0, uvRight = 0, uvBottom = 0;
|
||||
};
|
||||
|
||||
// ── Zone bounds (shared between Zone and coordinate projection) ──
|
||||
|
||||
struct ZoneBounds {
|
||||
float locLeft = 0, locRight = 0;
|
||||
float locTop = 0, locBottom = 0;
|
||||
};
|
||||
|
||||
/// ZMP-derived bounding rectangle in display UV [0,1] coordinates.
|
||||
/// Computed by scanning the ZMP grid for each zone's area ID.
|
||||
/// Maps pixel-for-pixel to the continent map tiles shown on screen.
|
||||
struct ZmpRect {
|
||||
float uMin = 0, uMax = 0;
|
||||
float vMin = 0, vMax = 0;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
// ── Overlay entry (exploration overlay from WorldMapOverlay.dbc) ──
|
||||
|
||||
struct OverlayEntry {
|
||||
uint32_t areaIDs[4] = {}; // Up to 4 AreaTable IDs contributing to this overlay
|
||||
std::string textureName; // Texture prefix (e.g., "Goldshire")
|
||||
uint16_t texWidth = 0, texHeight = 0; // Overlay size in pixels
|
||||
uint16_t offsetX = 0, offsetY = 0; // Pixel offset within zone map
|
||||
int tileCols = 0, tileRows = 0;
|
||||
// HitRect from WorldMapOverlay.dbc fields 13-16 — fast AABB pre-filter for
|
||||
// subzone hover detection in zone view (avoids sampling every overlay).
|
||||
uint16_t hitRectLeft = 0, hitRectRight = 0;
|
||||
uint16_t hitRectTop = 0, hitRectBottom = 0;
|
||||
// NOTE: texture pointers are managed by CompositeRenderer, not stored here.
|
||||
bool tilesLoaded = false;
|
||||
};
|
||||
|
||||
// ── Zone (from WorldMapArea.dbc) ─────────────────────────────
|
||||
|
||||
struct Zone {
|
||||
uint32_t wmaID = 0;
|
||||
uint32_t areaID = 0; // 0 = continent level
|
||||
std::string areaName; // texture folder name (from DBC)
|
||||
ZoneBounds bounds;
|
||||
uint32_t displayMapID = 0;
|
||||
uint32_t parentWorldMapID = 0;
|
||||
std::vector<uint32_t> exploreBits; // all AreaBit indices (zone + subzones)
|
||||
std::vector<OverlayEntry> overlays;
|
||||
};
|
||||
|
||||
// ── Party member dot (UI layer → world map overlay) ──────────
|
||||
|
||||
struct PartyDot {
|
||||
glm::vec3 renderPos; ///< Position in render-space coordinates
|
||||
uint32_t color; ///< RGBA packed color (IM_COL32 format)
|
||||
std::string name; ///< Member name (shown as tooltip on hover)
|
||||
};
|
||||
|
||||
// ── Taxi (flight master) node (UI layer → world map overlay) ─
|
||||
|
||||
struct TaxiNode {
|
||||
uint32_t id = 0; ///< TaxiNodes.dbc ID
|
||||
uint32_t mapId = 0; ///< WoW internal map ID (0=EK,1=Kal,530=Outland,571=Northrend)
|
||||
float wowX = 0, wowY = 0, wowZ = 0; ///< Canonical WoW coordinates
|
||||
std::string name; ///< Node name (shown as tooltip)
|
||||
bool known = false; ///< Player has discovered this node
|
||||
};
|
||||
|
||||
// ── Area Point of Interest from AreaPOI.dbc ──────────────────
|
||||
|
||||
struct POI {
|
||||
uint32_t id = 0;
|
||||
uint32_t importance = 0; ///< 0=small, 1=medium, 2=large (capital)
|
||||
uint32_t iconType = 0; ///< Icon category from AreaPOI.dbc
|
||||
uint32_t factionId = 0; ///< 0=neutral, 67=Horde, 469=Alliance
|
||||
float wowX = 0, wowY = 0, wowZ = 0; ///< Canonical WoW coordinates
|
||||
uint32_t mapId = 0; ///< WoW internal map ID
|
||||
std::string name;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
// ── Quest POI marker (from SMSG_QUEST_POI_QUERY_RESPONSE) ────
|
||||
|
||||
struct QuestPOI {
|
||||
float wowX = 0, wowY = 0; ///< Canonical WoW coordinates (centroid)
|
||||
std::string name; ///< Quest title
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
37
include/rendering/world_map/zone_metadata.hpp
Normal file
37
include/rendering/world_map/zone_metadata.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// zone_metadata.hpp — Zone level ranges, faction data, and label formatting.
|
||||
// Extracted from WorldMap::initZoneMeta and inline label formatting
|
||||
// (Phase 4 of refactoring plan). DRY — formatLabel used by multiple layers.
|
||||
#pragma once
|
||||
|
||||
#include "rendering/world_map/world_map_types.hpp"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
namespace world_map {
|
||||
|
||||
class ZoneMetadata {
|
||||
public:
|
||||
/// Initialize the zone metadata table (level ranges, factions).
|
||||
void initialize();
|
||||
|
||||
/// Look up metadata for a zone by area name. Returns nullptr if not found.
|
||||
const ZoneMeta* find(const std::string& areaName) const;
|
||||
|
||||
/// Format a zone label with level range and faction tag.
|
||||
/// e.g. "Elwynn (1-10) [Alliance]"
|
||||
static std::string formatLabel(const std::string& areaName,
|
||||
const ZoneMeta* meta);
|
||||
|
||||
/// Format hover label with level range and bracket-tag for faction.
|
||||
static std::string formatHoverLabel(const std::string& areaName,
|
||||
const ZoneMeta* meta);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, ZoneMeta> table_;
|
||||
};
|
||||
|
||||
} // namespace world_map
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue