Editor entity reparenting.
This commit is contained in:
+1
-1
Submodule assets updated: a3c92ac876...a1bb17dbe0
@@ -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<void(EntityWrapper)> 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<void(EntityWrapper, EntityWrapper)> 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<void(EntityWrapper, const std::string&)> 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<EntityWrapper, boost::filesystem::path> 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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
+113
-42
@@ -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<Texture>("Textures/Icons/shaft.png")->m_Texture;
|
||||
} catch (const std::exception&) { }
|
||||
GLuint rotateIcon = 0;
|
||||
try {
|
||||
rotateIcon = ResourceManager::Load<Texture>("Textures/Icons/circulararrows3.png")->m_Texture;
|
||||
} catch (const std::exception&) { }
|
||||
GLuint scaleIcon = 0;
|
||||
try {
|
||||
scaleIcon = ResourceManager::Load<Texture>("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,53 +111,53 @@ 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);
|
||||
}
|
||||
if (m_EntityFiles.count(entity) == 1) {
|
||||
nodeTitle << " (" << m_EntityFiles.at(entity).filename().string() << ")";
|
||||
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();
|
||||
}
|
||||
|
||||
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")) {
|
||||
} else if (m_CurrentlyDragging == entity) {
|
||||
LOG_DEBUG("Stopped dragging %i", entity.ID);
|
||||
m_CurrentlyDragging = EntityWrapper::Invalid;
|
||||
}
|
||||
// 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);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete")) {
|
||||
} else
|
||||
if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) {
|
||||
entitySave(entity, true);
|
||||
} else
|
||||
if (ImGui::MenuItem("Delete", "Del")) {
|
||||
entityDelete(entity);
|
||||
ImGui::CloseCurrentPopup();
|
||||
} else
|
||||
if (ImGui::MenuItem("Move to root")) {
|
||||
entityChangeParent(entity, EntityWrapper::Invalid);
|
||||
}
|
||||
drawModals();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once);
|
||||
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 {
|
||||
return false;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user