diff --git a/Editor/src/Actions/EditorSelection.h b/Editor/src/Actions/EditorSelection.h index 74798506..7b0a25ea 100644 --- a/Editor/src/Actions/EditorSelection.h +++ b/Editor/src/Actions/EditorSelection.h @@ -41,7 +41,6 @@ public: EditorSelection(const Nuake::Entity& entity) { Type = EditorSelectionType::Entity; - Nuake::Engine::GetCurrentScene()->m_SceneRenderer->mOutlineEntityID = (uint32_t)entity.GetHandle(); Entity = entity; } diff --git a/Editor/src/ComponentsPanel/TransformPanel.h b/Editor/src/ComponentsPanel/TransformPanel.h index 452b3018..f43146c2 100644 --- a/Editor/src/ComponentsPanel/TransformPanel.h +++ b/Editor/src/ComponentsPanel/TransformPanel.h @@ -27,14 +27,14 @@ public: ImGui::TableNextColumn(); Vector3 position = component.GetLocalPosition(); - ImGuiHelper::DrawVec3("Translation", &position); + ImGuiHelper::DrawVec3("Translation##" + entity.GetHandle(), &position); if (position != component.GetLocalPosition()) component.SetLocalPosition(position); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, 1, 1, 0)); - std::string resetLabel = std::string(ICON_FA_UNDO) + "##ResetTranslation"; + std::string resetLabel = std::string(ICON_FA_UNDO) + "##ResetTranslation" + std::to_string(entity.GetHandle()); if (ImGui::Button(resetLabel.c_str())) component.SetLocalPosition(Vector3(0, 0, 0)); @@ -52,7 +52,7 @@ public: Vector3 eulerDegreesOld = glm::degrees(glm::eulerAngles(currentRotation)); // Draw the ImGui widget for rotation - ImGuiHelper::DrawVec3("Rotation", &eulerDegreesOld); + ImGuiHelper::DrawVec3("Rotation##" + entity.GetHandle(), &eulerDegreesOld); // Calculate the delta in Euler angles Vector3 eulerDelta = eulerDegreesOld - glm::degrees(glm::eulerAngles(currentRotation)); @@ -84,7 +84,7 @@ public: ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, 1, 1, 0)); - std::string resetLabel = std::string(ICON_FA_UNDO) + "##ResetRotation"; + std::string resetLabel = std::string(ICON_FA_UNDO) + "##ResetRotation" + std::to_string(entity.GetHandle()); if (ImGui::Button(resetLabel.c_str())) { component.SetLocalRotation(Quat(1, 0, 0, 0)); @@ -97,7 +97,7 @@ public: ImGui::TableNextColumn(); Vector3 localScale = component.GetLocalScale(); - ImGuiHelper::DrawVec3("Scale", &localScale); + ImGuiHelper::DrawVec3("Scale##" + entity.GetHandle(), &localScale); if (localScale != component.GetLocalScale()) component.SetLocalScale(localScale); @@ -105,7 +105,7 @@ public: ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, 1, 1, 0)); - std::string resetLabel = std::string(ICON_FA_UNDO) + "##ResetScale"; + std::string resetLabel = std::string(ICON_FA_UNDO) + "##ResetScale" + std::to_string(entity.GetHandle()); if (ImGui::Button(resetLabel.c_str())) component.SetLocalScale(Vector3(1, 1, 1)); diff --git a/Editor/src/UIComponents/EntityTree.h b/Editor/src/UIComponents/EntityTree.h new file mode 100644 index 00000000..3f59c932 --- /dev/null +++ b/Editor/src/UIComponents/EntityTree.h @@ -0,0 +1,2 @@ +#pragma once + diff --git a/Editor/src/Windows/EditorInterface.cpp b/Editor/src/Windows/EditorInterface.cpp index d3bfc5ba..5de7aa6f 100644 --- a/Editor/src/Windows/EditorInterface.cpp +++ b/Editor/src/Windows/EditorInterface.cpp @@ -307,6 +307,11 @@ namespace Nuake { const auto& editorCam = Engine::GetCurrentScene()->GetCurrentCamera(); Matrix4 cameraView = editorCam->GetTransform(); Matrix4 cameraProjection = editorCam->GetPerspective(); + static Vector3 camPreviousPos = Engine::GetCurrentScene()->m_EditorCamera->Translation; + static Vector3 camNewPos = Vector3(0, 0, 0); + + Vector3 camDelta = camNewPos - camPreviousPos; + Vector3 previousGlobalPos = transform[3]; // Imguizmo calculates the delta from the gizmo, ImGuizmo::Manipulate( @@ -323,6 +328,15 @@ namespace Nuake { // we need to multiply by the inverse of the parent's global transform in order to revert // the changes from the parent transform. Matrix4 localTransform = Matrix4(transform); + + Vector3 newGlobalPos = transform[3]; + if(ImGui::IsKeyDown(ImGuiKey_LeftShift)) + { + Vector3 positionDelta = newGlobalPos - previousGlobalPos; + Engine::GetCurrentScene()->m_EditorCamera->Translation += positionDelta; + camNewPos = Engine::GetCurrentScene()->m_EditorCamera->Translation; + } + ParentComponent& parent = Selection.Entity.GetComponent(); if (parent.HasParent) { @@ -861,7 +875,7 @@ namespace Nuake { // Check if entity is already parent. ParentComponent& parentPayload = payload_entity.GetComponent(); - if (!EntityContainsItself(payload_entity, e) && parentPayload.Parent != e && std::count(parent.Children.begin(), parent.Children.end(), payload_entity) == 0) + if (!payload_entity.EntityContainsItself(payload_entity, e) && parentPayload.Parent != e && std::count(parent.Children.begin(), parent.Children.end(), payload_entity) == 0) { if (parentPayload.HasParent) { @@ -2287,26 +2301,6 @@ namespace Nuake { ImGui::End(); } - bool EditorInterface::EntityContainsItself(Entity source, Entity target) - { - ParentComponent& targeParentComponent = target.GetComponent(); - if (!targeParentComponent.HasParent) - return false; - - Entity currentParent = target.GetComponent().Parent; - while (currentParent != source) - { - if (currentParent.GetComponent().HasParent) - currentParent = currentParent.GetComponent().Parent; - else - return false; - - if (currentParent == source) - return true; - } - return true; - } - bool LogErrors = true; bool LogWarnings = true; bool LogDebug = true; @@ -2315,7 +2309,7 @@ namespace Nuake { { if (ImGui::Begin("Logger")) { - if (ImGui::Button("Clear")) + if (ImGui::Button("Clear", ImVec2(60, 28))) { Logger::ClearLogs(); SetStatusMessage("Logs cleared."); @@ -2323,77 +2317,78 @@ namespace Nuake { ImGui::SameLine(); - if (ImGui::BeginMenu("Edit")) + if (ImGui::Button(ICON_FA_FILTER, ImVec2(30, 28))) { - if (ImGui::MenuItem("Undo", "CTRL+Z")) {} - if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item - ImGui::Separator(); - if (ImGui::MenuItem("Project Settings", "")) - { - } - ImGui::EndMenu(); + ImGui::OpenPopup("filter_popup"); } + ImGui::SameLine(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2)); - ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 100); bool isEnabled = LogErrors; - if (isEnabled) + if (ImGui::BeginPopup("filter_popup")) { - Color color = Engine::GetProject()->Settings.PrimaryColor; - ImGui::PushStyleColor(ImGuiCol_Button, { color.r, color.g, color.b, 1.0f }); - } + ImGui::SeparatorText("Filters"); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2)); + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 100); - if (ImGui::Button(ICON_FA_BAN, ImVec2(30, 28))) - { - LogErrors = !LogErrors; - } + if (isEnabled) + { + Color color = Engine::GetProject()->Settings.PrimaryColor; + ImGui::PushStyleColor(ImGuiCol_Button, { color.r, color.g, color.b, 1.0f }); + } + + if (ImGui::Button((std::string(ICON_FA_BAN) + " Error").c_str())) + { + LogErrors = !LogErrors; + } + + UI::Tooltip("Display Errors"); + if (isEnabled) + { + ImGui::PopStyleColor(); + } + + isEnabled = LogWarnings; + if (isEnabled) + { + Color color = Engine::GetProject()->Settings.PrimaryColor; + ImGui::PushStyleColor(ImGuiCol_Button, { color.r, color.g, color.b, 1.0f }); + } + + if (ImGui::Button((std::string(ICON_FA_EXCLAMATION_TRIANGLE) + " Warning").c_str())) + { + LogWarnings = !LogWarnings; + } + + UI::Tooltip("Display Warnings"); + if (isEnabled) + { + ImGui::PopStyleColor(); + } + + isEnabled = LogDebug; + if (isEnabled) + { + Color color = Engine::GetProject()->Settings.PrimaryColor; + ImGui::PushStyleColor(ImGuiCol_Button, { color.r, color.g, color.b, 1.0f }); + } + + if (ImGui::Button((std::string(ICON_FA_INFO) + " Info").c_str())) + { + LogDebug = !LogDebug; + } + + UI::Tooltip("Display Verbose"); + if (isEnabled) + { + ImGui::PopStyleColor(); + } - UI::Tooltip("Display Errors"); - if (isEnabled) - { ImGui::PopStyleColor(); - } + ImGui::PopStyleVar(2); - ImGui::SameLine(); - - isEnabled = LogWarnings; - if (isEnabled) - { - Color color = Engine::GetProject()->Settings.PrimaryColor; - ImGui::PushStyleColor(ImGuiCol_Button, { color.r, color.g, color.b, 1.0f }); - } - - if (ImGui::Button(ICON_FA_EXCLAMATION_TRIANGLE, ImVec2(30, 28))) - { - LogWarnings = !LogWarnings; - } - - UI::Tooltip("Display Warnings"); - if (isEnabled) - { - ImGui::PopStyleColor(); - } - - ImGui::SameLine(); - - isEnabled = LogDebug; - if (isEnabled) - { - Color color = Engine::GetProject()->Settings.PrimaryColor; - ImGui::PushStyleColor(ImGuiCol_Button, { color.r, color.g, color.b, 1.0f }); - } - - if (ImGui::Button(ICON_FA_INFO, ImVec2(30, 28))) - { - LogDebug = !LogDebug; - } - - UI::Tooltip("Display Verbose"); - if (isEnabled) - { - ImGui::PopStyleColor(); + ImGui::EndPopup(); } ImGui::SameLine(); @@ -2416,8 +2411,6 @@ namespace Nuake { ImGui::PopStyleColor(); } - ImGui::PopStyleColor(); - ImGui::PopStyleVar(2); //ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); //if (ImGui::BeginChild("Log window", ImGui::GetContentRegionAvail(), false)) //{ @@ -2884,6 +2877,19 @@ namespace Nuake { } } + void EditorInterface::OpenPrefabWindow(const std::string& prefabPath) + { + if (!FileSystem::FileExists(prefabPath)) + { + return; + } + + Ref newPrefab = CreateRef(); + newPrefab->Path = prefabPath; + + prefabEditors.push_back(CreateRef(newPrefab)); + } + void NewProject() { if (Engine::GetProject() && Engine::GetProject()->FileExist()) @@ -3265,6 +3271,18 @@ namespace Nuake { pInterface.m_CurrentProject = Engine::GetProject(); + uint32_t selectedEntityID; + if (Selection.Type == EditorSelectionType::Entity && Selection.Entity.IsValid()) + { + selectedEntityID = Selection.Entity.GetHandle(); + } + else + { + selectedEntityID = 0; + } + + Nuake::Engine::GetCurrentScene()->m_SceneRenderer->mOutlineEntityID = selectedEntityID; + m_ProjectSettingsWindow->Draw(); m_DemoWindow.Draw(); @@ -3292,6 +3310,10 @@ namespace Nuake { DrawStatusBar(); + for (auto& prefabEditors : prefabEditors) + { + prefabEditors->Draw(); + } //pInterface.DrawEntitySettings(); DrawViewport(); DrawSceneTree(); @@ -3301,6 +3323,7 @@ namespace Nuake { filesystem->Draw(); filesystem->DrawDirectoryExplorer(); + if (isNewProject) { ImVec2 center = ImGui::GetMainViewport()->GetCenter(); @@ -3351,7 +3374,7 @@ namespace Nuake { isControllingCamera = editorCam->Update(ts, m_IsHoveringViewport && m_IsViewportFocused); const bool entityIsSelected = Selection.Type == EditorSelectionType::Entity && Selection.Entity.IsValid(); - if (editorCam->IsFlying() && entityIsSelected && Input::IsKeyPressed(Key::F)) + if (entityIsSelected && Input::IsKeyPressed(Key::F)) { editorCam->IsMoving = true; editorCam->TargetPos = Selection.Entity.GetComponent().GetGlobalPosition(); diff --git a/Editor/src/Windows/EditorInterface.h b/Editor/src/Windows/EditorInterface.h index 6347be65..bbc38c19 100644 --- a/Editor/src/Windows/EditorInterface.h +++ b/Editor/src/Windows/EditorInterface.h @@ -18,6 +18,7 @@ #include #include "ProjectSettings/ProjectSettingsWindow.h" +#include "PrefabEditor/PrefabEditorWindow.h" using namespace NuakeEditor; @@ -31,6 +32,7 @@ namespace Nuake { private: std::vector errors; + std::vector> prefabEditors; Ref SceneSnapshot; static NuakeEditor::CommandBuffer* mCommandBuffer; @@ -93,9 +95,9 @@ namespace Nuake void DrawSceneTree(); void DrawLogger(); void DrawProjectSettings(); - bool EntityContainsItself(Entity ent1, Entity ent2); void Overlay(); + void OpenPrefabWindow(const std::string& prefabPath); bool ShouldDrawAxis() const { return m_DrawAxis; } bool ShouldDrawCollision() const { return m_DebugCollisions; } bool ShouldDrawNavMesh() const { return m_DrawNavMesh; } diff --git a/Editor/src/Windows/EditorSelectionPanel.cpp b/Editor/src/Windows/EditorSelectionPanel.cpp index 1aff95e9..3c4579c7 100644 --- a/Editor/src/Windows/EditorSelectionPanel.cpp +++ b/Editor/src/Windows/EditorSelectionPanel.cpp @@ -66,9 +66,9 @@ void EditorSelectionPanel::ResolveFile(Ref file) } } -void EditorSelectionPanel::Draw(EditorSelection selection) +void EditorSelectionPanel::Draw(EditorSelection selection, const std::string& id) { - if (ImGui::Begin("Properties")) + if (ImGui::Begin(std::string("Properties##" + id).c_str())) { switch (selection.Type) { diff --git a/Editor/src/Windows/EditorSelectionPanel.h b/Editor/src/Windows/EditorSelectionPanel.h index 2271c8d2..43e79296 100644 --- a/Editor/src/Windows/EditorSelectionPanel.h +++ b/Editor/src/Windows/EditorSelectionPanel.h @@ -41,7 +41,7 @@ private: public: EditorSelectionPanel(); - void Draw(EditorSelection selection); + void Draw(EditorSelection selection, const std::string& id = ""); void DrawNone(); void DrawEntity(Nuake::Entity entity); diff --git a/Editor/src/Windows/FileSystemUI.cpp b/Editor/src/Windows/FileSystemUI.cpp index c2cb3e34..031ad0aa 100644 --- a/Editor/src/Windows/FileSystemUI.cpp +++ b/Editor/src/Windows/FileSystemUI.cpp @@ -271,6 +271,9 @@ namespace Nuake case FileType::Solution: OS::OpenIn(file->GetAbsolutePath()); break; + case FileType::Prefab: + this->Editor->OpenPrefabWindow(file->GetRelativePath()); + break; } } diff --git a/Editor/src/Windows/PrefabEditor/PrefabEditorWindow.cpp b/Editor/src/Windows/PrefabEditor/PrefabEditorWindow.cpp new file mode 100644 index 00000000..409f19bc --- /dev/null +++ b/Editor/src/Windows/PrefabEditor/PrefabEditorWindow.cpp @@ -0,0 +1,626 @@ +#include "PrefabEditorWindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace Nuake; + +PrefabEditorWindow::PrefabEditorWindow(Ref inPrefab) : + prefab(inPrefab) +{ + const Vector2 defaultSize = Vector2{ 640, 360 }; + viewportFramebuffer = CreateRef(true, defaultSize); + viewportFramebuffer->SetTexture(CreateRef(defaultSize, GL_RGB, GL_RGB16F, GL_FLOAT)); + + virtualScene = CreateRef(); + virtualScene->GetEnvironment()->CurrentSkyType = SkyType::ProceduralSky; + virtualScene->GetEnvironment()->ProceduralSkybox->SunDirection = { 0.58f, 0.34f, -0.74f }; + + Prefab::InstanceInScene(inPrefab->Path, virtualScene); + + Ref outputTexture = CreateRef(defaultSize, GL_RGB); + outputTexture->SetParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + outputTexture->SetParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + auto& previewLight = virtualScene->CreateEntity("_directionalLight").AddComponent(); + previewLight.Type = LightType::Directional; + previewLight.SetCastShadows(true); + previewLight.SyncDirectionWithSky = true; + previewLight.Strength = 5.5f; + +} + +void PrefabEditorWindow::Update(float ts) +{ + +} + +void PrefabEditorWindow::Draw() +{ + RenderScene(); + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + + ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; + ImGuiID dockspace_id = ImGui::GetID(prefab->Path.c_str()); + ImVec2 dockspace_size; + if (!isInitialized) + { + ImGui::SetNextWindowSize(ImVec2(1280, 720)); + } + + if(ImGui::Begin(prefab->Path.c_str(), 0, window_flags)) + { + dockspace_size = ImGui::GetContentRegionAvail(); + ImGui::DockSpace(dockspace_id, dockspace_size, dockspace_flags); + + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ImGui::MenuItem("New", "Ctrl+N"); + ImGui::MenuItem("Open", "Ctrl+O"); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + } + ImGui::End(); + + // Programmatically dock windows using DockBuilder API + if (!isInitialized) + { + isInitialized = true; + + // Create dock layout for the embedded dockspace + ImGui::DockBuilderRemoveNode(dockspace_id); // Clear any existing layout + ImGui::DockBuilderAddNode(dockspace_id, dockspace_flags | ImGuiDockNodeFlags_DockSpace); + ImGui::DockBuilderSetNodeSize(dockspace_id, dockspace_size); + + // Split the dockspace into two areas: left and right + ImGuiID dock_left_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.25f, nullptr, &dockspace_id); + ImGuiID dock_r_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.66f, nullptr, &dockspace_id); + + // Dock windows into the created nodes + ImGui::DockBuilderDockWindow(std::string("Prefab Hierarchy## " + prefab->Path).c_str(), dock_left_id); + ImGui::DockBuilderDockWindow(std::string("Viewport##" + prefab->Path).c_str(), dockspace_id); + ImGui::DockBuilderDockWindow(std::string("Properties##" + prefab->Path).c_str(), dock_r_id); + // Commit the dock layout + ImGui::DockBuilderFinish(dockspace_id); + } + + if (ImGui::Begin(std::string("Prefab Hierarchy## " + prefab->Path).c_str())) + { + Ref scene = virtualScene; + std::string searchQuery = ""; + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 8, 8 }); + ImGui::InputTextWithHint("##search", "Search entity", &searchQuery, 0, 0, 0); + ImGui::PopStyleVar(); + + ImGui::SameLine(); + + 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()) + { + if (Selection.Type == EditorSelectionType::Entity && Selection.Entity.IsValid()) + { + Selection.Entity.AddChild(entity); + } + else + { + auto& camera = Engine::GetCurrentScene()->m_EditorCamera; + Vector3 newEntityPos = camera->Translation + camera->Direction; + entity.GetComponent().SetLocalPosition(newEntityPos); + } + + Selection = EditorSelection(entity); + } + + ImGui::EndPopup(); + } + + // Draw a tree of entities. + 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 entities = scene->GetAllEntities(); + for (auto& e : entities) + { + ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanFullWidth; + std::string name = e.GetComponent().Name; + // If selected add selected flag. + if (Selection.Type == EditorSelectionType::Entity && Selection.Entity == e) + base_flags |= ImGuiTreeNodeFlags_Selected; + + // Draw all entity without parents. + if (!e.GetComponent().HasParent) + { + // Recursively draw childrens. + DrawEntityTree(e); + } + + // Pop font. + //ImGui::PopFont(); + + // Right click menu + //if (ImGui::BeginPopupContextItem()) + // ImGui::EndPopup(); + } + ImGui::PopStyleVar(); + } + ImGui::EndTable(); + + } + ImGui::EndChild(); + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } + ImGui::End(); + + SelectionPanel.Draw(Selection, prefab->Path); + + DrawViewportWindow(); +} + +void PrefabEditorWindow::DrawViewportWindow() +{ + + //viewportFramebuffer->Bind(); + ////RenderCommand::SetClearColor(Color(1, 0, 0, 1)); + //viewportFramebuffer->Clear(); + //viewportFramebuffer->Unbind(); + + //RenderScene(); + + if (ImGui::Begin(std::string("Viewport##" + prefab->Path).c_str())) + { + ImVec2 regionAvail = ImGui::GetContentRegionAvail(); + Vector2 viewportPanelSize = glm::vec2(regionAvail.x, regionAvail.y); + + if (viewportFramebuffer->GetSize() != viewportPanelSize) + viewportFramebuffer->QueueResize(viewportPanelSize); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));\ + viewportFramebuffer->QueueResize(viewportPanelSize); + ImGui::Image((void*)viewportFramebuffer->GetTexture(GL_COLOR_ATTACHMENT0)->GetID(), regionAvail, ImVec2(0, 1), ImVec2(1, 0)); + ImGui::PopStyleVar(); + + const Vector2& mousePos = Input::GetMousePosition(); + + const ImVec2& windowPos = ImGui::GetWindowPos(); + const auto windowPosNuake = Vector2(windowPos.x, windowPos.y); + const ImVec2& windowSize = ImGui::GetWindowSize(); + const bool isInsideWidth = mousePos.x > windowPos.x && mousePos.x < windowPos.x + windowSize.x; + const bool isInsideHeight = mousePos.y > windowPos.y && mousePos.y < windowPos.y + windowSize.y; + isHoveringViewport = isInsideWidth && isInsideHeight; + + auto& editorCam = virtualScene->m_EditorCamera; + bool isControllingCamera = editorCam->Update(Engine::GetTimestep(), isHoveringViewport); + + if (ImGui::IsWindowHovered() && isHoveringViewport && !isViewportFocused && ImGui::GetIO().WantCaptureMouse) + { + ImGui::FocusWindow(ImGui::GetCurrentWindow()); + } + + isViewportFocused = ImGui::IsWindowFocused(); + + + if (ImGui::GetIO().WantCaptureMouse && isHoveringViewport && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && isViewportFocused) + { + auto& gbuffer = virtualScene->m_SceneRenderer->GetGBuffer(); + auto pixelPos = (Input::GetMousePosition() - windowPosNuake); + pixelPos.y = gbuffer.GetSize().y - pixelPos.y; // imgui coords are inverted on the Y axi + + gbuffer.Bind(); + if (const int result = gbuffer.ReadPixel(3, pixelPos); result > 0) + { + auto ent = Nuake::Entity{ (entt::entity)(result - 1), virtualScene.get() }; + if (ent.IsValid()) + { + Selection = EditorSelection(ent); + virtualScene->m_SceneRenderer->mOutlineEntityID = (uint32_t)ent.GetHandle(); + } + } + else + { + Selection = EditorSelection(); // None + } + + gbuffer.Unbind(); + } + } + ImGui::End(); +} + +void PrefabEditorWindow::RenderScene() +{ + virtualScene->Update(Engine::GetTimestep()); + + Ref sceneRenderer = virtualScene->m_SceneRenderer; + Ref editorCam = virtualScene->m_EditorCamera; + editorCam->OnWindowResize(viewportFramebuffer->GetSize().x, viewportFramebuffer->GetSize().y); + virtualScene->Draw(*viewportFramebuffer.get()); + + sceneRenderer->BeginRenderScene(editorCam->GetPerspective(), editorCam->GetTransform(), editorCam->Translation); + sceneRenderer->RenderScene(*virtualScene, *viewportFramebuffer.get(), true); + +} + +void PrefabEditorWindow::DrawEntityTree(Nuake::Entity e) +{ + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{ 0.0f, 0.0f }); + + ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_FramePadding | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAllColumns; + + NameComponent& nameComponent = e.GetComponent(); + std::string name = nameComponent.Name; + + ParentComponent& parent = e.GetComponent(); + + if (Selection.Type == EditorSelectionType::Entity && Selection.Entity == e) + base_flags |= ImGuiTreeNodeFlags_Selected; + + ImGui::TableNextColumn(); + + // Write in normal font. + // ImGui::PushFont(normalFont); + + // If has no childrens draw tree node leaf + if (parent.Children.size() <= 0) + { + base_flags |= ImGuiTreeNodeFlags_Leaf; + } + + if (nameComponent.IsPrefab && e.HasComponent()) + { + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 255, 0, 255)); + } + else if (e.HasComponent()) + { + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 120)); + } + + //if (!m_IsRenaming && m_ShouldUnfoldEntityTree && Selection.Type == EditorSelectionType::Entity && e.GetScene()->EntityIsParent(Selection.Entity, e)) + //{ + // ImGui::SetNextItemOpen(true); + //} + + auto cursorPos = ImGui::GetCursorPos(); + ImGui::SetNextItemAllowOverlap(); + bool open = ImGui::TreeNodeEx(name.c_str(), base_flags); + + if (isRenaming) + { + if (Selection.Type == EditorSelectionType::Entity && Selection.Entity == e) + { + ImGui::SetCursorPosY(cursorPos.y); + ImGui::Indent(); + ImGui::InputText("##renamingEntity", &name); + ImGui::Unindent(); + if (Input::IsKeyDown(Key::ENTER)) + { + nameComponent.Name = name; + isRenaming = false; + } + } + } + + bool isDragging = false; + if (nameComponent.IsPrefab && e.HasComponent() || e.HasComponent()) + { + ImGui::PopStyleColor(); + } + else if (!isRenaming && ImGui::BeginDragDropSource()) + { + ImGui::SetDragDropPayload("ENTITYPrefab", (void*)&e, 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, e) && parentPayload.Parent != e && 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 = e; + 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 (e.HasComponent()) + { + e.GetComponent().ScriptPath = path; + } + else + { + e.AddComponent().ScriptPath = path; + } + } + ImGui::EndDragDropTarget(); + } + + + if (!isDragging && ImGui::IsItemHovered() && ImGui::IsMouseReleased(0)) + { + // We selected another another that we werent renaming + if (Selection.Entity != e) + { + isRenaming = false; + } + + Selection = EditorSelection(e); + } + + if (!isDragging && (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) || Input::IsKeyPressed(Key::F2)) + { + isRenaming = true; + } + + if (!isRenaming && Selection.Type == EditorSelectionType::Entity && Input::IsKeyPressed(Key::DELETE_KEY)) + { + //QueueDeletion = Selection.Entity; + } + + if (ImGui::BeginPopupContextItem()) + { + Selection = EditorSelection(e); + + 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 = Engine::GetCurrentScene()->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")) + { + //QueueDeletion = e; + } + + 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 = e.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 = e.GetComponent(); + if (!scriptComponent.ScriptPath.empty() && FileSystem::FileExists(scriptComponent.ScriptPath)) + { + OS::OpenIn(FileSystem::RelativeToAbsolute(scriptComponent.ScriptPath)); + } + } + ImGui::PopStyleColor(); + } + } + + ImGui::TableNextColumn(); + { + bool& isVisible = e.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) + { + // Caching list to prevent deletion while iterating. + std::vector childrens = parent.Children; + for (auto& c : childrens) + DrawEntityTree(c); + + ImGui::TreePop(); + } + + ImGui::PopStyleVar(); + //ImGui::PopFont(); +} diff --git a/Editor/src/Windows/PrefabEditor/PrefabEditorWindow.h b/Editor/src/Windows/PrefabEditor/PrefabEditorWindow.h new file mode 100644 index 00000000..15a0da6b --- /dev/null +++ b/Editor/src/Windows/PrefabEditor/PrefabEditorWindow.h @@ -0,0 +1,44 @@ +#pragma once +#include "src/Core/Core.h" + +#include "../EditorSelectionPanel.h" + + +namespace Nuake +{ + class Prefab; + class Scene; + class FrameBuffer; + class Texture; +} + +class PrefabEditorWindow +{ +public: + PrefabEditorWindow(Ref prefab); + ~PrefabEditorWindow() { + ASSERT(false); + }; + + void Update(float ts); + void Draw(); + +private: + Ref prefab; + Ref virtualScene; + Ref viewportFramebuffer; + Ref viewportTexture; + + bool isViewportFocused; + bool isHoveringViewport; + bool isRenaming; + bool isInitialized = false; + + EditorSelectionPanel SelectionPanel; + EditorSelection Selection; + +private: + void DrawViewportWindow(); + void RenderScene(); + void DrawEntityTree(Nuake::Entity e); +}; \ No newline at end of file diff --git a/Nuake/src/Core/Core.h b/Nuake/src/Core/Core.h index 1b32c8b4..565bdbd7 100644 --- a/Nuake/src/Core/Core.h +++ b/Nuake/src/Core/Core.h @@ -6,6 +6,7 @@ #include #include #include +#include #define ASSERT(x) if (!(x)) assert(false) diff --git a/Nuake/src/Scene/EditorCamera.cpp b/Nuake/src/Scene/EditorCamera.cpp index 944d173d..4f6c3036 100644 --- a/Nuake/src/Scene/EditorCamera.cpp +++ b/Nuake/src/Scene/EditorCamera.cpp @@ -52,19 +52,14 @@ namespace Nuake } } + + const bool smoothCamera = Engine::GetProject()->Settings.SmoothCamera; const float smoothCameraSpeed = Engine::GetProject()->Settings.SmoothCameraSpeed; Yaw = glm::mix(Yaw, TargetYaw, smoothCamera ? smoothCameraSpeed : 1.0f); Pitch = glm::mix(Pitch, TargetPitch, smoothCamera ? smoothCameraSpeed : 1.0f); - if (!controlled) - { - Input::ShowMouse(); - } - else if(hover) - { - Input::HideMouse(); - } + if (Input::IsKeyDown(Key::LEFT_ALT)) { @@ -168,6 +163,12 @@ namespace Nuake if (controlled) { + if (!wasControlled) + { + Input::HideMouse(); + wasControlled = true; + } + if (Input::IsMouseButtonDown(2)) { Vector3 movement = Vector3(0); @@ -189,6 +190,14 @@ namespace Nuake Input::YScroll = 0.0f; } } + else + { + if (wasControlled) + { + Input::ShowMouse(); + wasControlled = false; + } + } SetDirection(glm::normalize(Direction)); diff --git a/Nuake/src/Scene/EditorCamera.h b/Nuake/src/Scene/EditorCamera.h index 3db602e9..e1d6db91 100644 --- a/Nuake/src/Scene/EditorCamera.h +++ b/Nuake/src/Scene/EditorCamera.h @@ -10,7 +10,7 @@ namespace Nuake { private: bool m_IsFlying = false; - + bool wasControlled = false; public: EditorCamera() diff --git a/Nuake/src/Scene/Entities/Entity.cpp b/Nuake/src/Scene/Entities/Entity.cpp index 2be51bfc..ce865fc6 100644 --- a/Nuake/src/Scene/Entities/Entity.cpp +++ b/Nuake/src/Scene/Entities/Entity.cpp @@ -16,6 +16,26 @@ namespace Nuake } } + bool Entity::EntityContainsItself(Entity a, Entity b) + { + ParentComponent& targeParentComponent = b.GetComponent(); + if (!targeParentComponent.HasParent) + return false; + + Entity currentParent = b.GetComponent().Parent; + while (currentParent != a) + { + if (currentParent.GetComponent().HasParent) + currentParent = currentParent.GetComponent().Parent; + else + return false; + + if (currentParent == a) + return true; + } + return true; + } + json Entity::Serialize() { BEGIN_SERIALIZE(); diff --git a/Nuake/src/Scene/Entities/Entity.h b/Nuake/src/Scene/Entities/Entity.h index aacd59df..e4d37400 100644 --- a/Nuake/src/Scene/Entities/Entity.h +++ b/Nuake/src/Scene/Entities/Entity.h @@ -27,6 +27,8 @@ namespace Nuake return m_Scene->m_Registry.all_of(m_EntityHandle); } + bool EntityContainsItself(Entity a, Entity b); + bool IsValid() const { return m_EntityHandle != (entt::entity)-1 && m_Scene->m_Registry.valid((entt::entity)GetHandle()); diff --git a/Nuake/src/Scene/Entities/ImGuiHelper.h b/Nuake/src/Scene/Entities/ImGuiHelper.h index 716f7530..cd7cf3fb 100644 --- a/Nuake/src/Scene/Entities/ImGuiHelper.h +++ b/Nuake/src/Scene/Entities/ImGuiHelper.h @@ -91,8 +91,9 @@ public: return changed; } - static void DrawVec2(const std::string label, glm::vec2* values, float resetValue = 0.0f, float columnWidth = 100.0, float rate = 0.1f, float min = 0.0f) + static bool DrawVec2(const std::string label, glm::vec2* values, float resetValue = 0.0f, float columnWidth = 100.0, float rate = 0.1f, float min = 0.0f) { + bool changed = false; ImGuiIO& io = ImGui::GetIO(); auto boldFont = io.Fonts->Fonts[0]; @@ -109,13 +110,20 @@ public: ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{ 0.8f, 0.1f, 0.15f, 1.0f }); ImGui::PushFont(boldFont); if (ImGui::Button("###X", buttonSize)) + { values->x = resetValue; + changed = true; + } ImGui::PopFont(); ImGui::PopStyleColor(3); ImGui::SameLine(); ImGui::PushItemWidth(availWidth); - ImGui::DragFloat("##X", &values->x, rate, min, 0.0f, "%.2f"); + if (ImGui::DragFloat("##X", &values->x, rate, min, 0.0f, "%.2f")) + { + changed = true; + } + ImGui::PopItemWidth(); ImGui::SameLine(); @@ -124,17 +132,24 @@ public: ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{ 0.2f, 0.7f, 0.2f, 1.0f }); ImGui::PushFont(boldFont); if (ImGui::Button("Y", buttonSize)) + { values->y = resetValue; + changed = true; + } + ImGui::PopFont(); ImGui::PopStyleColor(3); ImGui::SameLine(); ImGui::PushItemWidth(availWidth); - ImGui::DragFloat("##Y", &values->y, rate, 0.0f, 0.0f, "%.2f"); + if (ImGui::DragFloat("##Y", &values->y, rate, 0.0f, 0.0f, "%.2f")) + { + changed = true; + } ImGui::PopItemWidth(); ImGui::PopStyleVar(); ImGui::PopID(); - //ImGui::Text("Hello"); + return changed; } }; \ No newline at end of file