fix(rendering,game): init bone SSBO to identity; stop movement before cast

Bone SSBO buffers were allocated for MAX_BONES (240) entries but only
the first numBones were written. Uninitialized GPU memory in the
remaining slots caused vertex spikes when any bone index exceeded the
model's actual bone count.

Also send MSG_MOVE_STOP before spell casts so the server doesn't reject
cast-time spells (e.g. hearthstone) with "can't do that while moving".
This commit is contained in:
Kelsi Davis 2026-04-04 01:16:28 -07:00
parent bde9bd20d8
commit c95147390b
2 changed files with 29 additions and 0 deletions

View file

@ -222,6 +222,14 @@ void SpellHandler::castSpell(uint32_t spellId, uint64_t targetGuid) {
return;
}
// Stop movement before casting — servers reject cast-time spells while moving
const uint32_t moveFlags = owner_.movementInfo.flags;
const bool isMoving = (moveFlags & 0x0Fu) != 0; // FORWARD|BACKWARD|STRAFE_LEFT|STRAFE_RIGHT
if (isMoving) {
owner_.movementInfo.flags &= ~0x0Fu;
owner_.sendMovement(Opcode::MSG_MOVE_STOP);
}
uint64_t target = targetGuid != 0 ? targetGuid : owner_.targetGuid;
// Self-targeted spells like hearthstone should not send a target
if (spellId == 8690) target = 0;

View file

@ -2024,6 +2024,13 @@ void CharacterRenderer::prepareRender(uint32_t frameIndex) {
&instance.boneBuffer[frameIndex], &instance.boneAlloc[frameIndex], &allocInfo);
instance.boneMapped[frameIndex] = allocInfo.pMappedData;
// Initialize all bone slots to identity so out-of-range indices
// produce correct (neutral) transforms instead of GPU garbage
if (instance.boneMapped[frameIndex]) {
auto* dst = static_cast<glm::mat4*>(instance.boneMapped[frameIndex]);
for (int j = 0; j < MAX_BONES; j++) dst[j] = glm::mat4(1.0f);
}
VkDescriptorSetAllocateInfo ai{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
ai.descriptorPool = boneDescPool_;
ai.descriptorSetCount = 1;
@ -2147,6 +2154,13 @@ void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
&instance.boneBuffer[frameIndex], &instance.boneAlloc[frameIndex], &allocInfo);
instance.boneMapped[frameIndex] = allocInfo.pMappedData;
// Initialize all bone slots to identity so out-of-range indices
// produce correct (neutral) transforms instead of GPU garbage
if (instance.boneMapped[frameIndex]) {
auto* dst = static_cast<glm::mat4*>(instance.boneMapped[frameIndex]);
for (int j = 0; j < MAX_BONES; j++) dst[j] = glm::mat4(1.0f);
}
// Allocate descriptor set for bone SSBO
VkDescriptorSetAllocateInfo ai{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
ai.descriptorPool = boneDescPool_;
@ -2787,6 +2801,13 @@ void CharacterRenderer::renderShadow(VkCommandBuffer cmd, const glm::mat4& light
&inst.boneBuffer[frameIndex], &inst.boneAlloc[frameIndex], &ai);
inst.boneMapped[frameIndex] = ai.pMappedData;
// Initialize all bone slots to identity so out-of-range indices
// produce correct (neutral) transforms instead of GPU garbage
if (inst.boneMapped[frameIndex]) {
auto* dst = static_cast<glm::mat4*>(inst.boneMapped[frameIndex]);
for (int j = 0; j < MAX_BONES; j++) dst[j] = glm::mat4(1.0f);
}
VkDescriptorSetAllocateInfo dsAI{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
dsAI.descriptorPool = boneDescPool_;
dsAI.descriptorSetCount = 1;