#include "open_engine/scene/components.hpp" #include "open_engine/scene/entity.hpp" #include "editor_component.hpp" #include #include #include #include namespace OpenEngine { using ComponentDrawer = std::function; SceneHierarchy::SceneHierarchy(const Ref& scene) : scene(scene) { } void SceneHierarchy::Init(const Ref& context) { RegisterDrawer("Transform", &TransformOnImGuiRender); RegisterDrawer("Sprite Renderer", &SpriteOnImGuiRender); RegisterDrawer("Camera", &CameraOnImGuiRender); RegisterDrawer("Mesh", &MeshOnImGuiRender); scene = context; selected_context = {}; } void ComponentPopup(Entity& selected_context) { ImGui::SeparatorText("Add"); if (!selected_context.HasComponent()) if (ImGui::MenuItem("Transform")) { selected_context.AddComponent(); ImGui::CloseCurrentPopup(); } if (!selected_context.HasComponent()) if (ImGui::MenuItem("Sprite")) { selected_context.AddComponent(); ImGui::CloseCurrentPopup(); } if (!selected_context.HasComponent()) if (ImGui::MenuItem("Camera")) { selected_context.AddComponent(); ImGui::CloseCurrentPopup(); } if (!selected_context.HasComponent()) if (ImGui::MenuItem("Mesh")) { selected_context.AddComponent(); ImGui::CloseCurrentPopup(); } } void EntityPopup(Ref& scene) { ImGui::SeparatorText("Add"); if (ImGui::MenuItem("Empty entity")) { scene->CreateEntity("Empty entity"); ImGui::CloseCurrentPopup(); } } void SceneHierarchy::OnImGuiRender() { // Scene hierarchy if (ImGui::Begin("Scene")) { // Draw all enitity tree names for (auto entity_entt : scene->GetRegistry().view()) { Entity entity{ entity_entt, scene.get() }; DrawEntityNode(entity); } // On click not on item, deselect if ((ImGui::IsMouseDown(0) && ImGui::IsWindowHovered()) || (ImGui::IsKeyDown(ImGuiKey_Escape) && ImGui::IsWindowHovered())) { selected_context = {}; renamed_entity = {}; } // On Right click open entity creation window if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems)) { EntityPopup(scene); ImGui::EndPopup(); } // Centred fullwidth bottom button to add entity float button_height = ImGui::GetFrameHeight(); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - button_height); if (ImGui::Button("Add Entity", ImVec2(ImGui::GetContentRegionAvail().x, 0.0f))) ImGui::OpenPopup("add_entity"); if (ImGui::BeginPopup("add_entity")) { EntityPopup(scene); ImGui::EndPopup(); } ImGui::End(); } // Property window ImGui::Begin("Properties"); if (selected_context) { DrawComponents(selected_context); if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems)) { ComponentPopup(selected_context); ImGui::EndPopup(); } // Centred fullwidth bottom button to add component float button_height = ImGui::GetFrameHeight(); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - button_height); if (ImGui::Button("Add Component", ImVec2(ImGui::GetContentRegionAvail().x, 0.0f))) ImGui::OpenPopup("add_component"); if (ImGui::BeginPopup("add_component")) { ComponentPopup(selected_context); ImGui::EndPopup(); } } ImGui::End(); } void SceneHierarchy::DrawEntityNode(Entity& entity) { auto& tag = entity.GetComponents().tag; bool entity_marked_deletion = false; if (renamed_entity == entity && selected_context == entity) { char buffer[255]; std::memset(buffer, 0, sizeof(buffer)); std::strncpy(buffer, tag.c_str(), sizeof(buffer)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); ImGui::SetKeyboardFocusHere(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::InputText("##tagEditor", buffer, sizeof(buffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll) || (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0))) { tag = buffer; renamed_entity = {}; } if (ImGui::IsKeyPressed(ImGuiKey_Escape)) renamed_entity = {}; ImGui::PopStyleVar(); } else { ImGuiTreeNodeFlags flags = ((selected_context == entity) ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_OpenOnArrow; flags |= ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanAvailWidth; bool opened = ImGui::TreeNodeEx((void*)(uint64_t)(uint32_t)entity, flags, "%s", tag.c_str()); if (ImGui::BeginPopupContextItem()) { if (ImGui::MenuItem("Delete entity")) entity_marked_deletion = true; ImGui::EndPopup(); } if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { // FocusViewportOnEntity(entity); // future implementation renamed_entity = {}; } else if (ImGui::IsItemClicked()) { double current_time = ImGui::GetTime(); double double_click_time = ImGui::GetIO().MouseDoubleClickTime; // Usually ~0.3s if (selected_context == entity) { renamed_entity = entity; if (current_time - last_selected_time > double_click_time) { renamed_entity = entity; } } else { // First time selecting selected_context = entity; last_selected_time = current_time; } } if (opened) ImGui::TreePop(); } if (entity_marked_deletion) { scene->DeleteEntity(entity); if (selected_context == entity) selected_context = {}; } } void SceneHierarchy::DrawComponents(Entity& entity) { auto& reg = scene->GetRegistry(); entt::entity handle = selected_context; if (!selected_context || !entity) 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); } }