From 8b9d626aec355aad4e8e54c53f05183b95f03fb3 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 14:10:56 -0700 Subject: [PATCH] feat: show directional arrow on world map player marker Replace the static filled circle with a red triangle arrow that rotates to match the character's current facing direction. Uses the same render-space yaw convention as the 3D scene so the arrow matches in-world orientation. --- include/rendering/world_map.hpp | 6 ++++-- src/rendering/world_map.cpp | 23 ++++++++++++++++++----- src/ui/game_screen.cpp | 3 ++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/rendering/world_map.hpp b/include/rendering/world_map.hpp index 77a98ec0..e908ffaa 100644 --- a/include/rendering/world_map.hpp +++ b/include/rendering/world_map.hpp @@ -51,7 +51,8 @@ public: 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); + 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& masks, bool hasData); @@ -71,7 +72,8 @@ private: float& top, float& bottom) const; void loadZoneTextures(int zoneIdx); void requestComposite(int zoneIdx); - void renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWidth, int screenHeight); + void renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWidth, int screenHeight, + float playerYawDeg); void updateExploration(const glm::vec3& playerRenderPos); void zoomIn(const glm::vec3& playerRenderPos); void zoomOut(); diff --git a/src/rendering/world_map.cpp b/src/rendering/world_map.cpp index 7ee4f43a..8163628d 100644 --- a/src/rendering/world_map.cpp +++ b/src/rendering/world_map.cpp @@ -835,7 +835,8 @@ void WorldMap::zoomOut() { // Main render (input + ImGui overlay) // -------------------------------------------------------- -void WorldMap::render(const glm::vec3& playerRenderPos, int screenWidth, int screenHeight) { +void WorldMap::render(const glm::vec3& playerRenderPos, int screenWidth, int screenHeight, + float playerYawDeg) { if (!initialized || !assetManager) return; auto& input = core::Input::getInstance(); @@ -886,14 +887,14 @@ void WorldMap::render(const glm::vec3& playerRenderPos, int screenWidth, int scr } if (!open) return; - renderImGuiOverlay(playerRenderPos, screenWidth, screenHeight); + renderImGuiOverlay(playerRenderPos, screenWidth, screenHeight, playerYawDeg); } // -------------------------------------------------------- // ImGui overlay // -------------------------------------------------------- -void WorldMap::renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWidth, int screenHeight) { +void WorldMap::renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWidth, int screenHeight, float playerYawDeg) { float sw = static_cast(screenWidth); float sh = static_cast(screenHeight); @@ -1014,8 +1015,20 @@ void WorldMap::renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWi playerUV.y >= 0.0f && playerUV.y <= 1.0f) { float px = imgMin.x + playerUV.x * displayW; float py = imgMin.y + playerUV.y * displayH; - drawList->AddCircleFilled(ImVec2(px, py), 6.0f, IM_COL32(255, 40, 40, 255)); - drawList->AddCircle(ImVec2(px, py), 6.0f, IM_COL32(0, 0, 0, 200), 0, 2.0f); + // Directional arrow: render-space (cos,sin) maps to screen (-dx,-dy) + // because render+X=west=left and render+Y=north=up (screen Y is down). + float yawRad = glm::radians(playerYawDeg); + float adx = -std::cos(yawRad); // screen-space arrow X + float ady = -std::sin(yawRad); // screen-space arrow Y + float apx = -ady, apy = adx; // perpendicular (left/right of arrow) + constexpr float TIP = 9.0f; // tip distance from center + constexpr float TAIL = 4.0f; // tail distance from center + constexpr float HALF = 5.0f; // half base width + ImVec2 tip(px + adx * TIP, py + ady * TIP); + ImVec2 bl (px - adx * TAIL + apx * HALF, py - ady * TAIL + apy * HALF); + ImVec2 br (px - adx * TAIL - apx * HALF, py - ady * TAIL - apy * HALF); + drawList->AddTriangleFilled(tip, bl, br, IM_COL32(255, 40, 40, 255)); + drawList->AddTriangle(tip, bl, br, IM_COL32(0, 0, 0, 200), 1.5f); } } diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 0c815b94..b3ffdb75 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -6539,10 +6539,11 @@ void GameScreen::renderWorldMap(game::GameHandler& gameHandler) { } glm::vec3 playerPos = renderer->getCharacterPosition(); + float playerYaw = renderer->getCharacterYaw(); auto* window = app.getWindow(); int screenW = window ? window->getWidth() : 1280; int screenH = window ? window->getHeight() : 720; - wm->render(playerPos, screenW, screenH); + wm->render(playerPos, screenW, screenH, playerYaw); // Sync showWorldMap_ if the map closed itself (e.g. ESC key inside the overlay). if (!wm->isOpen()) showWorldMap_ = false;