Implement WoW-style 3D billboard quest markers

Replace 2D ImGui text markers with proper 3D billboard sprites using BLP textures.

Features:
- Billboard rendering using Interface\GossipFrame\ BLP textures (yellow !, yellow ?, grey ?)
- WoW-style visual effects: bob animation, distance-based scaling, glow pass, distance fade
- Proper NPC height positioning with bounding box detection
- Camera-facing quads with depth testing but no depth write
- Shader-based alpha modulation for glow and fade effects

Technical changes:
- Created QuestMarkerRenderer class with billboard sprite system
- Integrated into Renderer initialization for both online and offline terrain loading
- Rewrote updateQuestMarkers() to use billboard system instead of M2 models
- Disabled old 2D ImGui renderQuestMarkers() in game_screen.cpp
- Added debug logging for initialization and marker tracking

Quest markers now render with proper WoW visual fidelity.
This commit is contained in:
Kelsi 2026-02-09 23:41:38 -08:00
parent 084a79a6bc
commit 71d14b77c9
8 changed files with 407 additions and 63 deletions

View file

@ -18,6 +18,7 @@
#include "rendering/wmo_renderer.hpp"
#include "rendering/m2_renderer.hpp"
#include "rendering/minimap.hpp"
#include "rendering/quest_marker_renderer.hpp"
#include "rendering/shader.hpp"
#include "pipeline/m2_loader.hpp"
#include <algorithm>
@ -330,6 +331,9 @@ bool Renderer::initialize(core::Window* win) {
minimap.reset();
}
// Create quest marker renderer (initialized later with AssetManager)
questMarkerRenderer = std::make_unique<QuestMarkerRenderer>();
// Create M2 renderer (for doodads)
m2Renderer = std::make_unique<M2Renderer>();
// Note: M2 renderer needs asset manager, will be initialized when terrain loads
@ -1871,6 +1875,11 @@ void Renderer::renderWorld(game::World* world) {
waterRenderer->render(*camera, time);
}
// Render quest markers (billboards above NPCs)
if (questMarkerRenderer && camera) {
questMarkerRenderer->render(*camera);
}
// Full-screen underwater tint so WMO/M2/characters also feel submerged.
if (false && underwater && underwaterOverlayShader && underwaterOverlayVAO) {
glDisable(GL_DEPTH_TEST);
@ -2224,6 +2233,9 @@ bool Renderer::loadTestTerrain(pipeline::AssetManager* assetManager, const std::
if (movementSoundManager) {
movementSoundManager->initialize(assetManager);
}
if (questMarkerRenderer) {
questMarkerRenderer->initialize(assetManager);
}
cachedAssetManager = assetManager;
}
@ -2323,6 +2335,9 @@ bool Renderer::loadTerrainArea(const std::string& mapName, int centerX, int cent
if (movementSoundManager && cachedAssetManager) {
movementSoundManager->initialize(cachedAssetManager);
}
if (questMarkerRenderer && cachedAssetManager) {
questMarkerRenderer->initialize(cachedAssetManager);
}
// Wire ambient sound manager to terrain manager for emitter registration
if (terrainManager && ambientSoundManager) {