mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-06 00:53:52 +00:00
feat(editor): undo object placement, snap to ground, keyboard shortcuts
- Ctrl+Z in Object/NPC mode undoes last placement (50-deep stack) - "Snap Ground" button raycasts straight down to place object on terrain - Useful for objects placed too high or moved off terrain surface - Undo stack adjusts indices when objects are removed mid-stack
This commit is contained in:
parent
ace6173401
commit
88abbfb564
5 changed files with 50 additions and 5 deletions
|
|
@ -212,10 +212,17 @@ void EditorApp::processEvents() {
|
|||
}
|
||||
}
|
||||
if (sc == SDL_SCANCODE_Z && (event.key.keysym.mod & KMOD_CTRL)) {
|
||||
if (event.key.keysym.mod & KMOD_SHIFT)
|
||||
terrainEditor_.redo();
|
||||
else
|
||||
terrainEditor_.undo();
|
||||
if (mode_ == EditorMode::Sculpt) {
|
||||
if (event.key.keysym.mod & KMOD_SHIFT)
|
||||
terrainEditor_.redo();
|
||||
else
|
||||
terrainEditor_.undo();
|
||||
} else if (mode_ == EditorMode::PlaceObject || mode_ == EditorMode::NPC) {
|
||||
if (!(event.key.keysym.mod & KMOD_SHIFT) && objectPlacer_.canUndoPlace()) {
|
||||
objectPlacer_.undoLastPlace();
|
||||
objectsDirty_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!io.WantCaptureKeyboard)
|
||||
|
|
@ -554,6 +561,21 @@ void EditorApp::setGizmoAxis(TransformAxis axis) {
|
|||
viewport_.getGizmo().setTarget(sel->position, sel->scale);
|
||||
}
|
||||
|
||||
void EditorApp::snapSelectedToGround() {
|
||||
auto* sel = objectPlacer_.getSelected();
|
||||
if (!sel || !terrain_.isLoaded()) return;
|
||||
|
||||
// Cast ray straight down from object position
|
||||
rendering::Ray ray;
|
||||
ray.origin = sel->position + glm::vec3(0, 0, 500);
|
||||
ray.direction = glm::vec3(0, 0, -1);
|
||||
glm::vec3 hitPos;
|
||||
if (terrainEditor_.raycastTerrain(ray, hitPos)) {
|
||||
sel->position.z = hitPos.z;
|
||||
objectsDirty_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorApp::resetCamera() {
|
||||
camera_.setPosition(glm::vec3(0.0f, 0.0f, 300.0f));
|
||||
camera_.setYawPitch(0.0f, -30.0f);
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public:
|
|||
|
||||
void startGizmoMode(TransformMode mode);
|
||||
void setGizmoAxis(TransformAxis axis);
|
||||
void snapSelectedToGround();
|
||||
TransformGizmo& getGizmo() { return viewport_.getGizmo(); }
|
||||
bool shouldOpenContextMenu() const { return openContextMenu_; }
|
||||
void clearContextMenuFlag() { openContextMenu_ = false; }
|
||||
|
|
|
|||
|
|
@ -360,8 +360,10 @@ void EditorUI::renderObjectPanel(EditorApp& app) {
|
|||
|
||||
if (changed) app.markObjectsDirty();
|
||||
|
||||
if (ImGui::Button("Delete", ImVec2(100, 0))) placer.deleteSelected();
|
||||
if (ImGui::Button("Snap Ground", ImVec2(100, 0)))
|
||||
app.snapSelectedToGround();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete", ImVec2(100, 0))) placer.deleteSelected();
|
||||
if (ImGui::Button("Duplicate", ImVec2(100, 0))) {
|
||||
PlacedObject copy = *sel;
|
||||
copy.uniqueId = 0;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ void ObjectPlacer::placeObject(const glm::vec3& position) {
|
|||
obj.selected = false;
|
||||
|
||||
objects_.push_back(obj);
|
||||
undoStack_.push_back(static_cast<int>(objects_.size() - 1));
|
||||
if (undoStack_.size() > 50) undoStack_.erase(undoStack_.begin());
|
||||
LOG_INFO("Placed ", (activeType_ == PlaceableType::M2 ? "M2" : "WMO"),
|
||||
": ", activePath_, " at (", position.x, ",", position.y, ",", position.z, ")");
|
||||
}
|
||||
|
|
@ -93,6 +95,19 @@ void ObjectPlacer::deleteSelected() {
|
|||
selectedIdx_ = -1;
|
||||
}
|
||||
|
||||
void ObjectPlacer::undoLastPlace() {
|
||||
if (undoStack_.empty()) return;
|
||||
int idx = undoStack_.back();
|
||||
undoStack_.pop_back();
|
||||
if (idx >= 0 && idx < static_cast<int>(objects_.size())) {
|
||||
if (selectedIdx_ == idx) selectedIdx_ = -1;
|
||||
else if (selectedIdx_ > idx) selectedIdx_--;
|
||||
objects_.erase(objects_.begin() + idx);
|
||||
// Adjust remaining undo indices
|
||||
for (auto& i : undoStack_) { if (i > idx) i--; }
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectPlacer::syncToTerrain() {
|
||||
if (!terrain_) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ public:
|
|||
float getPlacementScale() const { return placementScale_; }
|
||||
void setPlacementScale(float s) { placementScale_ = s; }
|
||||
|
||||
// Undo last placement
|
||||
bool canUndoPlace() const { return !undoStack_.empty(); }
|
||||
void undoLastPlace();
|
||||
|
||||
private:
|
||||
uint32_t nextUniqueId();
|
||||
|
||||
|
|
@ -65,6 +69,7 @@ private:
|
|||
PlaceableType activeType_ = PlaceableType::M2;
|
||||
|
||||
std::vector<PlacedObject> objects_;
|
||||
std::vector<int> undoStack_; // indices of recently placed objects
|
||||
int selectedIdx_ = -1;
|
||||
uint32_t uniqueIdCounter_ = 1;
|
||||
float placementRotY_ = 0.0f;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue