From 3fc44e944caad07007e69a299f705a05a8eb6b82 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 18 Jan 2016 21:18:23 +0100 Subject: [PATCH] Editor entity reparenting. --- assets | 2 +- include/Engine/Editor/EditorGUI.h | 16 ++- include/Engine/Editor/EditorSystem.h | 1 + src/Engine/Core/ComponentPool.cpp | 1 - src/Engine/Editor/EditorGUI.cpp | 161 ++++++++++++++++------- src/Engine/Editor/EditorSystem.cpp | 6 + src/Engine/Rendering/ImGuiRenderPass.cpp | 8 +- 7 files changed, 140 insertions(+), 55 deletions(-) diff --git a/assets b/assets index a3c92ac8..a1bb17db 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a3c92ac876dd061776c36d1594bd82264372f028 +Subproject commit a1bb17dbe0da3d55932c2e187da50bc0257691f4 diff --git a/include/Engine/Editor/EditorGUI.h b/include/Engine/Editor/EditorGUI.h index d37d4c83..917d216f 100644 --- a/include/Engine/Editor/EditorGUI.h +++ b/include/Engine/Editor/EditorGUI.h @@ -13,13 +13,13 @@ #include "../Core/EventBroker.h" #include "../Core/World.h" #include "../Core/EntityWrapper.h" +#include "../Core/ResourceManager.h" +#include "../Rendering/Texture.h" class EditorGUI { public: - EditorGUI(EventBroker* eventBroker) - : m_EventBroker(eventBroker) - { } + EditorGUI(EventBroker* eventBroker); void Draw(World* world); @@ -46,6 +46,9 @@ public: // Called when the user means to delete an entity. typedef std::function OnEntityDelete_t; void SetEntityDeleteCallback(OnEntityDelete_t f) { m_OnEntityDelete = f; } + // Called when the user means to change the parent of an entity. + typedef std::function OnEntityChangeParent_t; + void SetEntityChangeParentCallback(OnEntityChangeParent_t f) { m_OnEntityChangeParent = f; } // Called when the user means to attach a new component to an entity. typedef std::function OnComponentAttach_t; void SetComponentAttachCallback(OnComponentAttach_t f) { m_OnComponentAttach = f; } @@ -62,6 +65,7 @@ private: // State variables EntityWrapper m_CurrentSelection = EntityWrapper::Invalid; std::unordered_map m_EntityFiles; + EntityWrapper m_CurrentlyDragging = EntityWrapper::Invalid; std::string m_LastErrorMessage; // Callbacks @@ -70,21 +74,25 @@ private: OnEntitySave_t m_OnEntitySave = nullptr; OnEntityCreate_t m_OnEntityCreate = nullptr; OnEntityDelete_t m_OnEntityDelete = nullptr; + OnEntityChangeParent_t m_OnEntityChangeParent = nullptr; OnComponentAttach_t m_OnComponentAttach = nullptr; OnComponentDelete_t m_OnComponentDelete = nullptr; // Utility functions boost::filesystem::path fileOpenDialog(); boost::filesystem::path fileSaveDialog(); + const std::string formatEntityName(EntityWrapper entity); // Entity file handling methods void entityImport(World* world); - void entitySave(EntityWrapper entity); + void entitySave(EntityWrapper entity, bool saveAs = false); void entityCreate(World* world, EntityWrapper parent); void entityDelete(EntityWrapper entity); + void entityChangeParent(EntityWrapper entity, EntityWrapper parent); // UI drawing methods void drawMenu(); + void drawTools(); void drawEntities(World* world); void drawEntitiesRecursive(World* world, EntityID parent); bool drawEntityNode(EntityWrapper entity); diff --git a/include/Engine/Editor/EditorSystem.h b/include/Engine/Editor/EditorSystem.h index fe8ef6ff..70ab2f15 100644 --- a/include/Engine/Editor/EditorSystem.h +++ b/include/Engine/Editor/EditorSystem.h @@ -40,6 +40,7 @@ private: void OnEntitySave(EntityWrapper entity, boost::filesystem::path filePath); EntityWrapper OnEntityCreate(EntityWrapper parent); void OnEntityDelete(EntityWrapper entity); + void OnEntityChangeParent(EntityWrapper entity, EntityWrapper parent); void OnComponentAttach(EntityWrapper entity, const std::string& componentType); void OnComponentDelete(EntityWrapper entity, const std::string& componentType); }; \ No newline at end of file diff --git a/src/Engine/Core/ComponentPool.cpp b/src/Engine/Core/ComponentPool.cpp index ce24c1f7..2ef64d71 100644 --- a/src/Engine/Core/ComponentPool.cpp +++ b/src/Engine/Core/ComponentPool.cpp @@ -50,7 +50,6 @@ ComponentWrapper ComponentPool::GetByEntity(EntityID ent) return ComponentWrapper(m_ComponentInfo, m_EntityToComponent.at(ent)); } - bool ComponentPool::KnowsEntity(EntityID ent) { return m_EntityToComponent.find(ent) != m_EntityToComponent.end(); diff --git a/src/Engine/Editor/EditorGUI.cpp b/src/Engine/Editor/EditorGUI.cpp index 3cff403d..94aaceb4 100644 --- a/src/Engine/Editor/EditorGUI.cpp +++ b/src/Engine/Editor/EditorGUI.cpp @@ -1,9 +1,16 @@ #include "Editor/EditorGUI.h" +EditorGUI::EditorGUI(EventBroker* eventBroker) + : m_EventBroker(eventBroker) +{ + +} + void EditorGUI::Draw(World* world) { ImGui::ShowTestWindow(); drawMenu(); + drawTools(); drawEntities(world); drawComponents(m_CurrentSelection); } @@ -21,6 +28,34 @@ void EditorGUI::drawMenu() } +void EditorGUI::drawTools() +{ + if (!ImGui::Begin("Tools", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) { + return; + } + + GLuint translateIcon = 0; + try { + translateIcon = ResourceManager::Load("Textures/Icons/shaft.png")->m_Texture; + } catch (const std::exception&) { } + GLuint rotateIcon = 0; + try { + rotateIcon = ResourceManager::Load("Textures/Icons/circulararrows3.png")->m_Texture; + } catch (const std::exception&) { } + GLuint scaleIcon = 0; + try { + scaleIcon = ResourceManager::Load("Textures/Icons/increase10.png")->m_Texture; + } catch (const std::exception&) { } + + ImGui::ImageButton((void*)translateIcon, ImVec2(24, 24)); + ImGui::SameLine(); + ImGui::ImageButton((void*)rotateIcon, ImVec2(24, 24)); + ImGui::SameLine(); + ImGui::ImageButton((void*)scaleIcon, ImVec2(24, 24)); + + ImGui::End(); +} + void EditorGUI::drawEntities(World* world) { if (!ImGui::Begin("Entities")) { @@ -61,6 +96,7 @@ void EditorGUI::drawEntitiesRecursive(World* world, EntityID parent) bool EditorGUI::drawEntityNode(EntityWrapper entity) { + // Custom button hitbox to select entities on top of tree node ImVec2 pos = ImGui::GetCursorScreenPos(); float width = ImGui::GetContentRegionAvailWidth(); ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 14)); @@ -75,52 +111,52 @@ bool EditorGUI::drawEntityNode(EntityWrapper entity) if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) { SelectEntity(entity); } - //if (held) { - // ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0); - // if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) { - // if (m_UIDraggingEntity == EntityID_Invalid) { - // m_UIDraggingEntity = entity; - // LOG_DEBUG("Started drag of entity %i", m_UIDraggingEntity); - // } - // ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0)); - // ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings); - // ImGui::Text("#%i", m_UIDraggingEntity); - // ImGui::End(); - // } - //} - - // Compose title - std::stringstream nodeTitle; - const std::string& entityName = entity.World->GetName(entity); - if (!entityName.empty()) { - nodeTitle << entityName; - } else { - nodeTitle << "#" << entity.ID; + // Handle entity dragging + if (held) { + ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0); + if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) { + if (m_CurrentlyDragging == EntityWrapper::Invalid) { + m_CurrentlyDragging = entity; + LOG_DEBUG("Started dragging %i", entity.ID); + } + ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0)); + ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings); + ImGui::Text(formatEntityName(entity).c_str()); + ImGui::End(); + } + } else if (m_CurrentlyDragging == entity) { + LOG_DEBUG("Stopped dragging %i", entity.ID); + m_CurrentlyDragging = EntityWrapper::Invalid; } - if (m_EntityFiles.count(entity) == 1) { - nodeTitle << " (" << m_EntityFiles.at(entity).filename().string() << ")"; + // Entity context menu + std::string contextMenuUniqueID = std::string("EntityContextMenu") + std::to_string(entity.ID); + if (hovered && ImGui::IsMouseClicked(1)) { + ImGui::OpenPopup(contextMenuUniqueID.c_str()); + } + if (ImGui::BeginPopup(contextMenuUniqueID.c_str())) { + ImGui::TextDisabled(formatEntityName(entity).c_str()); + if (ImGui::MenuItem("Save", "Ctrl+S")) { + entitySave(entity); + } else + if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) { + entitySave(entity, true); + } else + if (ImGui::MenuItem("Delete", "Del")) { + entityDelete(entity); + } else + if (ImGui::MenuItem("Move to root")) { + entityChangeParent(entity, EntityWrapper::Invalid); + } + drawModals(); + ImGui::EndPopup(); } ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once); - if (ImGui::TreeNode(nodeTitle.str().c_str())) { - //if (m_UIDraggingEntity != EntityID_Invalid && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) { - // LOG_DEBUG("Changed parent of %i to %i", m_UIDraggingEntity, entity); - // changeParent(m_UIDraggingEntity, entity); - // m_UIDraggingEntity = EntityID_Invalid; - //} - - if (ImGui::BeginPopupContextItem("entity context menu")) { - if (ImGui::Button("Save")) { - entitySave(entity); - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("Delete")) { - entityDelete(entity); - ImGui::CloseCurrentPopup(); - } - drawModals(); - ImGui::EndPopup(); + if (ImGui::TreeNode(formatEntityName(entity).c_str())) { + // Handle drop events for reparenting + if (m_CurrentlyDragging != EntityWrapper::Invalid && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) { + entityChangeParent(m_CurrentlyDragging, entity); + m_CurrentlyDragging = EntityWrapper::Invalid; } return true; } else { @@ -133,7 +169,7 @@ void EditorGUI::drawComponents(EntityWrapper entity) std::stringstream title; title << "Components"; if (entity.Valid()) { - title << " #" << entity.ID << "###Components"; + title << formatEntityName(entity) << "###Components"; } if (!ImGui::Begin(title.str().c_str())) { ImGui::End(); @@ -380,6 +416,7 @@ bool EditorGUI::createDeleteButton(const std::string& componentType) return pressed; } + boost::filesystem::path EditorGUI::fileOpenDialog() { namespace bfs = boost::filesystem; @@ -412,6 +449,28 @@ boost::filesystem::path EditorGUI::fileSaveDialog() } } +const std::string EditorGUI::formatEntityName(EntityWrapper entity) +{ + if (!entity.Valid()) { + return "EntityID_Invalid"; + } + + std::stringstream name; + + const std::string& entityName = entity.World->GetName(entity); + if (!entityName.empty()) { + name << entityName; + } else { + name << "#" << entity.ID; + } + + if (m_EntityFiles.count(entity) == 1) { + name << " (" << m_EntityFiles.at(entity).filename().string() << ")"; + } + + return name.str(); +} + void EditorGUI::entityImport(World* world) { boost::filesystem::path filePath = fileOpenDialog(); @@ -428,10 +487,10 @@ void EditorGUI::entityImport(World* world) } } -void EditorGUI::entitySave(EntityWrapper entity) +void EditorGUI::entitySave(EntityWrapper entity, bool saveAs /* = false */) { boost::filesystem::path filePath; - if (m_EntityFiles.count(entity) == 1) { + if (!saveAs && m_EntityFiles.count(entity) == 1) { filePath = m_EntityFiles.at(entity); } else { filePath = fileSaveDialog(); @@ -472,3 +531,15 @@ void EditorGUI::entityDelete(EntityWrapper entity) SelectEntity(EntityWrapper::Invalid); } } + +void EditorGUI::entityChangeParent(EntityWrapper entity, EntityWrapper parent) +{ + if (entity == parent) { + return; + } + + if (m_OnEntityChangeParent != nullptr) { + m_OnEntityChangeParent(entity, parent); + LOG_DEBUG("Changed parent of %i to %i", entity.ID, parent.ID); + } +} diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 51ab2f1f..b1e09422 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -25,6 +25,7 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer, Render m_EditorGUI->SetEntitySaveCallback(std::bind(&EditorSystem::OnEntitySave, this, std::placeholders::_1, std::placeholders::_2)); m_EditorGUI->SetEntityCreateCallback(std::bind(&EditorSystem::OnEntityCreate, this, std::placeholders::_1)); m_EditorGUI->SetEntityDeleteCallback(std::bind(&EditorSystem::OnEntityDelete, this, std::placeholders::_1)); + m_EditorGUI->SetEntityChangeParentCallback(std::bind(&EditorSystem::OnEntityChangeParent, this, std::placeholders::_1, std::placeholders::_2)); m_EditorGUI->SetComponentAttachCallback(std::bind(&EditorSystem::OnComponentAttach, this, std::placeholders::_1, std::placeholders::_2)); m_EditorGUI->SetComponentDeleteCallback(std::bind(&EditorSystem::OnComponentDelete, this, std::placeholders::_1, std::placeholders::_2)); @@ -79,6 +80,11 @@ void EditorSystem::OnEntityDelete(EntityWrapper entity) entity.World->DeleteEntity(entity.ID); } +void EditorSystem::OnEntityChangeParent(EntityWrapper entity, EntityWrapper parent) +{ + entity.World->SetParent(entity.ID, parent.ID); +} + void EditorSystem::OnComponentAttach(EntityWrapper entity, const std::string& componentType) { entity.World->AttachComponent(entity.ID, componentType); diff --git a/src/Engine/Rendering/ImGuiRenderPass.cpp b/src/Engine/Rendering/ImGuiRenderPass.cpp index 96f987d9..e67eea71 100644 --- a/src/Engine/Rendering/ImGuiRenderPass.cpp +++ b/src/Engine/Rendering/ImGuiRenderPass.cpp @@ -105,7 +105,7 @@ void ImGuiRenderPass::Draw() if (pcmd->UserCallback) { pcmd->UserCallback(cmd_list, pcmd); } else { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glBindTexture(GL_TEXTURE_2D, (GLuint)pcmd->TextureId); glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); } @@ -190,7 +190,7 @@ bool ImGuiRenderPass::createDeviceObjects() "{\n" " Frag_UV = UV;\n" " Frag_Color = Color;\n" - " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" "}\n"; const GLchar* fragment_shader = @@ -201,7 +201,7 @@ bool ImGuiRenderPass::createDeviceObjects() "out vec4 Out_Color;\n" "void main()\n" "{\n" - " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" "}\n"; g_ShaderHandle = glCreateProgram(); @@ -271,7 +271,7 @@ bool ImGuiRenderPass::createFontsTexture() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; + io.Fonts->TexID = (void*)g_FontTexture; // Restore state glBindTexture(GL_TEXTURE_2D, last_texture);