mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Fix M2 animated instance flashing (deer/bird/critter pop-in)
Root cause: bonesDirty was a single bool shared across both double-buffered frame indices. When bones were copied to frame 0's SSBO and bonesDirty cleared, frame 1's newly-allocated SSBO would contain garbage/zeros and never get populated — causing animated M2 instances to flash invisible on alternating frames. Fix: Make bonesDirty per-frame-index (bool[2]) so each buffer independently tracks whether it needs bone data uploaded. When bones are recomputed, both indices are marked dirty. When uploaded during render, only the current frame index is cleared. New buffer allocations in prepareRender force their frame index dirty.
This commit is contained in:
parent
6cf08fbaa6
commit
ac3c90dd75
2 changed files with 13 additions and 7 deletions
|
|
@ -1687,7 +1687,7 @@ uint32_t M2Renderer::createInstance(uint32_t modelId, const glm::vec3& position,
|
|||
for (const auto& existing : instances) {
|
||||
if (existing.modelId == modelId && !existing.boneMatrices.empty()) {
|
||||
instance.boneMatrices = existing.boneMatrices;
|
||||
instance.bonesDirty = true;
|
||||
instance.bonesDirty[0] = instance.bonesDirty[1] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1791,7 +1791,7 @@ uint32_t M2Renderer::createInstanceWithMatrix(uint32_t modelId, const glm::mat4&
|
|||
for (const auto& existing : instances) {
|
||||
if (existing.modelId == modelId && !existing.boneMatrices.empty()) {
|
||||
instance.boneMatrices = existing.boneMatrices;
|
||||
instance.bonesDirty = true;
|
||||
instance.bonesDirty[0] = instance.bonesDirty[1] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1951,7 +1951,7 @@ static void computeBoneMatrices(const M2ModelGPU& model, M2Instance& instance) {
|
|||
instance.boneMatrices[i] = local;
|
||||
}
|
||||
}
|
||||
instance.bonesDirty = true;
|
||||
instance.bonesDirty[0] = instance.bonesDirty[1] = true;
|
||||
}
|
||||
|
||||
void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::mat4& viewProjection) {
|
||||
|
|
@ -2237,6 +2237,11 @@ void M2Renderer::prepareRender(uint32_t frameIndex, const Camera& camera) {
|
|||
&instance.boneBuffer[frameIndex], &instance.boneAlloc[frameIndex], &allocInfo);
|
||||
instance.boneMapped[frameIndex] = allocInfo.pMappedData;
|
||||
|
||||
// Force dirty so current boneMatrices get copied into this
|
||||
// newly-allocated buffer during render (prevents garbage/zero
|
||||
// bones when the other frame index already cleared bonesDirty).
|
||||
instance.bonesDirty[frameIndex] = true;
|
||||
|
||||
instance.boneSet[frameIndex] = allocateBoneSet();
|
||||
if (instance.boneSet[frameIndex]) {
|
||||
VkDescriptorBufferInfo bufInfo{};
|
||||
|
|
@ -2426,12 +2431,13 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
}
|
||||
bool useBones = needsBones;
|
||||
if (useBones) {
|
||||
// Upload bone matrices only when recomputed (skip frame-skipped instances)
|
||||
if (instance.bonesDirty && instance.boneMapped[frameIndex]) {
|
||||
// Upload bone matrices only when recomputed (per-frame-index tracking
|
||||
// ensures both double-buffered SSBOs get the latest bone data)
|
||||
if (instance.bonesDirty[frameIndex] && instance.boneMapped[frameIndex]) {
|
||||
int numBones = std::min(static_cast<int>(instance.boneMatrices.size()), 128);
|
||||
memcpy(instance.boneMapped[frameIndex], instance.boneMatrices.data(),
|
||||
numBones * sizeof(glm::mat4));
|
||||
instance.bonesDirty = false;
|
||||
instance.bonesDirty[frameIndex] = false;
|
||||
}
|
||||
|
||||
// Bind bone descriptor set (set 2)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue