// // Created by sfd on 25-5-29. // #include "SceneHierachyPanel.h" #include #include #include #include namespace Hazel { SceneHierachyPanel::SceneHierachyPanel(const Ref& context) { SetContext(context); } void SceneHierachyPanel::SetContext(const Ref& context) { m_Context = context; m_SelectionContext = {}; } void SceneHierachyPanel::OnImGuiRender() { ImGui::Begin("Scene Hierachy"); m_Context->m_Registry.view().each([&](auto entityID) { DrawEntityNode({entityID, m_Context.get()}); }); if (ImGui::IsMouseDown(0) && ImGui::IsWindowHovered()) { m_SelectionContext = { }; } if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems)) { if (ImGui::MenuItem("Create Empty Entity")) m_Context->CreateEntity("Empty Entity"); ImGui::EndPopup(); } ImGui::End(); ImGui::Begin("Properties"); if (m_SelectionContext) { DrawComponents(m_SelectionContext); } ImGui::End(); } void SceneHierachyPanel::DrawEntityNode(Entity entity) { auto& tag = entity.GetComponent().Tag; bool entityDeleted = false; ImGuiTreeNodeFlags flags = ((m_SelectionContext == entity) ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_OpenOnArrow; flags |= ImGuiTreeNodeFlags_SpanAvailWidth; const bool isopened = ImGui::TreeNodeEx((void*)(uint64_t)(uint32_t)entity, flags, tag.c_str()); if (ImGui::IsItemClicked()) { m_SelectionContext = entity; } if (ImGui::BeginPopupContextItem()) { if (ImGui::MenuItem("Delete Entity")) entityDeleted = true; ImGui::EndPopup(); } if (isopened) { ImGui::TreePop(); } if (entityDeleted) { m_Context->DestoryEntity(entity); if (m_SelectionContext == entity) { m_SelectionContext = {}; } } } static bool DrawVec3Control(const std::string& label, glm::vec3& values, float resetValue = 0.0f, float columnWidth = 100.0f) { ImGuiIO& io = ImGui::GetIO(); auto boldFont = io.Fonts->Fonts[0]; bool isChanged = false; ImGui::PushID(label.c_str()); const ImGuiContext* context = ImGui::GetCurrentContext(); ImGui::Columns(2); ImGui::SetColumnWidth(0, columnWidth); ImGui::Text(label.c_str()); ImGui::NextColumn(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {0.0f, 0.0f}); float lineHeight = context->Font->FontSize + context->Style.FramePadding.y * 2.0f; // float lineHeight = GImGui->Font->FontSize + GImGui->Style.FramePadding.y * 2.0f; ImVec2 buttonSize = {lineHeight + 3.0f, lineHeight}; ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.8f, 0.1f, 0.15f, 1.0f}); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4{0.9f, 0.2f, 0.2f, 1.0f}); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.8f, 0.1f, 0.15f, 1.0f}); ImGui::PushFont(boldFont); if (ImGui::Button("X", buttonSize)) { values.x = resetValue; isChanged = true; } ImGui::PopFont(); ImGui::PopStyleColor(3); ImGui::SameLine(); if (ImGui::DragFloat("##X", &values.x, 0.1f)) { isChanged = true; } ImGui::PopItemWidth(); ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.2f, 0.7f, 0.2f, 1.0f}); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4{0.3f, 0.8f, 0.3f, 1.0f}); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.2f, 0.7f, 0.2f, 1.0f}); ImGui::PushFont(boldFont); if (ImGui::Button("Y", buttonSize)) { values.y = resetValue; isChanged = true; } ImGui::PopFont(); ImGui::PopStyleColor(3); ImGui::SameLine(); if (ImGui::DragFloat("##Y", &values.y, 0.1f)) { isChanged = true; } ImGui::PopItemWidth(); ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.1f, 0.25f, 0.8f, 1.0f}); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4{0.2f, 0.35f, 0.9f, 1.0f}); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.1f, 0.25f, 0.8f, 1.0f}); ImGui::PushFont(boldFont); if (ImGui::Button("Z", buttonSize)) { values.z = resetValue; isChanged = true; } ImGui::PopFont(); ImGui::PopStyleColor(3); ImGui::SameLine(); if (ImGui::DragFloat("##Z", &values.z, 0.1f)) { isChanged = true; } ImGui::PopItemWidth(); ImGui::PopStyleVar(); ImGui::Columns(1); ImGui::PopID(); return isChanged; } template void DrawComponent(const std::string& name, Entity entity, UIfunction uiFunction) { constexpr ImGuiTreeNodeFlags treeNodeFlags = ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_FramePadding;; if (entity.HasComponent()) { ImGui::PushID(&entity); auto& component = entity.GetComponent(); ImVec2 contextReginAvail = ImGui::GetContentRegionAvail(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {4.0f, 4.0f}); auto context = ImGui::GetCurrentContext(); float lineHeight = context->Font->FontSize + context->Style.FramePadding.y * 2.0f; ImGui::Separator(); bool open = ImGui::TreeNodeEx((void*)(uint64_t)typeid(T).hash_code(), treeNodeFlags, name.c_str()); ImGui::PopStyleVar(); ImGui::SameLine(contextReginAvail.x - lineHeight * 0.5f); if (ImGui::Button("+", ImVec2(lineHeight, lineHeight))) { ImGui::OpenPopup("ComponentSettings"); } bool removeComponent = false; if (ImGui::BeginPopup("ComponentSettings")) { if (ImGui::MenuItem("Remove Component")) removeComponent = true; ImGui::EndPopup(); } if (open) { uiFunction(component); ImGui::TreePop(); } if (removeComponent) entity.RemoveComponent(); ImGui::PopID(); } } void SceneHierachyPanel::DrawComponents(Entity entity) { if (entity.HasComponent()) { auto& tag = entity.GetComponent().Tag; char buffer[256] = {}; strcpy_s(buffer,sizeof(buffer), tag.c_str()); if (ImGui::InputText("Tag", buffer, sizeof(buffer))) { tag = std::string(buffer); } } ImGui::SameLine(); ImGui::PushItemWidth(-1); if (ImGui::Button("Add")) ImGui::OpenPopup("Add"); if (ImGui::BeginPopup("Add")) { if (ImGui::MenuItem("Camera")) { m_SelectionContext.AddComponent(); ImGui::CloseCurrentPopup(); } if (ImGui::MenuItem("Sprite")) { m_SelectionContext.AddComponent(); ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } ImGui::PopItemWidth(); DrawComponent("Transform", entity, [](auto& component) { DrawVec3Control("Translation", component.Translation); glm::vec3 rotation = glm::degrees(component.Rotation); if (DrawVec3Control("Rotation", rotation)) { component.Rotation = glm::radians(rotation); } DrawVec3Control("Scale", component.Scale, 1.0f); }); DrawComponent("Camera", entity, [](auto& component) { auto& camera = component.Camera; ImGui::Checkbox("isPrimary", &component.Primary); static const char* projectionTypeStrings[] = { "Perspective", "Orthographic" }; const char* currentProjectionTypeString = projectionTypeStrings[(int)camera.GetProjectionType()]; if (ImGui::BeginCombo("Projection", currentProjectionTypeString)) { for (int i = 0; i < 2; i++) { const bool isSelected = currentProjectionTypeString == projectionTypeStrings[i]; if (ImGui::Selectable(projectionTypeStrings[i], isSelected)) { currentProjectionTypeString = projectionTypeStrings[i]; camera.SetProjectionType(static_cast(i)); } if (isSelected) { ImGui::SetItemDefaultFocus(); } } ImGui::EndCombo(); } if (camera.GetProjectionType() == ScenceCamera::ProjectionType::Perspective) { float fov = glm::degrees(camera.GetPerspectiveVerticalFOV()); if (ImGui::DragFloat("FOV", &fov, 0.1f)) camera.SetPerspectiveVerticalFOV(glm::radians(fov)); float near = camera.GetPerspectiveNearCLip(); if (ImGui::DragFloat("Near", &near)) camera.SetPerspectiveNearClip(near); float far = camera.GetPerspectiveFarCLip(); if (ImGui::DragFloat("Far", &far)) camera.SetPerspectiveFarClip(far); } if (camera.GetProjectionType() == ScenceCamera::ProjectionType::Orthographic) { float size = camera.GetOrthographicSize(); if (ImGui::DragFloat("Size", &size)) camera.SetOrthographicSize(size); float near = camera.GetOrthographicNearCLip(); if (ImGui::DragFloat("Near", &near)) camera.SetOrthographicNearClip(near); float far = camera.GetOrthographicFarCLip(); if (ImGui::DragFloat("Far", &far)) camera.SetOrthographicFarClip(far); ImGui::Checkbox("Fixed Aspect Ratio", &component.FixedAspectRatio); } }); DrawComponent("Sprite Renderer", entity, [](auto& component) { ImGui::ColorEdit4("Color", glm::value_ptr(component.Color)); }); } }