mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-09 10:33:51 +00:00
feat(animation): 452 named constants, 30-phase character animation state machine
Add animation_ids.hpp/cpp with all 452 WoW animation ID constants (anim::STAND, anim::RUN, anim::FIRE_BOW, ... anim::FLY_BACKWARDS, etc.), nameFromId() O(1) lookup, and flyVariant() compact 218-element ground→FLY_* resolver. Expand AnimationController into a full state machine with 20+ named states: spell cast (directed→omni→cast fallback chain, instant one-shot release), hit reactions (WOUND/CRIT/DODGE/BLOCK/SHIELD_BLOCK), stun, wounded idle, stealth animation substitution, loot, fishing channel, sit/sleep/kneel down→loop→up transitions, sheathe/unsheathe combat enter/exit, ranged weapons (BOW/GUN/CROSSBOW/THROWN with reload states), game object OPEN/CLOSE/DESTROY, vehicle enter/exit, mount flight directionals (FLY_LEFT/RIGHT/UP/DOWN/BACKWARDS), emote state variants, off-hand/pierce/dual-wield alternation, NPC birth/spawn/drown/rise, sprint aura override, totem idle, NPC greeting/farewell. Add spell_defines.hpp with SpellEffect (~45 constants) and SpellMissInfo (12 constants) namespaces; replace all magic numbers in spell_handler.cpp. Add GAMEOBJECT_BYTES_1 to update field table (all 4 expansion JSONs) and wire GameObjectStateCallback. Add DBC cross-validation on world entry. Expand tools/_ANIM_NAMES from ~35 to 452 entries in m2_viewer.py and asset_pipeline_gui.py. Add tests/test_animation_ids.cpp. Bug fixes included: - Stand state 1 was animating READY_2H(27) — fixed to SITTING(97) - Spell casts ended freeze-frame — add one-shot release animation - NPC 2H swing probe chain missing ATTACK_2H_LOOSE (polearm/staff) - Chair sits (states 2/4/5/6) incorrectly played floor-sit transition - STOP(3) used for all spell casts — replaced with model-aware chain
This commit is contained in:
parent
d54e262048
commit
e58f9b4b40
59 changed files with 3903 additions and 483 deletions
|
|
@ -250,13 +250,13 @@ bool Renderer::createPerFrameResources() {
|
|||
// --- Create descriptor pool for UBO + image sampler (normal frames + reflection) ---
|
||||
VkDescriptorPoolSize poolSizes[2]{};
|
||||
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
poolSizes[0].descriptorCount = MAX_FRAMES + 1; // +1 for reflection perFrame UBO
|
||||
poolSizes[0].descriptorCount = MAX_FRAMES * 2; // normal frames + reflection frames
|
||||
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
poolSizes[1].descriptorCount = MAX_FRAMES + 1;
|
||||
poolSizes[1].descriptorCount = MAX_FRAMES * 2;
|
||||
|
||||
VkDescriptorPoolCreateInfo poolInfo{};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolInfo.maxSets = MAX_FRAMES + 1; // +1 for reflection descriptor set
|
||||
poolInfo.maxSets = MAX_FRAMES * 2; // normal frames + reflection frames
|
||||
poolInfo.poolSizeCount = 2;
|
||||
poolInfo.pPoolSizes = poolSizes;
|
||||
|
||||
|
|
@ -344,42 +344,48 @@ bool Renderer::createPerFrameResources() {
|
|||
}
|
||||
reflPerFrameUBOMapped = mapInfo.pMappedData;
|
||||
|
||||
VkDescriptorSetLayout layouts[MAX_FRAMES];
|
||||
for (uint32_t i = 0; i < MAX_FRAMES; i++) layouts[i] = perFrameSetLayout;
|
||||
|
||||
VkDescriptorSetAllocateInfo setAlloc{};
|
||||
setAlloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
setAlloc.descriptorPool = sceneDescriptorPool;
|
||||
setAlloc.descriptorSetCount = 1;
|
||||
setAlloc.pSetLayouts = &perFrameSetLayout;
|
||||
setAlloc.descriptorSetCount = MAX_FRAMES;
|
||||
setAlloc.pSetLayouts = layouts;
|
||||
|
||||
if (vkAllocateDescriptorSets(device, &setAlloc, &reflPerFrameDescSet) != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to allocate reflection per-frame descriptor set");
|
||||
if (vkAllocateDescriptorSets(device, &setAlloc, reflPerFrameDescSet) != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to allocate reflection per-frame descriptor sets");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkDescriptorBufferInfo descBuf{};
|
||||
descBuf.buffer = reflPerFrameUBO;
|
||||
descBuf.offset = 0;
|
||||
descBuf.range = sizeof(GPUPerFrameData);
|
||||
// Bind each reflection descriptor to the same UBO but its own frame's shadow view
|
||||
for (uint32_t i = 0; i < MAX_FRAMES; i++) {
|
||||
VkDescriptorBufferInfo descBuf{};
|
||||
descBuf.buffer = reflPerFrameUBO;
|
||||
descBuf.offset = 0;
|
||||
descBuf.range = sizeof(GPUPerFrameData);
|
||||
|
||||
VkDescriptorImageInfo shadowImgInfo{};
|
||||
shadowImgInfo.sampler = shadowSampler;
|
||||
shadowImgInfo.imageView = shadowDepthView[0]; // reflection uses frame 0 shadow view
|
||||
shadowImgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
VkDescriptorImageInfo shadowImgInfo{};
|
||||
shadowImgInfo.sampler = shadowSampler;
|
||||
shadowImgInfo.imageView = shadowDepthView[i];
|
||||
shadowImgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
VkWriteDescriptorSet writes[2]{};
|
||||
writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[0].dstSet = reflPerFrameDescSet;
|
||||
writes[0].dstBinding = 0;
|
||||
writes[0].descriptorCount = 1;
|
||||
writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
writes[0].pBufferInfo = &descBuf;
|
||||
writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[1].dstSet = reflPerFrameDescSet;
|
||||
writes[1].dstBinding = 1;
|
||||
writes[1].descriptorCount = 1;
|
||||
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writes[1].pImageInfo = &shadowImgInfo;
|
||||
VkWriteDescriptorSet writes[2]{};
|
||||
writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[0].dstSet = reflPerFrameDescSet[i];
|
||||
writes[0].dstBinding = 0;
|
||||
writes[0].descriptorCount = 1;
|
||||
writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
writes[0].pBufferInfo = &descBuf;
|
||||
writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[1].dstSet = reflPerFrameDescSet[i];
|
||||
writes[1].dstBinding = 1;
|
||||
writes[1].descriptorCount = 1;
|
||||
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writes[1].pImageInfo = &shadowImgInfo;
|
||||
|
||||
vkUpdateDescriptorSets(device, 2, writes, 0, nullptr);
|
||||
vkUpdateDescriptorSets(device, 2, writes, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("Per-frame Vulkan resources created (shadow map ", SHADOW_MAP_SIZE, "x", SHADOW_MAP_SIZE, ")");
|
||||
|
|
@ -460,7 +466,7 @@ void Renderer::updatePerFrameUBO() {
|
|||
|
||||
currentFrameData.lightSpaceMatrix = lightSpaceMatrix;
|
||||
// Scale shadow bias proportionally to ortho extent to avoid acne at close range / gaps at far range
|
||||
float shadowBias = 0.8f * (shadowDistance_ / 300.0f);
|
||||
float shadowBias = glm::clamp(0.8f * (shadowDistance_ / 300.0f), 0.0f, 1.0f);
|
||||
currentFrameData.shadowParams = glm::vec4(shadowsEnabled ? 1.0f : 0.0f, shadowBias, 0.0f, 0.0f);
|
||||
|
||||
// Player water ripple data: pack player XY into shadowParams.zw, ripple strength into fogParams.w
|
||||
|
|
@ -566,7 +572,7 @@ bool Renderer::initialize(core::Window* win) {
|
|||
postProcessPipeline_ = std::make_unique<PostProcessPipeline>();
|
||||
postProcessPipeline_->initialize(vkCtx);
|
||||
|
||||
// Phase 2.5: Create render graph and register virtual resources
|
||||
// Create render graph and register virtual resources
|
||||
renderGraph_ = std::make_unique<RenderGraph>();
|
||||
renderGraph_->registerResource("shadow_depth");
|
||||
renderGraph_->registerResource("reflection_texture");
|
||||
|
|
@ -687,7 +693,7 @@ void Renderer::shutdown() {
|
|||
postProcessPipeline_.reset();
|
||||
}
|
||||
|
||||
// Phase 2.5: Destroy render graph
|
||||
// Destroy render graph
|
||||
renderGraph_.reset();
|
||||
|
||||
destroyPerFrameResources();
|
||||
|
|
@ -1018,8 +1024,26 @@ void Renderer::setInCombat(bool combat) {
|
|||
if (animationController_) animationController_->setInCombat(combat);
|
||||
}
|
||||
|
||||
void Renderer::setEquippedWeaponType(uint32_t inventoryType) {
|
||||
if (animationController_) animationController_->setEquippedWeaponType(inventoryType);
|
||||
void Renderer::setEquippedWeaponType(uint32_t inventoryType, bool is2HLoose, bool isFist,
|
||||
bool isDagger, bool hasOffHand, bool hasShield) {
|
||||
if (animationController_) animationController_->setEquippedWeaponType(inventoryType, is2HLoose, isFist, isDagger, hasOffHand, hasShield);
|
||||
}
|
||||
|
||||
void Renderer::triggerSpecialAttack(uint32_t spellId) {
|
||||
if (animationController_) animationController_->triggerSpecialAttack(spellId);
|
||||
}
|
||||
|
||||
void Renderer::setEquippedRangedType(RangedWeaponType type) {
|
||||
if (animationController_) animationController_->setEquippedRangedType(type);
|
||||
}
|
||||
|
||||
void Renderer::triggerRangedShot() {
|
||||
if (animationController_) animationController_->triggerRangedShot();
|
||||
}
|
||||
|
||||
RangedWeaponType Renderer::getEquippedRangedType() const {
|
||||
return animationController_ ? animationController_->getEquippedRangedType()
|
||||
: RangedWeaponType::NONE;
|
||||
}
|
||||
|
||||
void Renderer::setCharging(bool c) {
|
||||
|
|
@ -2797,8 +2821,8 @@ glm::mat4 Renderer::computeLightSpaceMatrix() {
|
|||
sunDir = -sunDir;
|
||||
}
|
||||
// Keep a minimum downward component so the frustum doesn't collapse at grazing angles.
|
||||
if (sunDir.z > -0.08f) {
|
||||
sunDir.z = -0.08f;
|
||||
if (sunDir.z > -0.15f) {
|
||||
sunDir.z = -0.15f;
|
||||
sunDir = glm::normalize(sunDir);
|
||||
}
|
||||
|
||||
|
|
@ -2986,6 +3010,11 @@ void Renderer::renderReflectionPass() {
|
|||
if (!waterRenderer || !camera || !waterRenderer->hasReflectionPass() || !waterRenderer->hasSurfaces()) return;
|
||||
if (currentCmd == VK_NULL_HANDLE || !reflPerFrameUBOMapped) return;
|
||||
|
||||
// Select the current frame's pre-bound reflection descriptor set
|
||||
// (each frame's set was bound to its own shadow depth view at init).
|
||||
uint32_t frame = vkCtx->getCurrentFrame();
|
||||
VkDescriptorSet reflDescSet = reflPerFrameDescSet[frame];
|
||||
|
||||
// Reflection pass uses 1x MSAA. Scene pipelines must be render-pass-compatible,
|
||||
// which requires matching sample counts. Only render scene into reflection when MSAA is off.
|
||||
bool canRenderScene = (vkCtx->getMsaaSamples() == VK_SAMPLE_COUNT_1_BIT);
|
||||
|
|
@ -3040,13 +3069,13 @@ void Renderer::renderReflectionPass() {
|
|||
skyParams.horizonGlow = lp.horizonGlow;
|
||||
}
|
||||
// weatherIntensity left at default 0 for reflection pass (no game handler in scope)
|
||||
skySystem->render(currentCmd, reflPerFrameDescSet, *camera, skyParams);
|
||||
skySystem->render(currentCmd, reflDescSet, *camera, skyParams);
|
||||
}
|
||||
if (terrainRenderer && terrainEnabled) {
|
||||
terrainRenderer->render(currentCmd, reflPerFrameDescSet, *camera);
|
||||
terrainRenderer->render(currentCmd, reflDescSet, *camera);
|
||||
}
|
||||
if (wmoRenderer) {
|
||||
wmoRenderer->render(currentCmd, reflPerFrameDescSet, *camera);
|
||||
wmoRenderer->render(currentCmd, reflDescSet, *camera);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3139,7 +3168,7 @@ void Renderer::renderShadowPass() {
|
|||
shadowDepthLayout_[frame] = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
}
|
||||
|
||||
// Phase 2.5: Build the per-frame render graph for off-screen pre-passes.
|
||||
// Build the per-frame render graph for off-screen pre-passes.
|
||||
// Declares passes as graph nodes with input/output dependencies.
|
||||
// compile() performs topological sort; execute() runs them with auto barriers.
|
||||
void Renderer::buildFrameGraph(game::GameHandler* gameHandler) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue