fix(editor): NPC ghost preview, scale control, frame sync for M2 rendering

- Ghost preview now shows in NPC mode (follows cursor with creature model)
- Added scale field to CreatureSpawn (default 1.0, slider 0.5-10x)
- NPC instances render at their configured scale
- Scale included in JSON save format
- M2Renderer::update() now runs AFTER beginFrame() so getCurrentFrame()
  returns the correct frame index — fixes instance SSBO mismatch that
  caused draws=0 despite loaded models
This commit is contained in:
Kelsi 2026-05-05 03:52:43 -07:00
parent 2980ca83e7
commit f38884856f
5 changed files with 13 additions and 3 deletions

View file

@ -344,13 +344,17 @@ void EditorApp::updateTerrainEditing(float dt) {
terrainEditor_.brush().setPosition(hitPos);
terrainEditor_.brush().setActive(true);
// Ghost preview for object placement
// Ghost preview for object/NPC placement
if (mode_ == EditorMode::PlaceObject && !objectPlacer_.getActivePath().empty()) {
viewport_.setGhostPreview(
objectPlacer_.getActivePath(), hitPos,
glm::vec3(0, objectPlacer_.getPlacementRotationY(), 0),
objectPlacer_.getPlacementScale());
} else if (mode_ != EditorMode::PlaceObject) {
} else if (mode_ == EditorMode::NPC && !npcSpawner_.getTemplate().modelPath.empty()) {
viewport_.setGhostPreview(
npcSpawner_.getTemplate().modelPath, hitPos,
glm::vec3(0, 0, 0), npcSpawner_.getTemplate().scale);
} else if (mode_ != EditorMode::PlaceObject && mode_ != EditorMode::NPC) {
viewport_.clearGhostPreview();
}

View file

@ -458,6 +458,8 @@ void EditorUI::renderNpcPanel(EditorApp& app) {
if (ImGui::InputText("Name##tmpl", nameBuf, sizeof(nameBuf)))
tmpl.name = nameBuf;
ImGui::SliderFloat("Scale", &tmpl.scale, 0.5f, 10.0f, "%.1f");
int lvl = tmpl.level;
if (ImGui::SliderInt("Level", &lvl, 1, 83)) {
tmpl.level = lvl;

View file

@ -242,7 +242,7 @@ 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, 1.0f);
m2Renderer_->createInstance(modelId, npc.position, rotRad, npc.scale);
}
}

View file

@ -69,6 +69,7 @@ bool NpcSpawner::saveToFile(const std::string& path) const {
f << " \"displayId\": " << s.displayId << ",\n";
f << " \"position\": [" << s.position.x << "," << s.position.y << "," << s.position.z << "],\n";
f << " \"orientation\": " << s.orientation << ",\n";
f << " \"scale\": " << s.scale << ",\n";
f << " \"level\": " << s.level << ",\n";
f << " \"health\": " << s.health << ",\n";
f << " \"mana\": " << s.mana << ",\n";

View file

@ -39,6 +39,9 @@ struct CreatureSpawn {
uint32_t armor = 0;
uint32_t faction = 0; // 0 = neutral
// Display
float scale = 1.0f;
// Behavior
CreatureBehavior behavior = CreatureBehavior::Stationary;
float wanderRadius = 10.0f;