From 504f301e4d8f483092ec9b5d27bd38dfa08463f0 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 18 Jan 2016 13:46:42 +0100 Subject: [PATCH] Added refactored property sheet for components --- include/Engine/Editor/EditorGUI.h | 18 +++ src/Engine/Editor/EditorGUI.cpp | 208 ++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) diff --git a/include/Engine/Editor/EditorGUI.h b/include/Engine/Editor/EditorGUI.h index 6c8b3499..77218e9d 100644 --- a/include/Engine/Editor/EditorGUI.h +++ b/include/Engine/Editor/EditorGUI.h @@ -7,6 +7,8 @@ #include #include #include "../Common.h" +#include "../GLM.h" +#include #include "../Core/EventBroker.h" #include "../Core/World.h" @@ -42,6 +44,10 @@ public: // Called when the user means to delete an entity. typedef std::function OnEntityDelete_t; void SetEntityCreateCallback(OnEntityDelete_t f) { m_OnEntityDelete = 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; } + private: EventBroker* m_EventBroker; @@ -55,11 +61,23 @@ private: OnEntitySave_t m_OnEntitySave = nullptr; OnEntityCreate_t m_OnEntityCreate = nullptr; OnEntityDelete_t m_OnEntityDelete = nullptr; + OnComponentAttach_t m_OnComponentAttach = nullptr; void drawMenu(); void drawEntities(World* world); void drawEntitiesRecursive(World* world, EntityID parent); bool drawEntityNode(EntityWrapper entity); + void drawComponents(EntityWrapper entity); + bool drawComponentNode(EntityWrapper entity, const ComponentInfo& componentType); + void drawComponentField(ComponentWrapper& c, const ComponentInfo::Field_t& field); + void drawComponentField_Vector(ComponentWrapper &c, const ComponentInfo::Field_t &field); + void drawComponentField_Color(ComponentWrapper &c, const ComponentInfo::Field_t &field); + void drawComponentField_int(ComponentWrapper &c, const ComponentInfo::Field_t &field); + void drawComponentField_enum(ComponentWrapper &c, const ComponentInfo::Field_t &field); + void drawComponentField_float(ComponentWrapper &c, const ComponentInfo::Field_t &field); + void drawComponentField_double(ComponentWrapper &c, const ComponentInfo::Field_t &field); + void drawComponentField_bool(ComponentWrapper &c, const ComponentInfo::Field_t &field); + void drawComponentField_string(ComponentWrapper &c, const ComponentInfo::Field_t &field); }; #endif \ No newline at end of file diff --git a/src/Engine/Editor/EditorGUI.cpp b/src/Engine/Editor/EditorGUI.cpp index 408a3cbf..388016a6 100644 --- a/src/Engine/Editor/EditorGUI.cpp +++ b/src/Engine/Editor/EditorGUI.cpp @@ -5,6 +5,7 @@ void EditorGUI::Draw(World* world) ImGui::ShowTestWindow(); drawMenu(); drawEntities(world); + drawComponents(m_CurrentSelection); } void EditorGUI::SelectEntity(EntityWrapper entity) @@ -122,3 +123,210 @@ bool EditorGUI::drawEntityNode(EntityWrapper entity) return false; } } + +void EditorGUI::drawComponents(EntityWrapper entity) +{ + std::stringstream title; + title << "Components"; + if (entity.Valid()) { + title << " #" << entity.ID << "###Components"; + } + if (!ImGui::Begin(title.str().c_str())) { + ImGui::End(); + return; + } + + if (!entity.Valid()) { + ImGui::End(); + return; + } + + auto& pools = entity.World->GetComponentPools(); + // Create list of component types available to be added + std::vector componentTypes; + for (auto& pair : pools) { + // Don't list components the entity already has attached + if (!entity.HasComponent(pair.first)) { + componentTypes.push_back(pair.first.c_str()); + } + } + // Draw combo box + ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() - 10.f); + int selectedItem = -1; + if (ImGui::Combo("", &selectedItem, componentTypes.data(), componentTypes.size())) { + if (selectedItem != -1) { + if (m_OnComponentAttach != nullptr) { + std::string chosenComponentType(componentTypes.at(selectedItem)); + m_OnComponentAttach(entity, chosenComponentType); + } + } + } + ImGui::PopItemWidth(); + + for (auto& pair : pools) { + const std::string& componentType = pair.first; + auto pool = pair.second; + // Don't show components the entity doesn't have attached + if (!entity.HasComponent(componentType)) { + continue; + } + // TODO: Add delete button here + drawComponent(entity, pool->ComponentInfo()); + } + + ImGui::End(); +} + +bool EditorGUI::drawComponent(EntityWrapper entity, const ComponentInfo& ci) +{ + if (!ImGui::CollapsingHeader(ci.Name.c_str(), nullptr, true, true)) { + return false; + } + + // Show component annotation + const std::string annotation = ci.Meta->Annotation; + if (!annotation.empty()) { + ImGui::TextWrapped(annotation.c_str()); + } + + // Draw component fields + ComponentWrapper& component = entity.World->GetComponent(entity.ID, ci.Name); + for (auto& kv : ci.Fields) { + const std::string& fieldName = kv.first; + const ComponentInfo::Field_t& field = kv.second; + + // Draw the field widget based on its type + drawComponentField(component, field); + ImGui::SameLine(); + // Draw field name + ImGui::Text(fieldName.c_str()); + // Draw potential field annotation + auto fieldAnnotationIt = ci.Meta->FieldAnnotations.find(fieldName); + if (fieldAnnotationIt != ci.Meta->FieldAnnotations.end()) { + ImGui::SameLine(); + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(fieldAnnotationIt->second.c_str()); + } + } + } + + return true; +} + +void EditorGUI::drawComponentField(ComponentWrapper& c, const ComponentInfo::Field_t& field) +{ + // Push an unique widget id so different components with fields with equal names are still counted as different + ImGui::PushID((c.Info.Name + field.Name).c_str()); + + if (field.Type == "Vector") { + drawComponentField_Vector(c, field); + } else if (field.Type == "Color") { + drawComponentField_Color(c, field); + //} else if (field.Type == "Quaternion") { + } else if (field.Type == "int") { + drawComponentField_int(c, field); + } else if (field.Type == "enum") { + drawComponentField_enum(c, field); + } else if (field.Type == "float") { + drawComponentField_float(c, field); + } else if (field.Type == "double") { + drawComponentField_double(c, field); + } else if (field.Type == "bool") { + drawComponentField_bool(c, field); + } else if (field.Type == "string") { + drawComponentField_string(c, field); + } else { + ImGui::TextDisabled(field.Type.c_str()); + } + + ImGui::PopID(); +} + +void EditorGUI::drawComponentField_Vector(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + auto& val = c.Field(field.Name); + if (field.Name == "Scale") { + // Limit scale values to a minimum of 0 + ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits::max()); + } else if (field.Name == "Orientation") { + // Make orentations have a period of 2*Pi + glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi())); + if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi())) { + val = tempVal; + } + } else { + ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits::lowest(), std::numeric_limits::max()); + } +} + +void EditorGUI::drawComponentField_Color(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + auto& val = c.Field(field.Name); + ImGui::ColorEdit4("", glm::value_ptr(val), true); +} + +void EditorGUI::drawComponentField_int(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + auto& val = c.Field(field.Name); + ImGui::InputInt("", &val); +} + +void EditorGUI::drawComponentField_enum(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + auto fieldEnumDefIt = c.Info.Meta->FieldEnumDefinitions.find(field.Name); + if (fieldEnumDefIt == c.Info.Meta->FieldEnumDefinitions.end()) { + drawComponentField_int(c, field); + return; + } + + auto& val = c.Field(field.Name); + int selectedItem = -1; + std::stringstream enumKeys; + std::vector enumValues; + int i = 0; + for (auto& kv : fieldEnumDefIt->second) { + enumKeys << kv.first << " (" << kv.second << ")" << '\0'; + enumValues.push_back(kv.second); + if (val == kv.second) { + selectedItem = i; + } + } + if (ImGui::Combo("", &selectedItem, enumKeys.str().c_str())) { + val = enumValues.at(selectedItem); + } +} + +void EditorGUI::drawComponentField_float(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + auto& val = c.Field(field.Name); + ImGui::InputFloat("", &val, 0.01f, 1.f); +} + +void EditorGUI::drawComponentField_double(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + float tempVal = static_cast(c.Field(field.Name)); + if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) { + c.SetField(field.Name, static_cast(tempVal)); + } +} + +void EditorGUI::drawComponentField_bool(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + auto& val = c.Field(field.Name); + ImGui::Checkbox("", &val); +} + +void EditorGUI::drawComponentField_string(ComponentWrapper &c, const ComponentInfo::Field_t &field) +{ + auto& val = c.Field(field.Name); + char tempString[1024]; // Let's just hope this is an sufficiently large buffer for strings :) + tempString[1023] = '\0'; // Null terminator just in case the string is larger than the buffer + // Copy the string into the buffer, taking the null terminator into account + memcpy(tempString, val.c_str(), std::min(val.length() + 1, sizeof(tempString) - 1)); + if (ImGui::InputText("", tempString, sizeof(tempString))) { + val = std::string(tempString); + } + // TODO: Handle drag and drop of files +} +