mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Vulcan Nightmare
Experimentally bringing up vulcan support
This commit is contained in:
parent
863a786c48
commit
83b576e8d9
189 changed files with 12147 additions and 7820 deletions
|
|
@ -3,8 +3,11 @@
|
|||
#include <string>
|
||||
#include <memory>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering { class VkContext; }
|
||||
|
||||
namespace core {
|
||||
|
||||
struct WindowConfig {
|
||||
|
|
@ -27,7 +30,7 @@ public:
|
|||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
void swapBuffers();
|
||||
void swapBuffers() {} // No-op: Vulkan presents in Renderer::endFrame()
|
||||
void pollEvents();
|
||||
|
||||
bool shouldClose() const { return shouldCloseFlag; }
|
||||
|
|
@ -44,12 +47,14 @@ public:
|
|||
void applyResolution(int w, int h);
|
||||
|
||||
SDL_Window* getSDLWindow() const { return window; }
|
||||
SDL_GLContext getGLContext() const { return glContext; }
|
||||
|
||||
// Vulkan context access
|
||||
rendering::VkContext* getVkContext() const { return vkContext.get(); }
|
||||
|
||||
private:
|
||||
WindowConfig config;
|
||||
SDL_Window* window = nullptr;
|
||||
SDL_GLContext glContext = nullptr;
|
||||
std::unique_ptr<rendering::VkContext> vkContext;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
|
|
|||
|
|
@ -1,142 +1,132 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* Celestial body renderer
|
||||
* Celestial body renderer (Vulkan)
|
||||
*
|
||||
* Renders sun and moon that move across the sky based on time of day.
|
||||
* Sun rises at dawn, sets at dusk. Moon is visible at night.
|
||||
*
|
||||
* Pipeline layout:
|
||||
* set 0 = perFrameLayout (camera UBO — view, projection, etc.)
|
||||
* push = CelestialPush (mat4 model + vec4 celestialColor + float intensity
|
||||
* + float moonPhase + float animTime = 96 bytes)
|
||||
*/
|
||||
class Celestial {
|
||||
public:
|
||||
Celestial();
|
||||
~Celestial();
|
||||
|
||||
bool initialize();
|
||||
/**
|
||||
* Initialize the renderer.
|
||||
* @param ctx Vulkan context
|
||||
* @param perFrameLayout Descriptor set layout for set 0 (camera UBO)
|
||||
*/
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Render celestial bodies (sun and moon)
|
||||
* @param camera Camera for view matrix
|
||||
* @param timeOfDay Time of day in hours (0-24)
|
||||
* @param sunDir Optional sun direction from lighting system (normalized)
|
||||
* @param sunColor Optional sun color from lighting system
|
||||
* @param gameTime Optional server game time in seconds (for deterministic moon phases)
|
||||
* Render celestial bodies (sun and moons).
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0, camera UBO)
|
||||
* @param timeOfDay Time of day in hours (0-24)
|
||||
* @param sunDir Optional sun direction from lighting system (normalized)
|
||||
* @param sunColor Optional sun colour from lighting system
|
||||
* @param gameTime Optional server game time in seconds (deterministic moon phases)
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay,
|
||||
const glm::vec3* sunDir = nullptr,
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||
float timeOfDay,
|
||||
const glm::vec3* sunDir = nullptr,
|
||||
const glm::vec3* sunColor = nullptr,
|
||||
float gameTime = -1.0f);
|
||||
|
||||
/**
|
||||
* Enable/disable celestial rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { renderingEnabled = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled; }
|
||||
|
||||
/**
|
||||
* Update celestial bodies (for moon phase cycling)
|
||||
* Update celestial bodies (moon phase cycling, haze timer).
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Set White Lady phase (primary moon, 0.0 = new, 0.5 = full, 1.0 = new)
|
||||
*/
|
||||
// --- Enable / disable ---
|
||||
void setEnabled(bool enabled) { renderingEnabled_ = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled_; }
|
||||
|
||||
// --- Moon phases ---
|
||||
/** Set White Lady phase (primary moon, 0 = new, 0.5 = full, 1 = new). */
|
||||
void setMoonPhase(float phase);
|
||||
float getMoonPhase() const { return whiteLadyPhase_; }
|
||||
|
||||
/**
|
||||
* Set Blue Child phase (secondary moon, 0.0 = new, 0.5 = full, 1.0 = new)
|
||||
*/
|
||||
/** Set Blue Child phase (secondary moon, 0 = new, 0.5 = full, 1 = new). */
|
||||
void setBlueChildPhase(float phase);
|
||||
float getBlueChildPhase() const { return blueChildPhase_; }
|
||||
|
||||
/**
|
||||
* Enable/disable automatic moon phase cycling
|
||||
*/
|
||||
void setMoonPhaseCycling(bool enabled) { moonPhaseCycling = enabled; }
|
||||
bool isMoonPhaseCycling() const { return moonPhaseCycling; }
|
||||
void setMoonPhaseCycling(bool enabled) { moonPhaseCycling_ = enabled; }
|
||||
bool isMoonPhaseCycling() const { return moonPhaseCycling_; }
|
||||
|
||||
/**
|
||||
* Enable/disable two-moon rendering (White Lady + Blue Child)
|
||||
*/
|
||||
/** Enable / disable two-moon rendering (White Lady + Blue Child). */
|
||||
void setDualMoonMode(bool enabled) { dualMoonMode_ = enabled; }
|
||||
bool isDualMoonMode() const { return dualMoonMode_; }
|
||||
|
||||
/**
|
||||
* Get sun position in world space
|
||||
*/
|
||||
// --- Positional / colour queries (unchanged from GL version) ---
|
||||
glm::vec3 getSunPosition(float timeOfDay) const;
|
||||
|
||||
/**
|
||||
* Get moon position in world space
|
||||
*/
|
||||
glm::vec3 getMoonPosition(float timeOfDay) const;
|
||||
|
||||
/**
|
||||
* Get sun color (changes with time of day)
|
||||
*/
|
||||
glm::vec3 getSunColor(float timeOfDay) const;
|
||||
|
||||
/**
|
||||
* Get sun intensity (0-1, fades at dawn/dusk)
|
||||
*/
|
||||
float getSunIntensity(float timeOfDay) const;
|
||||
float getSunIntensity(float timeOfDay) const;
|
||||
|
||||
private:
|
||||
void createCelestialQuad();
|
||||
void destroyCelestialQuad();
|
||||
// Push constant block — MUST match celestial.vert.glsl / celestial.frag.glsl
|
||||
struct CelestialPush {
|
||||
glm::mat4 model; // 64 bytes
|
||||
glm::vec4 celestialColor; // 16 bytes (xyz = colour, w unused)
|
||||
float intensity; // 4 bytes
|
||||
float moonPhase; // 4 bytes
|
||||
float animTime; // 4 bytes
|
||||
float _pad; // 4 bytes (round to 16-byte boundary = 96 bytes total)
|
||||
};
|
||||
static_assert(sizeof(CelestialPush) == 96, "CelestialPush size mismatch");
|
||||
|
||||
void renderSun(const Camera& camera, float timeOfDay,
|
||||
const glm::vec3* sunDir = nullptr,
|
||||
const glm::vec3* sunColor = nullptr);
|
||||
void renderMoon(const Camera& camera, float timeOfDay); // White Lady (primary)
|
||||
void renderBlueChild(const Camera& camera, float timeOfDay); // Blue Child (secondary)
|
||||
void createQuad();
|
||||
void destroyQuad();
|
||||
|
||||
void renderSun(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||
float timeOfDay,
|
||||
const glm::vec3* sunDir, const glm::vec3* sunColor);
|
||||
void renderMoon(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay);
|
||||
void renderBlueChild(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay);
|
||||
|
||||
float calculateCelestialAngle(float timeOfDay, float riseTime, float setTime) const;
|
||||
|
||||
/**
|
||||
* Compute moon phase from game time (deterministic)
|
||||
* @param gameTime Server game time in seconds
|
||||
* @param cycleDays Lunar cycle length in game days
|
||||
* @return Phase 0.0-1.0 (0=new, 0.5=full, 1.0=new)
|
||||
*/
|
||||
float computePhaseFromGameTime(float gameTime, float cycleDays) const;
|
||||
void updatePhasesFromGameTime(float gameTime);
|
||||
|
||||
/**
|
||||
* Update moon phases from game time (server-driven)
|
||||
*/
|
||||
void updatePhasesFromGameTime(float gameTime);
|
||||
// Vulkan objects
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
VkPipeline pipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkBuffer vertexBuffer_ = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc_ = VK_NULL_HANDLE;
|
||||
VkBuffer indexBuffer_ = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc_ = VK_NULL_HANDLE;
|
||||
|
||||
std::unique_ptr<Shader> celestialShader;
|
||||
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
|
||||
bool renderingEnabled = true;
|
||||
bool renderingEnabled_ = true;
|
||||
|
||||
// Moon phase system (two moons in Azeroth lore)
|
||||
float whiteLadyPhase_ = 0.5f; // 0.0-1.0 (0=new, 0.5=full) - primary moon
|
||||
float blueChildPhase_ = 0.25f; // 0.0-1.0 (0=new, 0.5=full) - secondary moon
|
||||
bool moonPhaseCycling = true;
|
||||
float moonPhaseTimer = 0.0f; // Fallback for deltaTime mode (development)
|
||||
float sunHazeTimer_ = 0.0f; // Always-running timer for sun haze animation
|
||||
bool dualMoonMode_ = true; // Default: render both moons (Azeroth-specific)
|
||||
float whiteLadyPhase_ = 0.5f; // 0-1, 0=new, 0.5=full
|
||||
float blueChildPhase_ = 0.25f; // 0-1
|
||||
bool moonPhaseCycling_ = true;
|
||||
float moonPhaseTimer_ = 0.0f; // Fallback deltaTime mode
|
||||
float sunHazeTimer_ = 0.0f; // Always-running haze animation timer
|
||||
bool dualMoonMode_ = true;
|
||||
|
||||
// WoW lunar cycle constants (in game days)
|
||||
// WoW day = 24 real minutes, so these are ~realistic game-world cycles
|
||||
static constexpr float WHITE_LADY_CYCLE_DAYS = 30.0f; // ~12 real hours for full cycle
|
||||
static constexpr float BLUE_CHILD_CYCLE_DAYS = 27.0f; // ~10.8 real hours (slightly faster)
|
||||
static constexpr float MOON_CYCLE_DURATION = 240.0f; // Fallback: 4 minutes (deltaTime mode)
|
||||
// WoW lunar cycle constants (game days; 1 game day = 24 real minutes)
|
||||
static constexpr float WHITE_LADY_CYCLE_DAYS = 30.0f;
|
||||
static constexpr float BLUE_CHILD_CYCLE_DAYS = 27.0f;
|
||||
static constexpr float MOON_CYCLE_DURATION = 240.0f; // Fallback: 4 minutes
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/character.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
|
@ -13,6 +13,8 @@ namespace rendering {
|
|||
|
||||
class CharacterRenderer;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
class VkTexture;
|
||||
|
||||
class CharacterPreview {
|
||||
public:
|
||||
|
|
@ -34,7 +36,8 @@ public:
|
|||
void render();
|
||||
void rotate(float yawDelta);
|
||||
|
||||
GLuint getTextureId() const { return colorTexture_; }
|
||||
// TODO: Vulkan offscreen render target for preview
|
||||
VkTexture* getTextureId() const { return nullptr; }
|
||||
int getWidth() const { return fboWidth_; }
|
||||
int getHeight() const { return fboHeight_; }
|
||||
|
||||
|
|
@ -51,9 +54,8 @@ private:
|
|||
std::unique_ptr<CharacterRenderer> charRenderer_;
|
||||
std::unique_ptr<Camera> camera_;
|
||||
|
||||
GLuint fbo_ = 0;
|
||||
GLuint colorTexture_ = 0;
|
||||
GLuint depthRenderbuffer_ = 0;
|
||||
// TODO: Vulkan offscreen render target
|
||||
// VkRenderTarget* renderTarget_ = nullptr;
|
||||
static constexpr int fboWidth_ = 400;
|
||||
static constexpr int fboHeight_ = 500;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/m2_loader.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
|
@ -14,9 +15,9 @@ namespace pipeline { class AssetManager; }
|
|||
namespace rendering {
|
||||
|
||||
// Forward declarations
|
||||
class Shader;
|
||||
class Texture;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
class VkTexture;
|
||||
|
||||
// Weapon attached to a character instance at a bone attachment point
|
||||
struct WeaponAttachment {
|
||||
|
|
@ -33,7 +34,7 @@ struct WeaponAttachment {
|
|||
* Features:
|
||||
* - Skeletal animation with bone transformations
|
||||
* - Keyframe interpolation (linear position/scale, slerp rotation)
|
||||
* - Vertex skinning (GPU-accelerated)
|
||||
* - Vertex skinning (GPU-accelerated via bone SSBO)
|
||||
* - Texture loading from BLP via AssetManager
|
||||
*/
|
||||
class CharacterRenderer {
|
||||
|
|
@ -41,7 +42,7 @@ public:
|
|||
CharacterRenderer();
|
||||
~CharacterRenderer();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout, pipeline::AssetManager* am);
|
||||
void shutdown();
|
||||
|
||||
void setAssetManager(pipeline::AssetManager* am) { assetManager = am; }
|
||||
|
|
@ -56,8 +57,8 @@ public:
|
|||
|
||||
void update(float deltaTime, const glm::vec3& cameraPos = glm::vec3(0.0f));
|
||||
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
void renderShadow(const glm::mat4& lightSpaceMatrix);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera);
|
||||
void renderShadow(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
void setInstancePosition(uint32_t instanceId, const glm::vec3& position);
|
||||
void setInstanceRotation(uint32_t instanceId, const glm::vec3& rotation);
|
||||
|
|
@ -65,8 +66,8 @@ public:
|
|||
void startFadeIn(uint32_t instanceId, float durationSeconds);
|
||||
const pipeline::M2Model* getModelData(uint32_t modelId) const;
|
||||
void setActiveGeosets(uint32_t instanceId, const std::unordered_set<uint16_t>& geosets);
|
||||
void setGroupTextureOverride(uint32_t instanceId, uint16_t geosetGroup, GLuint textureId);
|
||||
void setTextureSlotOverride(uint32_t instanceId, uint16_t textureSlot, GLuint textureId);
|
||||
void setGroupTextureOverride(uint32_t instanceId, uint16_t geosetGroup, VkTexture* texture);
|
||||
void setTextureSlotOverride(uint32_t instanceId, uint16_t textureSlot, VkTexture* texture);
|
||||
void clearTextureSlotOverride(uint32_t instanceId, uint16_t textureSlot);
|
||||
void setInstanceVisible(uint32_t instanceId, bool visible);
|
||||
void removeInstance(uint32_t instanceId);
|
||||
|
|
@ -88,45 +89,32 @@ public:
|
|||
/** Detach a weapon from the given attachment point. */
|
||||
void detachWeapon(uint32_t charInstanceId, uint32_t attachmentId);
|
||||
|
||||
/** Get the world-space transform of an attachment point on an instance.
|
||||
* Used for mount seats, weapon positions, etc.
|
||||
* @param instanceId The character/mount instance
|
||||
* @param attachmentId The attachment point ID (0=Mount, 1=RightHand, 2=LeftHand, etc.)
|
||||
* @param outTransform The resulting world-space transform matrix
|
||||
* @return true if attachment found and matrix computed
|
||||
*/
|
||||
/** Get the world-space transform of an attachment point on an instance. */
|
||||
bool getAttachmentTransform(uint32_t instanceId, uint32_t attachmentId, glm::mat4& outTransform);
|
||||
|
||||
size_t getInstanceCount() const { return instances.size(); }
|
||||
|
||||
void setFog(const glm::vec3& color, float start, float end) {
|
||||
fogColor = color; fogStart = start; fogEnd = end;
|
||||
}
|
||||
|
||||
void setLighting(const float lightDirIn[3], const float lightColorIn[3],
|
||||
const float ambientColorIn[3]) {
|
||||
lightDir = glm::vec3(lightDirIn[0], lightDirIn[1], lightDirIn[2]);
|
||||
lightColor = glm::vec3(lightColorIn[0], lightColorIn[1], lightColorIn[2]);
|
||||
ambientColor = glm::vec3(ambientColorIn[0], ambientColorIn[1], ambientColorIn[2]);
|
||||
}
|
||||
|
||||
void setShadowMap(GLuint depthTex, const glm::mat4& lightSpace) {
|
||||
shadowDepthTex = depthTex; lightSpaceMatrix = lightSpace; shadowEnabled = true;
|
||||
}
|
||||
void clearShadowMap() { shadowEnabled = false; }
|
||||
// Fog/lighting/shadow are now in per-frame UBO — keep stubs for callers that haven't been updated
|
||||
void setFog(const glm::vec3&, float, float) {}
|
||||
void setLighting(const float[3], const float[3], const float[3]) {}
|
||||
void setShadowMap(VkTexture*, const glm::mat4&) {}
|
||||
void clearShadowMap() {}
|
||||
|
||||
private:
|
||||
// GPU representation of M2 model
|
||||
struct M2ModelGPU {
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
VkBuffer indexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc = VK_NULL_HANDLE;
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t vertexCount = 0;
|
||||
|
||||
pipeline::M2Model data; // Original model data
|
||||
std::vector<glm::mat4> bindPose; // Inverse bind pose matrices
|
||||
|
||||
// Textures loaded from BLP (indexed by texture array position)
|
||||
std::vector<GLuint> textureIds;
|
||||
std::vector<VkTexture*> textureIds;
|
||||
};
|
||||
|
||||
// Character instance
|
||||
|
|
@ -151,11 +139,11 @@ private:
|
|||
// Empty = render all (for non-character models)
|
||||
std::unordered_set<uint16_t> activeGeosets;
|
||||
|
||||
// Per-geoset-group texture overrides (group → GL texture ID)
|
||||
std::unordered_map<uint16_t, GLuint> groupTextureOverrides;
|
||||
// Per-geoset-group texture overrides (group → VkTexture*)
|
||||
std::unordered_map<uint16_t, VkTexture*> groupTextureOverrides;
|
||||
|
||||
// Per-texture-slot overrides (slot → GL texture ID)
|
||||
std::unordered_map<uint16_t, GLuint> textureSlotOverrides;
|
||||
// Per-texture-slot overrides (slot → VkTexture*)
|
||||
std::unordered_map<uint16_t, VkTexture*> textureSlotOverrides;
|
||||
|
||||
// Weapon attachments (weapons parented to this instance's bones)
|
||||
std::vector<WeaponAttachment> weaponAttachments;
|
||||
|
|
@ -175,6 +163,12 @@ private:
|
|||
// Override model matrix (used for weapon instances positioned by parent bone)
|
||||
bool hasOverrideModelMatrix = false;
|
||||
glm::mat4 overrideModelMatrix{1.0f};
|
||||
|
||||
// Per-instance bone SSBO (double-buffered per frame)
|
||||
VkBuffer boneBuffer[2] = {};
|
||||
VmaAllocation boneAlloc[2] = {};
|
||||
void* boneMapped[2] = {};
|
||||
VkDescriptorSet boneSet[2] = {};
|
||||
};
|
||||
|
||||
void setupModelBuffers(M2ModelGPU& gpuModel);
|
||||
|
|
@ -183,6 +177,8 @@ private:
|
|||
void calculateBoneMatrices(CharacterInstance& instance);
|
||||
glm::mat4 getBoneTransform(const pipeline::M2Bone& bone, float time, int sequenceIndex);
|
||||
glm::mat4 getModelMatrix(const CharacterInstance& instance) const;
|
||||
void destroyModelGPU(M2ModelGPU& gpuModel);
|
||||
void destroyInstanceBones(CharacterInstance& inst);
|
||||
|
||||
// Keyframe interpolation helpers
|
||||
static int findKeyframeIndex(const std::vector<uint32_t>& timestamps, float time);
|
||||
|
|
@ -194,83 +190,76 @@ private:
|
|||
public:
|
||||
/**
|
||||
* Build a composited character skin texture by alpha-blending overlay
|
||||
* layers (e.g. underwear) onto a base skin BLP. Each overlay is placed
|
||||
* at the correct CharComponentTextureSections region based on its
|
||||
* filename (pelvis, torso, etc.). Returns the resulting GL texture ID.
|
||||
* layers onto a base skin BLP. Returns the resulting VkTexture*.
|
||||
*/
|
||||
GLuint compositeTextures(const std::vector<std::string>& layerPaths);
|
||||
VkTexture* compositeTextures(const std::vector<std::string>& layerPaths);
|
||||
|
||||
/**
|
||||
* Build a composited character skin with explicit region-based equipment overlays.
|
||||
* @param basePath Body skin texture path
|
||||
* @param baseLayers Underwear overlay paths (placed by filename keyword)
|
||||
* @param regionLayers Pairs of (region_index, blp_path) for equipment textures
|
||||
* @return GL texture ID of the composited result
|
||||
*/
|
||||
GLuint compositeWithRegions(const std::string& basePath,
|
||||
VkTexture* compositeWithRegions(const std::string& basePath,
|
||||
const std::vector<std::string>& baseLayers,
|
||||
const std::vector<std::pair<int, std::string>>& regionLayers);
|
||||
|
||||
/** Clear the composite texture cache (forces re-compositing on next call). */
|
||||
void clearCompositeCache();
|
||||
|
||||
/** Load a BLP texture from MPQ and return the GL texture ID (cached). */
|
||||
GLuint loadTexture(const std::string& path);
|
||||
GLuint getTransparentTexture() const { return transparentTexture; }
|
||||
/** Load a BLP texture from MPQ and return VkTexture* (cached). */
|
||||
VkTexture* loadTexture(const std::string& path);
|
||||
VkTexture* getTransparentTexture() const { return transparentTexture_.get(); }
|
||||
|
||||
/** Replace a loaded model's texture at the given slot with a new GL texture. */
|
||||
void setModelTexture(uint32_t modelId, uint32_t textureSlot, GLuint textureId);
|
||||
/** Replace a loaded model's texture at the given slot. */
|
||||
void setModelTexture(uint32_t modelId, uint32_t textureSlot, VkTexture* texture);
|
||||
|
||||
/** Reset a model's texture slot back to white fallback. */
|
||||
void resetModelTexture(uint32_t modelId, uint32_t textureSlot);
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<Shader> characterShader;
|
||||
GLuint shadowCasterProgram = 0;
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
|
||||
// Fog parameters
|
||||
glm::vec3 fogColor = glm::vec3(0.5f, 0.6f, 0.7f);
|
||||
float fogStart = 400.0f;
|
||||
float fogEnd = 1200.0f;
|
||||
// Vulkan pipelines (one per blend mode)
|
||||
VkPipeline opaquePipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline alphaTestPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline alphaPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline additivePipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Lighting parameters
|
||||
glm::vec3 lightDir = glm::vec3(0.0f, -1.0f, 0.3f);
|
||||
glm::vec3 lightColor = glm::vec3(1.5f, 1.4f, 1.3f);
|
||||
glm::vec3 ambientColor = glm::vec3(0.4f, 0.4f, 0.45f);
|
||||
// Descriptor set layouts
|
||||
VkDescriptorSetLayout perFrameLayout_ = VK_NULL_HANDLE; // set 0 (owned by Renderer)
|
||||
VkDescriptorSetLayout materialSetLayout_ = VK_NULL_HANDLE; // set 1
|
||||
VkDescriptorSetLayout boneSetLayout_ = VK_NULL_HANDLE; // set 2
|
||||
|
||||
// Shadow mapping
|
||||
GLuint shadowDepthTex = 0;
|
||||
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
|
||||
bool shadowEnabled = false;
|
||||
// Descriptor pool
|
||||
VkDescriptorPool materialDescPool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorPool boneDescPool_ = VK_NULL_HANDLE;
|
||||
|
||||
// Texture cache
|
||||
struct TextureCacheEntry {
|
||||
GLuint id = 0;
|
||||
std::unique_ptr<VkTexture> texture;
|
||||
size_t approxBytes = 0;
|
||||
uint64_t lastUse = 0;
|
||||
bool hasAlpha = false;
|
||||
bool colorKeyBlack = false;
|
||||
};
|
||||
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
||||
std::unordered_map<GLuint, bool> textureHasAlphaById_;
|
||||
std::unordered_map<GLuint, bool> textureColorKeyBlackById_;
|
||||
std::unordered_map<std::string, GLuint> compositeCache_; // key → GPU texture for reuse
|
||||
std::unordered_map<VkTexture*, bool> textureHasAlphaByPtr_;
|
||||
std::unordered_map<VkTexture*, bool> textureColorKeyBlackByPtr_;
|
||||
std::unordered_map<std::string, VkTexture*> compositeCache_; // key → texture for reuse
|
||||
std::unordered_set<std::string> failedTextureCache_; // negative cache for missing textures
|
||||
size_t textureCacheBytes_ = 0;
|
||||
uint64_t textureCacheCounter_ = 0;
|
||||
size_t textureCacheBudgetBytes_ = 1024ull * 1024 * 1024; // Default, overridden at init
|
||||
GLuint whiteTexture = 0;
|
||||
GLuint transparentTexture = 0;
|
||||
size_t textureCacheBudgetBytes_ = 1024ull * 1024 * 1024;
|
||||
std::unique_ptr<VkTexture> whiteTexture_;
|
||||
std::unique_ptr<VkTexture> transparentTexture_;
|
||||
|
||||
std::unordered_map<uint32_t, M2ModelGPU> models;
|
||||
std::unordered_map<uint32_t, CharacterInstance> instances;
|
||||
|
||||
uint32_t nextInstanceId = 1;
|
||||
|
||||
// Maximum bones supported (GPU uniform limit)
|
||||
// WoW character models can have 210+ bones; GPU reports 4096 components (~256 mat4)
|
||||
// Maximum bones supported
|
||||
static constexpr int MAX_BONES = 240;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <cstdint>
|
||||
|
|
@ -12,7 +12,7 @@ namespace pipeline { class AssetManager; }
|
|||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
class VkContext;
|
||||
class M2Renderer;
|
||||
|
||||
/// Renders a red-orange ribbon streak trailing behind the warrior during Charge,
|
||||
|
|
@ -22,7 +22,7 @@ public:
|
|||
ChargeEffect();
|
||||
~ChargeEffect();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
/// Try to load M2 spell models (Charge_Caster.m2, etc.)
|
||||
|
|
@ -41,7 +41,7 @@ public:
|
|||
void triggerImpact(const glm::vec3& position);
|
||||
|
||||
void update(float deltaTime);
|
||||
void render(const Camera& camera);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
bool isActive() const { return emitting_ || !trail_.empty() || !dustPuffs_.empty(); }
|
||||
|
||||
|
|
@ -59,10 +59,17 @@ private:
|
|||
static constexpr float TRAIL_SPAWN_DIST = 0.4f; // Min distance between trail points
|
||||
std::deque<TrailPoint> trail_;
|
||||
|
||||
GLuint ribbonVao_ = 0;
|
||||
GLuint ribbonVbo_ = 0;
|
||||
std::unique_ptr<Shader> ribbonShader_;
|
||||
std::vector<float> ribbonVerts_; // pos(3) + alpha(1) + heat(1) = 5 floats per vert
|
||||
// Vulkan objects
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
|
||||
// Ribbon pipeline + dynamic buffer
|
||||
VkPipeline ribbonPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout ribbonPipelineLayout_ = VK_NULL_HANDLE;
|
||||
::VkBuffer ribbonDynamicVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation ribbonDynamicVBAlloc_ = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo ribbonDynamicVBAllocInfo_{};
|
||||
VkDeviceSize ribbonDynamicVBSize_ = 0;
|
||||
std::vector<float> ribbonVerts_; // pos(3) + alpha(1) + heat(1) + height(1) = 6 floats per vert
|
||||
|
||||
// --- Dust puffs (small point sprites at feet) ---
|
||||
struct DustPuff {
|
||||
|
|
@ -77,9 +84,13 @@ private:
|
|||
static constexpr int MAX_DUST = 80;
|
||||
std::vector<DustPuff> dustPuffs_;
|
||||
|
||||
GLuint dustVao_ = 0;
|
||||
GLuint dustVbo_ = 0;
|
||||
std::unique_ptr<Shader> dustShader_;
|
||||
// Dust pipeline + dynamic buffer
|
||||
VkPipeline dustPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout dustPipelineLayout_ = VK_NULL_HANDLE;
|
||||
::VkBuffer dustDynamicVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation dustDynamicVBAlloc_ = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo dustDynamicVBAllocInfo_{};
|
||||
VkDeviceSize dustDynamicVBSize_ = 0;
|
||||
std::vector<float> dustVerts_;
|
||||
|
||||
bool emitting_ = false;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* @brief Renders procedural animated clouds on a sky dome
|
||||
* Procedural cloud renderer (Vulkan)
|
||||
*
|
||||
* Features:
|
||||
* - Procedural cloud generation using multiple noise layers
|
||||
* - Two cloud layers at different altitudes
|
||||
* - Animated wind movement
|
||||
* - Time-of-day color tinting (orange at sunrise/sunset)
|
||||
* - Transparency and soft edges
|
||||
* Renders animated procedural clouds on a sky hemisphere using FBM noise.
|
||||
* Two noise layers at different frequencies produce realistic cloud shapes.
|
||||
*
|
||||
* Pipeline layout:
|
||||
* set 0 = perFrameLayout (camera UBO — view, projection, etc.)
|
||||
* push = CloudPush (vec4 cloudColor + float density + float windOffset = 24 bytes)
|
||||
*
|
||||
* The vertex shader reads view/projection from set 0 directly; no per-object
|
||||
* model matrix is needed (clouds are locked to the sky dome).
|
||||
*/
|
||||
class Clouds {
|
||||
public:
|
||||
|
|
@ -27,68 +29,79 @@ public:
|
|||
~Clouds();
|
||||
|
||||
/**
|
||||
* @brief Initialize cloud system (generate mesh and shaders)
|
||||
* @return true if initialization succeeded
|
||||
* Initialize the cloud system.
|
||||
* @param ctx Vulkan context
|
||||
* @param perFrameLayout Descriptor set layout for set 0 (camera UBO)
|
||||
*/
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief Render clouds
|
||||
* @param camera The camera to render from
|
||||
* @param timeOfDay Current time (0-24 hours)
|
||||
* Render clouds.
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0, camera UBO)
|
||||
* @param timeOfDay Time of day in hours (0-24)
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay);
|
||||
|
||||
/**
|
||||
* @brief Update cloud animation
|
||||
* @param deltaTime Time since last frame
|
||||
* Update cloud animation (wind drift).
|
||||
* @param deltaTime Seconds since last frame
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable cloud rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
bool isEnabled() const { return enabled; }
|
||||
// --- Enable / disable ---
|
||||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
/**
|
||||
* @brief Set cloud density (0.0 = clear, 1.0 = overcast)
|
||||
*/
|
||||
// --- Cloud parameters ---
|
||||
/** Cloud coverage, 0 = clear, 1 = overcast. */
|
||||
void setDensity(float density);
|
||||
float getDensity() const { return density; }
|
||||
float getDensity() const { return density_; }
|
||||
|
||||
/**
|
||||
* @brief Set wind speed multiplier
|
||||
*/
|
||||
void setWindSpeed(float speed) { windSpeed = speed; }
|
||||
float getWindSpeed() const { return windSpeed; }
|
||||
void setWindSpeed(float speed) { windSpeed_ = speed; }
|
||||
float getWindSpeed() const { return windSpeed_; }
|
||||
|
||||
private:
|
||||
// Push constant block — must match clouds.frag.glsl
|
||||
struct CloudPush {
|
||||
glm::vec4 cloudColor; // 16 bytes (xyz = colour, w unused)
|
||||
float density; // 4 bytes
|
||||
float windOffset; // 4 bytes
|
||||
// total = 24 bytes
|
||||
};
|
||||
static_assert(sizeof(CloudPush) == 24, "CloudPush size mismatch");
|
||||
|
||||
void generateMesh();
|
||||
void cleanup();
|
||||
void createBuffers();
|
||||
void destroyBuffers();
|
||||
|
||||
glm::vec3 getCloudColor(float timeOfDay) const;
|
||||
|
||||
// OpenGL objects
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ebo = 0;
|
||||
std::unique_ptr<Shader> shader;
|
||||
// Vulkan objects
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
VkPipeline pipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkBuffer vertexBuffer_ = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc_ = VK_NULL_HANDLE;
|
||||
VkBuffer indexBuffer_ = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc_ = VK_NULL_HANDLE;
|
||||
|
||||
// Mesh data
|
||||
std::vector<glm::vec3> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
int triangleCount = 0;
|
||||
// Mesh data (CPU side, used during initialization only)
|
||||
std::vector<glm::vec3> vertices_;
|
||||
std::vector<uint32_t> indices_;
|
||||
int indexCount_ = 0;
|
||||
|
||||
// Cloud parameters
|
||||
bool enabled = true;
|
||||
float density = 0.5f; // Cloud coverage
|
||||
float windSpeed = 1.0f;
|
||||
float windOffset = 0.0f; // Accumulated wind movement
|
||||
bool enabled_ = true;
|
||||
float density_ = 0.5f;
|
||||
float windSpeed_ = 1.0f;
|
||||
float windOffset_ = 0.0f; // Accumulated wind movement
|
||||
|
||||
// Mesh generation parameters
|
||||
static constexpr int SEGMENTS = 32; // Horizontal segments
|
||||
static constexpr int RINGS = 8; // Vertical rings (only upper hemisphere)
|
||||
static constexpr float RADIUS = 900.0f; // Slightly smaller than skybox
|
||||
static constexpr int SEGMENTS = 32;
|
||||
static constexpr int RINGS = 8;
|
||||
static constexpr float RADIUS = 900.0f; // Slightly smaller than skybox
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* @brief Renders lens flare effect when looking at the sun
|
||||
|
|
@ -28,17 +28,25 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Initialize lens flare system
|
||||
* @param ctx Vulkan context
|
||||
* @param perFrameLayout Per-frame descriptor set layout (unused, kept for API consistency)
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
|
||||
/**
|
||||
* @brief Destroy Vulkan resources
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief Render lens flare effect
|
||||
* @param cmd Command buffer to record into
|
||||
* @param camera The camera to render from
|
||||
* @param sunPosition World-space sun position
|
||||
* @param timeOfDay Current time (0-24 hours)
|
||||
*/
|
||||
void render(const Camera& camera, const glm::vec3& sunPosition, float timeOfDay);
|
||||
void render(VkCommandBuffer cmd, const Camera& camera, const glm::vec3& sunPosition, float timeOfDay);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable lens flare rendering
|
||||
|
|
@ -60,15 +68,24 @@ private:
|
|||
float brightness; // Brightness multiplier
|
||||
};
|
||||
|
||||
struct FlarePushConstants {
|
||||
glm::vec2 position; // Screen-space position (-1 to 1)
|
||||
float size; // Size in screen space
|
||||
float aspectRatio; // Viewport aspect ratio
|
||||
glm::vec4 colorBrightness; // RGB color + brightness in w
|
||||
};
|
||||
|
||||
void generateFlareElements();
|
||||
void cleanup();
|
||||
float calculateSunVisibility(const Camera& camera, const glm::vec3& sunPosition) const;
|
||||
glm::vec2 worldToScreen(const Camera& camera, const glm::vec3& worldPos) const;
|
||||
|
||||
// OpenGL objects
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
std::unique_ptr<Shader> shader;
|
||||
VkContext* vkCtx = nullptr;
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
|
||||
// Flare elements
|
||||
std::vector<FlareElement> flareElements;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
// Forward declarations
|
||||
class Shader;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* Lightning system for thunder storm effects
|
||||
|
|
@ -26,11 +26,11 @@ public:
|
|||
Lightning();
|
||||
~Lightning();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
void update(float deltaTime, const Camera& camera);
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
// Control
|
||||
void setEnabled(bool enabled);
|
||||
|
|
@ -68,8 +68,8 @@ private:
|
|||
void updateFlash(float deltaTime);
|
||||
void spawnRandomStrike(const glm::vec3& cameraPos);
|
||||
|
||||
void renderBolts(const glm::mat4& viewProj);
|
||||
void renderFlash();
|
||||
void renderBolts(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
void renderFlash(VkCommandBuffer cmd);
|
||||
|
||||
bool enabled = true;
|
||||
float intensity = 0.5f; // Strike frequency multiplier
|
||||
|
|
@ -82,13 +82,22 @@ private:
|
|||
std::vector<LightningBolt> bolts;
|
||||
Flash flash;
|
||||
|
||||
// Rendering
|
||||
std::unique_ptr<Shader> boltShader;
|
||||
std::unique_ptr<Shader> flashShader;
|
||||
unsigned int boltVAO = 0;
|
||||
unsigned int boltVBO = 0;
|
||||
unsigned int flashVAO = 0;
|
||||
unsigned int flashVBO = 0;
|
||||
// Vulkan objects
|
||||
VkContext* vkCtx = nullptr;
|
||||
|
||||
// Bolt pipeline + dynamic buffer
|
||||
VkPipeline boltPipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout boltPipelineLayout = VK_NULL_HANDLE;
|
||||
::VkBuffer boltDynamicVB = VK_NULL_HANDLE;
|
||||
VmaAllocation boltDynamicVBAlloc = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo boltDynamicVBAllocInfo{};
|
||||
VkDeviceSize boltDynamicVBSize = 0;
|
||||
|
||||
// Flash pipeline + static quad buffer
|
||||
VkPipeline flashPipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout flashPipelineLayout = VK_NULL_HANDLE;
|
||||
::VkBuffer flashQuadVB = VK_NULL_HANDLE;
|
||||
VmaAllocation flashQuadVBAlloc = VK_NULL_HANDLE;
|
||||
|
||||
// Configuration
|
||||
static constexpr int MAX_BOLTS = 3;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VkContext;
|
||||
|
||||
class LoadingScreen {
|
||||
public:
|
||||
LoadingScreen();
|
||||
|
|
@ -15,32 +17,28 @@ public:
|
|||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
// Select a random loading screen image
|
||||
void selectRandomImage();
|
||||
|
||||
// Render the loading screen with progress bar and status text
|
||||
// Render the loading screen with progress bar and status text (pure ImGui)
|
||||
void render();
|
||||
|
||||
// Update loading progress (0.0 to 1.0)
|
||||
void setProgress(float progress) { loadProgress = progress; }
|
||||
|
||||
// Set loading status text
|
||||
void setStatus(const std::string& status) { statusText = status; }
|
||||
|
||||
// Must be set before initialize() for Vulkan texture upload
|
||||
void setVkContext(VkContext* ctx) { vkCtx = ctx; }
|
||||
|
||||
private:
|
||||
bool loadImage(const std::string& path);
|
||||
void createQuad();
|
||||
void createBarQuad();
|
||||
|
||||
GLuint textureId = 0;
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint shaderId = 0;
|
||||
VkContext* vkCtx = nullptr;
|
||||
|
||||
// Progress bar GL objects
|
||||
GLuint barVao = 0;
|
||||
GLuint barVbo = 0;
|
||||
GLuint barShaderId = 0;
|
||||
// Vulkan texture for background image
|
||||
VkImage bgImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory bgMemory = VK_NULL_HANDLE;
|
||||
VkImageView bgImageView = VK_NULL_HANDLE;
|
||||
VkSampler bgSampler = VK_NULL_HANDLE;
|
||||
VkDescriptorSet bgDescriptorSet = VK_NULL_HANDLE; // ImGui texture handle
|
||||
|
||||
std::vector<std::string> imagePaths;
|
||||
int currentImageIndex = 0;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/m2_loader.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
|
@ -20,15 +21,19 @@ namespace pipeline {
|
|||
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
class VkTexture;
|
||||
|
||||
/**
|
||||
* GPU representation of an M2 model
|
||||
*/
|
||||
struct M2ModelGPU {
|
||||
struct BatchGPU {
|
||||
GLuint texture = 0;
|
||||
VkTexture* texture = nullptr; // from cache, NOT owned
|
||||
VkDescriptorSet materialSet = VK_NULL_HANDLE; // set 1
|
||||
::VkBuffer materialUBO = VK_NULL_HANDLE;
|
||||
VmaAllocation materialUBOAlloc = VK_NULL_HANDLE;
|
||||
uint32_t indexStart = 0; // offset in indices (not bytes)
|
||||
uint32_t indexCount = 0;
|
||||
bool hasAlpha = false;
|
||||
|
|
@ -47,9 +52,10 @@ struct M2ModelGPU {
|
|||
float glowSize = 1.0f; // Approx radius of batch geometry
|
||||
};
|
||||
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ebo = 0;
|
||||
::VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
::VkBuffer indexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc = VK_NULL_HANDLE;
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t vertexCount = 0;
|
||||
std::vector<BatchGPU> batches;
|
||||
|
|
@ -109,14 +115,14 @@ struct M2ModelGPU {
|
|||
|
||||
// Particle emitter data (kept from M2Model)
|
||||
std::vector<pipeline::M2ParticleEmitter> particleEmitters;
|
||||
std::vector<GLuint> particleTextures; // Resolved GL textures per emitter
|
||||
std::vector<VkTexture*> particleTextures; // Resolved Vulkan textures per emitter
|
||||
|
||||
// Texture transform data for UV animation
|
||||
std::vector<pipeline::M2TextureTransform> textureTransforms;
|
||||
std::vector<uint16_t> textureTransformLookup;
|
||||
std::vector<int> idleVariationIndices; // Sequence indices for idle variations (animId 0)
|
||||
|
||||
bool isValid() const { return vao != 0 && indexCount > 0; }
|
||||
bool isValid() const { return vertexBuffer != VK_NULL_HANDLE && indexCount > 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -164,6 +170,12 @@ struct M2Instance {
|
|||
// Frame-skip optimization (update distant animations less frequently)
|
||||
uint8_t frameSkipCounter = 0;
|
||||
|
||||
// Per-instance bone SSBO (double-buffered)
|
||||
::VkBuffer boneBuffer[2] = {};
|
||||
VmaAllocation boneAlloc[2] = {};
|
||||
void* boneMapped[2] = {};
|
||||
VkDescriptorSet boneSet[2] = {};
|
||||
|
||||
void updateModelMatrix();
|
||||
};
|
||||
|
||||
|
|
@ -180,8 +192,29 @@ struct SmokeParticle {
|
|||
uint32_t instanceId = 0;
|
||||
};
|
||||
|
||||
// M2 material UBO — matches M2Material in m2.frag.glsl (set 1, binding 2)
|
||||
struct M2MaterialUBO {
|
||||
int32_t hasTexture;
|
||||
int32_t alphaTest;
|
||||
int32_t colorKeyBlack;
|
||||
float colorKeyThreshold;
|
||||
int32_t unlit;
|
||||
int32_t blendMode;
|
||||
float fadeAlpha;
|
||||
float interiorDarken;
|
||||
float specularIntensity;
|
||||
};
|
||||
|
||||
// M2 params UBO — matches M2Params in m2.vert.glsl (set 1, binding 1)
|
||||
struct M2ParamsUBO {
|
||||
float uvOffsetX;
|
||||
float uvOffsetY;
|
||||
int32_t texCoordSet;
|
||||
int32_t useBones;
|
||||
};
|
||||
|
||||
/**
|
||||
* M2 Model Renderer
|
||||
* M2 Model Renderer (Vulkan)
|
||||
*
|
||||
* Handles rendering of M2 models (doodads like trees, rocks, bushes)
|
||||
*/
|
||||
|
|
@ -190,137 +223,57 @@ public:
|
|||
M2Renderer();
|
||||
~M2Renderer();
|
||||
|
||||
bool initialize(pipeline::AssetManager* assets);
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout,
|
||||
pipeline::AssetManager* assets);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Check if a model is already loaded
|
||||
* @param modelId ID to check
|
||||
* @return True if model is loaded
|
||||
*/
|
||||
bool hasModel(uint32_t modelId) const;
|
||||
|
||||
/**
|
||||
* Load an M2 model to GPU
|
||||
* @param model Parsed M2 model data
|
||||
* @param modelId Unique ID for this model
|
||||
* @return True if successful
|
||||
*/
|
||||
bool loadModel(const pipeline::M2Model& model, uint32_t modelId);
|
||||
|
||||
/**
|
||||
* Create an instance of a loaded model
|
||||
* @param modelId ID of the loaded model
|
||||
* @param position World position
|
||||
* @param rotation Rotation in degrees (x, y, z)
|
||||
* @param scale Scale factor (1.0 = normal)
|
||||
* @return Instance ID
|
||||
*/
|
||||
uint32_t createInstance(uint32_t modelId, const glm::vec3& position,
|
||||
const glm::vec3& rotation = glm::vec3(0.0f),
|
||||
float scale = 1.0f);
|
||||
|
||||
/**
|
||||
* Create an instance with a pre-computed model matrix
|
||||
* Used for WMO doodads where the full transform is computed externally
|
||||
*/
|
||||
uint32_t createInstanceWithMatrix(uint32_t modelId, const glm::mat4& modelMatrix,
|
||||
const glm::vec3& position);
|
||||
|
||||
/**
|
||||
* Update animation state for all instances
|
||||
* @param deltaTime Time since last frame
|
||||
* @param cameraPos Camera world position (for frustum-culling bones)
|
||||
* @param viewProjection Combined view*projection matrix
|
||||
*/
|
||||
void update(float deltaTime, const glm::vec3& cameraPos, const glm::mat4& viewProjection);
|
||||
|
||||
/**
|
||||
* Render all visible instances
|
||||
* Render all visible instances (Vulkan)
|
||||
*/
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera);
|
||||
|
||||
/**
|
||||
* Initialize shadow pipeline (Phase 7)
|
||||
*/
|
||||
bool initializeShadow(VkRenderPass shadowRenderPass);
|
||||
|
||||
/**
|
||||
* Render depth-only pass for shadow casting
|
||||
*/
|
||||
void renderShadow(GLuint shadowShaderProgram, const glm::vec3& shadowCenter, float halfExtent);
|
||||
void renderShadow(VkCommandBuffer cmd, const glm::mat4& lightSpaceMatrix);
|
||||
|
||||
/**
|
||||
* Render smoke particles (call after render())
|
||||
* Render M2 particle emitters (point sprites)
|
||||
*/
|
||||
void renderSmokeParticles(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
void renderM2Particles(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
/**
|
||||
* Render M2 particle emitter particles (call after renderSmokeParticles())
|
||||
* Render smoke particles from chimneys etc.
|
||||
*/
|
||||
void renderM2Particles(const glm::mat4& view, const glm::mat4& proj);
|
||||
void renderSmokeParticles(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
/**
|
||||
* Update the world position of an existing instance (e.g., for transports)
|
||||
* @param instanceId Instance ID returned by createInstance()
|
||||
* @param position New world position
|
||||
*/
|
||||
void setInstancePosition(uint32_t instanceId, const glm::vec3& position);
|
||||
|
||||
/**
|
||||
* Update the full transform of an existing instance (e.g., for WMO doodads following parent WMO)
|
||||
* @param instanceId Instance ID returned by createInstance()
|
||||
* @param transform New world transform matrix
|
||||
*/
|
||||
void setInstanceTransform(uint32_t instanceId, const glm::mat4& transform);
|
||||
|
||||
/**
|
||||
* Remove a specific instance by ID
|
||||
* @param instanceId Instance ID returned by createInstance()
|
||||
*/
|
||||
void removeInstance(uint32_t instanceId);
|
||||
/**
|
||||
* Remove multiple instances with one spatial-index rebuild.
|
||||
*/
|
||||
void removeInstances(const std::vector<uint32_t>& instanceIds);
|
||||
|
||||
/**
|
||||
* Clear all models and instances
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Remove models that have no instances referencing them
|
||||
* Call periodically to free GPU memory
|
||||
*/
|
||||
void cleanupUnusedModels();
|
||||
|
||||
/**
|
||||
* Check collision with M2 objects and adjust position
|
||||
* @param from Starting position
|
||||
* @param to Desired position
|
||||
* @param adjustedPos Output adjusted position
|
||||
* @param playerRadius Collision radius of player
|
||||
* @return true if collision occurred
|
||||
*/
|
||||
bool checkCollision(const glm::vec3& from, const glm::vec3& to,
|
||||
glm::vec3& adjustedPos, float playerRadius = 0.5f) const;
|
||||
|
||||
/**
|
||||
* Approximate top surface height for standing/jumping on doodads.
|
||||
* @param glX World X
|
||||
* @param glY World Y
|
||||
* @param glZ Query/reference Z (used to ignore unreachable tops)
|
||||
*/
|
||||
std::optional<float> getFloorHeight(float glX, float glY, float glZ, float* outNormalZ = nullptr) const;
|
||||
|
||||
/**
|
||||
* Raycast against M2 bounding boxes for camera collision
|
||||
* @param origin Ray origin (e.g., character head position)
|
||||
* @param direction Ray direction (normalized)
|
||||
* @param maxDistance Maximum ray distance to check
|
||||
* @return Distance to first intersection, or maxDistance if no hit
|
||||
*/
|
||||
float raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3& direction, float maxDistance) const;
|
||||
|
||||
/**
|
||||
* Limit expensive collision/raycast queries to objects near a focus point.
|
||||
*/
|
||||
void setCollisionFocus(const glm::vec3& worldPos, float radius);
|
||||
void clearCollisionFocus();
|
||||
|
||||
|
|
@ -335,21 +288,12 @@ public:
|
|||
uint32_t getTotalTriangleCount() const;
|
||||
uint32_t getDrawCallCount() const { return lastDrawCallCount; }
|
||||
|
||||
void setFog(const glm::vec3& color, float start, float end) {
|
||||
fogColor = color; fogStart = start; fogEnd = end;
|
||||
}
|
||||
|
||||
void setLighting(const float lightDirIn[3], const float lightColorIn[3],
|
||||
const float ambientColorIn[3]) {
|
||||
lightDir = glm::vec3(lightDirIn[0], lightDirIn[1], lightDirIn[2]);
|
||||
lightColor = glm::vec3(lightColorIn[0], lightColorIn[1], lightColorIn[2]);
|
||||
ambientColor = glm::vec3(ambientColorIn[0], ambientColorIn[1], ambientColorIn[2]);
|
||||
}
|
||||
|
||||
void setShadowMap(GLuint depthTex, const glm::mat4& lightSpace) {
|
||||
shadowDepthTex = depthTex; lightSpaceMatrix = lightSpace; shadowEnabled = true;
|
||||
}
|
||||
void clearShadowMap() { shadowEnabled = false; }
|
||||
// Lighting/fog/shadow are now in per-frame UBO; these are no-ops for API compat
|
||||
void setFog(const glm::vec3& /*color*/, float /*start*/, float /*end*/) {}
|
||||
void setLighting(const float /*lightDirIn*/[3], const float /*lightColorIn*/[3],
|
||||
const float /*ambientColorIn*/[3]) {}
|
||||
void setShadowMap(uint32_t /*depthTex*/, const glm::mat4& /*lightSpace*/) {}
|
||||
void clearShadowMap() {}
|
||||
|
||||
void setInsideInterior(bool inside) { insideInterior = inside; }
|
||||
void setOnTaxi(bool onTaxi) { onTaxi_ = onTaxi; }
|
||||
|
|
@ -359,7 +303,51 @@ private:
|
|||
bool insideInterior = false;
|
||||
bool onTaxi_ = false;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
// Vulkan context
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
|
||||
// Vulkan pipelines (one per blend mode)
|
||||
VkPipeline opaquePipeline_ = VK_NULL_HANDLE; // blend mode 0
|
||||
VkPipeline alphaTestPipeline_ = VK_NULL_HANDLE; // blend mode 1
|
||||
VkPipeline alphaPipeline_ = VK_NULL_HANDLE; // blend mode 2
|
||||
VkPipeline additivePipeline_ = VK_NULL_HANDLE; // blend mode 3+
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Shadow rendering (Phase 7)
|
||||
VkPipeline shadowPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout shadowPipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout shadowParamsLayout_ = VK_NULL_HANDLE;
|
||||
VkDescriptorPool shadowParamsPool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSet shadowParamsSet_ = VK_NULL_HANDLE;
|
||||
::VkBuffer shadowParamsUBO_ = VK_NULL_HANDLE;
|
||||
VmaAllocation shadowParamsAlloc_ = VK_NULL_HANDLE;
|
||||
|
||||
// Particle pipelines
|
||||
VkPipeline particlePipeline_ = VK_NULL_HANDLE; // M2 emitter particles
|
||||
VkPipeline particleAdditivePipeline_ = VK_NULL_HANDLE; // Additive particle blend
|
||||
VkPipelineLayout particlePipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkPipeline smokePipeline_ = VK_NULL_HANDLE; // Smoke particles
|
||||
VkPipelineLayout smokePipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor set layouts
|
||||
VkDescriptorSetLayout materialSetLayout_ = VK_NULL_HANDLE; // set 1
|
||||
VkDescriptorSetLayout boneSetLayout_ = VK_NULL_HANDLE; // set 2
|
||||
VkDescriptorSetLayout particleTexLayout_ = VK_NULL_HANDLE; // particle set 1 (texture only)
|
||||
|
||||
// Descriptor pools
|
||||
VkDescriptorPool materialDescPool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorPool boneDescPool_ = VK_NULL_HANDLE;
|
||||
static constexpr uint32_t MAX_MATERIAL_SETS = 8192;
|
||||
static constexpr uint32_t MAX_BONE_SETS = 2048;
|
||||
|
||||
// Dynamic particle buffers
|
||||
::VkBuffer smokeVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation smokeVBAlloc_ = VK_NULL_HANDLE;
|
||||
void* smokeVBMapped_ = nullptr;
|
||||
::VkBuffer m2ParticleVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation m2ParticleVBAlloc_ = VK_NULL_HANDLE;
|
||||
void* m2ParticleVBMapped_ = nullptr;
|
||||
|
||||
std::unordered_map<uint32_t, M2ModelGPU> models;
|
||||
std::vector<M2Instance> instances;
|
||||
|
|
@ -367,37 +355,22 @@ private:
|
|||
uint32_t nextInstanceId = 1;
|
||||
uint32_t lastDrawCallCount = 0;
|
||||
|
||||
GLuint loadTexture(const std::string& path, uint32_t texFlags = 0);
|
||||
VkTexture* loadTexture(const std::string& path, uint32_t texFlags = 0);
|
||||
struct TextureCacheEntry {
|
||||
GLuint id = 0;
|
||||
std::unique_ptr<VkTexture> texture;
|
||||
size_t approxBytes = 0;
|
||||
uint64_t lastUse = 0;
|
||||
bool hasAlpha = true;
|
||||
bool colorKeyBlack = false;
|
||||
};
|
||||
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
||||
std::unordered_map<GLuint, bool> textureHasAlphaById_;
|
||||
std::unordered_map<GLuint, bool> textureColorKeyBlackById_;
|
||||
std::unordered_map<VkTexture*, bool> textureHasAlphaByPtr_;
|
||||
std::unordered_map<VkTexture*, bool> textureColorKeyBlackByPtr_;
|
||||
size_t textureCacheBytes_ = 0;
|
||||
uint64_t textureCacheCounter_ = 0;
|
||||
size_t textureCacheBudgetBytes_ = 2048ull * 1024 * 1024; // Default, overridden at init
|
||||
GLuint whiteTexture = 0;
|
||||
GLuint glowTexture = 0; // Soft radial gradient for glow sprites
|
||||
|
||||
// Lighting uniforms
|
||||
glm::vec3 lightDir = glm::vec3(0.5f, 0.5f, 1.0f);
|
||||
glm::vec3 lightColor = glm::vec3(1.5f, 1.4f, 1.3f);
|
||||
glm::vec3 ambientColor = glm::vec3(0.4f, 0.4f, 0.45f);
|
||||
|
||||
// Fog parameters
|
||||
glm::vec3 fogColor = glm::vec3(0.5f, 0.6f, 0.7f);
|
||||
float fogStart = 400.0f;
|
||||
float fogEnd = 1200.0f;
|
||||
|
||||
// Shadow mapping
|
||||
GLuint shadowDepthTex = 0;
|
||||
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
|
||||
bool shadowEnabled = false;
|
||||
size_t textureCacheBudgetBytes_ = 2048ull * 1024 * 1024;
|
||||
std::unique_ptr<VkTexture> whiteTexture_;
|
||||
std::unique_ptr<VkTexture> glowTexture_;
|
||||
|
||||
// Optional query-space culling for collision/raycast hot paths.
|
||||
bool collisionFocusEnabled = false;
|
||||
|
|
@ -458,17 +431,11 @@ private:
|
|||
|
||||
// Smoke particle system
|
||||
std::vector<SmokeParticle> smokeParticles;
|
||||
GLuint smokeVAO = 0;
|
||||
GLuint smokeVBO = 0;
|
||||
std::unique_ptr<Shader> smokeShader;
|
||||
static constexpr int MAX_SMOKE_PARTICLES = 1000;
|
||||
float smokeEmitAccum = 0.0f;
|
||||
std::mt19937 smokeRng{42};
|
||||
|
||||
// M2 particle emitter system
|
||||
GLuint m2ParticleShader_ = 0;
|
||||
GLuint m2ParticleVAO_ = 0;
|
||||
GLuint m2ParticleVBO_ = 0;
|
||||
static constexpr size_t MAX_M2_PARTICLES = 4000;
|
||||
std::mt19937 particleRng_{123};
|
||||
|
||||
|
|
@ -486,6 +453,15 @@ private:
|
|||
glm::vec3 interpFBlockVec3(const pipeline::M2FBlock& fb, float lifeRatio);
|
||||
void emitParticles(M2Instance& inst, const M2ModelGPU& gpu, float dt);
|
||||
void updateParticles(M2Instance& inst, float dt);
|
||||
|
||||
// Helper to allocate descriptor sets
|
||||
VkDescriptorSet allocateMaterialSet();
|
||||
VkDescriptorSet allocateBoneSet();
|
||||
|
||||
// Helper to destroy model GPU resources
|
||||
void destroyModelGPU(M2ModelGPU& model);
|
||||
// Helper to destroy instance bone buffers
|
||||
void destroyInstanceBones(M2Instance& inst);
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
|
@ -12,22 +13,28 @@ namespace wowee {
|
|||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
class VkTexture;
|
||||
class VkRenderTarget;
|
||||
|
||||
class Minimap {
|
||||
public:
|
||||
Minimap();
|
||||
~Minimap();
|
||||
|
||||
bool initialize(int size = 200);
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout, int size = 200);
|
||||
void shutdown();
|
||||
|
||||
void setAssetManager(pipeline::AssetManager* am) { assetManager = am; }
|
||||
void setMapName(const std::string& name);
|
||||
|
||||
void render(const Camera& playerCamera, const glm::vec3& centerWorldPos,
|
||||
int screenWidth, int screenHeight);
|
||||
/// Off-screen composite pass — call BEFORE the main render pass begins.
|
||||
void compositePass(VkCommandBuffer cmd, const glm::vec3& centerWorldPos);
|
||||
|
||||
/// Display quad — call INSIDE the main render pass.
|
||||
void render(VkCommandBuffer cmd, const Camera& playerCamera,
|
||||
const glm::vec3& centerWorldPos, int screenWidth, int screenHeight);
|
||||
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
|
@ -45,17 +52,15 @@ public:
|
|||
void zoomOut() { viewRadius = std::min(800.0f, viewRadius + 50.0f); }
|
||||
|
||||
// Public accessors for WorldMap
|
||||
GLuint getOrLoadTileTexture(int tileX, int tileY);
|
||||
VkTexture* getOrLoadTileTexture(int tileX, int tileY);
|
||||
void ensureTRSParsed() { if (!trsParsed) parseTRS(); }
|
||||
GLuint getTileQuadVAO() const { return tileQuadVAO; }
|
||||
const std::string& getMapName() const { return mapName; }
|
||||
|
||||
private:
|
||||
void parseTRS();
|
||||
void compositeTilesToFBO(const glm::vec3& centerWorldPos);
|
||||
void renderQuad(const Camera& playerCamera, const glm::vec3& centerWorldPos,
|
||||
int screenWidth, int screenHeight);
|
||||
void updateTileDescriptors(uint32_t frameIdx, int centerTileX, int centerTileY);
|
||||
|
||||
VkContext* vkCtx = nullptr;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
std::string mapName = "Azeroth";
|
||||
|
||||
|
|
@ -63,28 +68,36 @@ private:
|
|||
std::unordered_map<std::string, std::string> trsLookup;
|
||||
bool trsParsed = false;
|
||||
|
||||
// Tile texture cache: hash → GL texture ID
|
||||
std::unordered_map<std::string, GLuint> tileTextureCache;
|
||||
GLuint noDataTexture = 0; // dark fallback for missing tiles
|
||||
// Tile texture cache: hash → VkTexture
|
||||
std::unordered_map<std::string, std::unique_ptr<VkTexture>> tileTextureCache;
|
||||
std::unique_ptr<VkTexture> noDataTexture;
|
||||
|
||||
// Composite FBO (3x3 tiles = 768x768)
|
||||
GLuint compositeFBO = 0;
|
||||
GLuint compositeTexture = 0;
|
||||
// Composite render target (3x3 tiles = 768x768)
|
||||
std::unique_ptr<VkRenderTarget> compositeTarget;
|
||||
static constexpr int TILE_PX = 256;
|
||||
static constexpr int COMPOSITE_PX = TILE_PX * 3; // 768
|
||||
|
||||
// Tile compositing quad
|
||||
GLuint tileQuadVAO = 0;
|
||||
GLuint tileQuadVBO = 0;
|
||||
std::unique_ptr<Shader> tileShader;
|
||||
// Shared quad vertex buffer (6 verts, pos2 + uv2 = 16 bytes/vert)
|
||||
::VkBuffer quadVB = VK_NULL_HANDLE;
|
||||
VmaAllocation quadVBAlloc = VK_NULL_HANDLE;
|
||||
|
||||
// Screen quad
|
||||
GLuint quadVAO = 0;
|
||||
GLuint quadVBO = 0;
|
||||
std::unique_ptr<Shader> quadShader;
|
||||
// Descriptor resources (shared layout: 1 combined image sampler at binding 0)
|
||||
VkDescriptorSetLayout samplerSetLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorPool descPool = VK_NULL_HANDLE;
|
||||
static constexpr uint32_t MAX_DESC_SETS = 24;
|
||||
|
||||
// Tile composite pipeline (renders into VkRenderTarget)
|
||||
VkPipeline tilePipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout tilePipelineLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorSet tileDescSets[2][9] = {}; // [frameInFlight][tileSlot]
|
||||
|
||||
// Display pipeline (renders into main render pass)
|
||||
VkPipeline displayPipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout displayPipelineLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorSet displayDescSet = VK_NULL_HANDLE;
|
||||
|
||||
int mapSize = 200;
|
||||
float viewRadius = 400.0f; // world units visible in minimap radius
|
||||
float viewRadius = 400.0f;
|
||||
bool enabled = true;
|
||||
bool rotateWithCamera = false;
|
||||
bool squareShape = false;
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
class VkContext;
|
||||
|
||||
class MountDust {
|
||||
public:
|
||||
MountDust();
|
||||
~MountDust();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
// Spawn dust particles at mount feet when moving on ground
|
||||
void spawnDust(const glm::vec3& position, const glm::vec3& velocity, bool isMoving);
|
||||
|
||||
void update(float deltaTime);
|
||||
void render(const Camera& camera);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
private:
|
||||
struct Particle {
|
||||
|
|
@ -38,11 +38,18 @@ private:
|
|||
static constexpr int MAX_DUST_PARTICLES = 300;
|
||||
std::vector<Particle> particles;
|
||||
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
std::unique_ptr<Shader> shader;
|
||||
std::vector<float> vertexData;
|
||||
// Vulkan objects
|
||||
VkContext* vkCtx = nullptr;
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
// Dynamic mapped buffer for particle vertex data (updated every frame)
|
||||
::VkBuffer dynamicVB = VK_NULL_HANDLE;
|
||||
VmaAllocation dynamicVBAlloc = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo dynamicVBAllocInfo{};
|
||||
VkDeviceSize dynamicVBSize = 0;
|
||||
|
||||
std::vector<float> vertexData;
|
||||
float spawnAccum = 0.0f;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rendering/vk_texture.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* Renders quest markers as billboarded sprites above NPCs
|
||||
|
|
@ -20,7 +25,7 @@ public:
|
|||
QuestMarkerRenderer();
|
||||
~QuestMarkerRenderer();
|
||||
|
||||
bool initialize(pipeline::AssetManager* assetManager);
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout, pipeline::AssetManager* assetManager);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
|
|
@ -44,8 +49,11 @@ public:
|
|||
|
||||
/**
|
||||
* Render all quest markers (call after world rendering, before UI)
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0, contains camera UBO)
|
||||
* @param camera Camera for billboard calculation (CPU-side view matrix)
|
||||
*/
|
||||
void render(const Camera& camera);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera);
|
||||
|
||||
private:
|
||||
struct Marker {
|
||||
|
|
@ -55,16 +63,29 @@ private:
|
|||
};
|
||||
|
||||
std::unordered_map<uint64_t, Marker> markers_;
|
||||
|
||||
// OpenGL resources
|
||||
uint32_t vao_ = 0;
|
||||
uint32_t vbo_ = 0;
|
||||
uint32_t shaderProgram_ = 0;
|
||||
uint32_t textures_[3] = {0, 0, 0}; // available, turnin, incomplete
|
||||
|
||||
// Vulkan context
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
|
||||
// Pipeline
|
||||
VkPipeline pipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor resources for per-material texture (set 1)
|
||||
VkDescriptorSetLayout materialSetLayout_ = VK_NULL_HANDLE;
|
||||
VkDescriptorPool descriptorPool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSet texDescSets_[3] = {VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
|
||||
// Textures: available, turnin, incomplete
|
||||
VkTexture textures_[3];
|
||||
|
||||
// Quad vertex buffer
|
||||
VkBuffer quadVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation quadVBAlloc_ = VK_NULL_HANDLE;
|
||||
|
||||
void createQuad();
|
||||
void loadTextures(pipeline::AssetManager* assetManager);
|
||||
void createShader();
|
||||
void createDescriptorResources();
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
|
|
@ -5,9 +5,14 @@
|
|||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include "rendering/vk_frame_data.hpp"
|
||||
#include "rendering/sky_system.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace core { class Window; }
|
||||
namespace rendering { class VkContext; }
|
||||
namespace game { class World; class ZoneManager; class GameHandler; }
|
||||
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; }
|
||||
namespace pipeline { class AssetManager; }
|
||||
|
|
@ -28,7 +33,6 @@ class Clouds;
|
|||
class LensFlare;
|
||||
class Weather;
|
||||
class LightingManager;
|
||||
class SkySystem;
|
||||
class SwimEffects;
|
||||
class MountDust;
|
||||
class LevelUpEffect;
|
||||
|
|
@ -37,6 +41,7 @@ class CharacterRenderer;
|
|||
class WMORenderer;
|
||||
class M2Renderer;
|
||||
class Minimap;
|
||||
class WorldMap;
|
||||
class QuestMarkerRenderer;
|
||||
class Shader;
|
||||
|
||||
|
|
@ -101,19 +106,23 @@ public:
|
|||
TerrainManager* getTerrainManager() const { return terrainManager.get(); }
|
||||
PerformanceHUD* getPerformanceHUD() { return performanceHUD.get(); }
|
||||
WaterRenderer* getWaterRenderer() const { return waterRenderer.get(); }
|
||||
Skybox* getSkybox() const { return skybox.get(); }
|
||||
Celestial* getCelestial() const { return celestial.get(); }
|
||||
StarField* getStarField() const { return starField.get(); }
|
||||
Clouds* getClouds() const { return clouds.get(); }
|
||||
LensFlare* getLensFlare() const { return lensFlare.get(); }
|
||||
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; }
|
||||
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(); }
|
||||
WorldMap* getWorldMap() const { return worldMap.get(); }
|
||||
QuestMarkerRenderer* getQuestMarkerRenderer() const { return questMarkerRenderer.get(); }
|
||||
SkySystem* getSkySystem() const { return skySystem.get(); }
|
||||
const std::string& getCurrentZoneName() const { return currentZoneName; }
|
||||
VkContext* getVkContext() const { return vkCtx; }
|
||||
VkDescriptorSetLayout getPerFrameSetLayout() const { return perFrameSetLayout; }
|
||||
VkRenderPass getShadowRenderPass() const { return shadowRenderPass; }
|
||||
|
||||
// Third-person character follow
|
||||
void setCharacterFollow(uint32_t instanceId);
|
||||
|
|
@ -202,6 +211,7 @@ private:
|
|||
std::unique_ptr<WMORenderer> wmoRenderer;
|
||||
std::unique_ptr<M2Renderer> m2Renderer;
|
||||
std::unique_ptr<Minimap> minimap;
|
||||
std::unique_ptr<WorldMap> worldMap;
|
||||
std::unique_ptr<QuestMarkerRenderer> questMarkerRenderer;
|
||||
std::unique_ptr<audio::MusicManager> musicManager;
|
||||
std::unique_ptr<audio::FootstepManager> footstepManager;
|
||||
|
|
@ -214,31 +224,14 @@ private:
|
|||
std::unique_ptr<audio::SpellSoundManager> spellSoundManager;
|
||||
std::unique_ptr<audio::MovementSoundManager> movementSoundManager;
|
||||
std::unique_ptr<game::ZoneManager> zoneManager;
|
||||
std::unique_ptr<Shader> underwaterOverlayShader;
|
||||
uint32_t underwaterOverlayVAO = 0;
|
||||
uint32_t underwaterOverlayVBO = 0;
|
||||
|
||||
// Post-process FBO pipeline (HDR MSAA → resolve → tonemap)
|
||||
uint32_t sceneFBO = 0; // MSAA render target
|
||||
uint32_t sceneColorRBO = 0; // GL_RGBA16F multisampled renderbuffer
|
||||
uint32_t sceneDepthRBO = 0; // GL_DEPTH_COMPONENT24 multisampled renderbuffer
|
||||
uint32_t resolveFBO = 0; // Non-MSAA resolve target
|
||||
uint32_t resolveColorTex = 0; // GL_RGBA16F resolved texture (sampled by post-process)
|
||||
uint32_t resolveDepthTex = 0; // GL_DEPTH_COMPONENT24 resolved texture (for future SSAO)
|
||||
uint32_t screenQuadVAO = 0;
|
||||
uint32_t screenQuadVBO = 0;
|
||||
std::unique_ptr<Shader> postProcessShader;
|
||||
int fbWidth = 0, fbHeight = 0;
|
||||
|
||||
void initPostProcess(int w, int h);
|
||||
void resizePostProcess(int w, int h);
|
||||
void shutdownPostProcess();
|
||||
|
||||
// Shadow mapping
|
||||
static constexpr int SHADOW_MAP_SIZE = 2048;
|
||||
uint32_t shadowFBO = 0;
|
||||
uint32_t shadowDepthTex = 0;
|
||||
uint32_t shadowShaderProgram = 0;
|
||||
// Shadow mapping (Vulkan)
|
||||
static constexpr uint32_t SHADOW_MAP_SIZE = 2048;
|
||||
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;
|
||||
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
|
||||
glm::vec3 shadowCenter = glm::vec3(0.0f);
|
||||
bool shadowCenterInitialized = false;
|
||||
|
|
@ -250,9 +243,7 @@ public:
|
|||
bool areShadowsEnabled() const { return shadowsEnabled; }
|
||||
|
||||
private:
|
||||
void initShadowMap();
|
||||
void renderShadowPass();
|
||||
uint32_t compileShadowShader();
|
||||
glm::mat4 computeLightSpaceMatrix();
|
||||
|
||||
pipeline::AssetManager* cachedAssetManager = nullptr;
|
||||
|
|
@ -289,10 +280,13 @@ private:
|
|||
const glm::vec3* targetPosition = nullptr;
|
||||
bool inCombat_ = false;
|
||||
|
||||
// Selection circle rendering
|
||||
uint32_t selCircleVAO = 0;
|
||||
uint32_t selCircleVBO = 0;
|
||||
uint32_t selCircleShader = 0;
|
||||
// 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;
|
||||
int selCircleVertCount = 0;
|
||||
void initSelectionCircle();
|
||||
void renderSelectionCircle(const glm::mat4& view, const glm::mat4& projection);
|
||||
|
|
@ -360,6 +354,26 @@ private:
|
|||
bool taxiFlight_ = false;
|
||||
bool taxiAnimsLogged_ = false;
|
||||
|
||||
// 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;
|
||||
|
||||
bool createPerFrameResources();
|
||||
void destroyPerFrameResources();
|
||||
void updatePerFrameUBO();
|
||||
|
||||
bool terrainEnabled = true;
|
||||
bool terrainLoaded = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class VkContext;
|
||||
class Skybox;
|
||||
class Celestial;
|
||||
class StarField;
|
||||
|
|
@ -59,9 +61,11 @@ public:
|
|||
~SkySystem();
|
||||
|
||||
/**
|
||||
* Initialize sky system components
|
||||
* Initialize sky system components.
|
||||
* @param ctx Vulkan context (required for Vulkan renderers)
|
||||
* @param perFrameLayout Descriptor set layout for set 0 (camera UBO)
|
||||
*/
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx = nullptr, VkDescriptorSetLayout perFrameLayout = VK_NULL_HANDLE);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
|
|
@ -70,11 +74,14 @@ public:
|
|||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Render complete sky
|
||||
* @param camera Camera for view/projection
|
||||
* @param params Sky parameters from lighting system
|
||||
* Render complete sky.
|
||||
* @param cmd Active Vulkan command buffer
|
||||
* @param perFrameSet Per-frame descriptor set (set 0, camera UBO)
|
||||
* @param camera Camera for legacy sub-renderers (lens flare, etc.)
|
||||
* @param params Sky parameters from lighting system
|
||||
*/
|
||||
void render(const Camera& camera, const SkyParams& params);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||
const Camera& camera, const SkyParams& params);
|
||||
|
||||
/**
|
||||
* Enable/disable procedural stars (DEBUG/FALLBACK)
|
||||
|
|
@ -109,21 +116,21 @@ public:
|
|||
float getBlueChildPhase() const;
|
||||
|
||||
// Component accessors (for direct control if needed)
|
||||
Skybox* getSkybox() const { return skybox_.get(); }
|
||||
Skybox* getSkybox() const { return skybox_.get(); }
|
||||
Celestial* getCelestial() const { return celestial_.get(); }
|
||||
StarField* getStarField() const { return starField_.get(); }
|
||||
Clouds* getClouds() const { return clouds_.get(); }
|
||||
Clouds* getClouds() const { return clouds_.get(); }
|
||||
LensFlare* getLensFlare() const { return lensFlare_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Skybox> skybox_; // Authoritative sky (gradient now, M2 models later)
|
||||
std::unique_ptr<Celestial> celestial_; // Sun + 2 moons
|
||||
std::unique_ptr<StarField> starField_; // Fallback procedural stars
|
||||
std::unique_ptr<Clouds> clouds_; // Cloud layer
|
||||
std::unique_ptr<LensFlare> lensFlare_; // Sun lens flare
|
||||
std::unique_ptr<Skybox> skybox_; // Authoritative sky
|
||||
std::unique_ptr<Celestial> celestial_; // Sun + 2 moons
|
||||
std::unique_ptr<StarField> starField_; // Fallback procedural stars
|
||||
std::unique_ptr<Clouds> clouds_; // Cloud layer
|
||||
std::unique_ptr<LensFlare> lensFlare_; // Sun lens flare
|
||||
|
||||
bool proceduralStarsEnabled_ = false; // Default: OFF (skybox is authoritative)
|
||||
bool debugSkyMode_ = false; // Force procedural stars for debugging
|
||||
bool proceduralStarsEnabled_ = false;
|
||||
bool debugSkyMode_ = false;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* Skybox renderer
|
||||
|
|
@ -20,15 +21,16 @@ public:
|
|||
Skybox();
|
||||
~Skybox();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Render the skybox
|
||||
* @param camera Camera for view matrix (position is ignored for skybox)
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0, contains camera UBO)
|
||||
* @param timeOfDay Time of day in hours (0-24), affects sky color
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay = 12.0f);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay = 12.0f);
|
||||
|
||||
/**
|
||||
* Enable/disable skybox rendering
|
||||
|
|
@ -66,11 +68,16 @@ private:
|
|||
glm::vec3 getSkyColor(float altitude, float time) const;
|
||||
glm::vec3 getZenithColor(float time) const;
|
||||
|
||||
std::unique_ptr<Shader> skyShader;
|
||||
VkContext* vkCtx = nullptr;
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
VkBuffer indexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
int indexCount = 0;
|
||||
|
||||
float timeOfDay = 12.0f; // Default: noon
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* Star field renderer
|
||||
|
|
@ -21,17 +21,18 @@ public:
|
|||
StarField();
|
||||
~StarField();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Render the star field
|
||||
* @param camera Camera for view matrix
|
||||
* @param timeOfDay Time of day in hours (0-24)
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0, contains camera UBO)
|
||||
* @param timeOfDay Time of day in hours (0-24)
|
||||
* @param cloudDensity Optional cloud density from lighting (0-1, reduces star visibility)
|
||||
* @param fogDensity Optional fog density from lighting (reduces star visibility)
|
||||
* @param fogDensity Optional fog density from lighting (reduces star visibility)
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay,
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay,
|
||||
float cloudDensity = 0.0f, float fogDensity = 0.0f);
|
||||
|
||||
/**
|
||||
|
|
@ -57,8 +58,6 @@ private:
|
|||
|
||||
float getStarIntensity(float timeOfDay) const;
|
||||
|
||||
std::unique_ptr<Shader> starShader;
|
||||
|
||||
struct Star {
|
||||
glm::vec3 position;
|
||||
float brightness; // 0.3 to 1.0
|
||||
|
|
@ -68,8 +67,13 @@ private:
|
|||
std::vector<Star> stars;
|
||||
int starCount = 1000;
|
||||
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
VkContext* vkCtx = nullptr;
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
|
||||
float twinkleTime = 0.0f;
|
||||
bool renderingEnabled = true;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
|
|
@ -11,18 +11,18 @@ namespace rendering {
|
|||
class Camera;
|
||||
class CameraController;
|
||||
class WaterRenderer;
|
||||
class Shader;
|
||||
class VkContext;
|
||||
|
||||
class SwimEffects {
|
||||
public:
|
||||
SwimEffects();
|
||||
~SwimEffects();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
void update(const Camera& camera, const CameraController& cc,
|
||||
const WaterRenderer& water, float deltaTime);
|
||||
void render(const Camera& camera);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
private:
|
||||
struct Particle {
|
||||
|
|
@ -40,10 +40,24 @@ private:
|
|||
std::vector<Particle> ripples;
|
||||
std::vector<Particle> bubbles;
|
||||
|
||||
GLuint rippleVAO = 0, rippleVBO = 0;
|
||||
GLuint bubbleVAO = 0, bubbleVBO = 0;
|
||||
std::unique_ptr<Shader> rippleShader;
|
||||
std::unique_ptr<Shader> bubbleShader;
|
||||
// Vulkan objects
|
||||
VkContext* vkCtx = nullptr;
|
||||
|
||||
// Ripple pipeline + dynamic buffer
|
||||
VkPipeline ripplePipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout ripplePipelineLayout = VK_NULL_HANDLE;
|
||||
::VkBuffer rippleDynamicVB = VK_NULL_HANDLE;
|
||||
VmaAllocation rippleDynamicVBAlloc = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo rippleDynamicVBAllocInfo{};
|
||||
VkDeviceSize rippleDynamicVBSize = 0;
|
||||
|
||||
// Bubble pipeline + dynamic buffer
|
||||
VkPipeline bubblePipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout bubblePipelineLayout = VK_NULL_HANDLE;
|
||||
::VkBuffer bubbleDynamicVB = VK_NULL_HANDLE;
|
||||
VmaAllocation bubbleDynamicVBAlloc = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo bubbleDynamicVBAllocInfo{};
|
||||
VkDeviceSize bubbleDynamicVBSize = 0;
|
||||
|
||||
std::vector<float> rippleVertexData;
|
||||
std::vector<float> bubbleVertexData;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
#include "pipeline/terrain_mesh.hpp"
|
||||
#include "pipeline/blp_loader.hpp"
|
||||
#include "rendering/shader.hpp"
|
||||
#include "rendering/texture.hpp"
|
||||
#include "rendering/camera.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
|
@ -18,21 +17,35 @@ namespace pipeline { class AssetManager; }
|
|||
|
||||
namespace rendering {
|
||||
|
||||
class VkContext;
|
||||
class VkTexture;
|
||||
class Frustum;
|
||||
|
||||
/**
|
||||
* GPU-side terrain chunk data
|
||||
* GPU-side terrain chunk data (Vulkan)
|
||||
*/
|
||||
struct TerrainChunkGPU {
|
||||
GLuint vao = 0; // Vertex array object
|
||||
GLuint vbo = 0; // Vertex buffer
|
||||
GLuint ibo = 0; // Index buffer
|
||||
uint32_t indexCount = 0; // Number of indices to draw
|
||||
::VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
::VkBuffer indexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc = VK_NULL_HANDLE;
|
||||
uint32_t indexCount = 0;
|
||||
|
||||
// Texture IDs for this chunk
|
||||
GLuint baseTexture = 0;
|
||||
std::vector<GLuint> layerTextures;
|
||||
std::vector<GLuint> alphaTextures;
|
||||
// Material descriptor set (set 1: 7 samplers + params UBO)
|
||||
VkDescriptorSet materialSet = VK_NULL_HANDLE;
|
||||
|
||||
// Per-chunk params UBO (hasLayer1/2/3)
|
||||
::VkBuffer paramsUBO = VK_NULL_HANDLE;
|
||||
VmaAllocation paramsAlloc = VK_NULL_HANDLE;
|
||||
|
||||
// Texture handles (owned by cache, NOT destroyed per-chunk)
|
||||
VkTexture* baseTexture = nullptr;
|
||||
VkTexture* layerTextures[3] = {nullptr, nullptr, nullptr};
|
||||
VkTexture* alphaTextures[3] = {nullptr, nullptr, nullptr};
|
||||
int layerCount = 0;
|
||||
|
||||
// Per-chunk alpha textures (owned by this chunk, destroyed on removal)
|
||||
std::vector<std::unique_ptr<VkTexture>> ownedAlphaTextures;
|
||||
|
||||
// World position for culling
|
||||
float worldX = 0.0f;
|
||||
|
|
@ -46,13 +59,11 @@ struct TerrainChunkGPU {
|
|||
float boundingSphereRadius = 0.0f;
|
||||
glm::vec3 boundingSphereCenter = glm::vec3(0.0f);
|
||||
|
||||
bool isValid() const { return vao != 0 && vbo != 0 && ibo != 0; }
|
||||
bool isValid() const { return vertexBuffer != VK_NULL_HANDLE && indexBuffer != VK_NULL_HANDLE; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Terrain renderer
|
||||
*
|
||||
* Handles uploading terrain meshes to GPU and rendering them
|
||||
* Terrain renderer (Vulkan)
|
||||
*/
|
||||
class TerrainRenderer {
|
||||
public:
|
||||
|
|
@ -61,150 +72,92 @@ public:
|
|||
|
||||
/**
|
||||
* Initialize terrain renderer
|
||||
* @param ctx Vulkan context
|
||||
* @param perFrameLayout Descriptor set layout for set 0 (per-frame UBO)
|
||||
* @param assetManager Asset manager for loading textures
|
||||
*/
|
||||
bool initialize(pipeline::AssetManager* assetManager);
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout,
|
||||
pipeline::AssetManager* assetManager);
|
||||
|
||||
/**
|
||||
* Shutdown and cleanup GPU resources
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load terrain mesh and upload to GPU
|
||||
* @param mesh Terrain mesh to load
|
||||
* @param texturePaths Texture file paths from ADT
|
||||
* @param tileX Tile X coordinate for tracking ownership (-1 = untracked)
|
||||
* @param tileY Tile Y coordinate for tracking ownership (-1 = untracked)
|
||||
*/
|
||||
bool loadTerrain(const pipeline::TerrainMesh& mesh,
|
||||
const std::vector<std::string>& texturePaths,
|
||||
int tileX = -1, int tileY = -1);
|
||||
|
||||
/**
|
||||
* Remove all chunks belonging to a specific tile
|
||||
* @param tileX Tile X coordinate
|
||||
* @param tileY Tile Y coordinate
|
||||
*/
|
||||
void removeTile(int tileX, int tileY);
|
||||
|
||||
/**
|
||||
* Upload pre-loaded BLP textures to the GL texture cache.
|
||||
* Called before loadTerrain() so texture loading avoids file I/O.
|
||||
*/
|
||||
void uploadPreloadedTextures(const std::unordered_map<std::string, pipeline::BLPImage>& textures);
|
||||
|
||||
/**
|
||||
* Render loaded terrain
|
||||
* @param camera Camera for view/projection matrices
|
||||
* Render terrain
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0)
|
||||
* @param camera Camera for frustum culling
|
||||
*/
|
||||
void render(const Camera& camera);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera);
|
||||
|
||||
/**
|
||||
* Clear all loaded terrain
|
||||
* Render terrain into shadow depth map (Phase 6 stub)
|
||||
*/
|
||||
void renderShadow(VkCommandBuffer cmd, const glm::vec3& shadowCenter, float halfExtent);
|
||||
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Set lighting parameters
|
||||
*/
|
||||
void setLighting(const float lightDir[3], const float lightColor[3],
|
||||
const float ambientColor[3]);
|
||||
|
||||
/**
|
||||
* Set fog parameters
|
||||
*/
|
||||
void setFog(const float fogColor[3], float fogStart, float fogEnd);
|
||||
|
||||
/**
|
||||
* Enable/disable wireframe rendering
|
||||
*/
|
||||
void setWireframe(bool enabled) { wireframe = enabled; }
|
||||
|
||||
/**
|
||||
* Enable/disable frustum culling
|
||||
*/
|
||||
void setFrustumCulling(bool enabled) { frustumCullingEnabled = enabled; }
|
||||
|
||||
/**
|
||||
* Enable/disable distance fog
|
||||
*/
|
||||
void setFogEnabled(bool enabled) { fogEnabled = enabled; }
|
||||
bool isFogEnabled() const { return fogEnabled; }
|
||||
|
||||
/**
|
||||
* Render terrain geometry into shadow depth map
|
||||
*/
|
||||
void renderShadow(GLuint shaderProgram, const glm::vec3& shadowCenter, float halfExtent);
|
||||
// Shadow mapping stubs (Phase 6)
|
||||
void setShadowMap(VkDescriptorImageInfo /*depthInfo*/, const glm::mat4& /*lightSpaceMat*/) {}
|
||||
void clearShadowMap() {}
|
||||
|
||||
/**
|
||||
* Set shadow map for receiving shadows
|
||||
*/
|
||||
void setShadowMap(GLuint depthTex, const glm::mat4& lightSpaceMat) {
|
||||
shadowDepthTex = depthTex; lightSpaceMatrix = lightSpaceMat; shadowEnabled = true;
|
||||
}
|
||||
void clearShadowMap() { shadowEnabled = false; }
|
||||
|
||||
/**
|
||||
* Get statistics
|
||||
*/
|
||||
int getChunkCount() const { return static_cast<int>(chunks.size()); }
|
||||
int getRenderedChunkCount() const { return renderedChunks; }
|
||||
int getCulledChunkCount() const { return culledChunks; }
|
||||
int getTriangleCount() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Upload single chunk to GPU
|
||||
*/
|
||||
TerrainChunkGPU uploadChunk(const pipeline::ChunkMesh& chunk);
|
||||
|
||||
/**
|
||||
* Load texture from asset manager
|
||||
*/
|
||||
GLuint loadTexture(const std::string& path);
|
||||
|
||||
/**
|
||||
* Create alpha texture from raw alpha data
|
||||
*/
|
||||
GLuint createAlphaTexture(const std::vector<uint8_t>& alphaData);
|
||||
|
||||
/**
|
||||
* Check if chunk is in view frustum
|
||||
*/
|
||||
VkTexture* loadTexture(const std::string& path);
|
||||
VkTexture* createAlphaTexture(const std::vector<uint8_t>& alphaData);
|
||||
bool isChunkVisible(const TerrainChunkGPU& chunk, const Frustum& frustum);
|
||||
|
||||
/**
|
||||
* Calculate bounding sphere for chunk
|
||||
*/
|
||||
void calculateBoundingSphere(TerrainChunkGPU& chunk, const pipeline::ChunkMesh& meshChunk);
|
||||
VkDescriptorSet allocateMaterialSet();
|
||||
void writeMaterialDescriptors(VkDescriptorSet set, const TerrainChunkGPU& chunk);
|
||||
void destroyChunkGPU(TerrainChunkGPU& chunk);
|
||||
|
||||
VkContext* vkCtx = nullptr;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
// Pipeline
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkPipeline wireframePipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout materialSetLayout = VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor pool for material sets
|
||||
VkDescriptorPool materialDescPool = VK_NULL_HANDLE;
|
||||
static constexpr uint32_t MAX_MATERIAL_SETS = 8192;
|
||||
|
||||
// Loaded terrain chunks
|
||||
std::vector<TerrainChunkGPU> chunks;
|
||||
|
||||
// Texture cache (path -> GL texture ID)
|
||||
// Texture cache (path -> VkTexture)
|
||||
struct TextureCacheEntry {
|
||||
GLuint id = 0;
|
||||
std::unique_ptr<VkTexture> texture;
|
||||
size_t approxBytes = 0;
|
||||
uint64_t lastUse = 0;
|
||||
};
|
||||
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
||||
size_t textureCacheBytes_ = 0;
|
||||
uint64_t textureCacheCounter_ = 0;
|
||||
size_t textureCacheBudgetBytes_ = 4096ull * 1024 * 1024; // Default, overridden at init
|
||||
size_t textureCacheBudgetBytes_ = 4096ull * 1024 * 1024;
|
||||
|
||||
// Lighting parameters
|
||||
float lightDir[3] = {-0.5f, -1.0f, -0.5f};
|
||||
float lightColor[3] = {1.0f, 1.0f, 0.9f};
|
||||
float ambientColor[3] = {0.3f, 0.3f, 0.35f};
|
||||
|
||||
// Fog parameters
|
||||
float fogColor[3] = {0.5f, 0.6f, 0.7f};
|
||||
float fogStart = 400.0f;
|
||||
float fogEnd = 800.0f;
|
||||
// Fallback textures
|
||||
std::unique_ptr<VkTexture> whiteTexture;
|
||||
std::unique_ptr<VkTexture> opaqueAlphaTexture;
|
||||
|
||||
// Rendering state
|
||||
bool wireframe = false;
|
||||
|
|
@ -212,16 +165,6 @@ private:
|
|||
bool fogEnabled = true;
|
||||
int renderedChunks = 0;
|
||||
int culledChunks = 0;
|
||||
|
||||
// Default white texture (fallback)
|
||||
GLuint whiteTexture = 0;
|
||||
// Opaque alpha fallback for missing/invalid layer alpha maps
|
||||
GLuint opaqueAlphaTexture = 0;
|
||||
|
||||
// Shadow mapping (receiving)
|
||||
GLuint shadowDepthTex = 0;
|
||||
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
|
||||
bool shadowEnabled = false;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
55
include/rendering/vk_buffer.hpp
Normal file
55
include/rendering/vk_buffer.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include "rendering/vk_utils.hpp"
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VkContext;
|
||||
|
||||
// RAII wrapper for a Vulkan buffer with VMA allocation.
|
||||
// Supports vertex, index, uniform, and storage buffer usage.
|
||||
class VkBuffer {
|
||||
public:
|
||||
VkBuffer() = default;
|
||||
~VkBuffer();
|
||||
|
||||
VkBuffer(const VkBuffer&) = delete;
|
||||
VkBuffer& operator=(const VkBuffer&) = delete;
|
||||
VkBuffer(VkBuffer&& other) noexcept;
|
||||
VkBuffer& operator=(VkBuffer&& other) noexcept;
|
||||
|
||||
// Create a GPU-local buffer and upload data via staging
|
||||
bool uploadToGPU(VkContext& ctx, const void* data, VkDeviceSize size,
|
||||
VkBufferUsageFlags usage);
|
||||
|
||||
// Create a host-visible buffer (for uniform/dynamic data updated each frame)
|
||||
bool createMapped(VmaAllocator allocator, VkDeviceSize size,
|
||||
VkBufferUsageFlags usage);
|
||||
|
||||
// Update mapped buffer contents (only valid for mapped buffers)
|
||||
void updateMapped(const void* data, VkDeviceSize size, VkDeviceSize offset = 0);
|
||||
|
||||
void destroy();
|
||||
|
||||
::VkBuffer getBuffer() const { return buf_.buffer; }
|
||||
VkDeviceSize getSize() const { return size_; }
|
||||
void* getMappedData() const { return buf_.info.pMappedData; }
|
||||
bool isValid() const { return buf_.buffer != VK_NULL_HANDLE; }
|
||||
|
||||
// Descriptor info for uniform/storage buffer binding
|
||||
VkDescriptorBufferInfo descriptorInfo(VkDeviceSize offset = 0,
|
||||
VkDeviceSize range = VK_WHOLE_SIZE) const;
|
||||
|
||||
private:
|
||||
AllocatedBuffer buf_{};
|
||||
VmaAllocator allocator_ = VK_NULL_HANDLE;
|
||||
VkDeviceSize size_ = 0;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
141
include/rendering/vk_context.hpp
Normal file
141
include/rendering/vk_context.hpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <VkBootstrap.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
struct FrameData {
|
||||
VkCommandPool commandPool = VK_NULL_HANDLE;
|
||||
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
||||
VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE;
|
||||
VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE;
|
||||
VkFence inFlightFence = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
class VkContext {
|
||||
public:
|
||||
VkContext() = default;
|
||||
~VkContext();
|
||||
|
||||
VkContext(const VkContext&) = delete;
|
||||
VkContext& operator=(const VkContext&) = delete;
|
||||
|
||||
bool initialize(SDL_Window* window);
|
||||
void shutdown();
|
||||
|
||||
// Swapchain management
|
||||
bool recreateSwapchain(int width, int height);
|
||||
|
||||
// Frame operations
|
||||
VkCommandBuffer beginFrame(uint32_t& imageIndex);
|
||||
void endFrame(VkCommandBuffer cmd, uint32_t imageIndex);
|
||||
|
||||
// Single-time command buffer helpers
|
||||
VkCommandBuffer beginSingleTimeCommands();
|
||||
void endSingleTimeCommands(VkCommandBuffer cmd);
|
||||
|
||||
// Immediate submit for one-off GPU work (descriptor pool creation, etc.)
|
||||
void immediateSubmit(std::function<void(VkCommandBuffer cmd)>&& function);
|
||||
|
||||
// Accessors
|
||||
VkInstance getInstance() const { return instance; }
|
||||
VkPhysicalDevice getPhysicalDevice() const { return physicalDevice; }
|
||||
VkDevice getDevice() const { return device; }
|
||||
VkQueue getGraphicsQueue() const { return graphicsQueue; }
|
||||
uint32_t getGraphicsQueueFamily() const { return graphicsQueueFamily; }
|
||||
VmaAllocator getAllocator() const { return allocator; }
|
||||
VkSurfaceKHR getSurface() const { return surface; }
|
||||
|
||||
VkSwapchainKHR getSwapchain() const { return swapchain; }
|
||||
VkFormat getSwapchainFormat() const { return swapchainFormat; }
|
||||
VkExtent2D getSwapchainExtent() const { return swapchainExtent; }
|
||||
const std::vector<VkImageView>& getSwapchainImageViews() const { return swapchainImageViews; }
|
||||
uint32_t getSwapchainImageCount() const { return static_cast<uint32_t>(swapchainImages.size()); }
|
||||
|
||||
uint32_t getCurrentFrame() const { return currentFrame; }
|
||||
const FrameData& getCurrentFrameData() const { return frames[currentFrame]; }
|
||||
|
||||
// For ImGui
|
||||
VkRenderPass getImGuiRenderPass() const { return imguiRenderPass; }
|
||||
VkDescriptorPool getImGuiDescriptorPool() const { return imguiDescriptorPool; }
|
||||
const std::vector<VkFramebuffer>& getSwapchainFramebuffers() const { return swapchainFramebuffers; }
|
||||
|
||||
bool isSwapchainDirty() const { return swapchainDirty; }
|
||||
|
||||
private:
|
||||
bool createInstance(SDL_Window* window);
|
||||
bool createSurface(SDL_Window* window);
|
||||
bool selectPhysicalDevice();
|
||||
bool createLogicalDevice();
|
||||
bool createAllocator();
|
||||
bool createSwapchain(int width, int height);
|
||||
void destroySwapchain();
|
||||
bool createCommandPools();
|
||||
bool createSyncObjects();
|
||||
bool createImGuiResources();
|
||||
void destroyImGuiResources();
|
||||
|
||||
// vk-bootstrap objects (kept alive for swapchain recreation etc.)
|
||||
vkb::Instance vkbInstance_;
|
||||
vkb::PhysicalDevice vkbPhysicalDevice_;
|
||||
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VmaAllocator allocator = VK_NULL_HANDLE;
|
||||
|
||||
VkQueue graphicsQueue = VK_NULL_HANDLE;
|
||||
VkQueue presentQueue = VK_NULL_HANDLE;
|
||||
uint32_t graphicsQueueFamily = 0;
|
||||
uint32_t presentQueueFamily = 0;
|
||||
|
||||
// Swapchain
|
||||
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
|
||||
VkFormat swapchainFormat = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D swapchainExtent = {0, 0};
|
||||
std::vector<VkImage> swapchainImages;
|
||||
std::vector<VkImageView> swapchainImageViews;
|
||||
std::vector<VkFramebuffer> swapchainFramebuffers;
|
||||
bool swapchainDirty = false;
|
||||
|
||||
// Per-frame resources
|
||||
FrameData frames[MAX_FRAMES_IN_FLIGHT];
|
||||
uint32_t currentFrame = 0;
|
||||
|
||||
// Immediate submit resources
|
||||
VkCommandPool immCommandPool = VK_NULL_HANDLE;
|
||||
VkFence immFence = VK_NULL_HANDLE;
|
||||
|
||||
// Depth buffer (shared across all framebuffers)
|
||||
VkImage depthImage = VK_NULL_HANDLE;
|
||||
VkImageView depthImageView = VK_NULL_HANDLE;
|
||||
VmaAllocation depthAllocation = VK_NULL_HANDLE;
|
||||
VkFormat depthFormat = VK_FORMAT_D32_SFLOAT;
|
||||
|
||||
bool createDepthBuffer();
|
||||
void destroyDepthBuffer();
|
||||
|
||||
// ImGui resources
|
||||
VkRenderPass imguiRenderPass = VK_NULL_HANDLE;
|
||||
VkDescriptorPool imguiDescriptorPool = VK_NULL_HANDLE;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool enableValidation = true;
|
||||
#else
|
||||
bool enableValidation = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
29
include/rendering/vk_frame_data.hpp
Normal file
29
include/rendering/vk_frame_data.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
// Must match the PerFrame UBO layout in all shaders (std140 alignment)
|
||||
struct GPUPerFrameData {
|
||||
glm::mat4 view;
|
||||
glm::mat4 projection;
|
||||
glm::mat4 lightSpaceMatrix;
|
||||
glm::vec4 lightDir; // xyz = direction, w = unused
|
||||
glm::vec4 lightColor; // xyz = color, w = unused
|
||||
glm::vec4 ambientColor; // xyz = color, w = unused
|
||||
glm::vec4 viewPos; // xyz = camera pos, w = unused
|
||||
glm::vec4 fogColor; // xyz = color, w = unused
|
||||
glm::vec4 fogParams; // x = fogStart, y = fogEnd, z = time, w = unused
|
||||
glm::vec4 shadowParams; // x = enabled(0/1), y = strength, z = unused, w = unused
|
||||
};
|
||||
|
||||
// Push constants for the model matrix (most common case)
|
||||
struct GPUPushConstants {
|
||||
glm::mat4 model;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
118
include/rendering/vk_pipeline.hpp
Normal file
118
include/rendering/vk_pipeline.hpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
// Builder pattern for VkGraphicsPipeline creation.
|
||||
// Usage:
|
||||
// auto pipeline = PipelineBuilder()
|
||||
// .setShaders(vertStage, fragStage)
|
||||
// .setVertexInput(bindings, attributes)
|
||||
// .setTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
|
||||
// .setRasterization(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT)
|
||||
// .setDepthTest(true, true, VK_COMPARE_OP_LESS)
|
||||
// .setColorBlendAttachment(PipelineBuilder::blendAlpha())
|
||||
// .setLayout(pipelineLayout)
|
||||
// .setRenderPass(renderPass)
|
||||
// .build(device);
|
||||
|
||||
class PipelineBuilder {
|
||||
public:
|
||||
PipelineBuilder();
|
||||
|
||||
// Shader stages
|
||||
PipelineBuilder& setShaders(VkPipelineShaderStageCreateInfo vert,
|
||||
VkPipelineShaderStageCreateInfo frag);
|
||||
|
||||
// Vertex input
|
||||
PipelineBuilder& setVertexInput(
|
||||
const std::vector<VkVertexInputBindingDescription>& bindings,
|
||||
const std::vector<VkVertexInputAttributeDescription>& attributes);
|
||||
|
||||
// No vertex input (fullscreen quad generated in vertex shader)
|
||||
PipelineBuilder& setNoVertexInput();
|
||||
|
||||
// Input assembly
|
||||
PipelineBuilder& setTopology(VkPrimitiveTopology topology,
|
||||
VkBool32 primitiveRestart = VK_FALSE);
|
||||
|
||||
// Rasterization
|
||||
PipelineBuilder& setRasterization(VkPolygonMode polygonMode,
|
||||
VkCullModeFlags cullMode,
|
||||
VkFrontFace frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE);
|
||||
|
||||
// Depth test/write
|
||||
PipelineBuilder& setDepthTest(bool enable, bool writeEnable,
|
||||
VkCompareOp compareOp = VK_COMPARE_OP_LESS);
|
||||
|
||||
// No depth test (default)
|
||||
PipelineBuilder& setNoDepthTest();
|
||||
|
||||
// Depth bias (for shadow maps)
|
||||
PipelineBuilder& setDepthBias(float constantFactor, float slopeFactor);
|
||||
|
||||
// Color blend attachment
|
||||
PipelineBuilder& setColorBlendAttachment(
|
||||
VkPipelineColorBlendAttachmentState blendState);
|
||||
|
||||
// No color attachment (depth-only pass)
|
||||
PipelineBuilder& setNoColorAttachment();
|
||||
|
||||
// Multisampling
|
||||
PipelineBuilder& setMultisample(VkSampleCountFlagBits samples);
|
||||
|
||||
// Pipeline layout
|
||||
PipelineBuilder& setLayout(VkPipelineLayout layout);
|
||||
|
||||
// Render pass
|
||||
PipelineBuilder& setRenderPass(VkRenderPass renderPass, uint32_t subpass = 0);
|
||||
|
||||
// Dynamic state
|
||||
PipelineBuilder& setDynamicStates(const std::vector<VkDynamicState>& states);
|
||||
|
||||
// Build the pipeline
|
||||
VkPipeline build(VkDevice device) const;
|
||||
|
||||
// Common blend states
|
||||
static VkPipelineColorBlendAttachmentState blendDisabled();
|
||||
static VkPipelineColorBlendAttachmentState blendAlpha();
|
||||
static VkPipelineColorBlendAttachmentState blendAdditive();
|
||||
|
||||
private:
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaderStages_;
|
||||
std::vector<VkVertexInputBindingDescription> vertexBindings_;
|
||||
std::vector<VkVertexInputAttributeDescription> vertexAttributes_;
|
||||
VkPrimitiveTopology topology_ = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
VkBool32 primitiveRestart_ = VK_FALSE;
|
||||
VkPolygonMode polygonMode_ = VK_POLYGON_MODE_FILL;
|
||||
VkCullModeFlags cullMode_ = VK_CULL_MODE_NONE;
|
||||
VkFrontFace frontFace_ = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
bool depthTestEnable_ = false;
|
||||
bool depthWriteEnable_ = false;
|
||||
VkCompareOp depthCompareOp_ = VK_COMPARE_OP_LESS;
|
||||
bool depthBiasEnable_ = false;
|
||||
float depthBiasConstant_ = 0.0f;
|
||||
float depthBiasSlope_ = 0.0f;
|
||||
VkSampleCountFlagBits msaaSamples_ = VK_SAMPLE_COUNT_1_BIT;
|
||||
std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachments_;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkRenderPass renderPass_ = VK_NULL_HANDLE;
|
||||
uint32_t subpass_ = 0;
|
||||
std::vector<VkDynamicState> dynamicStates_;
|
||||
};
|
||||
|
||||
// Helper to create a pipeline layout from descriptor set layouts and push constant ranges
|
||||
VkPipelineLayout createPipelineLayout(VkDevice device,
|
||||
const std::vector<VkDescriptorSetLayout>& setLayouts,
|
||||
const std::vector<VkPushConstantRange>& pushConstants = {});
|
||||
|
||||
// Helper to create a descriptor set layout from bindings
|
||||
VkDescriptorSetLayout createDescriptorSetLayout(VkDevice device,
|
||||
const std::vector<VkDescriptorSetLayoutBinding>& bindings);
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
71
include/rendering/vk_render_target.hpp
Normal file
71
include/rendering/vk_render_target.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "rendering/vk_utils.hpp"
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* Off-screen render target encapsulating VkRenderPass + VkFramebuffer + color VkImage.
|
||||
* Used for minimap compositing, world map compositing, and other off-screen passes.
|
||||
*/
|
||||
class VkRenderTarget {
|
||||
public:
|
||||
VkRenderTarget() = default;
|
||||
~VkRenderTarget();
|
||||
|
||||
VkRenderTarget(const VkRenderTarget&) = delete;
|
||||
VkRenderTarget& operator=(const VkRenderTarget&) = delete;
|
||||
|
||||
/**
|
||||
* Create the render target with given dimensions and format.
|
||||
* Creates: color image, image view, sampler, render pass, framebuffer.
|
||||
*/
|
||||
bool create(VkContext& ctx, uint32_t width, uint32_t height,
|
||||
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM);
|
||||
|
||||
/**
|
||||
* Destroy all Vulkan resources.
|
||||
*/
|
||||
void destroy(VkDevice device, VmaAllocator allocator);
|
||||
|
||||
/**
|
||||
* Begin the off-screen render pass (clears to given color).
|
||||
* Must be called outside any other active render pass.
|
||||
*/
|
||||
void beginPass(VkCommandBuffer cmd,
|
||||
const VkClearColorValue& clear = {{0.0f, 0.0f, 0.0f, 1.0f}});
|
||||
|
||||
/**
|
||||
* End the off-screen render pass.
|
||||
* After this, the color image is in SHADER_READ_ONLY_OPTIMAL layout.
|
||||
*/
|
||||
void endPass(VkCommandBuffer cmd);
|
||||
|
||||
// Accessors
|
||||
VkImageView getColorImageView() const { return colorImage_.imageView; }
|
||||
VkSampler getSampler() const { return sampler_; }
|
||||
VkRenderPass getRenderPass() const { return renderPass_; }
|
||||
VkExtent2D getExtent() const { return { colorImage_.extent.width, colorImage_.extent.height }; }
|
||||
VkFormat getFormat() const { return colorImage_.format; }
|
||||
bool isValid() const { return framebuffer_ != VK_NULL_HANDLE; }
|
||||
|
||||
/**
|
||||
* Descriptor info for binding the color attachment as a texture in a shader.
|
||||
*/
|
||||
VkDescriptorImageInfo descriptorInfo() const;
|
||||
|
||||
private:
|
||||
AllocatedImage colorImage_{};
|
||||
VkSampler sampler_ = VK_NULL_HANDLE;
|
||||
VkRenderPass renderPass_ = VK_NULL_HANDLE;
|
||||
VkFramebuffer framebuffer_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
45
include/rendering/vk_shader.hpp
Normal file
45
include/rendering/vk_shader.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VkShaderModule {
|
||||
public:
|
||||
VkShaderModule() = default;
|
||||
~VkShaderModule();
|
||||
|
||||
VkShaderModule(const VkShaderModule&) = delete;
|
||||
VkShaderModule& operator=(const VkShaderModule&) = delete;
|
||||
VkShaderModule(VkShaderModule&& other) noexcept;
|
||||
VkShaderModule& operator=(VkShaderModule&& other) noexcept;
|
||||
|
||||
// Load a SPIR-V file from disk
|
||||
bool loadFromFile(VkDevice device, const std::string& path);
|
||||
|
||||
// Load from raw SPIR-V bytes
|
||||
bool loadFromMemory(VkDevice device, const uint32_t* code, size_t sizeBytes);
|
||||
|
||||
void destroy();
|
||||
|
||||
::VkShaderModule getModule() const { return module_; }
|
||||
bool isValid() const { return module_ != VK_NULL_HANDLE; }
|
||||
|
||||
// Create a VkPipelineShaderStageCreateInfo for this module
|
||||
VkPipelineShaderStageCreateInfo stageInfo(VkShaderStageFlagBits stage,
|
||||
const char* entryPoint = "main") const;
|
||||
|
||||
private:
|
||||
VkDevice device_ = VK_NULL_HANDLE;
|
||||
::VkShaderModule module_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
// Convenience: load a shader stage directly from a .spv file
|
||||
VkPipelineShaderStageCreateInfo loadShaderStage(VkDevice device,
|
||||
const std::string& path, VkShaderStageFlagBits stage);
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
78
include/rendering/vk_texture.hpp
Normal file
78
include/rendering/vk_texture.hpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include "rendering/vk_utils.hpp"
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VkContext;
|
||||
|
||||
class VkTexture {
|
||||
public:
|
||||
VkTexture() = default;
|
||||
~VkTexture();
|
||||
|
||||
VkTexture(const VkTexture&) = delete;
|
||||
VkTexture& operator=(const VkTexture&) = delete;
|
||||
VkTexture(VkTexture&& other) noexcept;
|
||||
VkTexture& operator=(VkTexture&& other) noexcept;
|
||||
|
||||
// Upload RGBA8 pixel data to GPU
|
||||
bool upload(VkContext& ctx, const uint8_t* pixels, uint32_t width, uint32_t height,
|
||||
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, bool generateMips = true);
|
||||
|
||||
// Upload with pre-existing mip data (array of mip levels)
|
||||
bool uploadMips(VkContext& ctx, const uint8_t* const* mipData, const uint32_t* mipSizes,
|
||||
uint32_t mipCount, uint32_t width, uint32_t height,
|
||||
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM);
|
||||
|
||||
// Create a depth/stencil texture (no upload)
|
||||
bool createDepth(VkContext& ctx, uint32_t width, uint32_t height,
|
||||
VkFormat format = VK_FORMAT_D32_SFLOAT);
|
||||
|
||||
// Create sampler with specified filtering
|
||||
bool createSampler(VkDevice device,
|
||||
VkFilter minFilter = VK_FILTER_LINEAR,
|
||||
VkFilter magFilter = VK_FILTER_LINEAR,
|
||||
VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
float maxAnisotropy = 16.0f);
|
||||
|
||||
// Overload with separate S/T address modes
|
||||
bool createSampler(VkDevice device,
|
||||
VkFilter filter,
|
||||
VkSamplerAddressMode addressModeU,
|
||||
VkSamplerAddressMode addressModeV,
|
||||
float maxAnisotropy = 16.0f);
|
||||
|
||||
// Create a comparison sampler (for shadow mapping)
|
||||
bool createShadowSampler(VkDevice device);
|
||||
|
||||
void destroy(VkDevice device, VmaAllocator allocator);
|
||||
|
||||
VkImage getImage() const { return image_.image; }
|
||||
VkImageView getImageView() const { return image_.imageView; }
|
||||
VkSampler getSampler() const { return sampler_; }
|
||||
uint32_t getWidth() const { return image_.extent.width; }
|
||||
uint32_t getHeight() const { return image_.extent.height; }
|
||||
VkFormat getFormat() const { return image_.format; }
|
||||
uint32_t getMipLevels() const { return mipLevels_; }
|
||||
bool isValid() const { return image_.image != VK_NULL_HANDLE; }
|
||||
|
||||
// Write descriptor info for binding
|
||||
VkDescriptorImageInfo descriptorInfo(VkImageLayout layout =
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) const;
|
||||
|
||||
private:
|
||||
void generateMipmaps(VkContext& ctx, VkFormat format, uint32_t width, uint32_t height);
|
||||
|
||||
AllocatedImage image_{};
|
||||
VkSampler sampler_ = VK_NULL_HANDLE;
|
||||
uint32_t mipLevels_ = 1;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
60
include/rendering/vk_utils.hpp
Normal file
60
include/rendering/vk_utils.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class VkContext;
|
||||
|
||||
struct AllocatedBuffer {
|
||||
VkBuffer buffer = VK_NULL_HANDLE;
|
||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo info{};
|
||||
};
|
||||
|
||||
struct AllocatedImage {
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||
VkImageView imageView = VK_NULL_HANDLE;
|
||||
VkExtent2D extent{};
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
};
|
||||
|
||||
// Buffer creation
|
||||
AllocatedBuffer createBuffer(VmaAllocator allocator, VkDeviceSize size,
|
||||
VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
|
||||
|
||||
void destroyBuffer(VmaAllocator allocator, AllocatedBuffer& buffer);
|
||||
|
||||
// Image creation
|
||||
AllocatedImage createImage(VkDevice device, VmaAllocator allocator,
|
||||
uint32_t width, uint32_t height, VkFormat format,
|
||||
VkImageUsageFlags usage, VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
uint32_t mipLevels = 1);
|
||||
|
||||
void destroyImage(VkDevice device, VmaAllocator allocator, AllocatedImage& image);
|
||||
|
||||
// Image layout transitions
|
||||
void transitionImageLayout(VkCommandBuffer cmd, VkImage image,
|
||||
VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||
VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VkPipelineStageFlags dstStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
|
||||
|
||||
// Staging upload helper — copies CPU data to a GPU-local buffer
|
||||
AllocatedBuffer uploadBuffer(VkContext& ctx, const void* data, VkDeviceSize size,
|
||||
VkBufferUsageFlags usage);
|
||||
|
||||
// Check VkResult and log on failure
|
||||
inline bool vkCheck(VkResult result, const char* msg) {
|
||||
if (result != VK_SUCCESS) {
|
||||
// Caller should log the message
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
#include <memory>
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
|
|
@ -16,127 +18,77 @@ namespace pipeline {
|
|||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* Water surface for a single map chunk
|
||||
*/
|
||||
struct WaterSurface {
|
||||
glm::vec3 position; // World position
|
||||
glm::vec3 origin; // Mesh origin (world)
|
||||
glm::vec3 stepX; // Mesh X step vector in world space
|
||||
glm::vec3 stepY; // Mesh Y step vector in world space
|
||||
float minHeight; // Minimum water height
|
||||
float maxHeight; // Maximum water height
|
||||
uint16_t liquidType; // LiquidType.dbc ID (WotLK)
|
||||
glm::vec3 position;
|
||||
glm::vec3 origin;
|
||||
glm::vec3 stepX;
|
||||
glm::vec3 stepY;
|
||||
float minHeight;
|
||||
float maxHeight;
|
||||
uint16_t liquidType;
|
||||
|
||||
// Owning tile coordinates (for per-tile removal)
|
||||
int tileX = -1, tileY = -1;
|
||||
|
||||
// Owning WMO instance ID (for WMO liquid removal, 0 = terrain water)
|
||||
uint32_t wmoId = 0;
|
||||
|
||||
// Water layer dimensions within chunk (0-7 offset, 1-8 size)
|
||||
uint8_t xOffset = 0;
|
||||
uint8_t yOffset = 0;
|
||||
uint8_t width = 8; // Width in tiles (1-8)
|
||||
uint8_t height = 8; // Height in tiles (1-8)
|
||||
uint8_t width = 8;
|
||||
uint8_t height = 8;
|
||||
|
||||
// Height map for water surface ((width+1) x (height+1) vertices)
|
||||
std::vector<float> heights;
|
||||
|
||||
// Render mask (which tiles have water)
|
||||
std::vector<uint8_t> mask;
|
||||
|
||||
// Render data
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
// Vulkan render data
|
||||
::VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
::VkBuffer indexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc = VK_NULL_HANDLE;
|
||||
int indexCount = 0;
|
||||
|
||||
// Per-surface material UBO
|
||||
::VkBuffer materialUBO = VK_NULL_HANDLE;
|
||||
VmaAllocation materialAlloc = VK_NULL_HANDLE;
|
||||
|
||||
// Material descriptor set (set 1)
|
||||
VkDescriptorSet materialSet = VK_NULL_HANDLE;
|
||||
|
||||
bool hasHeightData() const { return !heights.empty(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Water renderer
|
||||
*
|
||||
* Renders water surfaces with transparency and animation.
|
||||
* Supports multiple liquid types (water, ocean, magma, slime).
|
||||
* Water renderer (Vulkan)
|
||||
*/
|
||||
class WaterRenderer {
|
||||
public:
|
||||
WaterRenderer();
|
||||
~WaterRenderer();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load water surfaces from ADT terrain
|
||||
* @param terrain The ADT terrain data
|
||||
* @param append If true, add to existing water instead of replacing
|
||||
* @param tileX Tile X coordinate for tracking ownership (-1 = untracked)
|
||||
* @param tileY Tile Y coordinate for tracking ownership (-1 = untracked)
|
||||
*/
|
||||
void loadFromTerrain(const pipeline::ADTTerrain& terrain, bool append = false,
|
||||
int tileX = -1, int tileY = -1);
|
||||
|
||||
/**
|
||||
* Load water surface from WMO liquid data
|
||||
* @param liquid WMO liquid data from MLIQ chunk
|
||||
* @param modelMatrix WMO instance model matrix for transforming to world space
|
||||
* @param wmoId WMO instance ID for tracking ownership
|
||||
*/
|
||||
void loadFromWMO(const pipeline::WMOLiquid& liquid, const glm::mat4& modelMatrix, uint32_t wmoId);
|
||||
|
||||
/**
|
||||
* Remove all water surfaces belonging to a specific WMO instance
|
||||
* @param wmoId WMO instance ID
|
||||
*/
|
||||
void removeWMO(uint32_t wmoId);
|
||||
|
||||
/**
|
||||
* Remove all water surfaces belonging to a specific tile
|
||||
* @param tileX Tile X coordinate
|
||||
* @param tileY Tile Y coordinate
|
||||
*/
|
||||
void removeTile(int tileX, int tileY);
|
||||
|
||||
/**
|
||||
* Clear all water surfaces
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Render all water surfaces
|
||||
*/
|
||||
void render(const Camera& camera, float time);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera, float time);
|
||||
|
||||
/**
|
||||
* Enable/disable water rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { renderingEnabled = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled; }
|
||||
|
||||
/**
|
||||
* Query the water height at a given world position.
|
||||
* Returns the highest water surface height at that XY, or nullopt if no water.
|
||||
*/
|
||||
std::optional<float> getWaterHeightAt(float glX, float glY) const;
|
||||
std::optional<uint16_t> getWaterTypeAt(float glX, float glY) const;
|
||||
|
||||
/**
|
||||
* Get water surface count
|
||||
*/
|
||||
int getSurfaceCount() const { return static_cast<int>(surfaces.size()); }
|
||||
|
||||
/**
|
||||
* Set fog parameters
|
||||
*/
|
||||
void setFog(const glm::vec3& color, float start, float end) {
|
||||
fogColor = color; fogStart = start; fogEnd = end;
|
||||
}
|
||||
|
||||
private:
|
||||
void createWaterMesh(WaterSurface& surface);
|
||||
void destroyWaterMesh(WaterSurface& surface);
|
||||
|
|
@ -144,14 +96,20 @@ private:
|
|||
glm::vec4 getLiquidColor(uint16_t liquidType) const;
|
||||
float getLiquidAlpha(uint16_t liquidType) const;
|
||||
|
||||
std::unique_ptr<Shader> waterShader;
|
||||
void updateMaterialUBO(WaterSurface& surface);
|
||||
VkDescriptorSet allocateMaterialSet();
|
||||
|
||||
VkContext* vkCtx = nullptr;
|
||||
|
||||
// Pipeline
|
||||
VkPipeline waterPipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout materialSetLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorPool materialDescPool = VK_NULL_HANDLE;
|
||||
static constexpr uint32_t MAX_WATER_SETS = 2048;
|
||||
|
||||
std::vector<WaterSurface> surfaces;
|
||||
bool renderingEnabled = true;
|
||||
|
||||
// Fog parameters
|
||||
glm::vec3 fogColor = glm::vec3(0.5f, 0.6f, 0.7f);
|
||||
float fogStart = 800.0f; // Match WMO renderer fog settings
|
||||
float fogEnd = 1500.0f;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
class VkContext;
|
||||
|
||||
/**
|
||||
* @brief Weather particle system for rain and snow
|
||||
|
|
@ -20,7 +20,7 @@ class Shader;
|
|||
* - Particle recycling for efficiency
|
||||
* - Camera-relative positioning (follows player)
|
||||
* - Adjustable intensity (light, medium, heavy)
|
||||
* - GPU instanced rendering
|
||||
* - Vulkan point-sprite rendering
|
||||
*/
|
||||
class Weather {
|
||||
public:
|
||||
|
|
@ -35,9 +35,11 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Initialize weather system
|
||||
* @param ctx Vulkan context
|
||||
* @param perFrameLayout Descriptor set layout for the per-frame UBO (set 0)
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize();
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||
|
||||
/**
|
||||
* @brief Update weather particles
|
||||
|
|
@ -48,9 +50,10 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Render weather particles
|
||||
* @param camera Camera for rendering
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0, contains camera UBO)
|
||||
*/
|
||||
void render(const Camera& camera);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
/**
|
||||
* @brief Set weather type
|
||||
|
|
@ -75,6 +78,11 @@ public:
|
|||
*/
|
||||
int getParticleCount() const;
|
||||
|
||||
/**
|
||||
* @brief Clean up Vulkan resources
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
struct Particle {
|
||||
glm::vec3 position;
|
||||
|
|
@ -83,15 +91,20 @@ private:
|
|||
float maxLifetime;
|
||||
};
|
||||
|
||||
void cleanup();
|
||||
void resetParticles(const Camera& camera);
|
||||
void updateParticle(Particle& particle, const Camera& camera, float deltaTime);
|
||||
glm::vec3 getRandomPosition(const glm::vec3& center) const;
|
||||
|
||||
// OpenGL objects
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0; // Instance buffer
|
||||
std::unique_ptr<Shader> shader;
|
||||
// Vulkan objects
|
||||
VkContext* vkCtx = nullptr;
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
// Dynamic mapped buffer for particle positions (updated every frame)
|
||||
::VkBuffer dynamicVB = VK_NULL_HANDLE;
|
||||
VmaAllocation dynamicVBAlloc = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo dynamicVBAllocInfo{};
|
||||
VkDeviceSize dynamicVBSize = 0;
|
||||
|
||||
// Particles
|
||||
std::vector<Particle> particles;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
|
@ -19,12 +20,13 @@ namespace pipeline {
|
|||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
class Frustum;
|
||||
class M2Renderer;
|
||||
class VkContext;
|
||||
class VkTexture;
|
||||
|
||||
/**
|
||||
* WMO (World Model Object) Renderer
|
||||
* WMO (World Model Object) Renderer (Vulkan)
|
||||
*
|
||||
* Renders buildings, dungeons, and large structures from WMO files.
|
||||
* Features:
|
||||
|
|
@ -32,7 +34,6 @@ class M2Renderer;
|
|||
* - Batched rendering per group
|
||||
* - Frustum culling
|
||||
* - Portal visibility (future)
|
||||
* - Dynamic lighting support (future)
|
||||
*/
|
||||
class WMORenderer {
|
||||
public:
|
||||
|
|
@ -40,10 +41,13 @@ public:
|
|||
~WMORenderer();
|
||||
|
||||
/**
|
||||
* Initialize renderer and create shaders
|
||||
* Initialize renderer (Vulkan)
|
||||
* @param ctx Vulkan context
|
||||
* @param perFrameLayout Descriptor set layout for set 0 (per-frame UBO)
|
||||
* @param assetManager Asset manager for loading textures (optional)
|
||||
*/
|
||||
bool initialize(pipeline::AssetManager* assetManager = nullptr);
|
||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout,
|
||||
pipeline::AssetManager* assetManager = nullptr);
|
||||
|
||||
/**
|
||||
* Cleanup GPU resources
|
||||
|
|
@ -132,12 +136,22 @@ public:
|
|||
void clearInstances();
|
||||
|
||||
/**
|
||||
* Render all WMO instances
|
||||
* @param camera Camera for view/projection matrices
|
||||
* @param view View matrix
|
||||
* @param projection Projection matrix
|
||||
* Render all WMO instances (Vulkan)
|
||||
* @param cmd Command buffer to record into
|
||||
* @param perFrameSet Per-frame descriptor set (set 0)
|
||||
* @param camera Camera for frustum culling
|
||||
*/
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera);
|
||||
|
||||
/**
|
||||
* Initialize shadow pipeline (Phase 7)
|
||||
*/
|
||||
bool initializeShadow(VkRenderPass shadowRenderPass);
|
||||
|
||||
/**
|
||||
* Render depth-only for shadow casting
|
||||
*/
|
||||
void renderShadow(VkCommandBuffer cmd, const glm::mat4& lightSpaceMatrix);
|
||||
|
||||
/**
|
||||
* Get number of loaded models
|
||||
|
|
@ -204,32 +218,22 @@ public:
|
|||
uint32_t getDistanceCulledGroups() const { return lastDistanceCulledGroups; }
|
||||
|
||||
/**
|
||||
* Enable/disable GPU occlusion query culling
|
||||
* Enable/disable GPU occlusion query culling (stubbed in Vulkan)
|
||||
*/
|
||||
void setOcclusionCulling(bool enabled) { occlusionCulling = enabled; }
|
||||
bool isOcclusionCullingEnabled() const { return occlusionCulling; }
|
||||
void setOcclusionCulling(bool /*enabled*/) { /* stubbed */ }
|
||||
bool isOcclusionCullingEnabled() const { return false; }
|
||||
|
||||
/**
|
||||
* Get number of groups culled by occlusion queries last frame
|
||||
*/
|
||||
uint32_t getOcclusionCulledGroups() const { return lastOcclusionCulledGroups; }
|
||||
uint32_t getOcclusionCulledGroups() const { return 0; }
|
||||
|
||||
void setFog(const glm::vec3& color, float start, float end) {
|
||||
fogColor = color; fogStart = start; fogEnd = end;
|
||||
}
|
||||
|
||||
void setLighting(const float lightDir[3], const float lightColor[3],
|
||||
const float ambientColor[3]);
|
||||
|
||||
void setShadowMap(GLuint depthTex, const glm::mat4& lightSpace) {
|
||||
shadowDepthTex = depthTex; lightSpaceMatrix = lightSpace; shadowEnabled = true;
|
||||
}
|
||||
void clearShadowMap() { shadowEnabled = false; }
|
||||
|
||||
/**
|
||||
* Render depth-only for shadow casting (reuses VAOs)
|
||||
*/
|
||||
void renderShadow(const glm::mat4& lightView, const glm::mat4& lightProj, Shader& shadowShader);
|
||||
// Lighting/fog/shadow are now in the per-frame UBO; these are no-ops for API compat
|
||||
void setFog(const glm::vec3& /*color*/, float /*start*/, float /*end*/) {}
|
||||
void setLighting(const float /*lightDir*/[3], const float /*lightColor*/[3],
|
||||
const float /*ambientColor*/[3]) {}
|
||||
void setShadowMap(uint32_t /*depthTex*/, const glm::mat4& /*lightSpace*/) {}
|
||||
void clearShadowMap() {}
|
||||
|
||||
/**
|
||||
* Get floor height at a GL position via ray-triangle intersection.
|
||||
|
|
@ -297,13 +301,23 @@ public:
|
|||
void precomputeFloorCache();
|
||||
|
||||
private:
|
||||
// WMO material UBO — matches WMOMaterial in wmo.frag.glsl
|
||||
struct WMOMaterialUBO {
|
||||
int32_t hasTexture;
|
||||
int32_t alphaTest;
|
||||
int32_t unlit;
|
||||
int32_t isInterior;
|
||||
float specularIntensity;
|
||||
};
|
||||
|
||||
/**
|
||||
* WMO group GPU resources
|
||||
*/
|
||||
struct GroupResources {
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ebo = 0;
|
||||
::VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation vertexAlloc = VK_NULL_HANDLE;
|
||||
::VkBuffer indexBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation indexAlloc = VK_NULL_HANDLE;
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t vertexCount = 0;
|
||||
glm::vec3 boundingBoxMin;
|
||||
|
|
@ -322,13 +336,17 @@ private:
|
|||
|
||||
// Pre-merged batches for efficient rendering (computed at load time)
|
||||
struct MergedBatch {
|
||||
GLuint texId;
|
||||
bool hasTexture;
|
||||
bool alphaTest;
|
||||
VkTexture* texture = nullptr; // from cache, NOT owned
|
||||
VkDescriptorSet materialSet = VK_NULL_HANDLE; // set 1
|
||||
::VkBuffer materialUBO = VK_NULL_HANDLE;
|
||||
VmaAllocation materialUBOAlloc = VK_NULL_HANDLE;
|
||||
bool hasTexture = false;
|
||||
bool alphaTest = false;
|
||||
bool unlit = false;
|
||||
uint32_t blendMode = 0;
|
||||
std::vector<GLsizei> counts;
|
||||
std::vector<const void*> offsets;
|
||||
bool isTransparent = false; // blendMode >= 2
|
||||
// For multi-draw: store index ranges
|
||||
struct DrawRange { uint32_t firstIndex; uint32_t indexCount; };
|
||||
std::vector<DrawRange> draws;
|
||||
};
|
||||
std::vector<MergedBatch> mergedBatches;
|
||||
|
||||
|
|
@ -401,7 +419,7 @@ private:
|
|||
std::vector<DoodadTemplate> doodadTemplates;
|
||||
|
||||
// Texture handles for this model (indexed by texture path order)
|
||||
std::vector<GLuint> textures;
|
||||
std::vector<VkTexture*> textures; // non-owning, from cache
|
||||
|
||||
// Material texture indices (materialId -> texture index)
|
||||
std::vector<uint32_t> materialTextureIndices;
|
||||
|
|
@ -458,13 +476,6 @@ private:
|
|||
*/
|
||||
bool createGroupResources(const pipeline::WMOGroup& group, GroupResources& resources, uint32_t groupFlags = 0);
|
||||
|
||||
/**
|
||||
* Render a single group
|
||||
*/
|
||||
void renderGroup(const GroupResources& group, const ModelData& model,
|
||||
const glm::mat4& modelMatrix,
|
||||
const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
/**
|
||||
* Check if group is visible in frustum
|
||||
*/
|
||||
|
|
@ -479,11 +490,6 @@ private:
|
|||
|
||||
/**
|
||||
* Get visible groups via portal traversal
|
||||
* @param model The WMO model data
|
||||
* @param cameraLocalPos Camera position in model space
|
||||
* @param frustum Frustum for portal visibility testing
|
||||
* @param modelMatrix Transform for world-space frustum test
|
||||
* @param outVisibleGroups Output set of visible group indices
|
||||
*/
|
||||
void getVisibleGroupsViaPortals(const ModelData& model,
|
||||
const glm::vec3& cameraLocalPos,
|
||||
|
|
@ -502,23 +508,17 @@ private:
|
|||
/**
|
||||
* Load a texture from path
|
||||
*/
|
||||
GLuint loadTexture(const std::string& path);
|
||||
VkTexture* loadTexture(const std::string& path);
|
||||
|
||||
/**
|
||||
* Initialize occlusion query resources (bbox VAO, shader)
|
||||
* Allocate a material descriptor set from the pool
|
||||
*/
|
||||
void initOcclusionResources();
|
||||
VkDescriptorSet allocateMaterialSet();
|
||||
|
||||
/**
|
||||
* Run occlusion query pre-pass for an instance
|
||||
* Destroy GPU resources for a single group
|
||||
*/
|
||||
void runOcclusionQueries(const WMOInstance& instance, const ModelData& model,
|
||||
const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
/**
|
||||
* Check if a group passed occlusion test (uses previous frame results)
|
||||
*/
|
||||
bool isGroupOccluded(uint32_t instanceId, uint32_t groupIndex) const;
|
||||
void destroyGroupGPU(GroupResources& group);
|
||||
|
||||
struct GridCell {
|
||||
int x;
|
||||
|
|
@ -541,8 +541,8 @@ private:
|
|||
void rebuildSpatialIndex();
|
||||
void gatherCandidates(const glm::vec3& queryMin, const glm::vec3& queryMax, std::vector<size_t>& outIndices) const;
|
||||
|
||||
// Shader
|
||||
std::unique_ptr<Shader> shader;
|
||||
// Vulkan context
|
||||
VkContext* vkCtx_ = nullptr;
|
||||
|
||||
// Asset manager for loading textures
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
|
|
@ -553,9 +553,31 @@ private:
|
|||
// Current map name for zone-specific floor cache
|
||||
std::string mapName_;
|
||||
|
||||
// Texture cache (path -> texture ID)
|
||||
// Vulkan pipelines
|
||||
VkPipeline opaquePipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline transparentPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline wireframePipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Shadow rendering (Phase 7)
|
||||
VkPipeline shadowPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout shadowPipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout shadowParamsLayout_ = VK_NULL_HANDLE;
|
||||
VkDescriptorPool shadowParamsPool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSet shadowParamsSet_ = VK_NULL_HANDLE;
|
||||
::VkBuffer shadowParamsUBO_ = VK_NULL_HANDLE;
|
||||
VmaAllocation shadowParamsAlloc_ = VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor set layouts
|
||||
VkDescriptorSetLayout materialSetLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor pool for material sets
|
||||
VkDescriptorPool materialDescPool_ = VK_NULL_HANDLE;
|
||||
static constexpr uint32_t MAX_MATERIAL_SETS = 8192;
|
||||
|
||||
// Texture cache (path -> VkTexture)
|
||||
struct TextureCacheEntry {
|
||||
GLuint id = 0;
|
||||
std::unique_ptr<VkTexture> texture;
|
||||
size_t approxBytes = 0;
|
||||
uint64_t lastUse = 0;
|
||||
};
|
||||
|
|
@ -565,7 +587,7 @@ private:
|
|||
size_t textureCacheBudgetBytes_ = 2048ull * 1024 * 1024; // Default, overridden at init
|
||||
|
||||
// Default white texture
|
||||
GLuint whiteTexture = 0;
|
||||
std::unique_ptr<VkTexture> whiteTexture_;
|
||||
|
||||
// Loaded models (modelId -> ModelData)
|
||||
std::unordered_map<uint32_t, ModelData> loadedModels;
|
||||
|
|
@ -581,38 +603,11 @@ private:
|
|||
bool frustumCulling = true;
|
||||
bool portalCulling = false; // Disabled by default - needs debugging
|
||||
bool distanceCulling = false; // Disabled - causes ground to disappear
|
||||
bool occlusionCulling = false; // GPU occlusion queries - disabled, adds overhead
|
||||
float maxGroupDistance = 500.0f;
|
||||
float maxGroupDistanceSq = 250000.0f; // maxGroupDistance^2
|
||||
uint32_t lastDrawCalls = 0;
|
||||
mutable uint32_t lastPortalCulledGroups = 0;
|
||||
mutable uint32_t lastDistanceCulledGroups = 0;
|
||||
mutable uint32_t lastOcclusionCulledGroups = 0;
|
||||
|
||||
// Occlusion query resources
|
||||
GLuint bboxVao = 0;
|
||||
GLuint bboxVbo = 0;
|
||||
std::unique_ptr<Shader> occlusionShader;
|
||||
// Query objects per (instance, group) - reused each frame
|
||||
// Key: (instanceId << 16) | groupIndex
|
||||
mutable std::unordered_map<uint32_t, GLuint> occlusionQueries;
|
||||
// Results from previous frame (1 frame latency to avoid GPU stalls)
|
||||
mutable std::unordered_map<uint32_t, bool> occlusionResults;
|
||||
|
||||
// Fog parameters
|
||||
glm::vec3 fogColor = glm::vec3(0.5f, 0.6f, 0.7f);
|
||||
float fogStart = 3000.0f; // Increased to allow clearer visibility at distance
|
||||
float fogEnd = 4000.0f; // Increased to match extended view distance
|
||||
|
||||
// Lighting parameters
|
||||
float lightDir[3] = {-0.3f, -0.7f, -0.6f};
|
||||
float lightColor[3] = {1.5f, 1.4f, 1.3f};
|
||||
float ambientColor[3] = {0.55f, 0.55f, 0.6f};
|
||||
|
||||
// Shadow mapping
|
||||
GLuint shadowDepthTex = 0;
|
||||
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
|
||||
bool shadowEnabled = false;
|
||||
|
||||
// Optional query-space culling for collision/raycast hot paths.
|
||||
bool collisionFocusEnabled = false;
|
||||
|
|
@ -636,7 +631,6 @@ private:
|
|||
std::vector<uint32_t> visibleGroups; // group indices that passed culling
|
||||
uint32_t portalCulled = 0;
|
||||
uint32_t distanceCulled = 0;
|
||||
uint32_t occlusionCulled = 0;
|
||||
};
|
||||
|
||||
// Collision query profiling (per frame).
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -11,7 +13,9 @@ namespace wowee {
|
|||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class VkContext;
|
||||
class VkTexture;
|
||||
class VkRenderTarget;
|
||||
|
||||
struct WorldMapZone {
|
||||
uint32_t wmaID = 0;
|
||||
|
|
@ -22,8 +26,8 @@ struct WorldMapZone {
|
|||
uint32_t parentWorldMapID = 0;
|
||||
uint32_t exploreFlag = 0;
|
||||
|
||||
// Per-zone cached textures
|
||||
GLuint tileTextures[12] = {};
|
||||
// Per-zone cached textures (owned by WorldMap::zoneTextures)
|
||||
VkTexture* tileTextures[12] = {};
|
||||
bool tilesLoaded = false;
|
||||
};
|
||||
|
||||
|
|
@ -32,8 +36,15 @@ public:
|
|||
WorldMap();
|
||||
~WorldMap();
|
||||
|
||||
void initialize(pipeline::AssetManager* assetManager);
|
||||
bool initialize(VkContext* ctx, pipeline::AssetManager* assetManager);
|
||||
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);
|
||||
|
||||
void setMapName(const std::string& name);
|
||||
void setServerExplorationMask(const std::vector<uint32_t>& masks, bool hasData);
|
||||
bool isOpen() const { return open; }
|
||||
|
|
@ -42,9 +53,6 @@ public:
|
|||
private:
|
||||
enum class ViewLevel { WORLD, CONTINENT, ZONE };
|
||||
|
||||
void createFBO();
|
||||
void createTileShader();
|
||||
void createQuad();
|
||||
void enterWorldView();
|
||||
void loadZonesFromDBC();
|
||||
int findBestContinentForPlayer(const glm::vec3& playerRenderPos) const;
|
||||
|
|
@ -53,15 +61,15 @@ private:
|
|||
bool getContinentProjectionBounds(int contIdx, float& left, float& right,
|
||||
float& top, float& bottom) const;
|
||||
void loadZoneTextures(int zoneIdx);
|
||||
void compositeZone(int zoneIdx);
|
||||
void requestComposite(int zoneIdx);
|
||||
void renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWidth, int screenHeight);
|
||||
void updateExploration(const glm::vec3& playerRenderPos);
|
||||
void zoomIn(const glm::vec3& playerRenderPos);
|
||||
void zoomOut();
|
||||
|
||||
// World pos → map UV using a specific zone's bounds
|
||||
glm::vec2 renderPosToMapUV(const glm::vec3& renderPos, int zoneIdx) const;
|
||||
void destroyZoneTextures();
|
||||
|
||||
VkContext* vkCtx = nullptr;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
bool initialized = false;
|
||||
bool open = false;
|
||||
|
|
@ -70,28 +78,45 @@ private:
|
|||
|
||||
// All zones for current map
|
||||
std::vector<WorldMapZone> zones;
|
||||
int continentIdx = -1; // index of AreaID=0 entry in zones
|
||||
int currentIdx = -1; // currently displayed zone index
|
||||
int continentIdx = -1;
|
||||
int currentIdx = -1;
|
||||
ViewLevel viewLevel = ViewLevel::CONTINENT;
|
||||
int compositedIdx = -1; // which zone is currently composited in FBO
|
||||
int compositedIdx = -1;
|
||||
int pendingCompositeIdx = -1;
|
||||
|
||||
// FBO for composited map (4x3 tiles = 1024x768)
|
||||
// FBO replacement (4x3 tiles = 1024x768)
|
||||
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; // 1024
|
||||
static constexpr int FBO_H = GRID_ROWS * TILE_PX; // 768
|
||||
static constexpr int FBO_W = GRID_COLS * TILE_PX;
|
||||
static constexpr int FBO_H = GRID_ROWS * TILE_PX;
|
||||
|
||||
GLuint fbo = 0;
|
||||
GLuint fboTexture = 0;
|
||||
std::unique_ptr<Shader> tileShader;
|
||||
GLuint tileQuadVAO = 0;
|
||||
GLuint tileQuadVBO = 0;
|
||||
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 = 32;
|
||||
|
||||
// Tile composite pipeline
|
||||
VkPipeline tilePipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout tilePipelineLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorSet tileDescSets[2][12] = {}; // [frameInFlight][tileSlot]
|
||||
|
||||
// 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;
|
||||
|
||||
// Exploration / fog of war
|
||||
std::vector<uint32_t> serverExplorationMask;
|
||||
bool hasServerExplorationMask = false;
|
||||
std::unordered_set<int> exploredZones; // zone indices the player has visited
|
||||
std::unordered_set<int> exploredZones;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "game/game_handler.hpp"
|
||||
#include "game/inventory.hpp"
|
||||
#include "rendering/world_map.hpp"
|
||||
// WorldMap is now owned by Renderer, accessed via getWorldMap()
|
||||
#include "rendering/character_preview.hpp"
|
||||
#include "ui/inventory_screen.hpp"
|
||||
#include "ui/quest_log_screen.hpp"
|
||||
|
|
@ -212,7 +212,7 @@ private:
|
|||
QuestLogScreen questLogScreen;
|
||||
SpellbookScreen spellbookScreen;
|
||||
TalentScreen talentScreen;
|
||||
rendering::WorldMap worldMap;
|
||||
// WorldMap is now owned by Renderer (accessed via renderer->getWorldMap())
|
||||
|
||||
// Spell icon cache: spellId -> GL texture ID
|
||||
std::unordered_map<uint32_t, GLuint> spellIconCache_;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue