diff --git a/editor/include/editor.hpp b/editor/include/editor.hpp index e3ccfe6..4b7722d 100755 --- a/editor/include/editor.hpp +++ b/editor/include/editor.hpp @@ -3,12 +3,6 @@ #include -#include "open_engine/events/mouse_event.hpp" -#include "open_engine/input/input_system.hpp" -#include "open_engine/input/mouse_codes.hpp" -#include "open_engine/renderer/render_command.hpp" -#include "open_engine/renderer/renderer2d.hpp" -#include "open_engine/scene/components.hpp" #include "panels/scene_hierarchy.hpp" #include @@ -94,7 +88,7 @@ namespace OpenEngine { } */ - scene_hierarchy.SetContext(scene); + scene_hierarchy.Init(scene); } void OnDetach() override @@ -364,7 +358,7 @@ namespace OpenEngine { if (!file.empty()) { scene = CreateRef(); scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); - scene_hierarchy.SetContext(scene); + scene_hierarchy.Init(scene); SceneSerializer serializer(scene); serializer.Deserialize(file); @@ -373,7 +367,7 @@ namespace OpenEngine { if (ImGui::MenuItem("New Scene", "Ctrl+N")) { scene = CreateRef(); scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y); - scene_hierarchy.SetContext(scene); + scene_hierarchy.Init(scene); } ImGui::Separator(); if (ImGui::MenuItem("Exit")) diff --git a/editor/include/editor_component.hpp b/editor/include/editor_component.hpp new file mode 100644 index 0000000..36a5a14 --- /dev/null +++ b/editor/include/editor_component.hpp @@ -0,0 +1,27 @@ +#ifndef EDITOR_COMPONENT_HPP +#define EDITOR_COMPONENT_HPP + +#include "open_engine/scene/components.hpp" + +#include +#include + +namespace OpenEngine { + + /* + void DrawVec3Control(const char* label, glm::vec3& values, + float reset_value = 0.0f, float column_width = 100.0f, + const std::array labels = {"x", "y", "z"}); + */ + + void TagOnImGuiRender(entt::registry& registry, entt::entity entity); + + void TransformOnImGuiRender(entt::registry& registry, entt::entity entity); + + void SpriteOnImGuiRender(entt::registry& registry, entt::entity entity); + + void CameraOnImGuiRender(entt::registry& registry, entt::entity entity); + +} + +#endif // EDITOR_COMPONENT_HPP diff --git a/editor/include/panels/scene_hierarchy.hpp b/editor/include/panels/scene_hierarchy.hpp index c99533d..ac0ad1f 100644 --- a/editor/include/panels/scene_hierarchy.hpp +++ b/editor/include/panels/scene_hierarchy.hpp @@ -1,17 +1,27 @@ #ifndef SCENE_HIERARCHY_HPP #define SCENE_HIERARCHY_HPP -#include "imgui.h" +#include +#include #include namespace OpenEngine { + + using ComponentDrawer = std::function; + + struct ComponentUI { + std::string name; + std::function draw_func; + std::function remove_func; + }; + class SceneHierarchy { public: SceneHierarchy() = default; SceneHierarchy(const Ref& scene); - void SetContext(const Ref& scene); + void Init(const Ref& scene); void OnImGuiRender(); @@ -22,6 +32,7 @@ namespace OpenEngine { void DrawEntityNode(Entity& entity); void DrawComponents(Entity& entity); + /* template void DrawComponentDrawer(Entity& entity, const char* header) { @@ -62,8 +73,19 @@ namespace OpenEngine { if (component_marked_deletion) entity.RemoveComponents(); } + */ private: + template + static void RegisterDrawer(const std::string& name, std::function func) { + drawers[entt::type_id().hash()] = { + name, + func, + [](entt::registry& reg, entt::entity ent) { reg.remove(ent); } + }; + } + + inline static std::map drawers; Ref scene; Entity selected_context, renamed_entity; double last_selected_time = 10.0; diff --git a/editor/src/editor_component.cpp b/editor/src/editor_component.cpp new file mode 100644 index 0000000..5434c99 --- /dev/null +++ b/editor/src/editor_component.cpp @@ -0,0 +1,86 @@ +#include +#include + +namespace OpenEngine { + + void TagOnImGuiRender(entt::registry& registry, entt::entity entity) + { + char buffer[256]; + + auto& tag = registry.get(entity).tag; + std::memset(buffer, 0, sizeof(buffer)); + std::strncpy(buffer, tag.c_str(), sizeof(buffer)); + + if (ImGui::InputText("##tagEditor", buffer, sizeof(buffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll) + || (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0))) + tag = buffer; + }; + + void TransformOnImGuiRender(entt::registry& registry, entt::entity entity) + { + auto& transform = registry.get(entity); + DrawVec3Control("Position", transform.translation); + glm::vec3 rotation_comp = glm::degrees(transform.rotation); + DrawVec3Control("Rotation", rotation_comp); + transform.rotation = glm::radians(rotation_comp); + DrawVec3Control("Scale", transform.scale); + }; + + void SpriteOnImGuiRender(entt::registry& registry, entt::entity entity) + { + auto& sprite = registry.get(entity); + ImGui::ColorEdit4("color", glm::value_ptr(sprite.color)); + }; + + void CameraOnImGuiRender(entt::registry& registry, entt::entity entity) + { + auto& camera_comp = registry.get(entity); + auto& camera = camera_comp.camera; + ImGui::Checkbox("Is primary", &camera_comp.primary); + const char* projection_type_strings[] = {"Perspective", "Orthographic"}; + const char* current_projection_type = projection_type_strings[(int)camera.GetProjectionType()]; + + if (ImGui::BeginCombo("Projection", current_projection_type)) { + for (int i = 0; i < 2; i++) { + bool is_selected = current_projection_type == projection_type_strings[i]; + if (ImGui::Selectable(projection_type_strings[i], is_selected)) { + current_projection_type = projection_type_strings[i]; + camera.SetProjectionType((SceneCamera::ProjectionType)i); + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + if (camera.GetProjectionType() == SceneCamera::ProjectionType::Perspective) { + float fov = camera.GetVerticalFov(); + if (ImGui::DragFloat("Fov", &fov, 0.1f)) + camera.SetVerticalFov(fov); + + float near_clip = camera.GetPerspectiveNearClip(); + if (ImGui::DragFloat("Near clip", &near_clip, 0.1f)) + camera.SetPerspectiveNearClip(near_clip); + + float far_clip = camera.GetPerspectiveFarClip(); + if (ImGui::DragFloat("Far clip", &far_clip, 0.1f)) + camera.SetPerspectiveFarClip(far_clip); + } + else if (camera.GetProjectionType() == SceneCamera::ProjectionType::Orthographic) { + ImGui::Checkbox("Has fixed aspect ratio", &camera_comp.fixed_aspect_ratio); + float ortho_size = camera.GetOrthographicSize(); + if (ImGui::DragFloat("Orthographic size", &ortho_size, 0.1f)) + camera.SetOrthographicSize(ortho_size); + + float near_clip = camera.GetOrthographicNearClip(); + if (ImGui::DragFloat("Near clip", &near_clip, 0.1f)) + camera.SetOrthographicNearClip(near_clip); + + float far_clip = camera.GetOrthographicFarClip(); + if (ImGui::DragFloat("Far clip", &far_clip, 0.1f)) + camera.SetOrthographicFarClip(far_clip); + } + + }; +} diff --git a/editor/src/panels/scene_hierarchy.cpp b/editor/src/panels/scene_hierarchy.cpp index 5abbe89..7a23b74 100644 --- a/editor/src/panels/scene_hierarchy.cpp +++ b/editor/src/panels/scene_hierarchy.cpp @@ -1,5 +1,7 @@ #include "open_engine/scene/components.hpp" #include "open_engine/scene/entity.hpp" +#include "editor_component.hpp" + #include #include @@ -7,13 +9,20 @@ #include namespace OpenEngine { + + using ComponentDrawer = std::function; + SceneHierarchy::SceneHierarchy(const Ref& scene) : scene(scene) { } - void SceneHierarchy::SetContext(const Ref& context) + void SceneHierarchy::Init(const Ref& context) { + RegisterDrawer("Transform", &TransformOnImGuiRender); + RegisterDrawer("Sprite Renderer", &SpriteOnImGuiRender); + RegisterDrawer("Camera", &CameraOnImGuiRender); + scene = context; selected_context = {}; } @@ -184,11 +193,54 @@ namespace OpenEngine { void SceneHierarchy::DrawComponents(Entity& entity) { - if (entity.HasComponent()) - entity.GetComponents().OnImGuiRender(entity); + auto& reg = scene->GetRegistry(); + entt::entity handle = selected_context; - DrawComponentDrawer(entity, "Transform"); - DrawComponentDrawer(entity, "Sprite"); - DrawComponentDrawer(entity, "Camera"); + if (!selected_context) + return; + + entity.GetComponents(); + TagOnImGuiRender(reg, entity); + + std::vector component_to_delete; // 0 is null/invalid in entt usually + // Iterate through every component type entt knows about + for (auto [id, storage] : reg.storage()) { + if (storage.contains(handle)) { + if (drawers.contains(id)) { + bool component_marked_deletion = false; + ImVec2 region_available = ImGui::GetContentRegionAvail(); + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_DefaultOpen + | ImGuiTreeNodeFlags_Framed + | ImGuiTreeNodeFlags_AllowOverlap; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4, 4 }); + float line_height = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f; + + bool opened = ImGui::TreeNodeEx((void*)(intptr_t)id, tree_node_flags, "%s", drawers[id].name.c_str()); + ImGui::SameLine(region_available.x - line_height * 0.5f); + + ImGui::PushStyleColor(ImGuiCol_Button, { 0.290f, 0.301f, 0.388f, 1.0f }); + if (ImGui::Button("...", ImVec2{ line_height, line_height })) + ImGui::OpenPopup("component_settings"); + ImGui::PopStyleColor(); + + ImGui::PopStyleVar(); + + if (ImGui::BeginPopup("component_settings")) { + if (ImGui::MenuItem("Remove component")) + component_to_delete.emplace_back(id); + + ImGui::EndPopup(); + } + + if (opened) { + drawers[id].draw_func(reg, handle); + ImGui::TreePop(); + } + } + } + } + + for (auto& id : component_to_delete) + drawers[id].remove_func(reg, entity); } } diff --git a/open_engine/include/open_engine.hpp b/open_engine/include/open_engine.hpp index b82cd3a..e3927ee 100644 --- a/open_engine/include/open_engine.hpp +++ b/open_engine/include/open_engine.hpp @@ -12,7 +12,6 @@ #include "open_engine/scene/native_scriptable_entity.hpp" #include "open_engine/scene/scene_serializer.hpp" #include "open_engine/scene/components.hpp" -#include "open_engine/scene/components.hpp" #include "open_engine/core/file_dialogs.hpp" #include "open_engine/core/time.hpp" diff --git a/open_engine/include/open_engine/scene/components.hpp b/open_engine/include/open_engine/scene/components.hpp index cfdab9c..a33e779 100644 --- a/open_engine/include/open_engine/scene/components.hpp +++ b/open_engine/include/open_engine/scene/components.hpp @@ -31,20 +31,9 @@ namespace OpenEngine { TagComponent(const TagComponent&) = default; TagComponent(const std::string& tag) : tag(tag) {} - - void OnImGuiRender(Entity& entity) - { - char buffer[256]; - std::memset(buffer, 0, sizeof(buffer)); - std::strncpy(buffer, tag.c_str(), sizeof(buffer)); - - if (ImGui::InputText("##tagEditor", buffer, sizeof(buffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll) - || (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0))) - tag = buffer; - }; }; - struct TransformComponent + struct TransformComponent { glm::vec3 translation = { 0.0f, 0.0f, 0.0f }; glm::vec3 rotation = { 0.0f, 0.0f, 0.0f }; @@ -64,18 +53,9 @@ namespace OpenEngine { return transform; }; - - void OnImGuiRender(Entity& entity) - { - DrawVec3Control("Position", translation); - glm::vec3 rotation_comp = glm::degrees(rotation); - DrawVec3Control("Rotation", rotation_comp); - rotation = glm::radians(rotation_comp); - DrawVec3Control("Scale", scale); - }; }; - struct SpriteRendererComponent + struct SpriteRendererComponent { glm::vec4 color{ 1.0f, 1.0f, 1.0f, 1.0f }; @@ -83,14 +63,9 @@ namespace OpenEngine { SpriteRendererComponent(const SpriteRendererComponent&) = default; SpriteRendererComponent(const glm::vec4& color) : color(color) {} - - void OnImGuiRender(Entity& entity) - { - ImGui::ColorEdit4("color", glm::value_ptr(color)); - }; }; - struct CameraComponent + struct CameraComponent { SceneCamera camera; bool primary = true; @@ -98,65 +73,18 @@ namespace OpenEngine { CameraComponent() = default; CameraComponent(const CameraComponent&) = default; - - void OnImGuiRender(Entity& entity) - { - ImGui::Checkbox("Is primary", &primary); - const char* projection_type_strings[] = {"Perspective", "Orthographic"}; - const char* current_projection_type = projection_type_strings[(int)camera.GetProjectionType()]; - - if (ImGui::BeginCombo("Projection", current_projection_type)) { - for (int i = 0; i < 2; i++) { - bool is_selected = current_projection_type == projection_type_strings[i]; - if (ImGui::Selectable(projection_type_strings[i], is_selected)) { - current_projection_type = projection_type_strings[i]; - camera.SetProjectionType((SceneCamera::ProjectionType)i); - } - - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - - if (camera.GetProjectionType() == SceneCamera::ProjectionType::Perspective) { - float fov = camera.GetVerticalFov(); - if (ImGui::DragFloat("Fov", &fov, 0.1f)) - camera.SetVerticalFov(fov); - - float near_clip = camera.GetPerspectiveNearClip(); - if (ImGui::DragFloat("Near clip", &near_clip, 0.1f)) - camera.SetPerspectiveNearClip(near_clip); - - float far_clip = camera.GetPerspectiveFarClip(); - if (ImGui::DragFloat("Far clip", &far_clip, 0.1f)) - camera.SetPerspectiveFarClip(far_clip); - } - else if (camera.GetProjectionType() == SceneCamera::ProjectionType::Orthographic) { - ImGui::Checkbox("Has fixed aspect ratio", &fixed_aspect_ratio); - float ortho_size = camera.GetOrthographicSize(); - if (ImGui::DragFloat("Orthographic size", &ortho_size, 0.1f)) - camera.SetOrthographicSize(ortho_size); - - float near_clip = camera.GetOrthographicNearClip(); - if (ImGui::DragFloat("Near clip", &near_clip, 0.1f)) - camera.SetOrthographicNearClip(near_clip); - - float far_clip = camera.GetOrthographicFarClip(); - if (ImGui::DragFloat("Far clip", &far_clip, 0.1f)) - camera.SetOrthographicFarClip(far_clip); - } - - }; }; - struct NativeScriptComponent + struct NativeScriptComponent { NativeScriptableEntity* instance = nullptr; NativeScriptableEntity* (*InstanciateScript)(); void (*DestroyInstanceScript)(NativeScriptComponent*); + void OnImGuiRender(Entity& entity) + {}; + template void Bind() {