mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-26 00:40:15 +00:00
feat: implement M2 ribbon emitter rendering for spell trail effects
Parse M2RibbonEmitter data (WotLK format) from M2 files — bone index, position, color/alpha/height tracks, edgesPerSecond, edgeLifetime, gravity. Add CPU-side trail simulation per instance (edge birth at bone world position, lifetime expiry, gravity droop). New m2_ribbon.vert/frag shaders render a triangle-strip quad per emitter using the existing particleTexLayout_ descriptor set. Supports both alpha-blend and additive pipeline variants based on material blend mode. Fixes invisible spell trail effects (~5-10%% of spell visuals) that were silently skipped.
This commit is contained in:
parent
022d387d95
commit
1108aa9ae6
9 changed files with 604 additions and 1 deletions
|
|
@ -165,6 +165,29 @@ struct M2ParticleEmitter {
|
|||
bool enabled = true;
|
||||
};
|
||||
|
||||
// Ribbon emitter definition parsed from M2 (WotLK format)
|
||||
struct M2RibbonEmitter {
|
||||
int32_t ribbonId = 0;
|
||||
uint32_t bone = 0; // Bone that drives the ribbon spine
|
||||
glm::vec3 position{0.0f}; // Offset from bone pivot
|
||||
|
||||
uint16_t textureIndex = 0; // First texture lookup index
|
||||
uint16_t materialIndex = 0; // First material lookup index (blend mode)
|
||||
|
||||
// Animated tracks
|
||||
M2AnimationTrack colorTrack; // RGB 0..1
|
||||
M2AnimationTrack alphaTrack; // float 0..1 (stored as fixed16 on disk)
|
||||
M2AnimationTrack heightAboveTrack; // Half-width above bone
|
||||
M2AnimationTrack heightBelowTrack; // Half-width below bone
|
||||
M2AnimationTrack visibilityTrack; // 0=hidden, 1=visible
|
||||
|
||||
float edgesPerSecond = 15.0f; // How many edge points are generated per second
|
||||
float edgeLifetime = 0.5f; // Seconds before edges expire
|
||||
float gravity = 0.0f; // Downward pull on edges per s²
|
||||
uint16_t textureRows = 1;
|
||||
uint16_t textureCols = 1;
|
||||
};
|
||||
|
||||
// Complete M2 model structure
|
||||
struct M2Model {
|
||||
// Model metadata
|
||||
|
|
@ -213,6 +236,9 @@ struct M2Model {
|
|||
// Particle emitters
|
||||
std::vector<M2ParticleEmitter> particleEmitters;
|
||||
|
||||
// Ribbon emitters
|
||||
std::vector<M2RibbonEmitter> ribbonEmitters;
|
||||
|
||||
// Collision mesh (simplified geometry for physics)
|
||||
std::vector<glm::vec3> collisionVertices;
|
||||
std::vector<uint16_t> collisionIndices; // 3 per triangle
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
|
|
@ -130,6 +131,11 @@ struct M2ModelGPU {
|
|||
std::vector<VkTexture*> particleTextures; // Resolved Vulkan textures per emitter
|
||||
std::vector<VkDescriptorSet> particleTexSets; // Pre-allocated descriptor sets per emitter (stable, avoids per-frame alloc)
|
||||
|
||||
// Ribbon emitter data (kept from M2Model)
|
||||
std::vector<pipeline::M2RibbonEmitter> ribbonEmitters;
|
||||
std::vector<VkTexture*> ribbonTextures; // Resolved texture per ribbon emitter
|
||||
std::vector<VkDescriptorSet> ribbonTexSets; // Descriptor sets per ribbon emitter
|
||||
|
||||
// Texture transform data for UV animation
|
||||
std::vector<pipeline::M2TextureTransform> textureTransforms;
|
||||
std::vector<uint16_t> textureTransformLookup;
|
||||
|
|
@ -180,6 +186,19 @@ struct M2Instance {
|
|||
std::vector<float> emitterAccumulators; // fractional particle counter per emitter
|
||||
std::vector<M2Particle> particles;
|
||||
|
||||
// Ribbon emitter state
|
||||
struct RibbonEdge {
|
||||
glm::vec3 worldPos; // Spine world position when this edge was born
|
||||
glm::vec3 color; // Interpolated color at birth
|
||||
float alpha; // Interpolated alpha at birth
|
||||
float heightAbove;// Half-width above spine
|
||||
float heightBelow;// Half-width below spine
|
||||
float age; // Seconds since spawned
|
||||
};
|
||||
// One deque of edges per ribbon emitter on this instance
|
||||
std::vector<std::deque<RibbonEdge>> ribbonEdges;
|
||||
std::vector<float> ribbonEdgeAccumulators; // fractional edge counter per emitter
|
||||
|
||||
// Cached model flags (set at creation to avoid per-frame hash lookups)
|
||||
bool cachedHasAnimation = false;
|
||||
bool cachedDisableAnimation = false;
|
||||
|
|
@ -295,6 +314,11 @@ public:
|
|||
*/
|
||||
void renderSmokeParticles(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
/**
|
||||
* Render M2 ribbon emitters (spell trails / wing effects)
|
||||
*/
|
||||
void renderM2Ribbons(VkCommandBuffer cmd, VkDescriptorSet perFrameSet);
|
||||
|
||||
void setInstancePosition(uint32_t instanceId, const glm::vec3& position);
|
||||
void setInstanceTransform(uint32_t instanceId, const glm::mat4& transform);
|
||||
void setInstanceAnimationFrozen(uint32_t instanceId, bool frozen);
|
||||
|
|
@ -374,6 +398,11 @@ private:
|
|||
VkPipeline smokePipeline_ = VK_NULL_HANDLE; // Smoke particles
|
||||
VkPipelineLayout smokePipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Ribbon pipelines (additive + alpha-blend)
|
||||
VkPipeline ribbonPipeline_ = VK_NULL_HANDLE; // Alpha-blend ribbons
|
||||
VkPipeline ribbonAdditivePipeline_ = VK_NULL_HANDLE; // Additive ribbons
|
||||
VkPipelineLayout ribbonPipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor set layouts
|
||||
VkDescriptorSetLayout materialSetLayout_ = VK_NULL_HANDLE; // set 1
|
||||
VkDescriptorSetLayout boneSetLayout_ = VK_NULL_HANDLE; // set 2
|
||||
|
|
@ -385,6 +414,12 @@ private:
|
|||
static constexpr uint32_t MAX_MATERIAL_SETS = 8192;
|
||||
static constexpr uint32_t MAX_BONE_SETS = 8192;
|
||||
|
||||
// Dynamic ribbon vertex buffer (CPU-written triangle strip)
|
||||
static constexpr size_t MAX_RIBBON_VERTS = 2048; // 9 floats each
|
||||
::VkBuffer ribbonVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation ribbonVBAlloc_ = VK_NULL_HANDLE;
|
||||
void* ribbonVBMapped_ = nullptr;
|
||||
|
||||
// Dynamic particle buffers
|
||||
::VkBuffer smokeVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation smokeVBAlloc_ = VK_NULL_HANDLE;
|
||||
|
|
@ -535,6 +570,7 @@ 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);
|
||||
void updateRibbons(M2Instance& inst, const M2ModelGPU& gpu, float dt);
|
||||
|
||||
// Helper to allocate descriptor sets
|
||||
VkDescriptorSet allocateMaterialSet();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue