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.
This commit is contained in:
Kelsi 2026-03-17 14:10:56 -07:00
parent b23dbc9ab7
commit 8b9d626aec
3 changed files with 24 additions and 8 deletions

View file

@ -51,7 +51,8 @@ public:
void compositePass(VkCommandBuffer cmd); void compositePass(VkCommandBuffer cmd);
/// ImGui overlay — call INSIDE the main render pass (during ImGui frame). /// 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 setMapName(const std::string& name);
void setServerExplorationMask(const std::vector<uint32_t>& masks, bool hasData); void setServerExplorationMask(const std::vector<uint32_t>& masks, bool hasData);
@ -71,7 +72,8 @@ private:
float& top, float& bottom) const; float& top, float& bottom) const;
void loadZoneTextures(int zoneIdx); void loadZoneTextures(int zoneIdx);
void requestComposite(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 updateExploration(const glm::vec3& playerRenderPos);
void zoomIn(const glm::vec3& playerRenderPos); void zoomIn(const glm::vec3& playerRenderPos);
void zoomOut(); void zoomOut();

View file

@ -835,7 +835,8 @@ void WorldMap::zoomOut() {
// Main render (input + ImGui overlay) // 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; if (!initialized || !assetManager) return;
auto& input = core::Input::getInstance(); auto& input = core::Input::getInstance();
@ -886,14 +887,14 @@ void WorldMap::render(const glm::vec3& playerRenderPos, int screenWidth, int scr
} }
if (!open) return; if (!open) return;
renderImGuiOverlay(playerRenderPos, screenWidth, screenHeight); renderImGuiOverlay(playerRenderPos, screenWidth, screenHeight, playerYawDeg);
} }
// -------------------------------------------------------- // --------------------------------------------------------
// ImGui overlay // 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<float>(screenWidth); float sw = static_cast<float>(screenWidth);
float sh = static_cast<float>(screenHeight); float sh = static_cast<float>(screenHeight);
@ -1014,8 +1015,20 @@ void WorldMap::renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWi
playerUV.y >= 0.0f && playerUV.y <= 1.0f) { playerUV.y >= 0.0f && playerUV.y <= 1.0f) {
float px = imgMin.x + playerUV.x * displayW; float px = imgMin.x + playerUV.x * displayW;
float py = imgMin.y + playerUV.y * displayH; float py = imgMin.y + playerUV.y * displayH;
drawList->AddCircleFilled(ImVec2(px, py), 6.0f, IM_COL32(255, 40, 40, 255)); // Directional arrow: render-space (cos,sin) maps to screen (-dx,-dy)
drawList->AddCircle(ImVec2(px, py), 6.0f, IM_COL32(0, 0, 0, 200), 0, 2.0f); // 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);
} }
} }

View file

@ -6539,10 +6539,11 @@ void GameScreen::renderWorldMap(game::GameHandler& gameHandler) {
} }
glm::vec3 playerPos = renderer->getCharacterPosition(); glm::vec3 playerPos = renderer->getCharacterPosition();
float playerYaw = renderer->getCharacterYaw();
auto* window = app.getWindow(); auto* window = app.getWindow();
int screenW = window ? window->getWidth() : 1280; int screenW = window ? window->getWidth() : 1280;
int screenH = window ? window->getHeight() : 720; 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). // Sync showWorldMap_ if the map closed itself (e.g. ESC key inside the overlay).
if (!wm->isOpen()) showWorldMap_ = false; if (!wm->isOpen()) showWorldMap_ = false;