2026-02-02 12:24:50 -08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <cstdint>
|
2026-02-10 19:53:23 -08:00
|
|
|
#include <vector>
|
2026-03-07 22:03:28 -08:00
|
|
|
#include <future>
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <glm/glm.hpp>
|
2026-02-21 19:41:21 -08:00
|
|
|
#include <vulkan/vulkan.h>
|
|
|
|
|
#include <vk_mem_alloc.h>
|
|
|
|
|
#include "rendering/vk_frame_data.hpp"
|
2026-03-07 22:03:28 -08:00
|
|
|
#include "rendering/vk_utils.hpp"
|
2026-02-21 19:41:21 -08:00
|
|
|
#include "rendering/sky_system.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace core { class Window; }
|
2026-02-21 19:41:21 -08:00
|
|
|
namespace rendering { class VkContext; }
|
2026-02-10 19:30:45 -08:00
|
|
|
namespace game { class World; class ZoneManager; class GameHandler; }
|
Implement comprehensive audio control panel with tabbed settings interface
Adds complete audio volume controls for all 11 audio systems with master volume. Reorganizes settings window into Video, Audio, and Gameplay tabs for better UX.
Audio Features:
- Master volume control affecting all audio systems
- Individual volume sliders for: Music, Ambient, UI, Combat, Spell, Movement, Footsteps, NPC Voices, Mounts, Activity sounds
- Real-time volume adjustment with master volume multiplier
- Restore defaults button per tab
Technical Changes:
- Added getVolumeScale() getters to all audio managers
- Integrated all 10 audio managers into renderer (UI, Combat, Spell, Movement added)
- Expanded game_screen.hpp with 11 pending volume variables
- Reorganized settings window using ImGui tab bars (Video/Audio/Gameplay)
- Audio settings uses scrollable child window for 11 volume controls
- Settings window expanded to 520x720px to accommodate comprehensive controls
2026-02-09 17:07:22 -08:00
|
|
|
namespace audio { class MusicManager; class FootstepManager; class ActivitySoundManager; class MountSoundManager; class NpcVoiceManager; class AmbientSoundManager; class UiSoundManager; class CombatSoundManager; class SpellSoundManager; class MovementSoundManager; enum class FootstepSurface : uint8_t; enum class VoiceType; }
|
2026-02-02 12:24:50 -08:00
|
|
|
namespace pipeline { class AssetManager; }
|
|
|
|
|
|
|
|
|
|
namespace rendering {
|
|
|
|
|
|
|
|
|
|
class Camera;
|
|
|
|
|
class CameraController;
|
|
|
|
|
class Scene;
|
|
|
|
|
class TerrainRenderer;
|
|
|
|
|
class TerrainManager;
|
|
|
|
|
class PerformanceHUD;
|
|
|
|
|
class WaterRenderer;
|
|
|
|
|
class Skybox;
|
|
|
|
|
class Celestial;
|
|
|
|
|
class StarField;
|
|
|
|
|
class Clouds;
|
|
|
|
|
class LensFlare;
|
|
|
|
|
class Weather;
|
2026-02-10 13:48:50 -08:00
|
|
|
class LightingManager;
|
2026-02-02 12:24:50 -08:00
|
|
|
class SwimEffects;
|
2026-02-09 01:24:17 -08:00
|
|
|
class MountDust;
|
2026-02-19 20:36:25 -08:00
|
|
|
class LevelUpEffect;
|
2026-02-19 21:13:13 -08:00
|
|
|
class ChargeEffect;
|
2026-02-02 12:24:50 -08:00
|
|
|
class CharacterRenderer;
|
|
|
|
|
class WMORenderer;
|
|
|
|
|
class M2Renderer;
|
|
|
|
|
class Minimap;
|
2026-02-21 19:41:21 -08:00
|
|
|
class WorldMap;
|
2026-02-09 23:41:38 -08:00
|
|
|
class QuestMarkerRenderer;
|
2026-02-22 05:58:45 -08:00
|
|
|
class CharacterPreview;
|
2026-02-03 20:40:59 -08:00
|
|
|
class Shader;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
class Renderer {
|
|
|
|
|
public:
|
|
|
|
|
Renderer();
|
|
|
|
|
~Renderer();
|
|
|
|
|
|
|
|
|
|
bool initialize(core::Window* window);
|
|
|
|
|
void shutdown();
|
|
|
|
|
|
|
|
|
|
void beginFrame();
|
|
|
|
|
void endFrame();
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
void renderWorld(game::World* world, game::GameHandler* gameHandler = nullptr);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update renderer (camera, etc.)
|
|
|
|
|
*/
|
|
|
|
|
void update(float deltaTime);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load test terrain for debugging
|
|
|
|
|
* @param assetManager Asset manager to load terrain data
|
|
|
|
|
* @param adtPath Path to ADT file (e.g., "World\\Maps\\Azeroth\\Azeroth_32_49.adt")
|
|
|
|
|
*/
|
|
|
|
|
bool loadTestTerrain(pipeline::AssetManager* assetManager, const std::string& adtPath);
|
|
|
|
|
|
2026-03-02 08:11:36 -08:00
|
|
|
/**
|
|
|
|
|
* Initialize all sub-renderers (WMO, M2, Character, terrain, water, minimap, etc.)
|
|
|
|
|
* without loading any ADT tile. Used by WMO-only maps (dungeons/raids/BGs).
|
|
|
|
|
*/
|
|
|
|
|
bool initializeRenderers(pipeline::AssetManager* assetManager, const std::string& mapName);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
/**
|
|
|
|
|
* Enable/disable terrain rendering
|
|
|
|
|
*/
|
|
|
|
|
void setTerrainEnabled(bool enabled) { terrainEnabled = enabled; }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enable/disable wireframe mode
|
|
|
|
|
*/
|
|
|
|
|
void setWireframeMode(bool enabled);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load terrain tiles around position
|
|
|
|
|
* @param mapName Map name (e.g., "Azeroth", "Kalimdor")
|
|
|
|
|
* @param centerX Center tile X coordinate
|
|
|
|
|
* @param centerY Center tile Y coordinate
|
|
|
|
|
* @param radius Load radius in tiles
|
|
|
|
|
*/
|
|
|
|
|
bool loadTerrainArea(const std::string& mapName, int centerX, int centerY, int radius = 1);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enable/disable terrain streaming
|
|
|
|
|
*/
|
|
|
|
|
void setTerrainStreaming(bool enabled);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render performance HUD
|
|
|
|
|
*/
|
|
|
|
|
void renderHUD();
|
|
|
|
|
|
|
|
|
|
Camera* getCamera() { return camera.get(); }
|
|
|
|
|
CameraController* getCameraController() { return cameraController.get(); }
|
|
|
|
|
Scene* getScene() { return scene.get(); }
|
|
|
|
|
TerrainRenderer* getTerrainRenderer() const { return terrainRenderer.get(); }
|
|
|
|
|
TerrainManager* getTerrainManager() const { return terrainManager.get(); }
|
|
|
|
|
PerformanceHUD* getPerformanceHUD() { return performanceHUD.get(); }
|
|
|
|
|
WaterRenderer* getWaterRenderer() const { return waterRenderer.get(); }
|
2026-02-21 19:41:21 -08:00
|
|
|
Skybox* getSkybox() const { return skySystem ? skySystem->getSkybox() : nullptr; }
|
|
|
|
|
Celestial* getCelestial() const { return skySystem ? skySystem->getCelestial() : nullptr; }
|
|
|
|
|
StarField* getStarField() const { return skySystem ? skySystem->getStarField() : nullptr; }
|
|
|
|
|
Clouds* getClouds() const { return skySystem ? skySystem->getClouds() : nullptr; }
|
|
|
|
|
LensFlare* getLensFlare() const { return skySystem ? skySystem->getLensFlare() : nullptr; }
|
2026-02-02 12:24:50 -08:00
|
|
|
Weather* getWeather() const { return weather.get(); }
|
|
|
|
|
CharacterRenderer* getCharacterRenderer() const { return characterRenderer.get(); }
|
|
|
|
|
WMORenderer* getWMORenderer() const { return wmoRenderer.get(); }
|
|
|
|
|
M2Renderer* getM2Renderer() const { return m2Renderer.get(); }
|
|
|
|
|
Minimap* getMinimap() const { return minimap.get(); }
|
2026-02-21 19:41:21 -08:00
|
|
|
WorldMap* getWorldMap() const { return worldMap.get(); }
|
2026-02-09 23:41:38 -08:00
|
|
|
QuestMarkerRenderer* getQuestMarkerRenderer() const { return questMarkerRenderer.get(); }
|
2026-02-10 19:30:45 -08:00
|
|
|
SkySystem* getSkySystem() const { return skySystem.get(); }
|
2026-02-02 12:24:50 -08:00
|
|
|
const std::string& getCurrentZoneName() const { return currentZoneName; }
|
2026-02-21 19:41:21 -08:00
|
|
|
VkContext* getVkContext() const { return vkCtx; }
|
|
|
|
|
VkDescriptorSetLayout getPerFrameSetLayout() const { return perFrameSetLayout; }
|
|
|
|
|
VkRenderPass getShadowRenderPass() const { return shadowRenderPass; }
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Third-person character follow
|
|
|
|
|
void setCharacterFollow(uint32_t instanceId);
|
|
|
|
|
glm::vec3& getCharacterPosition() { return characterPosition; }
|
|
|
|
|
uint32_t getCharacterInstanceId() const { return characterInstanceId; }
|
|
|
|
|
float getCharacterYaw() const { return characterYaw; }
|
2026-02-08 03:05:38 -08:00
|
|
|
void setCharacterYaw(float yawDeg) { characterYaw = yawDeg; }
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Emote support
|
|
|
|
|
void playEmote(const std::string& emoteName);
|
2026-02-19 20:36:25 -08:00
|
|
|
void triggerLevelUpEffect(const glm::vec3& position);
|
2026-02-02 12:24:50 -08:00
|
|
|
void cancelEmote();
|
|
|
|
|
bool isEmoteActive() const { return emoteActive; }
|
2026-02-07 20:02:14 -08:00
|
|
|
static std::string getEmoteText(const std::string& emoteName, const std::string* targetName = nullptr);
|
2026-02-14 14:30:09 -08:00
|
|
|
static uint32_t getEmoteDbcId(const std::string& emoteName);
|
2026-02-14 15:11:43 -08:00
|
|
|
static std::string getEmoteTextByDbcId(uint32_t dbcId, const std::string& senderName, const std::string* targetName = nullptr);
|
|
|
|
|
static uint32_t getEmoteAnimByDbcId(uint32_t dbcId);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Targeting support
|
|
|
|
|
void setTargetPosition(const glm::vec3* pos);
|
2026-02-07 13:56:58 -08:00
|
|
|
void setInCombat(bool combat) { inCombat_ = combat; }
|
2026-02-02 12:24:50 -08:00
|
|
|
bool isMoving() const;
|
2026-02-05 14:01:26 -08:00
|
|
|
void triggerMeleeSwing();
|
2026-02-06 15:41:29 -08:00
|
|
|
void setEquippedWeaponType(uint32_t inventoryType) { equippedWeaponInvType_ = inventoryType; meleeAnimId = 0; }
|
2026-02-19 21:13:13 -08:00
|
|
|
void setCharging(bool charging) { charging_ = charging; }
|
|
|
|
|
bool isCharging() const { return charging_; }
|
|
|
|
|
void startChargeEffect(const glm::vec3& position, const glm::vec3& direction);
|
|
|
|
|
void emitChargeEffect(const glm::vec3& position, const glm::vec3& direction);
|
|
|
|
|
void stopChargeEffect();
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-07 17:59:40 -08:00
|
|
|
// Mount rendering
|
Fix mount sounds, grey WMO meshes, taxi landing, tree animations, and classic dismount
- Per-family mount sounds (kodo, tallstrider, mechanostrider, etc.) detected from M2 model path
- Skip WMO groups with SHOW_SKYBOX flag or all-untextured batches (grey mesh in Orgrimmar)
- Freeze physics during taxi landing until terrain loads to prevent falling through void
- Disable bone animations on tropical vegetation (palm, bamboo, banana, etc.) to fix wiggling
- Snap player to final taxi waypoint on flight completion
- Extract mount aura spell ID from classic UNIT_FIELD_AURAS for CMSG_CANCEL_AURA dismount
- Increase /unstuck forward nudge to 5 units
2026-02-14 21:04:20 -08:00
|
|
|
void setMounted(uint32_t mountInstId, uint32_t mountDisplayId, float heightOffset, const std::string& modelPath = "");
|
2026-02-08 03:05:38 -08:00
|
|
|
void setTaxiFlight(bool onTaxi) { taxiFlight_ = onTaxi; }
|
2026-02-08 22:05:38 -08:00
|
|
|
void setMountPitchRoll(float pitch, float roll) { mountPitch_ = pitch; mountRoll_ = roll; }
|
2026-02-07 17:59:40 -08:00
|
|
|
void clearMount();
|
|
|
|
|
bool isMounted() const { return mountInstanceId_ != 0; }
|
|
|
|
|
|
2026-02-06 13:47:03 -08:00
|
|
|
// Selection circle for targeted entity
|
|
|
|
|
void setSelectionCircle(const glm::vec3& pos, float radius, const glm::vec3& color);
|
|
|
|
|
void clearSelectionCircle();
|
|
|
|
|
|
2026-02-03 16:21:48 -08:00
|
|
|
// CPU timing stats (milliseconds, last frame).
|
|
|
|
|
double getLastUpdateMs() const { return lastUpdateMs; }
|
|
|
|
|
double getLastRenderMs() const { return lastRenderMs; }
|
|
|
|
|
double getLastCameraUpdateMs() const { return lastCameraUpdateMs; }
|
|
|
|
|
double getLastTerrainRenderMs() const { return lastTerrainRenderMs; }
|
|
|
|
|
double getLastWMORenderMs() const { return lastWMORenderMs; }
|
|
|
|
|
double getLastM2RenderMs() const { return lastM2RenderMs; }
|
2026-02-05 15:44:42 -08:00
|
|
|
audio::MusicManager* getMusicManager() { return musicManager.get(); }
|
2026-02-17 16:26:49 -08:00
|
|
|
game::ZoneManager* getZoneManager() { return zoneManager.get(); }
|
2026-02-05 17:32:21 -08:00
|
|
|
audio::FootstepManager* getFootstepManager() { return footstepManager.get(); }
|
|
|
|
|
audio::ActivitySoundManager* getActivitySoundManager() { return activitySoundManager.get(); }
|
2026-02-09 01:04:53 -08:00
|
|
|
audio::MountSoundManager* getMountSoundManager() { return mountSoundManager.get(); }
|
2026-02-09 01:29:44 -08:00
|
|
|
audio::NpcVoiceManager* getNpcVoiceManager() { return npcVoiceManager.get(); }
|
2026-02-09 14:50:14 -08:00
|
|
|
audio::AmbientSoundManager* getAmbientSoundManager() { return ambientSoundManager.get(); }
|
Implement comprehensive audio control panel with tabbed settings interface
Adds complete audio volume controls for all 11 audio systems with master volume. Reorganizes settings window into Video, Audio, and Gameplay tabs for better UX.
Audio Features:
- Master volume control affecting all audio systems
- Individual volume sliders for: Music, Ambient, UI, Combat, Spell, Movement, Footsteps, NPC Voices, Mounts, Activity sounds
- Real-time volume adjustment with master volume multiplier
- Restore defaults button per tab
Technical Changes:
- Added getVolumeScale() getters to all audio managers
- Integrated all 10 audio managers into renderer (UI, Combat, Spell, Movement added)
- Expanded game_screen.hpp with 11 pending volume variables
- Reorganized settings window using ImGui tab bars (Video/Audio/Gameplay)
- Audio settings uses scrollable child window for 11 volume controls
- Settings window expanded to 520x720px to accommodate comprehensive controls
2026-02-09 17:07:22 -08:00
|
|
|
audio::UiSoundManager* getUiSoundManager() { return uiSoundManager.get(); }
|
|
|
|
|
audio::CombatSoundManager* getCombatSoundManager() { return combatSoundManager.get(); }
|
|
|
|
|
audio::SpellSoundManager* getSpellSoundManager() { return spellSoundManager.get(); }
|
|
|
|
|
audio::MovementSoundManager* getMovementSoundManager() { return movementSoundManager.get(); }
|
2026-02-10 13:48:50 -08:00
|
|
|
LightingManager* getLightingManager() { return lightingManager.get(); }
|
2026-02-03 16:21:48 -08:00
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
private:
|
2026-02-20 20:31:04 -08:00
|
|
|
void runDeferredWorldInitStep(float deltaTime);
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
core::Window* window = nullptr;
|
|
|
|
|
std::unique_ptr<Camera> camera;
|
|
|
|
|
std::unique_ptr<CameraController> cameraController;
|
|
|
|
|
std::unique_ptr<Scene> scene;
|
|
|
|
|
std::unique_ptr<TerrainRenderer> terrainRenderer;
|
|
|
|
|
std::unique_ptr<TerrainManager> terrainManager;
|
|
|
|
|
std::unique_ptr<PerformanceHUD> performanceHUD;
|
|
|
|
|
std::unique_ptr<WaterRenderer> waterRenderer;
|
|
|
|
|
std::unique_ptr<Skybox> skybox;
|
|
|
|
|
std::unique_ptr<Celestial> celestial;
|
|
|
|
|
std::unique_ptr<StarField> starField;
|
|
|
|
|
std::unique_ptr<Clouds> clouds;
|
|
|
|
|
std::unique_ptr<LensFlare> lensFlare;
|
|
|
|
|
std::unique_ptr<Weather> weather;
|
2026-02-10 13:48:50 -08:00
|
|
|
std::unique_ptr<LightingManager> lightingManager;
|
2026-02-10 19:30:45 -08:00
|
|
|
std::unique_ptr<SkySystem> skySystem; // Coordinator for sky rendering
|
2026-02-02 12:24:50 -08:00
|
|
|
std::unique_ptr<SwimEffects> swimEffects;
|
2026-02-09 01:24:17 -08:00
|
|
|
std::unique_ptr<MountDust> mountDust;
|
2026-02-19 20:36:25 -08:00
|
|
|
std::unique_ptr<LevelUpEffect> levelUpEffect;
|
2026-02-19 21:13:13 -08:00
|
|
|
std::unique_ptr<ChargeEffect> chargeEffect;
|
2026-02-02 12:24:50 -08:00
|
|
|
std::unique_ptr<CharacterRenderer> characterRenderer;
|
|
|
|
|
std::unique_ptr<WMORenderer> wmoRenderer;
|
|
|
|
|
std::unique_ptr<M2Renderer> m2Renderer;
|
|
|
|
|
std::unique_ptr<Minimap> minimap;
|
2026-02-21 19:41:21 -08:00
|
|
|
std::unique_ptr<WorldMap> worldMap;
|
2026-02-09 23:41:38 -08:00
|
|
|
std::unique_ptr<QuestMarkerRenderer> questMarkerRenderer;
|
2026-02-02 12:24:50 -08:00
|
|
|
std::unique_ptr<audio::MusicManager> musicManager;
|
2026-02-03 14:55:32 -08:00
|
|
|
std::unique_ptr<audio::FootstepManager> footstepManager;
|
2026-02-03 19:49:56 -08:00
|
|
|
std::unique_ptr<audio::ActivitySoundManager> activitySoundManager;
|
2026-02-09 01:04:53 -08:00
|
|
|
std::unique_ptr<audio::MountSoundManager> mountSoundManager;
|
2026-02-09 01:29:44 -08:00
|
|
|
std::unique_ptr<audio::NpcVoiceManager> npcVoiceManager;
|
2026-02-09 14:50:14 -08:00
|
|
|
std::unique_ptr<audio::AmbientSoundManager> ambientSoundManager;
|
Implement comprehensive audio control panel with tabbed settings interface
Adds complete audio volume controls for all 11 audio systems with master volume. Reorganizes settings window into Video, Audio, and Gameplay tabs for better UX.
Audio Features:
- Master volume control affecting all audio systems
- Individual volume sliders for: Music, Ambient, UI, Combat, Spell, Movement, Footsteps, NPC Voices, Mounts, Activity sounds
- Real-time volume adjustment with master volume multiplier
- Restore defaults button per tab
Technical Changes:
- Added getVolumeScale() getters to all audio managers
- Integrated all 10 audio managers into renderer (UI, Combat, Spell, Movement added)
- Expanded game_screen.hpp with 11 pending volume variables
- Reorganized settings window using ImGui tab bars (Video/Audio/Gameplay)
- Audio settings uses scrollable child window for 11 volume controls
- Settings window expanded to 520x720px to accommodate comprehensive controls
2026-02-09 17:07:22 -08:00
|
|
|
std::unique_ptr<audio::UiSoundManager> uiSoundManager;
|
|
|
|
|
std::unique_ptr<audio::CombatSoundManager> combatSoundManager;
|
|
|
|
|
std::unique_ptr<audio::SpellSoundManager> spellSoundManager;
|
|
|
|
|
std::unique_ptr<audio::MovementSoundManager> movementSoundManager;
|
2026-02-02 12:24:50 -08:00
|
|
|
std::unique_ptr<game::ZoneManager> zoneManager;
|
2026-02-21 19:41:21 -08:00
|
|
|
// Shadow mapping (Vulkan)
|
2026-02-23 04:59:39 -08:00
|
|
|
static constexpr uint32_t SHADOW_MAP_SIZE = 4096;
|
2026-02-21 19:41:21 -08:00
|
|
|
VkImage shadowDepthImage = VK_NULL_HANDLE;
|
|
|
|
|
VmaAllocation shadowDepthAlloc = VK_NULL_HANDLE;
|
|
|
|
|
VkImageView shadowDepthView = VK_NULL_HANDLE;
|
|
|
|
|
VkSampler shadowSampler = VK_NULL_HANDLE;
|
|
|
|
|
VkRenderPass shadowRenderPass = VK_NULL_HANDLE;
|
|
|
|
|
VkFramebuffer shadowFramebuffer = VK_NULL_HANDLE;
|
2026-02-22 10:23:20 -08:00
|
|
|
VkImageLayout shadowDepthLayout_ = VK_IMAGE_LAYOUT_UNDEFINED;
|
2026-02-04 16:08:35 -08:00
|
|
|
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
|
2026-02-04 16:22:18 -08:00
|
|
|
glm::vec3 shadowCenter = glm::vec3(0.0f);
|
|
|
|
|
bool shadowCenterInitialized = false;
|
2026-02-21 02:23:08 -08:00
|
|
|
bool shadowsEnabled = true;
|
2026-03-07 22:29:06 -08:00
|
|
|
float shadowDistance_ = 300.0f; // Shadow frustum half-extent (default: 300 units)
|
2026-02-25 10:22:05 -08:00
|
|
|
uint32_t shadowFrameCounter_ = 0;
|
2026-02-23 08:47:38 -08:00
|
|
|
|
2026-02-04 16:08:35 -08:00
|
|
|
|
2026-02-05 16:11:24 -08:00
|
|
|
public:
|
2026-02-22 05:58:45 -08:00
|
|
|
// Character preview registration (for off-screen composite pass)
|
|
|
|
|
void registerPreview(CharacterPreview* preview);
|
|
|
|
|
void unregisterPreview(CharacterPreview* preview);
|
|
|
|
|
|
2026-02-05 16:11:24 -08:00
|
|
|
void setShadowsEnabled(bool enabled) { shadowsEnabled = enabled; }
|
|
|
|
|
bool areShadowsEnabled() const { return shadowsEnabled; }
|
2026-03-07 22:29:06 -08:00
|
|
|
void setShadowDistance(float dist) { shadowDistance_ = glm::clamp(dist, 40.0f, 500.0f); }
|
2026-03-06 20:38:58 -08:00
|
|
|
float getShadowDistance() const { return shadowDistance_; }
|
2026-02-22 02:59:24 -08:00
|
|
|
void setMsaaSamples(VkSampleCountFlagBits samples);
|
2026-02-05 16:11:24 -08:00
|
|
|
|
2026-03-07 23:13:01 -08:00
|
|
|
// FSR (FidelityFX Super Resolution) upscaling
|
2026-03-07 22:03:28 -08:00
|
|
|
void setFSREnabled(bool enabled);
|
|
|
|
|
bool isFSREnabled() const { return fsr_.enabled; }
|
|
|
|
|
void setFSRQuality(float scaleFactor); // 0.50=Perf, 0.59=Balanced, 0.67=Quality, 0.77=UltraQuality
|
|
|
|
|
void setFSRSharpness(float sharpness); // 0.0 - 2.0
|
|
|
|
|
float getFSRScaleFactor() const { return fsr_.scaleFactor; }
|
|
|
|
|
float getFSRSharpness() const { return fsr_.sharpness; }
|
2026-03-07 23:13:01 -08:00
|
|
|
void setFSR2Enabled(bool enabled);
|
|
|
|
|
bool isFSR2Enabled() const { return fsr2_.enabled; }
|
2026-03-08 19:33:07 -07:00
|
|
|
#if WOWEE_HAS_AMD_FSR2
|
|
|
|
|
bool isAmdFsr2SdkAvailable() const { return true; }
|
|
|
|
|
#else
|
|
|
|
|
bool isAmdFsr2SdkAvailable() const { return false; }
|
|
|
|
|
#endif
|
2026-03-07 22:03:28 -08:00
|
|
|
|
2026-03-06 19:15:34 -08:00
|
|
|
void setWaterRefractionEnabled(bool enabled);
|
|
|
|
|
bool isWaterRefractionEnabled() const;
|
|
|
|
|
|
2026-02-05 16:11:24 -08:00
|
|
|
private:
|
2026-02-22 03:37:47 -08:00
|
|
|
void applyMsaaChange();
|
|
|
|
|
VkSampleCountFlagBits pendingMsaaSamples_ = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
|
bool msaaChangePending_ = false;
|
2026-02-04 16:08:35 -08:00
|
|
|
void renderShadowPass();
|
|
|
|
|
glm::mat4 computeLightSpaceMatrix();
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
pipeline::AssetManager* cachedAssetManager = nullptr;
|
|
|
|
|
uint32_t currentZoneId = 0;
|
|
|
|
|
std::string currentZoneName;
|
2026-02-09 01:39:12 -08:00
|
|
|
bool inTavern_ = false;
|
2026-02-09 15:21:07 -08:00
|
|
|
bool inBlacksmith_ = false;
|
2026-02-11 22:27:02 -08:00
|
|
|
float musicSwitchCooldown_ = 0.0f;
|
2026-02-20 20:31:04 -08:00
|
|
|
bool deferredWorldInitEnabled_ = true;
|
|
|
|
|
bool deferredWorldInitPending_ = false;
|
|
|
|
|
uint8_t deferredWorldInitStage_ = 0;
|
|
|
|
|
float deferredWorldInitCooldown_ = 0.0f;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Third-person character state
|
|
|
|
|
glm::vec3 characterPosition = glm::vec3(0.0f);
|
|
|
|
|
uint32_t characterInstanceId = 0;
|
|
|
|
|
float characterYaw = 0.0f;
|
|
|
|
|
|
|
|
|
|
// Character animation state
|
2026-02-19 21:50:32 -08:00
|
|
|
enum class CharAnimState { IDLE, WALK, RUN, JUMP_START, JUMP_MID, JUMP_END, SIT_DOWN, SITTING, EMOTE, SWIM_IDLE, SWIM, MELEE_SWING, MOUNT, CHARGE, COMBAT_IDLE };
|
2026-02-02 12:24:50 -08:00
|
|
|
CharAnimState charAnimState = CharAnimState::IDLE;
|
|
|
|
|
void updateCharacterAnimation();
|
2026-02-03 14:55:32 -08:00
|
|
|
bool isFootstepAnimationState() const;
|
|
|
|
|
bool shouldTriggerFootstepEvent(uint32_t animationId, float animationTimeMs, float animationDurationMs);
|
|
|
|
|
audio::FootstepSurface resolveFootstepSurface() const;
|
2026-02-05 14:01:26 -08:00
|
|
|
uint32_t resolveMeleeAnimId();
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Emote state
|
|
|
|
|
bool emoteActive = false;
|
|
|
|
|
uint32_t emoteAnimId = 0;
|
|
|
|
|
bool emoteLoop = false;
|
|
|
|
|
|
|
|
|
|
// Target facing
|
|
|
|
|
const glm::vec3* targetPosition = nullptr;
|
2026-02-07 13:56:58 -08:00
|
|
|
bool inCombat_ = false;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Selection circle rendering (Vulkan)
|
|
|
|
|
VkPipeline selCirclePipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipelineLayout selCirclePipelineLayout = VK_NULL_HANDLE;
|
|
|
|
|
::VkBuffer selCircleVertBuf = VK_NULL_HANDLE;
|
|
|
|
|
VmaAllocation selCircleVertAlloc = VK_NULL_HANDLE;
|
|
|
|
|
::VkBuffer selCircleIdxBuf = VK_NULL_HANDLE;
|
|
|
|
|
VmaAllocation selCircleIdxAlloc = VK_NULL_HANDLE;
|
2026-02-06 13:47:03 -08:00
|
|
|
int selCircleVertCount = 0;
|
|
|
|
|
void initSelectionCircle();
|
2026-03-07 22:03:28 -08:00
|
|
|
void renderSelectionCircle(const glm::mat4& view, const glm::mat4& projection, VkCommandBuffer overrideCmd = VK_NULL_HANDLE);
|
2026-02-06 13:47:03 -08:00
|
|
|
glm::vec3 selCirclePos{0.0f};
|
|
|
|
|
glm::vec3 selCircleColor{1.0f, 0.0f, 0.0f};
|
|
|
|
|
float selCircleRadius = 1.5f;
|
|
|
|
|
bool selCircleVisible = false;
|
|
|
|
|
|
2026-02-21 20:01:01 -08:00
|
|
|
// Fullscreen color overlay (underwater tint)
|
|
|
|
|
VkPipeline overlayPipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipelineLayout overlayPipelineLayout = VK_NULL_HANDLE;
|
|
|
|
|
void initOverlayPipeline();
|
2026-03-07 22:03:28 -08:00
|
|
|
void renderOverlay(const glm::vec4& color, VkCommandBuffer overrideCmd = VK_NULL_HANDLE);
|
|
|
|
|
|
|
|
|
|
// FSR 1.0 upscaling state
|
|
|
|
|
struct FSRState {
|
|
|
|
|
bool enabled = false;
|
|
|
|
|
bool needsRecreate = false;
|
|
|
|
|
float scaleFactor = 0.77f; // Ultra Quality default
|
|
|
|
|
float sharpness = 0.5f;
|
|
|
|
|
uint32_t internalWidth = 0;
|
|
|
|
|
uint32_t internalHeight = 0;
|
|
|
|
|
|
|
|
|
|
// Off-screen scene target (reduced resolution)
|
|
|
|
|
AllocatedImage sceneColor{}; // 1x color (non-MSAA render target / MSAA resolve target)
|
|
|
|
|
AllocatedImage sceneDepth{}; // Depth (matches current MSAA sample count)
|
|
|
|
|
AllocatedImage sceneMsaaColor{}; // MSAA color target (only when MSAA > 1x)
|
|
|
|
|
AllocatedImage sceneDepthResolve{}; // Depth resolve (only when MSAA + depth resolve)
|
|
|
|
|
VkFramebuffer sceneFramebuffer = VK_NULL_HANDLE;
|
|
|
|
|
VkSampler sceneSampler = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
// Upscale pipeline
|
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSetLayout descSetLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorPool descPool = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSet descSet = VK_NULL_HANDLE;
|
|
|
|
|
};
|
|
|
|
|
FSRState fsr_;
|
|
|
|
|
bool initFSRResources();
|
|
|
|
|
void destroyFSRResources();
|
|
|
|
|
void renderFSRUpscale();
|
2026-02-21 20:01:01 -08:00
|
|
|
|
2026-03-07 23:13:01 -08:00
|
|
|
// FSR 2.2 temporal upscaling state
|
|
|
|
|
struct FSR2State {
|
|
|
|
|
bool enabled = false;
|
|
|
|
|
bool needsRecreate = false;
|
|
|
|
|
float scaleFactor = 0.77f;
|
2026-03-08 18:52:04 -07:00
|
|
|
float sharpness = 3.0f; // Very strong RCAS to counteract upscale softness
|
2026-03-07 23:13:01 -08:00
|
|
|
uint32_t internalWidth = 0;
|
|
|
|
|
uint32_t internalHeight = 0;
|
|
|
|
|
|
|
|
|
|
// Off-screen scene targets (internal resolution, no MSAA — FSR2 replaces AA)
|
|
|
|
|
AllocatedImage sceneColor{};
|
|
|
|
|
AllocatedImage sceneDepth{};
|
|
|
|
|
VkFramebuffer sceneFramebuffer = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
// Samplers
|
|
|
|
|
VkSampler linearSampler = VK_NULL_HANDLE; // For color
|
|
|
|
|
VkSampler nearestSampler = VK_NULL_HANDLE; // For depth / motion vectors
|
|
|
|
|
|
|
|
|
|
// Motion vector buffer (internal resolution)
|
|
|
|
|
AllocatedImage motionVectors{};
|
|
|
|
|
|
|
|
|
|
// History buffers (display resolution, ping-pong)
|
|
|
|
|
AllocatedImage history[2]{};
|
|
|
|
|
uint32_t currentHistory = 0; // Output index (0 or 1)
|
|
|
|
|
|
|
|
|
|
// Compute pipelines
|
|
|
|
|
VkPipeline motionVecPipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipelineLayout motionVecPipelineLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSetLayout motionVecDescSetLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorPool motionVecDescPool = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSet motionVecDescSet = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
|
|
VkPipeline accumulatePipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipelineLayout accumulatePipelineLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSetLayout accumulateDescSetLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorPool accumulateDescPool = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSet accumulateDescSets[2] = {}; // Per ping-pong
|
|
|
|
|
|
|
|
|
|
// RCAS sharpening pass (display resolution)
|
|
|
|
|
VkPipeline sharpenPipeline = VK_NULL_HANDLE;
|
|
|
|
|
VkPipelineLayout sharpenPipelineLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSetLayout sharpenDescSetLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorPool sharpenDescPool = VK_NULL_HANDLE;
|
2026-03-08 01:22:15 -08:00
|
|
|
VkDescriptorSet sharpenDescSets[2] = {};
|
2026-03-07 23:13:01 -08:00
|
|
|
|
|
|
|
|
// Previous frame state for motion vector reprojection
|
|
|
|
|
glm::mat4 prevViewProjection = glm::mat4(1.0f);
|
|
|
|
|
glm::vec2 prevJitter = glm::vec2(0.0f);
|
|
|
|
|
uint32_t frameIndex = 0;
|
|
|
|
|
bool needsHistoryReset = true;
|
2026-03-08 18:52:04 -07:00
|
|
|
|
|
|
|
|
// Convergent accumulation: jitter for N frames then freeze
|
|
|
|
|
int convergenceFrame = 0;
|
|
|
|
|
static constexpr int convergenceMaxFrames = 8;
|
|
|
|
|
glm::mat4 lastStableVP = glm::mat4(1.0f);
|
2026-03-07 23:13:01 -08:00
|
|
|
};
|
|
|
|
|
FSR2State fsr2_;
|
|
|
|
|
bool initFSR2Resources();
|
|
|
|
|
void destroyFSR2Resources();
|
|
|
|
|
void dispatchMotionVectors();
|
|
|
|
|
void dispatchTemporalAccumulate();
|
|
|
|
|
void renderFSR2Sharpen();
|
|
|
|
|
static float halton(uint32_t index, uint32_t base);
|
|
|
|
|
|
2026-02-03 14:55:32 -08:00
|
|
|
// Footstep event tracking (animation-driven)
|
|
|
|
|
uint32_t footstepLastAnimationId = 0;
|
|
|
|
|
float footstepLastNormTime = 0.0f;
|
|
|
|
|
bool footstepNormInitialized = false;
|
2026-02-07 18:38:36 -08:00
|
|
|
|
2026-02-09 00:40:50 -08:00
|
|
|
// Footstep surface cache (avoid expensive queries every step)
|
|
|
|
|
mutable audio::FootstepSurface cachedFootstepSurface{};
|
|
|
|
|
mutable glm::vec3 cachedFootstepPosition{0.0f, 0.0f, 0.0f};
|
|
|
|
|
mutable float cachedFootstepUpdateTimer{999.0f}; // Force initial query
|
|
|
|
|
|
2026-02-07 18:38:36 -08:00
|
|
|
// Mount footstep tracking (separate from player's)
|
|
|
|
|
uint32_t mountFootstepLastAnimId = 0;
|
|
|
|
|
float mountFootstepLastNormTime = 0.0f;
|
|
|
|
|
bool mountFootstepNormInitialized = false;
|
2026-02-03 19:49:56 -08:00
|
|
|
bool sfxStateInitialized = false;
|
|
|
|
|
bool sfxPrevGrounded = true;
|
|
|
|
|
bool sfxPrevJumping = false;
|
|
|
|
|
bool sfxPrevFalling = false;
|
|
|
|
|
bool sfxPrevSwimming = false;
|
2026-02-03 14:55:32 -08:00
|
|
|
|
2026-02-19 21:13:13 -08:00
|
|
|
bool charging_ = false;
|
2026-02-05 14:01:26 -08:00
|
|
|
float meleeSwingTimer = 0.0f;
|
|
|
|
|
float meleeSwingCooldown = 0.0f;
|
|
|
|
|
float meleeAnimDurationMs = 0.0f;
|
|
|
|
|
uint32_t meleeAnimId = 0;
|
2026-02-06 15:41:29 -08:00
|
|
|
uint32_t equippedWeaponInvType_ = 0;
|
2026-02-05 14:01:26 -08:00
|
|
|
|
2026-02-07 17:59:40 -08:00
|
|
|
// Mount state
|
2026-02-10 19:30:45 -08:00
|
|
|
// Mount animation capabilities (discovered at mount time, varies per model)
|
|
|
|
|
struct MountAnimSet {
|
|
|
|
|
uint32_t jumpStart = 0; // Jump start animation
|
|
|
|
|
uint32_t jumpLoop = 0; // Jump airborne loop
|
|
|
|
|
uint32_t jumpEnd = 0; // Jump landing
|
|
|
|
|
uint32_t rearUp = 0; // Rear-up / special flourish
|
|
|
|
|
uint32_t run = 0; // Run animation (discovered, don't assume)
|
|
|
|
|
uint32_t stand = 0; // Stand animation (discovered)
|
2026-02-10 19:53:23 -08:00
|
|
|
std::vector<uint32_t> fidgets; // Idle fidget animations (head turn, tail swish, etc.)
|
2026-02-10 19:30:45 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum class MountAction { None, Jump, RearUp };
|
|
|
|
|
|
2026-02-07 17:59:40 -08:00
|
|
|
uint32_t mountInstanceId_ = 0;
|
|
|
|
|
float mountHeightOffset_ = 0.0f;
|
2026-02-08 22:05:38 -08:00
|
|
|
float mountPitch_ = 0.0f; // Up/down tilt (radians)
|
|
|
|
|
float mountRoll_ = 0.0f; // Left/right banking (radians)
|
2026-02-11 18:25:04 -08:00
|
|
|
int mountSeatAttachmentId_ = -1; // -1 unknown, -2 unavailable
|
|
|
|
|
glm::vec3 smoothedMountSeatPos_ = glm::vec3(0.0f);
|
|
|
|
|
bool mountSeatSmoothingInit_ = false;
|
2026-02-10 19:30:45 -08:00
|
|
|
float prevMountYaw_ = 0.0f; // Previous yaw for turn rate calculation (procedural lean)
|
|
|
|
|
float lastDeltaTime_ = 0.0f; // Cached for use in updateCharacterAnimation()
|
|
|
|
|
MountAction mountAction_ = MountAction::None; // Current mount action (jump/rear-up)
|
|
|
|
|
uint32_t mountActionPhase_ = 0; // 0=start, 1=loop, 2=end (for jump chaining)
|
|
|
|
|
MountAnimSet mountAnims_; // Cached animation IDs for current mount
|
2026-02-10 19:53:23 -08:00
|
|
|
float mountIdleFidgetTimer_ = 0.0f; // Timer for random idle fidgets
|
|
|
|
|
float mountIdleSoundTimer_ = 0.0f; // Timer for ambient idle sounds
|
2026-02-10 19:59:01 -08:00
|
|
|
uint32_t mountActiveFidget_ = 0; // Currently playing fidget animation ID (0 = none)
|
2026-02-08 03:05:38 -08:00
|
|
|
bool taxiFlight_ = false;
|
2026-02-17 02:23:41 -08:00
|
|
|
bool taxiAnimsLogged_ = false;
|
2026-02-07 17:59:40 -08:00
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Vulkan frame state
|
|
|
|
|
VkContext* vkCtx = nullptr;
|
|
|
|
|
VkCommandBuffer currentCmd = VK_NULL_HANDLE;
|
|
|
|
|
uint32_t currentImageIndex = 0;
|
|
|
|
|
|
|
|
|
|
// Per-frame UBO + descriptors (set 0)
|
|
|
|
|
static constexpr uint32_t MAX_FRAMES = 2;
|
|
|
|
|
VkDescriptorSetLayout perFrameSetLayout = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorPool sceneDescriptorPool = VK_NULL_HANDLE;
|
|
|
|
|
VkDescriptorSet perFrameDescSets[MAX_FRAMES] = {};
|
|
|
|
|
VkBuffer perFrameUBOs[MAX_FRAMES] = {};
|
|
|
|
|
VmaAllocation perFrameUBOAllocs[MAX_FRAMES] = {};
|
|
|
|
|
void* perFrameUBOMapped[MAX_FRAMES] = {};
|
|
|
|
|
GPUPerFrameData currentFrameData{};
|
|
|
|
|
float globalTime = 0.0f;
|
|
|
|
|
|
2026-02-22 22:34:48 -08:00
|
|
|
// Per-frame reflection UBO (mirrors camera for planar reflections)
|
|
|
|
|
VkBuffer reflPerFrameUBO = VK_NULL_HANDLE;
|
|
|
|
|
VmaAllocation reflPerFrameUBOAlloc = VK_NULL_HANDLE;
|
|
|
|
|
void* reflPerFrameUBOMapped = nullptr;
|
|
|
|
|
VkDescriptorSet reflPerFrameDescSet = VK_NULL_HANDLE;
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
bool createPerFrameResources();
|
|
|
|
|
void destroyPerFrameResources();
|
|
|
|
|
void updatePerFrameUBO();
|
2026-02-22 22:34:48 -08:00
|
|
|
void setupWater1xPass();
|
|
|
|
|
void renderReflectionPass();
|
2026-02-21 19:41:21 -08:00
|
|
|
|
2026-03-07 22:03:28 -08:00
|
|
|
// ── Multithreaded secondary command buffer recording ──
|
|
|
|
|
// Indices into secondaryCmds_ arrays
|
|
|
|
|
static constexpr uint32_t SEC_SKY = 0; // sky (main thread)
|
|
|
|
|
static constexpr uint32_t SEC_TERRAIN = 1; // terrain (worker 0)
|
|
|
|
|
static constexpr uint32_t SEC_WMO = 2; // WMO (worker 1)
|
|
|
|
|
static constexpr uint32_t SEC_CHARS = 3; // selection circle + characters (main thread)
|
|
|
|
|
static constexpr uint32_t SEC_M2 = 4; // M2 + particles + glow (worker 2)
|
|
|
|
|
static constexpr uint32_t SEC_POST = 5; // water + weather + effects (main thread)
|
|
|
|
|
static constexpr uint32_t SEC_IMGUI = 6; // ImGui (main thread, non-FSR only)
|
|
|
|
|
static constexpr uint32_t NUM_SECONDARIES = 7;
|
|
|
|
|
static constexpr uint32_t NUM_WORKERS = 3; // terrain, WMO, M2
|
|
|
|
|
|
|
|
|
|
// Per-worker command pools (thread-safe: one pool per thread)
|
|
|
|
|
VkCommandPool workerCmdPools_[NUM_WORKERS] = {};
|
|
|
|
|
// Main-thread command pool for its secondary buffers
|
|
|
|
|
VkCommandPool mainSecondaryCmdPool_ = VK_NULL_HANDLE;
|
|
|
|
|
// Pre-allocated secondary command buffers [secondaryIndex][frameInFlight]
|
|
|
|
|
VkCommandBuffer secondaryCmds_[NUM_SECONDARIES][MAX_FRAMES] = {};
|
|
|
|
|
|
|
|
|
|
bool parallelRecordingEnabled_ = false; // set true after pools/buffers created
|
|
|
|
|
bool createSecondaryCommandResources();
|
|
|
|
|
void destroySecondaryCommandResources();
|
|
|
|
|
VkCommandBuffer beginSecondary(uint32_t secondaryIndex);
|
|
|
|
|
void setSecondaryViewportScissor(VkCommandBuffer cmd);
|
|
|
|
|
|
|
|
|
|
// Cached render pass state for secondary buffer inheritance
|
|
|
|
|
VkRenderPass activeRenderPass_ = VK_NULL_HANDLE;
|
|
|
|
|
VkFramebuffer activeFramebuffer_ = VK_NULL_HANDLE;
|
|
|
|
|
VkExtent2D activeRenderExtent_ = {0, 0};
|
|
|
|
|
|
2026-02-22 05:58:45 -08:00
|
|
|
// Active character previews for off-screen rendering
|
|
|
|
|
std::vector<CharacterPreview*> activePreviews_;
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
bool terrainEnabled = true;
|
|
|
|
|
bool terrainLoaded = false;
|
2026-02-03 16:21:48 -08:00
|
|
|
|
|
|
|
|
// CPU timing stats (last frame/update).
|
|
|
|
|
double lastUpdateMs = 0.0;
|
|
|
|
|
double lastRenderMs = 0.0;
|
|
|
|
|
double lastCameraUpdateMs = 0.0;
|
|
|
|
|
double lastTerrainRenderMs = 0.0;
|
|
|
|
|
double lastWMORenderMs = 0.0;
|
|
|
|
|
double lastM2RenderMs = 0.0;
|
2026-02-02 12:24:50 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace rendering
|
|
|
|
|
} // namespace wowee
|