From a3dc799692db6ecda4a9851a45d335f6bd0de421 Mon Sep 17 00:00:00 2001 From: antopilo Date: Sun, 26 Jan 2025 00:07:15 -0500 Subject: [PATCH] Started editor windows refactor --- Editor/EditorContext.h | 38 ++ Editor/IEditorWidget.h | 17 + Editor/SceneEditorWindow.cpp | 54 ++ Editor/SceneEditorWindow.h | 42 ++ Editor/SceneHierarchyWidget.cpp | 532 ++++++++++++++++++ Editor/SceneHierarchyWidget.h | 28 + Editor/SelectionPropertyWidget.cpp | 17 + Editor/SelectionPropertyWidget.h | 18 + Editor/src/Windows/EditorInterface.cpp | 98 +++- Editor/src/Windows/EditorInterface.h | 8 +- Nuake/src/Rendering/Vulkan/DebugCmd.cpp | 26 + Nuake/src/Rendering/Vulkan/DebugCmd.h | 23 + .../Rendering/Vulkan/SceneRenderPipeline.cpp | 5 +- .../Rendering/Vulkan/SceneRenderPipeline.h | 6 +- Nuake/src/UI/ImUI.h | 2 +- 15 files changed, 893 insertions(+), 21 deletions(-) create mode 100644 Editor/EditorContext.h create mode 100644 Editor/IEditorWidget.h create mode 100644 Editor/SceneEditorWindow.cpp create mode 100644 Editor/SceneEditorWindow.h create mode 100644 Editor/SceneHierarchyWidget.cpp create mode 100644 Editor/SceneHierarchyWidget.h create mode 100644 Editor/SelectionPropertyWidget.cpp create mode 100644 Editor/SelectionPropertyWidget.h create mode 100644 Nuake/src/Rendering/Vulkan/DebugCmd.cpp create mode 100644 Nuake/src/Rendering/Vulkan/DebugCmd.h diff --git a/Editor/EditorContext.h b/Editor/EditorContext.h new file mode 100644 index 00000000..49a89548 --- /dev/null +++ b/Editor/EditorContext.h @@ -0,0 +1,38 @@ +#pragma once +#include "src/Core/Core.h" +#include "src/Core/MulticastDelegate.h" + +#include "src/Actions/EditorSelection.h" + +namespace Nuake +{ + class Scene; +} + +class EditorContext +{ +private: + EditorSelection selection; + Ref scene; + +public: + EditorContext() = default; + ~EditorContext() = default; + + EditorContext(Ref inScene) + : scene(inScene) + { + } + +public: + MulticastDelegate OnSelectionChanged; + + const EditorSelection& GetSelection() const { return selection; } + void SetSelection(EditorSelection inSelection) + { + selection = selection; + OnSelectionChanged.Broadcast(selection); + } + + Ref GetScene() const { return scene; } +}; \ No newline at end of file diff --git a/Editor/IEditorWidget.h b/Editor/IEditorWidget.h new file mode 100644 index 00000000..8c63a81b --- /dev/null +++ b/Editor/IEditorWidget.h @@ -0,0 +1,17 @@ +#pragma once + +class EditorContext; + +class IEditorWidget +{ +protected: + EditorContext& editorContext; + +public: + IEditorWidget(EditorContext& inContext) : editorContext(inContext) {} + ~IEditorWidget() {}; + +public: + virtual void Update(float ts) = 0; + virtual void Draw() = 0; +}; \ No newline at end of file diff --git a/Editor/SceneEditorWindow.cpp b/Editor/SceneEditorWindow.cpp new file mode 100644 index 00000000..478c87f4 --- /dev/null +++ b/Editor/SceneEditorWindow.cpp @@ -0,0 +1,54 @@ +#include "SceneEditorWindow.h" + +#include "SelectionPropertyWidget.h" +#include "SceneHierarchyWidget.h" + +#include +#include + +using namespace Nuake; + +SceneEditorWindow::SceneEditorWindow(Ref inScene) +{ + editorContext = EditorContext(inScene); + + RegisterWidget(); + RegisterWidget(); +} + +void SceneEditorWindow::Update(float ts) +{ + for (auto& widget : widgets) + { + widget->Update(ts); + } +} + +void SceneEditorWindow::Draw() +{ + Ref scene = editorContext.GetScene(); + const std::string sceneName = scene->GetName(); + + // This is to prevent other windows of other scene editors to dock + // into this window + ImGuiWindowClass windowClass; + windowClass.ClassId = ImHashStr("SceneEditor"); + windowClass.DockingAllowUnclassed = false; + ImGui::SetNextWindowClass(&windowClass); + + if (ImGui::Begin(sceneName.c_str())) + { + ImGuiWindowClass localSceneEditorClass; + localSceneEditorClass.ClassId = ImHashStr(sceneName.c_str()); + std::string dockspaceId = std::string("Dockspace##" + sceneName); + ImGui::DockSpace(ImGui::GetID(dockspaceId.c_str()), ImGui::GetContentRegionAvail(), ImGuiDockNodeFlags_None, &localSceneEditorClass); + + for (auto& widget : widgets) + { + widget->Draw(); + } + + ImGui::End(); + } +} + diff --git a/Editor/SceneEditorWindow.h b/Editor/SceneEditorWindow.h new file mode 100644 index 00000000..3812f51a --- /dev/null +++ b/Editor/SceneEditorWindow.h @@ -0,0 +1,42 @@ +#pragma once +#include "src/Core/Core.h" + +#include "IEditorWidget.h" +#include "EditorContext.h" + +#include +#include + +// Forward declarations +namespace Nuake +{ + class Scene; + class Entity; +} + +class SceneHierarchyWidget; + +class SceneEditorWindow +{ +private: + std::string windowID; // This is used for imgui docking + + EditorContext editorContext; + + std::vector> widgets; +public: + SceneEditorWindow(Ref scene); + ~SceneEditorWindow() = default; + +public: + void Update(float ts); + void Draw(); + +private: + template + inline void RegisterWidget() + { + widgets.push_back(CreateScope(editorContext)); + } + +}; \ No newline at end of file diff --git a/Editor/SceneHierarchyWidget.cpp b/Editor/SceneHierarchyWidget.cpp new file mode 100644 index 00000000..b1e6529f --- /dev/null +++ b/Editor/SceneHierarchyWidget.cpp @@ -0,0 +1,532 @@ +#include "src/Core/Core.h" +#include "SceneHierarchyWidget.h" + +#include "src/UI/ImUI.h" + +#include +#include +#include "src/Scene/Entities/Entity.h" +#include "src/Scene/Components.h" + +#include "SceneEditorWindow.h" +#include +#include +#include "Engine.h" +#include "src/Resource/Project.h" + +using namespace Nuake; + +SceneHierarchyWidget::SceneHierarchyWidget(EditorContext& inCtx) : IEditorWidget(inCtx) +{ + +} + +void SceneHierarchyWidget::Update(float ts) +{ + //std::string searchQuery = ""; + // + //ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 8, 8 }); + //ImGui::InputTextWithHint("##search", "Search entity", &searchQuery, 0, 0, 0); + //ImGui::PopStyleVar(); +} + +void SceneHierarchyWidget::Draw() +{ + DrawSearchBar(); + + DrawCreateEntityButton(); + + DrawEntityTree(); +} + +void SceneHierarchyWidget::DrawSearchBar() +{ + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 8, 8 }); + ImGui::InputTextWithHint("##search", "Search entity", &searchQuery, 0, 0, 0); + ImGui::PopStyleVar(); + ImGui::SameLine(); +} + +void SceneHierarchyWidget::DrawCreateEntityButton() +{ + Ref scene = editorContext.GetScene(); + if (UI::PrimaryButton("Add Entity", { ImGui::GetContentRegionAvail().x, 0 })) + { + ImGui::OpenPopup("create_entity_popup"); + } + + if (ImGui::BeginPopup("create_entity_popup")) + { + Nuake::Entity entity; + if (ImGui::MenuItem("Empty")) + { + entity = scene->CreateEntity("Empty"); + } + + if (ImGui::BeginMenu("3D")) + { + if (ImGui::MenuItem("Camera")) + { + entity = scene->CreateEntity("Camera"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Model")) + { + entity = scene->CreateEntity("Model"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Skinned Model")) + { + entity = scene->CreateEntity("Skinned Model"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Sprite")) + { + entity = scene->CreateEntity("Sprite"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Particle Emitter")) + { + entity = scene->CreateEntity("Particle Emitter"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Light")) + { + entity = scene->CreateEntity("Light"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Quake Map")) + { + entity = scene->CreateEntity("Quake Map"); + entity.AddComponent(); + } + if (ImGui::MenuItem("NavMesh Volume")) + { + entity = scene->CreateEntity("NavMesh Volume"); + entity.AddComponent(); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Physics")) + { + if (ImGui::MenuItem("Character Controller")) + { + entity = scene->CreateEntity("Character Controller"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Rigid Body")) + { + entity = scene->CreateEntity("Rigid Body"); + entity.AddComponent(); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Colliders")) + { + if (ImGui::MenuItem("Box Collider")) + { + entity = scene->CreateEntity("Box Collider"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Sphere Collider")) + { + entity = scene->CreateEntity("Sphere Collider"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Capsule Collider")) + { + entity = scene->CreateEntity("Capsule Collider"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Cylinder Collider")) + { + entity = scene->CreateEntity("Cylinder Collider"); + entity.AddComponent(); + } + if (ImGui::MenuItem("Mesh Collider")) + { + entity = scene->CreateEntity("Mesh Collider"); + entity.AddComponent(); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Audio")) + { + if (ImGui::MenuItem("Audio Emitter")) + { + entity = scene->CreateEntity("Audio Emitter"); + entity.AddComponent(); + } + ImGui::EndMenu(); + } + + if (entity.IsValid()) + { + EditorSelection selection = editorContext.GetSelection(); + if (selection.Type == EditorSelectionType::Entity && selection.Entity.IsValid()) + { + selection.Entity.AddChild(entity); + } + else + { + auto& camera = editorContext.GetScene()->m_EditorCamera; + Vector3 newEntityPos = camera->Translation + camera->Direction; + entity.GetComponent().SetLocalPosition(newEntityPos); + } + + editorContext.SetSelection(EditorSelection(entity)); + } + + ImGui::EndPopup(); + } +} + +void SceneHierarchyWidget::DrawEntityTree() +{ + Ref scene = editorContext.GetScene(); + + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(26.f / 255.0f, 26.f / 255.0f, 26.f / 255.0f, 1)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 4, 4 }); + if (ImGui::BeginChild("Scene tree", ImGui::GetContentRegionAvail(), false)) + { + if (ImGui::BeginTable("entity_table", 4, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingStretchProp)) + { + ImGui::TableSetupColumn(" Label", ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_IndentDisable | ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Script", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_IndentDisable | ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Visibility ", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_IndentDisable | ImGuiTableColumnFlags_WidthFixed); + ImGui::TableHeadersRow(); + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0)); + + std::vector entitiesToDisplay; + if (searchQuery.empty()) + { + entitiesToDisplay = scene->GetAllEntities(); + } + else + { + auto view = scene->m_Registry.view(); + for (auto& e : view) + { + auto& nameComponent = view.get(e); + if (String::RemoveWhiteSpace(String::ToUpper(nameComponent.Name)).find(String::RemoveWhiteSpace(String::ToUpper(searchQuery))) != std::string::npos) + { + entitiesToDisplay.push_back({ e, scene.get() }); + } + } + } + + // Display valid entities + for (Nuake::Entity e : entitiesToDisplay) + { + // Draw all entity without parents. + bool displayAllHierarchy = searchQuery.empty(); + if ((displayAllHierarchy && !e.GetComponent().HasParent) || !displayAllHierarchy) + { + // TODO(antopilo): Add font regular font + // ImGui::PushFont(normalFont); + + // Recursively draw childrens if not searching + DrawEntity(e, displayAllHierarchy); + + // ImGui::PopFont(); + } + } + ImGui::PopStyleVar(); + } + ImGui::EndTable(); + } + + ImGui::EndChild(); + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + if(ImGui::BeginDragDropTarget()) // Drag n drop new prefab file into scene tree + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("_Prefab")) + { + char* file = (char*)payload->Data; + std::string fullPath = std::string(file, 256); + std::string relPath = FileSystem::AbsoluteToRelative(fullPath); + auto newPrefabInstance = Prefab::New(relPath); + newPrefabInstance->Root.GetComponent().SetPrefab(newPrefabInstance); + newPrefabInstance->Root.GetComponent().IsPrefab = true; + } + ImGui::EndDragDropTarget(); + } + + if (deletionQueue.GetHandle() != -1) + { + scene->DestroyEntity(deletionQueue); + + // Unselect + editorContext.SetSelection(EditorSelection()); + + deletionQueue = Nuake::Entity{ (entt::entity)-1, scene.get() }; + } +} + +void SceneHierarchyWidget::DrawEntity(Nuake::Entity entity, bool drawChildrens) +{ + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{ 0.0f, 0.0f }); + ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_FramePadding | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAllColumns; + + NameComponent& nameComponent = entity.GetComponent(); + std::string name = nameComponent.Name; + + ParentComponent& parent = entity.GetComponent(); + + ImGui::TableNextColumn(); + + // Draw first column Name + + // Highlight if the entity is currently selected + auto selection = editorContext.GetSelection(); + if (selection.Type == EditorSelectionType::Entity && selection.Entity == entity) + { + base_flags |= ImGuiTreeNodeFlags_Selected; + } + + // Display arrow if it has children + bool isPrefab = entity.HasComponent(); + if (parent.Children.size() == 0 || isPrefab || !drawChildrens) + { + base_flags |= ImGuiTreeNodeFlags_Leaf; + } + + // Hightlight if the entity is a prefab or BSP + if (nameComponent.IsPrefab && entity.HasComponent()) + { + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 255, 0, 255)); + } + else if (entity.HasComponent()) + { + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 120)); + } + + // TODO(antopilo) unfolding when mouse picking + + + ImVec2 cursorPos = ImGui::GetCursorPos(); + ImVec2 cursorPosition = ImGui::GetCursorScreenPos(); + const auto& cleanName = String::RemoveWhiteSpace(String::ToUpper(name)); + const size_t searchIt = cleanName.find(String::RemoveWhiteSpace(String::ToUpper(searchQuery))); + + ImGui::SetNextItemAllowOverlap(); + bool open = ImGui::TreeNodeEx(name.c_str(), base_flags); + + if (!searchQuery.empty() && searchIt != std::string::npos) + { + int firstLetterFoundIndex = static_cast(searchIt); + + const auto foundStr = name.substr(0, firstLetterFoundIndex + searchQuery.size()); + auto highlightBeginPos = ImGui::CalcTextSize(foundStr.c_str()); + auto highlightEndPos = ImGui::CalcTextSize(searchQuery.c_str()); + + auto fg = ImGui::GetForegroundDrawList(); + auto color = Engine::GetProject()->Settings.PrimaryColor; + auto rgbColor = IM_COL32(color.r * 255.0f, color.g * 255.0f, color.b * 255.0f, std::min(color.a, 0.2f) * 255.0f); + + fg->AddRectFilled(ImVec2(cursorPosition.x + 20.0f, cursorPosition.y + 4.0f), ImVec2(cursorPosition.x + highlightEndPos.x + 26.0f, cursorPosition.y + highlightEndPos.y + 6.0f), rgbColor, 4.0f); + } + + // Renaming behavior + if (isRenaming) + { + if (selection.Type == EditorSelectionType::Entity && selection.Entity == entity) + { + ImGui::SetCursorPosY(cursorPos.y); + ImGui::Indent(); + ImGui::InputText("##renamingEntity", &name); + ImGui::Unindent(); + if (Nuake::Input::IsKeyDown(Key::ENTER)) + { + nameComponent.Name = name; + isRenaming = false; + } + } + } + + // Drag and drop behavior + bool isDragging = false; + if (nameComponent.IsPrefab && entity.HasComponent() || entity.HasComponent()) + { + ImGui::PopStyleColor(); + } + else if (!isRenaming && ImGui::BeginDragDropSource()) + { + ImGui::SetDragDropPayload("ENTITYPrefab", (void*)&entity, sizeof(Nuake::Entity)); + ImGui::Text(name.c_str()); + ImGui::EndDragDropSource(); + } + + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITYPrefab")) + { + Nuake::Entity payload_entity = *(const Nuake::Entity*)payload->Data; + + // Check if entity is already parent. + ParentComponent& parentPayload = payload_entity.GetComponent(); + if (!payload_entity.EntityContainsItself(payload_entity, entity) && parentPayload.Parent != entity && std::count(parent.Children.begin(), parent.Children.end(), payload_entity) == 0) + { + if (parentPayload.HasParent) + { + // Erase remove idiom. + ParentComponent& childOfParent = parentPayload.Parent.GetComponent(); + childOfParent.Children.erase(std::remove(childOfParent.Children.begin(), childOfParent.Children.end(), payload_entity), childOfParent.Children.end()); + } + + parentPayload.Parent = entity; + parentPayload.HasParent = true; + parent.Children.push_back(payload_entity); + } + } + else if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("_NetScript")) + { + char* file = (char*)payload->Data; + + std::string fullPath = std::string(file, 512); + std::string path = Nuake::FileSystem::AbsoluteToRelative(std::move(fullPath)); + + if (entity.HasComponent()) + { + entity.GetComponent().ScriptPath = path; + } + else + { + entity.AddComponent().ScriptPath = path; + } + } + ImGui::EndDragDropTarget(); + } + + if (!isDragging && ImGui::IsItemHovered() && ImGui::IsMouseReleased(0)) + { + // We selected another another that we werent renaming + if (selection.Entity != entity) + { + isRenaming = false; + } + + editorContext.SetSelection(EditorSelection(entity)); + } + + if (!isDragging && (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) || Input::IsKeyPressed(Key::F2)) + { + isRenaming = true; + } + + if (!isRenaming && selection.Type == EditorSelectionType::Entity && Input::IsKeyPressed(Key::DELETE_KEY)) + { + this->deletionQueue = selection.Entity; + } + + if (ImGui::BeginPopupContextItem()) + { + editorContext.SetSelection(EditorSelection(entity)); + + Nuake::Entity entity = selection.Entity; + if (entity.HasComponent()) + { + // Moves the editor camera to the camera position and direction. + if (ImGui::Selectable("Focus camera")) + { + Ref editorCam = editorContext.GetScene()->m_EditorCamera; + Vector3 camDirection = entity.GetComponent().CameraInstance->GetDirection(); + camDirection.z *= -1.0f; + editorCam->SetTransform(glm::inverse(entity.GetComponent().GetGlobalTransform())); + } + ImGui::Separator(); + } + + if (ImGui::Selectable("Remove")) + { + deletionQueue = entity; + } + + if (entity.GetComponent().HasParent && ImGui::Selectable("Move to root")) + { + auto& parentComp = selection.Entity.GetComponent(); + if (parentComp.HasParent) + { + auto& parentParentComp = parentComp.Parent.GetComponent(); + parentParentComp.RemoveChildren(entity); + parentComp.HasParent = false; + } + } + + if (ImGui::Selectable("Save entity as a new prefab")) + { + Ref newPrefab = Prefab::CreatePrefabFromEntity(selection.Entity); + std::string savePath = Nuake::FileDialog::SaveFile("*.prefab"); + if (!String::EndsWith(savePath, ".prefab")) + { + savePath += ".prefab"; + } + + if (!savePath.empty()) + { + newPrefab->SaveAs(savePath); + selection.Entity.AddComponent().PrefabInstance = newPrefab; + } + } + ImGui::EndPopup(); + } + ImGui::TableNextColumn(); + + ImGui::TextColored(ImVec4(0.5, 0.5, 0.5, 1.0), ""); + + ImGui::TableNextColumn(); + + { + bool hasScript = entity.HasComponent(); + if (hasScript) + { + std::string scrollIcon = std::string(ICON_FA_SCROLL) + "##" + name; + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + if (ImGui::Button(scrollIcon.c_str(), { 40, 0 })) + { + auto& scriptComponent = entity.GetComponent(); + if (!scriptComponent.ScriptPath.empty() && FileSystem::FileExists(scriptComponent.ScriptPath)) + { + OS::OpenIn(FileSystem::RelativeToAbsolute(scriptComponent.ScriptPath)); + } + } + ImGui::PopStyleColor(); + } + } + + ImGui::TableNextColumn(); + { + bool& isVisible = entity.GetComponent().Visible; + std::string visibilityIcon = isVisible ? ICON_FA_EYE : ICON_FA_EYE_SLASH; + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + if (ImGui::Button(visibilityIcon.c_str(), { 40, 0 })) + { + isVisible = !isVisible; + } + ImGui::PopStyleColor(); + } + + if (open) + { + if (drawChildrens && !isPrefab) + { + // Caching list to prevent deletion while iterating. + std::vector childrens = parent.Children; + for (auto& e : childrens) + { + DrawEntity(e); + } + } + + ImGui::TreePop(); + } + + ImGui::PopStyleVar(); +} \ No newline at end of file diff --git a/Editor/SceneHierarchyWidget.h b/Editor/SceneHierarchyWidget.h new file mode 100644 index 00000000..e53c2c4e --- /dev/null +++ b/Editor/SceneHierarchyWidget.h @@ -0,0 +1,28 @@ +#pragma once +#include "IEditorWidget.h" + +#include "src/Scene/Entities/Entity.h" + +class EditorContext; + +class SceneHierarchyWidget : public IEditorWidget +{ +public: + SceneHierarchyWidget(EditorContext& inCtx); + ~SceneHierarchyWidget() = default; + +public: + void Update(float ts) override; + void Draw() override; + +private: + std::string searchQuery; + bool isRenaming; + Nuake::Entity deletionQueue; + + void DrawSearchBar(); + void DrawCreateEntityButton(); + + void DrawEntityTree(); + void DrawEntity(Nuake::Entity entity, bool drawChildrens = true); +}; diff --git a/Editor/SelectionPropertyWidget.cpp b/Editor/SelectionPropertyWidget.cpp new file mode 100644 index 00000000..5b2293ab --- /dev/null +++ b/Editor/SelectionPropertyWidget.cpp @@ -0,0 +1,17 @@ +#include "SelectionPropertyWidget.h" + +SelectionPropertyWidget::SelectionPropertyWidget(EditorContext& inCtx) + : IEditorWidget(inCtx) +{ + +} + +void SelectionPropertyWidget::Update(float ts) +{ + +} + +void SelectionPropertyWidget::Draw() +{ + +} \ No newline at end of file diff --git a/Editor/SelectionPropertyWidget.h b/Editor/SelectionPropertyWidget.h new file mode 100644 index 00000000..f2f0c219 --- /dev/null +++ b/Editor/SelectionPropertyWidget.h @@ -0,0 +1,18 @@ +#pragma once +#include "IEditorWidget.h" + +class EditorContext; + +class SelectionPropertyWidget : public IEditorWidget +{ +public: + SelectionPropertyWidget(EditorContext& inCtx); + ~SelectionPropertyWidget() = default; + +public: + void Update(float ts) override; + void Draw() override; + +private: + +}; \ No newline at end of file diff --git a/Editor/src/Windows/EditorInterface.cpp b/Editor/src/Windows/EditorInterface.cpp index 4e27cc91..b7af3a49 100644 --- a/Editor/src/Windows/EditorInterface.cpp +++ b/Editor/src/Windows/EditorInterface.cpp @@ -65,6 +65,7 @@ #include #include "src/Rendering/Vulkan/SceneRenderPipeline.h" +#include namespace Nuake { @@ -182,7 +183,7 @@ namespace Nuake { // Title bar drag area // On Windows we hook into the GLFW win32 window internals ImGui::SetCursorPos(ImVec2(windowPadding.x, windowPadding.y + titlebarVerticalOffset)); // Reset cursor pos - + const auto titleBarDragSize = ImVec2(w - buttonsAreaWidth, titlebarHeight); if (Window::Get()->IsMaximized()) @@ -191,7 +192,7 @@ namespace Nuake { if (windowMousePosY >= 0.0f && windowMousePosY <= 5.0f) m_TitleBarHovered = true; // Account for the top-most pixels which don't register } - + auto curPos = ImGui::GetCursorPos(); bool isOnMenu = false; { @@ -204,7 +205,7 @@ namespace Nuake { } } - + { // Centered Window title @@ -236,12 +237,12 @@ namespace Nuake { { int iconWidth = std::max(MinimizeTexture->GetWidth(), 24); int iconHeight = std::max(MinimizeTexture->GetHeight(), 24); - + if (ImGui::InvisibleButton("Minimize", ImVec2(buttonWidth, buttonHeight))) { glfwIconifyWindow(Window::Get()->GetHandle()); } - + auto rect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); UI::DrawButtonImage(MinimizeTexture, buttonColN, buttonColH, buttonColP, rect); } @@ -280,19 +281,38 @@ namespace Nuake { { glfwSetWindowShouldClose(Window::Get()->GetHandle(), true); } - + UI::DrawButtonImage(CloseIconTexture, UI::TextCol, UI::TextCol, buttonColP); } // Second bar with play stop pause etc - ImGui::SetCursorPosX(windowPadding.x); + ImGui::SetCursorPosX(windowPadding.x + NuakeTexture->GetSize().x); ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 2.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2)); ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); - + //ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, ImGui::GetStyle().WindowPadding.y)); + //ImGui::PushStyleVar(ImGuiStyleVar_TabBarBorderSize, 0); + // + //ImGui::SetNextWindowSize({ 300.0f, 25.0f }); + //if (ImGui::BeginTabBar("SceneTabsBar", ImGuiTabBarFlags_None)) + //{ + // ImGui::BeginTabItem("Scene 1"); + // ImGui::BeginTabItem("Scene 2"); + // ImGui::BeginTabItem("Scene 3"); + // ImGui::EndTabItem(); + // + // ImGui::EndTabBar(); + //} + // + //ImGui::PopStyleVar(2); + // + // + //ImGui::SameLine(); ImGui::Dummy({ ImGui::GetContentRegionAvail().x / 2.0f - (76.0f / 2.0f), 8.0f }); ImGui::SameLine(); + //ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.0f); + if (Engine::IsPlayMode() && Engine::GetTimeScale() != 0.0f) { @@ -607,8 +627,8 @@ namespace Nuake { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); std::string name = ICON_FA_GAMEPAD + std::string(" Scene"); ImGuiWindowClass window_class; - window_class.DockNodeFlagsOverrideSet = ImGuiDockNodeFlags_NoTabBar; - ImGui::SetNextWindowClass(&window_class); + //window_class.DockNodeFlagsOverrideSet = ImGuiDockNodeFlags_NoTabBar; + //ImGui::SetNextWindowClass(&window_class); if (ImGui::Begin(name.c_str(), 0, ImGuiWindowFlags_NoDecoration)) { @@ -1471,8 +1491,6 @@ namespace Nuake { // Recursively draw childrens if not searching const std::string entityName = e.GetComponent().Name; - - DrawEntityTree(e, displayAllHierarchy); ImGui::PopFont(); @@ -2107,6 +2125,19 @@ namespace Nuake { prefabEditors.push_back(CreateRef(newPrefab)); } + void EditorInterface::OpenSceneWindow(const std::string& scenePath) + { + if (!FileSystem::FileExists(scenePath)) + { + return; + } + + Ref newScene = Scene::New(); + newScene->Path = scenePath; + newScene->Deserialize(json::parse(FileSystem::ReadFile(scenePath))); + sceneEditors.push_back(CreateRef(newScene)); + } + void NewProject() { if (Engine::GetProject() && Engine::GetProject()->FileExist()) @@ -2442,9 +2473,11 @@ namespace Nuake { VkRenderer::Get().SceneRenderer->sceneRenderPipeline->OnDebugDraw().AddRaw(this, &EditorInterface::OnDebugDraw); } - void EditorInterface::OnDebugDraw() + void EditorInterface::OnDebugDraw(DebugCmd& cmd) { - Logger::Log("On debug draw"); + + //cmd.DrawLine({ 0, 0, 0 }, { 10, 10, 10 }, { 1, 0, 0, 1 }); + //cmd.DrawCube({ 0, 0, 0 }, { 1, 1, 1 }, { 0, 1, 0, 1 }); } bool isLoadingProject = false; @@ -2699,12 +2732,40 @@ namespace Nuake { float titleBarHeight; DrawTitlebar(titleBarHeight); ImGui::SetCursorPosY(titleBarHeight); + ImGuiIO& io = ImGui::GetIO(); ImGuiStyle& style = ImGui::GetStyle(); float minWinSizeX = style.WindowMinSize.x; style.WindowMinSize.x = 370.0f; - ImGui::DockSpace(ImGui::GetID("MyDockspace")); + + ImGuiWindowClass top_level_class; + top_level_class.ClassId = ImHashStr("SceneEditor"); + top_level_class.DockingAllowUnclassed = false; + + ImGui::DockSpace(ImGui::GetID("SceneEditorDockSpace"), {0, 0}, 0, &top_level_class); style.WindowMinSize.x = minWinSizeX; + ImGuiDockNode* node = (ImGuiDockNode*)GImGui->DockContext.Nodes.GetVoidPtr(ImGui::GetID("SceneEditorDockSpace")); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 64); + if(ImGui::DockNodeBeginAmendTabBar(node)) + { + ImGui::SetNextItemWidth(48); + if (ImGui::BeginTabItem("##logoPadding", 0, ImGuiTabItemFlags_Leading)) + { + + ImGui::EndTabItem(); + } + ImGui::DockNodeEndAmendTabBar(); + } + + ImGui::SetNextWindowClass(&top_level_class); + ImGui::Begin("SceneEditor"); + { + ImGuiWindowClass inside_document_class; + inside_document_class.ClassId = ImHashStr("SceneEditor1"); + ImGui::DockSpace(ImGui::GetID("SceneEditorWindowDockspace"), ImGui::GetContentRegionAvail(), ImGuiDockNodeFlags_None, &inside_document_class); + } + ImGui::End(); + ImGui::End(); //DrawMenuBar(); //DrawMenuBars(); @@ -2721,6 +2782,12 @@ namespace Nuake { { prefabEditors->Draw(); } + + for (auto& sceneEditor : sceneEditors) + { + sceneEditor->Draw(); + } + //pInterface.DrawEntitySettings(); DrawViewport(); DrawSceneTree(); @@ -2813,6 +2880,7 @@ namespace Nuake { { std::string entityTypeName = ""; + if (entity.HasComponent()) { entityTypeName = "Light"; diff --git a/Editor/src/Windows/EditorInterface.h b/Editor/src/Windows/EditorInterface.h index db754e7c..d4856caf 100644 --- a/Editor/src/Windows/EditorInterface.h +++ b/Editor/src/Windows/EditorInterface.h @@ -19,6 +19,7 @@ #include #include "ProjectSettings/ProjectSettingsWindow.h" #include "PrefabEditor/PrefabEditorWindow.h" +#include "../../SceneEditorWindow.h" using namespace NuakeEditor; @@ -28,6 +29,7 @@ namespace Nuake class Material; class FileSystemUI; class VulkanImage; + class DebugCmd; class EditorInterface { @@ -35,6 +37,8 @@ namespace Nuake bool m_TitleBarHovered = false; std::vector errors; std::vector> prefabEditors; + std::vector> sceneEditors; + Ref SceneSnapshot; static NuakeEditor::CommandBuffer* mCommandBuffer; @@ -103,7 +107,7 @@ namespace Nuake void EndMenubar(); void OnSceneLoaded(Ref scene); - void OnDebugDraw(); + void OnDebugDraw(Nuake::DebugCmd& cmd); void SetStatusMessage(const std::string& msg, const Color& color = Color(0.08f, 0.08f, 0.08f, 1.0f)) { m_StatusMessage = msg; m_StatusBarColor = color; } void DrawViewport(); @@ -116,6 +120,8 @@ namespace Nuake void Overlay(); void OpenPrefabWindow(const std::string& prefabPath); + void OpenSceneWindow(const std::string& scenePath); + bool ShouldDrawAxis() const { return m_DrawAxis; } bool ShouldDrawShapes() const { return m_DrawShapes; } bool ShouldDrawGizmos() const { return m_DrawGizmos; } diff --git a/Nuake/src/Rendering/Vulkan/DebugCmd.cpp b/Nuake/src/Rendering/Vulkan/DebugCmd.cpp new file mode 100644 index 00000000..4cbc5ec6 --- /dev/null +++ b/Nuake/src/Rendering/Vulkan/DebugCmd.cpp @@ -0,0 +1,26 @@ +#include "DebugCmd.h" + + +using namespace Nuake; + +DebugCmd::DebugCmd(Cmd& inCmd) : + cmd(inCmd) +{ +} + +void DebugCmd::DrawLine(const Vector3& start, const Vector3& end, const Color& color) const +{ + +} + +void DebugCmd::DrawSphere(const Vector2& position, float radius, const Color& color) const +{ +} + +void DebugCmd::DrawCube(const Vector3& position, const Vector3& size, const Color& color) const +{ +} + +void DebugCmd::DrawAABB(const Vector3& min, const Vector3& max, const Color& color) const +{ +} \ No newline at end of file diff --git a/Nuake/src/Rendering/Vulkan/DebugCmd.h b/Nuake/src/Rendering/Vulkan/DebugCmd.h new file mode 100644 index 00000000..88568e27 --- /dev/null +++ b/Nuake/src/Rendering/Vulkan/DebugCmd.h @@ -0,0 +1,23 @@ +#pragma once +#include "Cmd.h" + +namespace Nuake +{ + // This is the API to render anything debugging related + // Like lines, shapes, gizmos, etc. + class DebugCmd + { + private: + Cmd& cmd; + + public: + DebugCmd(Cmd& inCmd); + ~DebugCmd() = default; + + public: + void DrawLine(const Vector3& start, const Vector3& end, const Color& color) const; + void DrawSphere(const Vector2& position, float radius, const Color& color) const; + void DrawCube(const Vector3& position, const Vector3& size, const Color& color) const; + void DrawAABB(const Vector3& min, const Vector3& max, const Color& color) const; + }; +} \ No newline at end of file diff --git a/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp b/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp index 2edc1d40..a2696aac 100644 --- a/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp +++ b/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp @@ -10,6 +10,7 @@ #include "src/Scene/Components/ModelComponent.h" #include +#include "DebugCmd.h" using namespace Nuake; @@ -280,8 +281,8 @@ void SceneRenderPipeline::Render(PassRenderContext& ctx) GBufferPipeline.Execute(ctx, pipelineInputs); // Debug drawing - // Get delegate - OnDebugDraw().Broadcast(); + DebugCmd debugCmd = DebugCmd(ctx.commandBuffer); + OnDebugDraw().Broadcast(debugCmd); } Ref SceneRenderPipeline::ResizeImage(Ref image, const Vector2& size) diff --git a/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.h b/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.h index 72e8a8b8..f7338dca 100644 --- a/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.h +++ b/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.h @@ -62,6 +62,8 @@ namespace Nuake int BlurDirection; }; + class DebugCmd; + // This class handles all the rendering of the scene class SceneRenderPipeline { @@ -98,7 +100,7 @@ namespace Nuake static RenderPipeline GBufferPipeline; // Delegates - MulticastDelegate<> DebugDrawDelegate; + MulticastDelegate DebugDrawDelegate; public: SceneRenderPipeline(); @@ -108,7 +110,7 @@ namespace Nuake void Render(PassRenderContext& ctx); Ref GetOutput() { return TonemappedOutput; } - MulticastDelegate<>& OnDebugDraw() { return DebugDrawDelegate; } + MulticastDelegate& OnDebugDraw() { return DebugDrawDelegate; } private: Ref ResizeImage(Ref image, const Vector2& size); diff --git a/Nuake/src/UI/ImUI.h b/Nuake/src/UI/ImUI.h index c9aed303..0832a486 100644 --- a/Nuake/src/UI/ImUI.h +++ b/Nuake/src/UI/ImUI.h @@ -5,7 +5,7 @@ #include "imgui/imgui.h" #include "imgui/imgui_internal.h" #include "../../../Editor/src/Misc/InterfaceFonts.h" - +#include "imgui/imgui_stdlib.h" #include "src/Resource/FontAwesome5.h" #include "src/Rendering/Textures/Texture.h" #include