Files
Hazel/Sandbox/src/Editor/Panels/SceneHierachyPanel.cpp

342 lines
11 KiB
C++

//
// Created by sfd on 25-5-29.
//
#include "SceneHierachyPanel.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <glm/gtc/type_ptr.hpp>
#include <Hazel/Scene/Components.h>
namespace Hazel
{
SceneHierachyPanel::SceneHierachyPanel(const Ref<Scene>& context)
{
SetContext(context);
}
void SceneHierachyPanel::SetContext(const Ref<Scene>& context)
{
m_Context = context;
m_SelectionContext = {};
}
void SceneHierachyPanel::OnImGuiRender()
{
ImGui::Begin("Scene Hierachy");
m_Context->m_Registry.view<entt::entity>().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<TagComponent>().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<typename T, typename UIfunction>
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<T>())
{
ImGui::PushID(&entity);
auto& component = entity.GetComponent<T>();
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<T>();
ImGui::PopID();
}
}
void SceneHierachyPanel::DrawComponents(Entity entity)
{
if (entity.HasComponent<TagComponent>())
{
auto& tag = entity.GetComponent<TagComponent>().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<CameraComponent>();
ImGui::CloseCurrentPopup();
}
if (ImGui::MenuItem("Sprite"))
{
m_SelectionContext.AddComponent<SpriteRendererComponent>();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::PopItemWidth();
DrawComponent<TransformComponent>("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<CameraComponent>("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<ScenceCamera::ProjectionType>(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<SpriteRendererComponent>("Sprite Renderer", entity, [](auto& component)
{
ImGui::ColorEdit4("Color", glm::value_ptr(component.Color));
});
}
}