From f38884856fae8b8e8b1b7ef51c98c349721da40f Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 03:52:43 -0700 Subject: [PATCH] fix(editor): NPC ghost preview, scale control, frame sync for M2 rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- tools/editor/editor_app.cpp | 8 ++++++-- tools/editor/editor_ui.cpp | 2 ++ tools/editor/editor_viewport.cpp | 2 +- tools/editor/npc_spawner.cpp | 1 + tools/editor/npc_spawner.hpp | 3 +++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index d29c36ca..12c975bb 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -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(); } diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index fc553e52..bd3f5989 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -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; diff --git a/tools/editor/editor_viewport.cpp b/tools/editor/editor_viewport.cpp index c8b33d70..3ab9b912 100644 --- a/tools/editor/editor_viewport.cpp +++ b/tools/editor/editor_viewport.cpp @@ -242,7 +242,7 @@ void EditorViewport::rebuildObjects(const std::vector& 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); } } diff --git a/tools/editor/npc_spawner.cpp b/tools/editor/npc_spawner.cpp index 5cf2e376..48a1fd63 100644 --- a/tools/editor/npc_spawner.cpp +++ b/tools/editor/npc_spawner.cpp @@ -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"; diff --git a/tools/editor/npc_spawner.hpp b/tools/editor/npc_spawner.hpp index eed6d13b..4efe2410 100644 --- a/tools/editor/npc_spawner.hpp +++ b/tools/editor/npc_spawner.hpp @@ -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;