fix(editor): create M2 instances AFTER upload flush (isValid fix)

ROOT CAUSE: createInstance() checks mdlRef.isValid() which requires
vertexBuffer != VK_NULL_HANDLE. But vertex buffers are uploaded via
staging and only finalized by waitAllUploads(). Instances were being
created BEFORE the upload flush, so vertexBuffer was still null,
cachedIsValid was set to false, and all instances were skipped during
render (0 draws despite loaded models).

Fix: split rebuildObjects into two phases:
1. Load all models (upload geometry to staging)
2. waitAllUploads + pollUploadBatches (finalize GPU buffers)
3. Create all instances (vertexBuffer is now valid, isValid() = true)

This matches the client's terrain_manager pattern where models are
loaded on background threads and instances created after finalization.
This commit is contained in:
Kelsi 2026-05-06 00:11:44 -07:00
parent c9c4a15e9a
commit a845723635

View file

@ -219,8 +219,6 @@ void EditorViewport::rebuildObjects(const std::vector<PlacedObject>& objects,
model.vertices.size(), " verts)");
m2ModelIds[obj.path] = modelId;
}
glm::vec3 rotRad = glm::radians(obj.rotation);
m2Renderer_->createInstance(modelId, obj.position, rotRad, obj.scale);
} else if (obj.type == PlaceableType::WMO && wmoRenderer_) {
uint32_t modelId;
@ -368,14 +366,30 @@ void EditorViewport::rebuildObjects(const std::vector<PlacedObject>& objects,
}
m2ModelIds[npc.modelPath] = modelId;
}
glm::vec3 rotRad = glm::radians(glm::vec3(0, 0, npc.orientation));
m2Renderer_->createInstance(modelId, npc.position, rotRad, npc.scale);
}
}
// Finalize all GPU uploads BEFORE creating instances
// (vertex buffers must be valid for isValid() check in createInstance)
vkCtx_->waitAllUploads();
vkCtx_->pollUploadBatches();
// Now create instances (vertex buffers are finalized)
for (const auto& obj : objects) {
if (obj.type == PlaceableType::M2) {
auto it = m2ModelIds.find(obj.path);
if (it == m2ModelIds.end()) continue;
glm::vec3 rotRad = glm::radians(obj.rotation);
m2Renderer_->createInstance(it->second, obj.position, rotRad, obj.scale);
}
}
for (const auto& npc : npcs) {
auto it = m2ModelIds.find(npc.modelPath);
if (it == m2ModelIds.end()) continue;
glm::vec3 rotRad = glm::radians(glm::vec3(0, 0, npc.orientation));
m2Renderer_->createInstance(it->second, npc.position, rotRad, npc.scale);
}
// Update NPC markers via dedicated method
updateNpcMarkers(npcs);
}