From 1676ff377a47d0f9a5cbdf1f5b685e7a18a1a949 Mon Sep 17 00:00:00 2001 From: Benualdo Date: Mon, 30 Sep 2024 21:44:08 +0200 Subject: [PATCH] Fix undo/redo Prefab overrides properties UID + Undo/Redo destroy WIP --- Editor.xml | 8 +- data/Scenes/Aiguelongue.scene | 40 ++++---- src/core/IFactory.h | 2 +- .../DynamicProperties/DynamicProperty.hpp | 8 +- src/core/Object/Factory.cpp | 6 +- src/core/Object/Factory.h | 6 +- .../UndoRedo/Entry/UndoRedoDestroyEntry.h | 38 ++++++++ .../UndoRedo/Entry/UndoRedoDestroyEntry.hpp | 91 +++++++++++++++++++ .../UndoRedo/Entry/UndoRedoPropertyEntry.h | 2 - src/core/UndoRedo/UndoRedo.cpp | 1 + src/core/UndoRedo/UndoRedo.h | 1 + src/core/UndoRedo/UndoRedoEntry.hpp | 2 +- src/core/core.vcxproj | 2 + src/core/core.vcxproj.filters | 6 ++ src/editor/Editor.cpp | 33 +++++-- src/editor/ImGui/Window/ImGuiWindow.cpp | 53 +++++++---- 16 files changed, 236 insertions(+), 63 deletions(-) create mode 100644 src/core/UndoRedo/Entry/UndoRedoDestroyEntry.h create mode 100644 src/core/UndoRedo/Entry/UndoRedoDestroyEntry.hpp diff --git a/Editor.xml b/Editor.xml index b99935dd4..2b63af7f0 100644 --- a/Editor.xml +++ b/Editor.xml @@ -1,9 +1,9 @@ - - - + + + @@ -14,6 +14,6 @@ - + diff --git a/data/Scenes/Aiguelongue.scene b/data/Scenes/Aiguelongue.scene index 4e9c50639..11c8fff40 100644 --- a/data/Scenes/Aiguelongue.scene +++ b/data/Scenes/Aiguelongue.scene @@ -462,7 +462,7 @@ - + @@ -500,7 +500,7 @@ - + @@ -524,7 +524,7 @@ - + @@ -540,7 +540,7 @@ - + @@ -578,7 +578,7 @@ - + @@ -594,7 +594,7 @@ - + @@ -610,7 +610,7 @@ - + @@ -670,7 +670,7 @@ - + @@ -1906,7 +1906,7 @@ - + @@ -1944,7 +1944,7 @@ - + @@ -2525,7 +2525,7 @@ - + @@ -2533,7 +2533,7 @@ - + @@ -2549,7 +2549,7 @@ - + @@ -2587,7 +2587,7 @@ - + @@ -2595,7 +2595,7 @@ - + @@ -2611,7 +2611,7 @@ - + @@ -2649,7 +2649,7 @@ - + @@ -2657,7 +2657,7 @@ - + @@ -2673,7 +2673,7 @@ - + @@ -2761,7 +2761,7 @@ - + diff --git a/src/core/IFactory.h b/src/core/IFactory.h index 302d4dc4c..15c4735a3 100644 --- a/src/core/IFactory.h +++ b/src/core/IFactory.h @@ -54,7 +54,7 @@ namespace vg::core virtual UID RegisterUID (IObject * _object) = 0; virtual void ReleaseUID (IObject * _object, UID & _uid) = 0; virtual const UIDObjectHash & GetUIDObjects () const = 0; - virtual IObject * FindByUID (UID _uid) = 0; + virtual IObject * FindByUID (UID _uid) const = 0; }; } diff --git a/src/core/Object/DynamicProperties/DynamicProperty.hpp b/src/core/Object/DynamicProperties/DynamicProperty.hpp index 2d8c02697..2cc5b0cd1 100644 --- a/src/core/Object/DynamicProperties/DynamicProperty.hpp +++ b/src/core/Object/DynamicProperties/DynamicProperty.hpp @@ -7,7 +7,7 @@ namespace vg::core { super::registerProperties(_desc); setPropertyFlag(DynamicProperty, m_name, PropertyFlags::NotVisible, true); - setPropertyFlag(DynamicProperty, m_uid, PropertyFlags::NotVisible, true); + //setPropertyFlag(DynamicProperty, m_uid, PropertyFlags::NotVisible, true); setPropertyFlag(DynamicProperty, m_originalUID, PropertyFlags::NotVisible, true); setPropertyFlag(DynamicProperty, m_objectFlags, PropertyFlags::NotVisible, true); @@ -21,18 +21,18 @@ namespace vg::core { SetName(_name); SetParent(_parent); + RegisterUID(); } //-------------------------------------------------------------------------------------- DynamicProperty::~DynamicProperty() { - + } //-------------------------------------------------------------------------------------- bool DynamicProperty::RegisterUID() { - return false; + return super::RegisterUID(); } - } \ No newline at end of file diff --git a/src/core/Object/Factory.cpp b/src/core/Object/Factory.cpp index bdf95cb25..4979e68e4 100644 --- a/src/core/Object/Factory.cpp +++ b/src/core/Object/Factory.cpp @@ -444,7 +444,7 @@ namespace vg::core } //-------------------------------------------------------------------------------------- - IObject * Factory::FindByUID(UID _uid) + IObject * Factory::FindByUID(UID _uid) const { lock_guard lock(m_uidObjectHashMutex); @@ -822,7 +822,7 @@ namespace vg::core if (it != m_initValues.end()) { io::Buffer * buffer = it->second; - bool result = serializeFromMemory(_object, *buffer); + bool result = serializeObjectFromMemory(_object, *buffer); VG_SAFE_DELETE(it->second); m_initValues.erase(it); return result; @@ -832,7 +832,7 @@ namespace vg::core } //-------------------------------------------------------------------------------------- - bool Factory::serializeFromMemory(IObject * _object, io::Buffer & _buffer) + bool Factory::serializeObjectFromMemory(IObject * _object, io::Buffer & _buffer) { const char * className = _object->GetClassName(); const auto * classDesc = GetClassDescriptor(className); diff --git a/src/core/Object/Factory.h b/src/core/Object/Factory.h index 2fda099b5..7a47c3e45 100644 --- a/src/core/Object/Factory.h +++ b/src/core/Object/Factory.h @@ -45,7 +45,7 @@ namespace vg::core UID RegisterUID (IObject * _object) final override; void ReleaseUID (IObject * _object, UID & _uid) final override; const UIDObjectHash & GetUIDObjects () const final override; - IObject * FindByUID (UID _uid) final override; + IObject * FindByUID (UID _uid) const final override; //protected: bool SerializeFromXML (IObject * _object, const XMLElem * _xmlElem) const; @@ -62,7 +62,7 @@ namespace vg::core bool serializeObjectToMemory (const IObject * _object, io::Buffer & _buffer); void serializePropertyToMemory (const IObject * _object, const IProperty * _prop, io::Buffer & _buffer); - bool serializeFromMemory (IObject * _object, io::Buffer & _buffer); + bool serializeObjectFromMemory (IObject * _object, io::Buffer & _buffer); void serializePropertyFromMemory (IObject * _object, const IProperty * _prop, io::Buffer & _buffer); void ReleaseAsync (core::IObject * _object); @@ -84,6 +84,6 @@ namespace vg::core u8 m_objectsToReleaseTableIndex = 0; core::unordered_map m_initValues; UIDObjectHash m_uidObjectHash; - mutex m_uidObjectHashMutex; + mutable mutex m_uidObjectHashMutex; }; } \ No newline at end of file diff --git a/src/core/UndoRedo/Entry/UndoRedoDestroyEntry.h b/src/core/UndoRedo/Entry/UndoRedoDestroyEntry.h new file mode 100644 index 000000000..da98ca72c --- /dev/null +++ b/src/core/UndoRedo/Entry/UndoRedoDestroyEntry.h @@ -0,0 +1,38 @@ +#pragma once + +#include "core/IUndoRedo.h" +#include "core/File/Buffer.h" + +namespace vg::core +{ + class IObject; + class IProperty; + class IDynamicProperty; + + //-------------------------------------------------------------------------------------- + class UndoRedoDestroyEntry final : public UndoRedoEntry + { + using super = UndoRedoEntry; + + public: + UndoRedoDestroyEntry(IObject * _object, IObject * _parent, uint _indexInParent); + + void BeforeChange() final override; + void AfterChange() final override; + + void Undo() final override; + void Redo() final override; + + string GetEntryName() const final override; + string GetObjectName() const final override; + string GetDescription() const final override; + + IObject * getParent() const; + + private: + io::Buffer m_buffer; + string m_className; + UID m_parentUID; + uint m_indexInParent; + }; +} \ No newline at end of file diff --git a/src/core/UndoRedo/Entry/UndoRedoDestroyEntry.hpp b/src/core/UndoRedo/Entry/UndoRedoDestroyEntry.hpp new file mode 100644 index 000000000..4a9e77b32 --- /dev/null +++ b/src/core/UndoRedo/Entry/UndoRedoDestroyEntry.hpp @@ -0,0 +1,91 @@ +#include "UndoRedoDestroyEntry.h" +#include "core/Kernel.h" +#include "core/Object/Factory.h" +#include "core/IGameObject.h" +#include "core/string/string.h" + +namespace vg::core +{ + //-------------------------------------------------------------------------------------- + UndoRedoDestroyEntry::UndoRedoDestroyEntry(IObject * _object, IObject * _parent, uint _indexInParent) : + UndoRedoEntry(_object) + { + m_className = _object->GetClassName(); + m_parentUID = _parent->GetUID(); + m_indexInParent = _indexInParent; + } + + //-------------------------------------------------------------------------------------- + void UndoRedoDestroyEntry::BeforeChange() + { + Factory * factory = (Factory *)Kernel::getFactory(); + if (auto * object = GetObject()) + { + m_buffer.resetWrite(); + factory->serializeObjectToMemory(object, m_buffer); + } + } + + //-------------------------------------------------------------------------------------- + void UndoRedoDestroyEntry::AfterChange() + { + + } + + //-------------------------------------------------------------------------------------- + string UndoRedoDestroyEntry::GetEntryName() const + { + return "Destroy"; + } + + //-------------------------------------------------------------------------------------- + string UndoRedoDestroyEntry::GetObjectName() const + { + return super::GetObjectName(); + } + + //-------------------------------------------------------------------------------------- + string UndoRedoDestroyEntry::GetDescription() const + { + return fmt::sprintf("(%s) \"%s\"", m_className, m_objectName); + } + + //-------------------------------------------------------------------------------------- + IObject * UndoRedoDestroyEntry::getParent() const + { + IFactory * factory = Kernel::getFactory(); + return factory->FindByUID(m_parentUID); + } + + //-------------------------------------------------------------------------------------- + void UndoRedoDestroyEntry::Undo() + { + VG_INFO("[Undo/Redo] Undo Destroy Object %s (UID=0x%08X)", GetDescription().c_str(), m_objectUID); + + Factory * factory = (Factory *)Kernel::getFactory(); + + if (IGameObject * parent = dynamic_cast(getParent())) + { + IGameObject * obj = (IGameObject*)factory->CreateObject(m_className.c_str(), m_objectName, parent); + m_buffer.resetRead(); + factory->serializeObjectFromMemory(obj, m_buffer); + VG_ASSERT(m_objectUID == obj->GetUID()); + + parent->AddChild(obj); + //obj->OnLoad(); + obj->Release(); + } + } + + //-------------------------------------------------------------------------------------- + void UndoRedoDestroyEntry::Redo() + { + VG_INFO("[Undo/Redo] Redo Destroy Object \"%s\" (UID=0x%08X)", m_objectName.c_str(), m_objectUID); + + if (auto * obj = dynamic_cast(GetObject())) + { + if (auto * parent = dynamic_cast(obj->GetParent())) + parent->RemoveChild(obj); + } + } +} \ No newline at end of file diff --git a/src/core/UndoRedo/Entry/UndoRedoPropertyEntry.h b/src/core/UndoRedo/Entry/UndoRedoPropertyEntry.h index 032c0c6b3..9853a52ef 100644 --- a/src/core/UndoRedo/Entry/UndoRedoPropertyEntry.h +++ b/src/core/UndoRedo/Entry/UndoRedoPropertyEntry.h @@ -9,8 +9,6 @@ namespace vg::core class IProperty; class IDynamicProperty; - //-------------------------------------------------------------------------------------- - // TODO: do not store object raw pointers but safe GUID instead //-------------------------------------------------------------------------------------- class UndoRedoPropertyEntry final : public UndoRedoEntry { diff --git a/src/core/UndoRedo/UndoRedo.cpp b/src/core/UndoRedo/UndoRedo.cpp index 3c57b27a0..5185adee3 100644 --- a/src/core/UndoRedo/UndoRedo.cpp +++ b/src/core/UndoRedo/UndoRedo.cpp @@ -9,6 +9,7 @@ #include "UndoRedoEntry.hpp" #include "UndoRedoEntryGroup.hpp" #include "Entry/UndoRedoPropertyEntry.hpp" +#include "Entry/UndoRedoDestroyEntry.hpp" namespace vg::core { diff --git a/src/core/UndoRedo/UndoRedo.h b/src/core/UndoRedo/UndoRedo.h index 6c3c81af2..e62a9d1fb 100644 --- a/src/core/UndoRedo/UndoRedo.h +++ b/src/core/UndoRedo/UndoRedo.h @@ -3,6 +3,7 @@ #include "UndoRedo_consts.h" #include "UndoRedoManager.h" #include "Entry/UndoRedoPropertyEntry.h" +#include "Entry/UndoRedoDestroyEntry.h" namespace vg::core { diff --git a/src/core/UndoRedo/UndoRedoEntry.hpp b/src/core/UndoRedo/UndoRedoEntry.hpp index aeecb2b33..e0ea234ed 100644 --- a/src/core/UndoRedo/UndoRedoEntry.hpp +++ b/src/core/UndoRedo/UndoRedoEntry.hpp @@ -7,7 +7,7 @@ namespace vg::core { if (nullptr != _object) { - m_objectUID = _object->GetUID(); + m_objectUID = _object->GetUID(false); VG_ASSERT(m_objectUID); m_objectName = _object->GetName(); diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 643eb212c..331014ceb 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -154,6 +154,8 @@ + + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 9ba1b5db6..4a1189be2 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -447,6 +447,12 @@ UndoRedo + + UndoRedo\Entry + + + UndoRedo\Entry + diff --git a/src/editor/Editor.cpp b/src/editor/Editor.cpp index 92bddca48..4101f328a 100644 --- a/src/editor/Editor.cpp +++ b/src/editor/Editor.cpp @@ -6,6 +6,7 @@ #include "core/IGameObject.h" #include "core/File/File.h" #include "core/Timer/Timer.h" +#include "core/UndoRedo/UndoRedo.h" #include "gfx/IViewGUI.h" #include "renderer/IRenderer.h" #include "renderer/IImGuiAdapter.h" @@ -751,16 +752,30 @@ namespace vg::editor void Editor::deleteGameObjects(core::vector & _gameObjects) { ImGui::OnMsgBoxClickedFunc deleteGameObject = [=]() mutable + { + auto * undoRedoManager = Kernel::getUndoRedoManager(); + + // Prepare undo/redo + UndoRedoTarget undoRedoTarget(this, nullptr); + + auto * undoRedoGroup = new UndoRedoEntryGroup("Destroy"); + for (uint i = 0; i < _gameObjects.size(); ++i) + undoRedoGroup->AddSubEntry(new UndoRedoDestroyEntry(_gameObjects[i], _gameObjects[i]->GetParent(), i)); + undoRedoManager->BeforeChange(undoRedoGroup); + + for (uint i = 0; i < _gameObjects.size(); ++i) { - for (uint i = 0; i < _gameObjects.size(); ++i) - { - IGameObject * gameObjectToDelete = _gameObjects[i]; - IGameObject * parentGameObject = dynamic_cast(gameObjectToDelete->GetParent()); - if (nullptr != parentGameObject) - parentGameObject->RemoveChild(gameObjectToDelete); - } - return true; - }; + IGameObject * gameObjectToDelete = _gameObjects[i]; + IGameObject * parentGameObject = dynamic_cast(gameObjectToDelete->GetParent()); + if (nullptr != parentGameObject) + parentGameObject->RemoveChild(gameObjectToDelete); + } + + // Finalize Undo/Redo entry after editing + undoRedoManager->AfterChange(); + + return true; + }; string msg; if (_gameObjects.size() > 1) diff --git a/src/editor/ImGui/Window/ImGuiWindow.cpp b/src/editor/ImGui/Window/ImGuiWindow.cpp index 3404b1048..64fc1ee35 100644 --- a/src/editor/ImGui/Window/ImGuiWindow.cpp +++ b/src/editor/ImGui/Window/ImGuiWindow.cpp @@ -407,11 +407,33 @@ namespace vg::editor const auto flags = _prop->GetFlags(); auto editFormat = scalarTraits::is_integer ? (scalarTraits::is_signed ? g_editIntFormat : g_editUIntFormat) : g_editFloatFormat; + + bool uid = false; + bool invalidUID = false; if (asBool(PropertyFlags::Hexadecimal & flags)) + { + if (!strcmp(_prop->GetName(), "m_uid") || !strcmp(_prop->GetName(), "m_originalUID")) + { + uid = true; + + const auto * factory = Kernel::getFactory(); + if (!factory->FindByUID((UID)temp[0])) + invalidUID = true; + + } editFormat = "0x%0X"; + } VG_ASSERT(!asBool(PropertyFlags::Color & flags) || (count == 3 || count == 4)); + if (uid) + { + auto * imGuiAdapter = Editor::get()->getRenderer()->GetImGuiAdapter(); + + if (invalidUID) + ImGui::PushStyleColor(ImGuiCol_Text, imGuiAdapter->GetErrorColor()); + } + if (_propContext.m_readOnly) ImGui::BeginDisabled(true); @@ -446,11 +468,19 @@ namespace vg::editor EditingState editingState = undoRedoBeforeEdit(edited, _propContext, _object, _prop, (S*)&temp[0], _ptr, InteractionType::Continuous); - ImGuiWindow::drawPropertyLabel(_propContext, _prop); - if (_propContext.m_readOnly) ImGui::EndDisabled(); + string tooltip = _prop->GetDescription() ? (string)_prop->GetDescription() : ""; + + if (invalidUID) + tooltip += " (invalid)"; + + ImGuiWindow::drawPropertyLabel(_propContext, _prop->GetDisplayName(), tooltip.c_str()); + + if (invalidUID) + ImGui::PopStyleColor(); + if (edited) { if (asBool(PropertyFlags::EulerAngle & flags)) @@ -797,8 +827,6 @@ namespace vg::editor //-------------------------------------------------------------------------------------- ImVec4 ImGuiWindow::getPropertyColor(const PropertyContext & _propContext) { - ImGui::SameLine(); - if (_propContext.m_isPrefabInstance) { if (_propContext.m_isPrefabOverride) @@ -826,22 +854,11 @@ namespace vg::editor //-------------------------------------------------------------------------------------- void ImGuiWindow::drawPropertyLabel(const PropertyContext & _propContext, const char * _label, const char * _tooltip) { - auto x = ImGui::GetCursorPosX(); - ImGui::SameLine(); - - ImGui::PushStyleColor(ImGuiCol_Text, getPropertyColor(_propContext)); - - // ugly workaround - if (x>100) - ImGui::SetCursorPosX(x); - ImGui::Text(_label); - if (_tooltip && ImGui::IsItemHovered()) + if (_tooltip && _tooltip[0] != '\0' && ImGui::IsItemHovered()) ImGui::SetTooltip(_tooltip); - - ImGui::PopStyleColor(); }; //-------------------------------------------------------------------------------------- @@ -1024,6 +1041,8 @@ namespace vg::editor const bool isEnumArray = asBool(PropertyFlags::EnumArray & flags); const auto enumArrayTreeNodeFlags = ImGuiTreeNodeFlags_DefaultOpen; + ImGui::PushStyleColor(ImGuiCol_Text, getPropertyColor(propContext)); + //ImGui::BeginDisabled(readOnly); { switch (type) @@ -1992,6 +2011,8 @@ namespace vg::editor } //ImGui::EndDisabled(); + ImGui::PopStyleColor(); + //ImGui::SameLine(); //ImGui::Text(label.c_str());