From 766abbc0eca47f6e8fafb30bdb3fdfcaa34f1add Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Wed, 28 Sep 2022 07:43:37 +0800 Subject: [PATCH 01/32] #comment Added camera component and system --- SHADE_Engine/src/Camera/SHCameraComponent.cpp | 130 ++++++++++++++++++ SHADE_Engine/src/Camera/SHCameraComponent.h | 85 ++++++++++++ SHADE_Engine/src/Camera/SHCameraSystem.cpp | 95 +++++++++++++ SHADE_Engine/src/Camera/SHCameraSystem.h | 40 ++++++ .../src/ECS_Base/Components/SHComponent.h | 5 +- 5 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 SHADE_Engine/src/Camera/SHCameraComponent.cpp create mode 100644 SHADE_Engine/src/Camera/SHCameraComponent.h create mode 100644 SHADE_Engine/src/Camera/SHCameraSystem.cpp create mode 100644 SHADE_Engine/src/Camera/SHCameraSystem.h diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.cpp b/SHADE_Engine/src/Camera/SHCameraComponent.cpp new file mode 100644 index 00000000..9b84081f --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraComponent.cpp @@ -0,0 +1,130 @@ +#include "SHpch.h" +#include "SHCameraComponent.h" + + +namespace SHADE +{ + SHCameraComponent::SHCameraComponent() + :yaw(0.0f), pitch(0.0f), roll(0.0f) + , width(1920.0f), height(1080.0f), zNear(0.01f), zFar(10000.0f), fov(90.0f), movementSpeed(1.0f), turnSpeed(1.0f) + , perspProj(false), dirtyView(true), dirtyProj(true) + , viewMatrix(), projMatrix() + , position() + { + + } + + SHCameraComponent::~SHCameraComponent() + { + } + + void SHCameraComponent::SetYaw(float yaw) noexcept + { + this->yaw = yaw; + dirtyView = true; + } + + void SHCameraComponent::SetPitch(float pitch) noexcept + { + this->pitch = pitch; + dirtyView = true; + } + + void SHCameraComponent::SetRoll(float roll) noexcept + { + this->roll = roll; + dirtyView = true; + } + void SHCameraComponent::SetPositionX(float x) noexcept + { + position[0] = x; + dirtyView = true; + } + void SHCameraComponent::SetPositionY(float y) noexcept + { + position[1] = y; + dirtyView = true; + } + void SHCameraComponent::SetPositionZ(float z) noexcept + { + position[2] = z; + dirtyView = true; + } + void SHCameraComponent::SetPosition(float x,float y, float z) noexcept + { + position[0] = x; + position[1] = y; + position[2] = z; + dirtyView = true; + } + void SHCameraComponent::SetPosition(SHVec3& pos) noexcept + { + this->position = pos; + dirtyView = true; + } + + void SHCameraComponent::SetWidth(float width) noexcept + { + this->width = width; + dirtyProj = true; + } + + + void SHCameraComponent::SetHeight(float height) noexcept + { + this->height = height; + dirtyProj = true; + } + + void SHCameraComponent::SetNear(float znear) noexcept + { + this->zNear = znear; + dirtyProj = true; + } + + void SHCameraComponent::SetFar(float zFar) noexcept + { + this->zFar = zFar; + dirtyProj = true; + } + + void SHCameraComponent::SetFOV(float fov) noexcept + { + this->fov = fov; + dirtyProj = true; + } + + float SHCameraComponent::GetYaw() const noexcept + { + return yaw; + } + + float SHCameraComponent::GetPitch() const noexcept + { + return pitch; + } + float SHCameraComponent::GetRoll() const noexcept + { + return roll; + } + float SHCameraComponent::GetAspectRatio() const noexcept + { + return width/height; + } + + float SHCameraComponent::GetFOV() const noexcept + { + return fov; + } + + const SHMatrix& SHCameraComponent::GetViewMatrix() const noexcept + { + return viewMatrix; + } + + const SHMatrix& SHCameraComponent::GetProjMatrix() const noexcept + { + return projMatrix; + } + +} diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.h b/SHADE_Engine/src/Camera/SHCameraComponent.h new file mode 100644 index 00000000..acfc9740 --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraComponent.h @@ -0,0 +1,85 @@ +#pragma once + +#include "ECS_Base/Components/SHComponent.h" +#include "Math/Vector/SHVec3.h" +#include "Math/SHMatrix.h" +#include "SH_API.h" + +namespace SHADE +{ + + class SH_API SHCameraComponent : public SHComponent + { + private: + + float yaw; + float pitch; + float roll; + + float width; + float height; + float zNear; + float zFar; + float fov; + + bool dirtyView; + bool dirtyProj; + + + SHMatrix viewMatrix; + SHMatrix projMatrix; + SHVec3 position; + + bool perspProj; + + + + + public: + friend class SHCameraSystem; + + SHCameraComponent(); + ~SHCameraComponent(); + + + //Getters and setters. + void SetYaw(float yaw) noexcept; + void SetPitch(float pitch) noexcept; + void SetRoll(float roll) noexcept; + void SetPositionX(float x) noexcept; + void SetPositionY(float y) noexcept; + void SetPositionZ(float z) noexcept; + void SetPosition(float x, float y, float z) noexcept; + void SetPosition(SHVec3& pos) noexcept; + + void SetWidth(float width) noexcept; + void SetHeight(float height) noexcept; + void SetNear(float znear) noexcept; + void SetFar(float zfar) noexcept; + void SetFOV(float fov) noexcept; + + + + float GetYaw() const noexcept; + float GetPitch() const noexcept; + float GetRoll() const noexcept; + + float GetAspectRatio() const noexcept; + float GetFOV() const noexcept; + + const SHMatrix& GetViewMatrix() const noexcept; + const SHMatrix& GetProjMatrix() const noexcept; + + + float movementSpeed; + SHVec3 turnSpeed; + + protected: + + + + + }; + + +} diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp new file mode 100644 index 00000000..24d96028 --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -0,0 +1,95 @@ +#include "SHpch.h" +#include "SHCameraSystem.h" +#include "Math/SHMathHelpers.h" +#include "Input/SHInputManagerSystem.h" + + + +namespace SHADE +{ + + void SHCameraSystem::EditorCameraUpdate::Execute(double dt) noexcept + { + SHCameraSystem* system = static_cast(GetSystem()); + + if (SHInputManagerSystem::GetKey(SHInputManagerSystem::SH_KEYCODE::A)) + { + system->editorCamera.SetPositionX(system->editorCamera.position[0] - dt * system->editorCamera.movementSpeed); + + } + system->UpdateCameraComponent(system->editorCamera); + } + + void SHCameraSystem::UpdateCameraComponent(SHCameraComponent& camera) noexcept + { + if (camera.dirtyView) + { + SHVec3 target{ 0.0f,0.0f,1.0f }; + SHVec3 up = { 0.0f,1.0f,0.0f }; + + SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); + SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); + //SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); + + target = SHVec3::Normalise(target); + + SHVec3::RotateZ(up, camera.roll); + up = SHVec3::Normalise(up); + + + SHVec3 view = target - camera.position; view = SHVec3::Normalise(view); + SHVec3 right = SHVec3::Cross(view, up); right = SHVec3::Normalise(right); + const SHVec3 UP = SHVec3::Cross(right, view); + + camera.viewMatrix = SHMatrix::Identity; + camera.viewMatrix(0, 0) = UP[0]; + camera.viewMatrix(1, 0) = UP[1]; + camera.viewMatrix(2, 0) = UP[2]; + camera.viewMatrix(0, 1) = right[0]; + camera.viewMatrix(1, 1) = right[1]; + camera.viewMatrix(2, 1) = right[2]; + camera.viewMatrix(0, 2) = view[0]; + camera.viewMatrix(1, 2) = view[1]; + camera.viewMatrix(2, 2) = view[2]; + camera.viewMatrix(3, 0) = -UP.Dot(camera.position); + camera.viewMatrix(3, 1) = -right.Dot(camera.position); + camera.viewMatrix(3, 2) = -view.Dot(camera.position); + + camera.dirtyView = false; + } + if (camera.dirtyProj == true) + { + if (camera.perspProj == true) + { + const float ASPECT_RATIO = camera.GetAspectRatio(); + const float TAN_HALF_FOV = tan(camera.fov * 0.5f); + camera.projMatrix = SHMatrix::Identity; + camera.projMatrix(0, 0) = 1.0f / (ASPECT_RATIO * TAN_HALF_FOV); + camera.projMatrix(1, 1) = 1.0f / TAN_HALF_FOV; + camera.projMatrix(2, 2) = camera.zFar / (camera.zFar - camera.zNear); + camera.projMatrix(2, 3) = 1.0f; + camera.projMatrix(3, 2) = -(camera.zFar * camera.zNear) / (camera.zFar - camera.zNear); + + camera.dirtyProj = false; + } + else + { + const float R = camera.width * 0.5f; + const float L = -R; + const float T = camera.height * 0.5f; + const float B = -T; + + camera.projMatrix = SHMatrix::Identity; + camera.projMatrix(0, 0) = 2.0f / (R - L); + camera.projMatrix(1, 1) = 2.0f / (B - T); + camera.projMatrix(2, 2) = 1.0f / (camera.zFar - camera.zNear); + camera.projMatrix(3, 0) = -(R + L) / (R - L); + camera.projMatrix(3, 1) = -(B + T) / (B - T); + camera.projMatrix(3, 2) = -camera.zNear / (camera.zFar - camera.zNear); + + camera.dirtyProj = false; + } + } + } + +} diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h new file mode 100644 index 00000000..7742078d --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ECS_Base/System/SHSystem.h" +#include "SHCameraComponent.h" +#include "ECS_Base/System/SHSystemRoutine.h" + + +namespace SHADE +{ + class SHCameraSystem :public SHSystem + { + private: + //A camera component that represents editor camera. + //This is not tied to any entity. Hence this EID should not be used. + SHCameraComponent editorCamera; + + + + public: + + class EditorCameraUpdate final : public SHSystemRoutine + { + public: + + EditorCameraUpdate() : SHSystemRoutine("Editor Camera Update", true) { }; + virtual void Execute(double dt) noexcept override final; + + }; + friend class EditorCameraUpdate; + + + protected: + + void UpdateCameraComponent(SHCameraComponent& camera) noexcept; + + }; + + + +} diff --git a/SHADE_Engine/src/ECS_Base/Components/SHComponent.h b/SHADE_Engine/src/ECS_Base/Components/SHComponent.h index aba3ba51..865f3078 100644 --- a/SHADE_Engine/src/ECS_Base/Components/SHComponent.h +++ b/SHADE_Engine/src/ECS_Base/Components/SHComponent.h @@ -47,6 +47,9 @@ namespace SHADE { } + + + public: //Whether or not this component is active. //Systems using this component should are responsible for checking the active state of the component before running their functionality. @@ -58,7 +61,7 @@ namespace SHADE * \return uint32_t * The entityID that this component belongs to. ***************************************************************************/ - uint32_t GetEID()const + uint32_t GetEID()const noexcept { return this->entityID; } From 228868604a0f7fbdb7a1ac1bf88bad3a3325c81f Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Sun, 16 Oct 2022 14:20:35 +0800 Subject: [PATCH 02/32] Editor fixes Integration of mouse pick Add editor state Add editor layout --- Assets/Editor/Layouts/Default.ini | 42 +++++++++ .../src/Application/SBApplication.cpp | 17 +++- .../HierarchyPanel/SHHierarchyPanel.cpp | 31 ++++++- .../HierarchyPanel/SHHierarchyPanel.h | 2 + .../Inspector/SHEditorComponentView.hpp | 9 +- .../Inspector/SHEditorInspector.cpp | 2 +- .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 88 +++++++++++++++---- .../EditorWindow/MenuBar/SHEditorMenuBar.h | 1 + .../Profiling/SHEditorProfiler.cpp | 2 +- .../Editor/EditorWindow/SHEditorWindow.cpp | 16 +++- .../src/Editor/EditorWindow/SHEditorWindow.h | 7 +- .../EditorWindow/SHEditorWindowIncludes.h | 3 +- .../ViewportWindow/SHEditorViewport.cpp | 45 ++++++++++ .../ViewportWindow/SHEditorViewport.h | 28 ++++++ SHADE_Engine/src/Editor/SHEditor.cpp | 83 +++++++++++++---- SHADE_Engine/src/Editor/SHEditor.hpp | 21 ++++- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 8 +- .../MiddleEnd/Interface/SHMousePickSystem.cpp | 4 +- .../MiddleEnd/Interface/SHMousePickSystem.h | 2 +- 19 files changed, 349 insertions(+), 62 deletions(-) create mode 100644 Assets/Editor/Layouts/Default.ini create mode 100644 SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp create mode 100644 SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h diff --git a/Assets/Editor/Layouts/Default.ini b/Assets/Editor/Layouts/Default.ini new file mode 100644 index 00000000..b953b3a5 --- /dev/null +++ b/Assets/Editor/Layouts/Default.ini @@ -0,0 +1,42 @@ +[Window][MainStatusBar] +Pos=0,1060 +Size=1920,20 +Collapsed=0 + +[Window][SHEditorMenuBar] +Pos=0,24 +Size=1920,1036 +Collapsed=0 + +[Window][Hierarchy Panel] +Pos=0,89 +Size=286,971 +Collapsed=0 +DockId=0x00000004,0 + +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Inspector] +Pos=1494,24 +Size=426,1036 +Collapsed=0 +DockId=0x00000006,0 + +[Window][Profiler] +Pos=0,24 +Size=286,63 +Collapsed=0 +DockId=0x00000003,0 + +[Docking][Data] +DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,55 Size=1920,1036 Split=X + DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1492,1036 Split=X + DockNode ID=0x00000001 Parent=0x00000005 SizeRef=286,1036 Split=Y Selected=0xE096E5AE + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=140,63 Selected=0x1E6EB881 + DockNode ID=0x00000004 Parent=0x00000001 SizeRef=140,971 Selected=0xE096E5AE + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1204,1036 CentralNode=1 + DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=426,1036 Selected=0xE7039252 + diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 202a3852..d1d3e087 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -130,18 +130,29 @@ namespace Sandbox void SBApplication::Update(void) { SHGraphicsSystem* graphicsSystem = SHADE::SHSystemManager::GetSystem(); + SHEditor* editor = SHADE::SHSystemManager::GetSystem(); //TODO: Change true to window is open while (!window.WindowShouldClose()) { SHFrameRateController::UpdateFRC(); SHInputManager::UpdateInput(SHFrameRateController::GetRawDeltaTime()); SHSceneManager::UpdateSceneManager(); - SHSceneManager::SceneUpdate(0.016f); +#ifdef SHEDITOR + if(editor->editorState == SHEditor::State::PLAY) +#endif + SHSceneManager::SceneUpdate(0.016f); +#ifdef SHEDITOR + SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, 0.016f); + if(auto editor = SHSystemManager::GetSystem()) + { + editor->PollPicking(); + } +#else SHSystemManager::RunRoutines(false, 0.016f); +#endif } - // Finish all graphics jobs first - graphicsSystem->AwaitGraphicsExecution(); + graphicsSystem->AwaitGraphicsExecution(); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 0f25175f..42c9da66 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -66,8 +66,8 @@ namespace SHADE editor->selectedEntities.clear(); } ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal); - ImGui::End(); } + ImGui::End(); } void SHHierarchyPanel::Exit() @@ -75,6 +75,11 @@ namespace SHADE SHEditorWindow::Exit(); } + void SHHierarchyPanel::SetScrollTo(EntityID eid) + { + scrollTo = eid; + } + //#==============================================================# //|| Private Member Functions || //#==============================================================# @@ -82,7 +87,20 @@ namespace SHADE { if (ImGui::BeginMenuBar()) { - if (ImGui::SmallButton(ICON_MD_ADD)) + + ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x - 35.0f); + if(ImGui::SmallButton(ICON_MD_DESELECT)) + { + auto editor = SHSystemManager::GetSystem(); + editor->selectedEntities.clear(); + } + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Clear Selections"); + ImGui::EndTooltip(); + } + if (ImGui::SmallButton(ICON_MD_ADD_CIRCLE)) { SHEntityManager::CreateEntity(); } @@ -103,6 +121,13 @@ namespace SHADE //Get node data (Children, eid, selected) auto& children = currentNode->GetChildren(); EntityID eid = currentNode->GetEntityID(); + + if(scrollTo != MAX_EID && eid == scrollTo) + { + ImGui::SetScrollHereY(); + scrollTo = MAX_EID; + } + auto editor = SHSystemManager::GetSystem(); const bool isSelected = (std::ranges::find(editor->selectedEntities, eid) != editor->selectedEntities.end()); @@ -157,7 +182,7 @@ namespace SHADE } ImGui::EndPopup(); } - + //Handle node selection if (ImGui::IsItemHovered()) { diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index 78e445fd..88983ca9 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -23,11 +23,13 @@ namespace SHADE void Init() override; void Update() override; void Exit() override; + void SetScrollTo(EntityID eid); private: void DrawMenuBar() const noexcept; ImRect RecursivelyDrawEntityNode(SHSceneNode*); void CreateChildEntity(EntityID parentEID) const noexcept; std::string filter; bool isAnyNodeSelected = false; + EntityID scrollTo = MAX_EID; };//class SHHierarchyPanel }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 93f6984e..47fec730 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -207,8 +207,10 @@ namespace SHADE auto& colliders = component->GetColliders(); int const size = static_cast(colliders.size()); ImGui::BeginChild("Colliders", {0.0f, colliders.empty() ? 1.0f : 250.0f}, true); + std::optional colliderToDelete{std::nullopt}; for (int i{}; i < size; ++i) { + ImGui::PushID(i); SHCollider& collider = component->GetCollider(i); auto cursorPos = ImGui::GetCursorPos(); @@ -235,9 +237,14 @@ namespace SHADE } if(ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data())) { - component->RemoveCollider(i); + colliderToDelete = i; } SHEditorWidgets::EndPanel(); + ImGui::PopID(); + } + if(colliderToDelete.has_value()) + { + component->RemoveCollider(colliderToDelete.value()); } ImGui::EndChild(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index a9b1c724..da09f345 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -99,8 +99,8 @@ namespace SHADE } } - ImGui::End(); } + ImGui::End(); } void SHEditorInspector::Exit() diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index a49af994..3cb6561d 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -36,6 +36,11 @@ namespace SHADE void SHEditorMenuBar::Init() { SHEditorWindow::Init(); + constexpr std::string_view path = "../../Assets/Editor/Layouts"; + for(auto const& entry : std::filesystem::directory_iterator(path)) + { + layoutPaths.push_back(entry.path()); + } } void SHEditorMenuBar::Update() @@ -87,20 +92,6 @@ namespace SHADE ImGui::EndDisabled(); ImGui::EndMenu(); } - if(ImGui::BeginMenu("Theme")) - { - auto styles = rttr::type::get().get_enumeration(); - auto values = styles.get_values(); - for (auto style : values) - { - if(ImGui::Selectable(style.to_string().c_str())) - { - if(auto editor = SHSystemManager::GetSystem()) - editor->SetStyle(style.convert()); - } - } - ImGui::EndMenu(); - } if (ImGui::BeginMenu("Scripts")) { if (ImGui::Selectable("Generate Visual Studio Project")) @@ -120,18 +111,79 @@ namespace SHADE } ImGui::EndMenu(); } + + if (ImGui::BeginMenu("Window")) + { + for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values) + { + if (window.get() != this) + ImGui::Checkbox(window->windowName.data(), &window->isOpen); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Theme")) + { + const auto styles = rttr::type::get().get_enumeration(); + auto values = styles.get_values(); + for (auto style : values) + { + if (ImGui::Selectable(style.to_string().c_str())) + { + if (auto editor = SHSystemManager::GetSystem()) + editor->SetStyle(style.convert()); + } + } + ImGui::EndMenu(); + } + if(ImGui::BeginMenu("Layout")) + { + for(auto const& entry : layoutPaths) + { + if(ImGui::Selectable(entry.stem().string().c_str())) + { + ImGui::LoadIniSettingsFromDisk(entry.string().c_str()); + } + } + ImGui::EndMenu(); + } ImGui::EndMainMenuBar(); } - const ImGuiID dockspace_id = ImGui::GetID("DockSpace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspaceFlags); + const ImGuiID dockspaceId = ImGui::GetID("DockSpace"); + ImGui::DockSpace(dockspaceId, ImVec2(0.0f, 0.0f), dockspaceFlags); ImGui::End(); } } void SHEditorMenuBar::DrawSecondaryBar() const noexcept { - + ImGuiViewport* viewport = ImGui::GetMainViewport(); + if(ImGui::BeginViewportSideBar("##SecondaryMenuBar", viewport, ImGuiDir_Up, ImGui::GetFrameHeight(), ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_MenuBar)) + { + ImGui::BeginMenuBar(); + ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x * 0.5f - 80.f); + const auto editor = SHSystemManager::GetSystem(); + ImGui::BeginDisabled(editor->editorState == SHEditor::State::PLAY); + if(ImGui::SmallButton(ICON_MD_PLAY_ARROW)) + { + editor->editorState = SHEditor::State::PLAY; + } + ImGui::EndDisabled(); + ImGui::BeginDisabled(editor->editorState == SHEditor::State::PAUSE); + if(ImGui::SmallButton(ICON_MD_PAUSE)) + { + editor->editorState = SHEditor::State::PAUSE; + } + ImGui::EndDisabled(); + ImGui::BeginDisabled(editor->editorState == SHEditor::State::STOP); + if(ImGui::SmallButton(ICON_MD_STOP)) + { + editor->editorState = SHEditor::State::STOP; + } + ImGui::EndDisabled(); + ImGui::EndMenuBar(); + } + ImGui::End(); } void SHEditorMenuBar::DrawStatusBar() const noexcept @@ -142,8 +194,8 @@ namespace SHADE if (ImGui::BeginViewportSideBar("MainStatusBar", ImGui::GetMainViewport(), ImGuiDir_Down, menuBarHeight, editorMenuBarFlags)) { ImGui::Text("Entity count: "); - ImGui::End(); } + ImGui::End(); ImGui::PopStyleVar(3); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h index 616ba43e..7cbcd696 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h @@ -18,5 +18,6 @@ namespace SHADE void DrawSecondaryBar() const noexcept; void DrawStatusBar() const noexcept; float menuBarHeight = 20.0f; + std::vector layoutPaths; };//class SHEditorMenuBar }//namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/Profiling/SHEditorProfiler.cpp b/SHADE_Engine/src/Editor/EditorWindow/Profiling/SHEditorProfiler.cpp index 4b36fe5d..ef2ff8c0 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Profiling/SHEditorProfiler.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Profiling/SHEditorProfiler.cpp @@ -37,8 +37,8 @@ namespace SHADE if(Begin()) { ImGui::PlotLines("DT", frames.data(), static_cast(frames.size()), 0, nullptr, 0.0f, 16.0f); - ImGui::End(); } + ImGui::End(); } void SHEditorProfiler::Exit() diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp index 2e2c820c..8f677be6 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp @@ -19,7 +19,7 @@ namespace SHADE //|| Public Member Functions || //#==============================================================# SHEditorWindow::SHEditorWindow(std::string_view const& name, ImGuiWindowFlags const& inFlags) - : isOpen(true), windowName(name), windowFlags(inFlags), io(ImGui::GetIO()) + : windowName(name), windowFlags(inFlags), io(ImGui::GetIO()) { } @@ -40,7 +40,19 @@ namespace SHADE //#==============================================================# bool SHEditorWindow::Begin() { - return ImGui::Begin(windowName.data(), &isOpen, windowFlags); + bool result = ImGui::Begin(windowName.data(), &isOpen, windowFlags); + + auto wndSize = ImGui::GetWindowSize(); + if(size.x != wndSize.x || size.y != wndSize.y) + { + size = {wndSize.x, wndSize.y}; + OnResize(); + } + return result; + } + + void SHEditorWindow::OnResize() + { } }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h index 244ef677..78e1b6cb 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h @@ -5,6 +5,8 @@ //#==============================================================# #include +#include "Math/Vector/SHVec2.h" + //#==============================================================# //|| Forward Declarations || //#==============================================================# @@ -21,11 +23,14 @@ namespace SHADE virtual void Init(); virtual void Update(); virtual void Exit(); - bool isOpen = false; + bool isOpen; std::string_view windowName; protected: virtual bool Begin(); + virtual void OnResize(); ImGuiWindowFlags windowFlags = 0; ImGuiIO& io; + SHVec2 size; + SHVec2 pos; };//class SHEditorWindow }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h index d1ebfbf4..f0267b06 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h @@ -2,4 +2,5 @@ #include "MenuBar/SHEditorMenuBar.h" //Menu Bar #include "HierarchyPanel/SHHierarchyPanel.h" //Hierarchy Panel #include "Inspector/SHEditorInspector.h" //Inspector -#include "Profiling/SHEditorProfiler.h" //Profiler \ No newline at end of file +#include "Profiling/SHEditorProfiler.h" //Profiler +#include "ViewportWindow/SHEditorViewport.h" //Editor Viewport \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp new file mode 100644 index 00000000..99ca5067 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp @@ -0,0 +1,45 @@ +#include "SHpch.h" +#include "SHEditorViewport.h" + +namespace SHADE +{ + SHEditorViewport::SHEditorViewport() + :SHEditorWindow("Viewport", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar) + { + } + + void SHEditorViewport::Init() + { + SHEditorWindow::Init(); + + } + + void SHEditorViewport::Update() + { + SHEditorWindow::Update(); + if(Begin()) + { + DrawMenuBar(); + } + ImGui::End(); + } + + void SHEditorViewport::Exit() + { + SHEditorWindow::Exit(); + } + + void SHEditorViewport::OnResize() + { + SHEditorWindow::OnResize(); + //Get graphics system to resize swapchain image + } + + void SHEditorViewport::DrawMenuBar() const noexcept + { + if(ImGui::BeginMenuBar()) + { + ImGui::EndMenuBar(); + } + } +}//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h new file mode 100644 index 00000000..baee0941 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h @@ -0,0 +1,28 @@ +#pragma once +//#==============================================================# +//|| Library Includes || +//#==============================================================# +#include + +//#==============================================================# +//|| SHADE Includes || +//#==============================================================# +#include "imgui_internal.h" +#include "ECS_Base/SHECSMacros.h" +#include "Editor/EditorWindow/SHEditorWindow.h" + +namespace SHADE +{ + class SHEditorViewport final : public SHEditorWindow + { + public: + SHEditorViewport(); + void Init() override; + void Update() override; + void Exit() override; + protected: + virtual void OnResize() override; + private: + void DrawMenuBar() const noexcept; + };//class SHEditorViewport +}//namespace SHADE diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index d9fc8373..d0bf0c4f 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -43,6 +43,8 @@ #include #include +#include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" + RTTR_REGISTRATION { using namespace SHADE; @@ -73,6 +75,7 @@ namespace SHADE //#==============================================================# void SHEditor::Init() { + IMGUI_CHECKVERSION(); if(auto context = ImGui::CreateContext()) { @@ -82,11 +85,21 @@ namespace SHADE } } - ImGuiIO& io = ImGui::GetIO(); (void)io; + //Add editor windows + SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //Enable for Multi-Viewports - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; //Enable docking + io = &ImGui::GetIO(); + + io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //Enable for Multi-Viewports + io->ConfigFlags |= ImGuiConfigFlags_DockingEnable; //Enable docking + io->IniFilename = "../../Assets/Editor/Layouts/UserLayout.ini"; + + InitLayout(); InitFonts(); @@ -98,11 +111,11 @@ namespace SHADE SetStyle(Style::SHADE); - //Add editor windows - SHEditorWindowManager::CreateEditorWindow(); - SHEditorWindowManager::CreateEditorWindow(); - SHEditorWindowManager::CreateEditorWindow(); - SHEditorWindowManager::CreateEditorWindow(); + + for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values) + { + window->Init(); + } SHLOG_INFO("Successfully initialised SHADE Engine Editor") } @@ -116,7 +129,7 @@ namespace SHADE if(window->isOpen) window->Update(); } - + if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z)) { SHCommandManager::RedoCommand(); @@ -125,36 +138,46 @@ namespace SHADE { SHCommandManager::UndoCommand(); } - - + Render(); } void SHEditor::Render() { ImGui::Render(); - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + if (io->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); } } + void SHEditor::InitLayout() noexcept + { + if(!std::filesystem::exists(io->IniFilename)) + { + std::filesystem::copy_file("../../Assets/Editor/Layouts/Default.ini", io->IniFilename); + } + //eventually load preferred layout here + } + void SHEditor::InitFonts() noexcept { - ImGuiIO& io = ImGui::GetIO(); - ImFont* mainFont = io.Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path + ImFont* mainFont = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path - static const ImWchar icon_ranges[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 }; + constexpr ImWchar icon_ranges[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 }; ImFontConfig icons_config{}; icons_config.MergeMode = true; icons_config.GlyphOffset.y = 5.f; - ImFont* UIFont = io.Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges); //TODO: Change to config based assets path + ImFont* UIFont = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges); //TODO: Change to config based assets path - io.Fonts->Build(); + io->Fonts->Build(); } void SHEditor::Exit() { + for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values) + { + window->Init(); + } ImGui_ImplVulkan_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); @@ -309,6 +332,28 @@ namespace SHADE }); } + void SHEditor::PollPicking() + { + if (auto gfxSystem = SHSystemManager::GetSystem()) + { + if (!ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) + { + EntityID pickedEID = gfxSystem->GetMousePickSystem()->GetPickedEntity(); + if(pickedEID == MAX_EID) + return; + if (!ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + { + if (const auto hierarchyPanel = SHEditorWindowManager::GetEditorWindow()) + { + hierarchyPanel->SetScrollTo(pickedEID); + } + selectedEntities.clear(); + } + selectedEntities.push_back(pickedEID); + } + } + } + void SHEditor::NewFrame() { SDL_Event event; diff --git a/SHADE_Engine/src/Editor/SHEditor.hpp b/SHADE_Engine/src/Editor/SHEditor.hpp index d579a9b5..baa6324e 100644 --- a/SHADE_Engine/src/Editor/SHEditor.hpp +++ b/SHADE_Engine/src/Editor/SHEditor.hpp @@ -90,11 +90,11 @@ namespace SHADE return reinterpret_cast(editorWindows[GetEditorWindowID()].get()); } + static EditorWindowMap editorWindows; private: // Number of windows; used for Editor Window ID Generation static EditorWindowID windowCount; // Map of Editor Windows - static EditorWindowMap editorWindows; friend class SHEditor; }; @@ -110,10 +110,17 @@ namespace SHADE class SH_API EditorRoutine final : public SHSystemRoutine { public: - EditorRoutine() = default; + EditorRoutine():SHSystemRoutine("Editor routine", true) {}; void Execute(double dt) noexcept override final; }; + enum class State : uint8_t + { + PLAY, + PAUSE, + STOP + }; + /** * @brief Style options * @@ -162,9 +169,13 @@ namespace SHADE void SetSDLWindow(SDL_Window* inSDLWindow){sdlWindow = inSDLWindow;}; + void PollPicking(); + // List of selected entities std::vector selectedEntities; + State editorState = State::STOP; + private: /** * @brief Start new frame for editor @@ -177,7 +188,7 @@ namespace SHADE */ void Render(); - + void InitLayout() noexcept; void InitFonts() noexcept; @@ -186,6 +197,8 @@ namespace SHADE // Handle to command buffer used for ImGui Vulkan Backend Handle imguiCommandBuffer; - SDL_Window* sdlWindow; + SDL_Window* sdlWindow {nullptr}; + + ImGuiIO* io{nullptr}; };//class SHEditor }//namespace SHADE diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 09f1c93e..c31b8c4d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -571,7 +571,7 @@ namespace SHADE /* System Routine Functions - BeginRoutine */ /*-----------------------------------------------------------------------------------*/ SHGraphicsSystem::BeginRoutine::BeginRoutine() - : SHSystemRoutine("Graphics System Frame Set Up", false) + : SHSystemRoutine("Graphics System Frame Set Up", true) {} void SHGraphicsSystem::BeginRoutine::Execute(double) noexcept @@ -583,7 +583,7 @@ namespace SHADE /* System Routine Functions - RenderRoutine */ /*-----------------------------------------------------------------------------------*/ SHGraphicsSystem::RenderRoutine::RenderRoutine() - : SHSystemRoutine("Graphics System Render", false) + : SHSystemRoutine("Graphics System Render", true) {} void SHGraphicsSystem::RenderRoutine::Execute(double dt) noexcept @@ -595,7 +595,7 @@ namespace SHADE /* System Routine Functions - EndRoutine */ /*-----------------------------------------------------------------------------------*/ SHGraphicsSystem::EndRoutine::EndRoutine() - : SHSystemRoutine("Graphics System Frame Clean Up", false) + : SHSystemRoutine("Graphics System Frame Clean Up", true) {} void SHGraphicsSystem::EndRoutine::Execute(double) noexcept @@ -607,7 +607,7 @@ namespace SHADE /* System Routine Functions - BatcherDispatcherRoutine */ /*-----------------------------------------------------------------------------------*/ SHGraphicsSystem::BatcherDispatcherRoutine::BatcherDispatcherRoutine() - : SHSystemRoutine("Graphics System Batcher Dispatcher", false) + : SHSystemRoutine("Graphics System Batcher Dispatcher", true) {} void SHGraphicsSystem::BatcherDispatcherRoutine::Execute(double) noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp index ee8665d5..8522a8d5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp @@ -11,8 +11,6 @@ namespace SHADE { void SHMousePickSystem::Init(Handle logicalDevice, std::span> cmdPools, Handle eidAttachment) noexcept { - pickedEID = 0; - // Create command buffers for (auto& pool : cmdPools) { @@ -34,7 +32,7 @@ namespace SHADE void SHMousePickSystem::Run(Handle queue, uint32_t frameIndex) noexcept { // if input detected - if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::LEFT_CTRL) && SHInputManager::GetKeyUp(SHInputManager::SH_KEYCODE::LMB)) + if (SHInputManager::GetKeyUp(SHInputManager::SH_KEYCODE::LMB)) { afterCopyFence->Reset(); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.h index 080a192c..7ec193b4 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.h @@ -30,7 +30,7 @@ namespace SHADE Handle imageDataDstBuffer; //! eid picked from screen - EntityID pickedEID; + EntityID pickedEID = MAX_EID; public: /*-----------------------------------------------------------------------*/ /* PUBLIC MEMBER FUNCTIONS */ From 7f2935dcf6b1795795fdcae856cf8be1ab8f34de Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Sun, 16 Oct 2022 15:36:14 +0800 Subject: [PATCH 03/32] add window pos change for editor windows add git ignore for user ini --- .gitignore | 2 + .../Editor/EditorWindow/SHEditorWindow.cpp | 14 +- .../src/Editor/EditorWindow/SHEditorWindow.h | 6 +- .../ViewportWindow/SHEditorViewport.cpp | 11 ++ .../ViewportWindow/SHEditorViewport.h | 3 +- SHADE_Engine/src/Editor/SHEditor.cpp | 156 +++++++++--------- 6 files changed, 109 insertions(+), 83 deletions(-) diff --git a/.gitignore b/.gitignore index 1110510c..5d998cfd 100644 --- a/.gitignore +++ b/.gitignore @@ -362,3 +362,5 @@ MigrationBackup/ *.csproj *.filters + +Assets/Editor/Layouts/UserLayout.ini diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp index 8f677be6..491c1bc2 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp @@ -43,16 +43,26 @@ namespace SHADE bool result = ImGui::Begin(windowName.data(), &isOpen, windowFlags); auto wndSize = ImGui::GetWindowSize(); - if(size.x != wndSize.x || size.y != wndSize.y) + if(windowSize.x != wndSize.x || windowSize.y != wndSize.y) { - size = {wndSize.x, wndSize.y}; + windowSize = {wndSize.x, wndSize.y}; OnResize(); } + auto wndPos = ImGui::GetWindowPos(); + if(windowPos.x != wndPos.x || windowPos.y != wndPos.y) + { + windowPos = {wndPos.x, wndPos.y}; + OnPosChange(); + } return result; } void SHEditorWindow::OnResize() { } + + void SHEditorWindow::OnPosChange() + { + } }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h index 78e1b6cb..3e7a2a5c 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h @@ -28,9 +28,11 @@ namespace SHADE protected: virtual bool Begin(); virtual void OnResize(); + virtual void OnPosChange(); + ImGuiWindowFlags windowFlags = 0; ImGuiIO& io; - SHVec2 size; - SHVec2 pos; + SHVec2 windowSize; + SHVec2 windowPos; };//class SHEditorWindow }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp index 99ca5067..60c91754 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp @@ -1,6 +1,9 @@ #include "SHpch.h" #include "SHEditorViewport.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" + namespace SHADE { SHEditorViewport::SHEditorViewport() @@ -20,6 +23,9 @@ namespace SHADE if(Begin()) { DrawMenuBar(); + auto gfxSystem = SHSystemManager::GetSystem(); + //auto const& descriptorSet = gfxSystem->GetPostOffscreenRenderSystem()->GetDescriptorSetGroup()->GetVkHandle()[0]; + //ImGui::Image((ImTextureID)descriptorSet, ImGui::GetWindowSize()); } ImGui::End(); } @@ -35,6 +41,11 @@ namespace SHADE //Get graphics system to resize swapchain image } + void SHEditorViewport::OnPosChange() + { + SHEditorWindow::OnPosChange(); + } + void SHEditorViewport::DrawMenuBar() const noexcept { if(ImGui::BeginMenuBar()) diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h index baee0941..5f4a5919 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h @@ -21,7 +21,8 @@ namespace SHADE void Update() override; void Exit() override; protected: - virtual void OnResize() override; + void OnResize() override; + void OnPosChange() override; private: void DrawMenuBar() const noexcept; };//class SHEditorViewport diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index d0bf0c4f..e7606337 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -190,86 +190,86 @@ namespace SHADE default: case Style::SHADE: { - ImGuiStyle& imStyle = ImGui::GetStyle(); - ImVec4* colors = imStyle.Colors; - colors[ImGuiCol_Text] = ImVec4(0.706f, 0.729f, 0.757f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.172f, 0.184f, 0.203f, 1.f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.19f, 0.19f, 0.19f, 0.92f); - colors[ImGuiCol_Border] = ImVec4(0.19f, 0.19f, 0.19f, 0.29f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.24f); - colors[ImGuiCol_FrameBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); - colors[ImGuiCol_TitleBg] = colors[ImGuiCol_WindowBg]; - colors[ImGuiCol_TitleBgActive] = colors[ImGuiCol_WindowBg]; - colors[ImGuiCol_TitleBgCollapsed] = colors[ImGuiCol_WindowBg]; - colors[ImGuiCol_MenuBarBg] = ImVec4(0.129f, 0.141f, 0.157f, 1.f); - colors[ImGuiCol_ScrollbarBg] = colors[ImGuiCol_WindowBg]; - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); - colors[ImGuiCol_CheckMark] = ImVec4(0.627f, 0.239f, 0.761f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); - colors[ImGuiCol_Button] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.15f, 0.15f, 0.15f, 0.54f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.00f, 0.00f, 0.00f, 0.36f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.22f, 0.23f, 0.33f); - colors[ImGuiCol_Separator] = colors[ImGuiCol_MenuBarBg]; - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); - colors[ImGuiCol_Tab] = colors[ImGuiCol_WindowBg]; - colors[ImGuiCol_TabHovered] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); - colors[ImGuiCol_TabActive] = ImVec4(0.14f, 0.14f, 0.14f, 0.8f); - colors[ImGuiCol_TabUnfocused] = colors[ImGuiCol_WindowBg]; - colors[ImGuiCol_TabUnfocusedActive] = colors[ImGuiCol_WindowBg]; - colors[ImGuiCol_DockingPreview] = ImVec4(0.627f, 0.239f, 0.761f, 1.00f); - colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.855f, 0.6f, 0.941f, 1.00f); - colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_TableHeaderBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_TableBorderStrong] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_TableBorderLight] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); - colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); - colors[ImGuiCol_DragDropTarget] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); - colors[ImGuiCol_NavHighlight] = ImVec4(0.73f, 0.73f, 0.73f, 0.7f); + ImGuiStyle& imStyle = ImGui::GetStyle(); + ImVec4* colors = imStyle.Colors; + colors[ImGuiCol_Text] = ImVec4(0.706f, 0.729f, 0.757f, 1.00f); + colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + colors[ImGuiCol_WindowBg] = ImVec4(0.172f, 0.184f, 0.203f, 1.f); + colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_PopupBg] = ImVec4(0.19f, 0.19f, 0.19f, 0.92f); + colors[ImGuiCol_Border] = ImVec4(0.19f, 0.19f, 0.19f, 0.29f); + colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.24f); + colors[ImGuiCol_FrameBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_TitleBg] = colors[ImGuiCol_WindowBg]; + colors[ImGuiCol_TitleBgActive] = colors[ImGuiCol_WindowBg]; + colors[ImGuiCol_TitleBgCollapsed] = colors[ImGuiCol_WindowBg]; + colors[ImGuiCol_MenuBarBg] = ImVec4(0.129f, 0.141f, 0.157f, 1.f); + colors[ImGuiCol_ScrollbarBg] = colors[ImGuiCol_WindowBg]; + colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); + colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f); + colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); + colors[ImGuiCol_CheckMark] = ImVec4(0.627f, 0.239f, 0.761f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); + colors[ImGuiCol_Button] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.15f, 0.15f, 0.15f, 0.54f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.00f, 0.00f, 0.00f, 0.36f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.22f, 0.23f, 0.33f); + colors[ImGuiCol_Separator] = colors[ImGuiCol_MenuBarBg]; + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); + colors[ImGuiCol_Tab] = colors[ImGuiCol_WindowBg]; + colors[ImGuiCol_TabHovered] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + colors[ImGuiCol_TabActive] = ImVec4(0.14f, 0.14f, 0.14f, 0.8f); + colors[ImGuiCol_TabUnfocused] = colors[ImGuiCol_WindowBg]; + colors[ImGuiCol_TabUnfocusedActive] = colors[ImGuiCol_WindowBg]; + colors[ImGuiCol_DockingPreview] = ImVec4(0.627f, 0.239f, 0.761f, 1.00f); + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.855f, 0.6f, 0.941f, 1.00f); + colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_TableHeaderBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TableBorderStrong] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TableBorderLight] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); + colors[ImGuiCol_TextSelectedBg] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_DragDropTarget] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + colors[ImGuiCol_NavHighlight] = ImVec4(0.73f, 0.73f, 0.73f, 0.7f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.141f, 0.141f, 0.141f, 0.70f); - colors[ImGuiCol_NavWindowingDimBg] = colors[ImGuiCol_NavHighlight]; - colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.2f, 0.2f, 0.2f, 0.65f); + colors[ImGuiCol_NavWindowingDimBg] = colors[ImGuiCol_NavHighlight]; + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.2f, 0.2f, 0.2f, 0.65f); - imStyle.WindowPadding = ImVec2(8.00f, 8.00f); - imStyle.FramePadding = ImVec2(5.00f, 2.00f); - imStyle.CellPadding = ImVec2(6.00f, 8.00f); - imStyle.ItemSpacing = ImVec2(6.00f, 6.00f); - imStyle.ItemInnerSpacing = ImVec2(6.00f, 6.00f); - imStyle.TouchExtraPadding = ImVec2(0.00f, 0.00f); - imStyle.IndentSpacing = 25; - imStyle.ScrollbarSize = 15; - imStyle.GrabMinSize = 10; - imStyle.WindowBorderSize = 0.6f; - imStyle.ChildBorderSize = 1; - imStyle.PopupBorderSize = 1; - imStyle.FrameBorderSize = 1; - imStyle.TabBorderSize = 1; - imStyle.WindowRounding = 7; - imStyle.ChildRounding = 4; - imStyle.FrameRounding = 3; - imStyle.PopupRounding = 4; - imStyle.ScrollbarRounding = 9; - imStyle.GrabRounding = 3; - imStyle.LogSliderDeadzone = 4; - imStyle.TabRounding = 4; + imStyle.WindowPadding = ImVec2(8.00f, 8.00f); + imStyle.FramePadding = ImVec2(5.00f, 2.00f); + imStyle.CellPadding = ImVec2(6.00f, 8.00f); + imStyle.ItemSpacing = ImVec2(6.00f, 6.00f); + imStyle.ItemInnerSpacing = ImVec2(6.00f, 6.00f); + imStyle.TouchExtraPadding = ImVec2(0.00f, 0.00f); + imStyle.IndentSpacing = 25; + imStyle.ScrollbarSize = 15; + imStyle.GrabMinSize = 10; + imStyle.WindowBorderSize = 0.6f; + imStyle.ChildBorderSize = 1; + imStyle.PopupBorderSize = 1; + imStyle.FrameBorderSize = 1; + imStyle.TabBorderSize = 1; + imStyle.WindowRounding = 7; + imStyle.ChildRounding = 4; + imStyle.FrameRounding = 3; + imStyle.PopupRounding = 4; + imStyle.ScrollbarRounding = 9; + imStyle.GrabRounding = 3; + imStyle.LogSliderDeadzone = 4; + imStyle.TabRounding = 4; imStyle.WindowMenuButtonPosition = ImGuiDir_None; } break; From 7fc417e35a3907b6d56736ca86d6937a9ada1b11 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Sun, 16 Oct 2022 18:16:05 +0800 Subject: [PATCH 04/32] Parent/child multiple entities (WIP) --- .../HierarchyPanel/SHHierarchyPanel.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 42c9da66..13f3b679 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -149,16 +149,21 @@ namespace SHADE if (SHDragDrop::BeginSource()) { ImGui::Text("Moving EID: %zu", eid); - SHDragDrop::SetPayload(DRAG_EID, &eid); + SHDragDrop::SetPayload>(DRAG_EID, &editor->selectedEntities); SHDragDrop::EndSource(); } else if (SHDragDrop::BeginTarget()) //If Received DragDrop { - if (const EntityID* eidPayload = SHDragDrop::AcceptPayload(DRAG_EID)) //If payload is valid + if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(DRAG_EID)) //If payload is valid { - EntityID const dropEID = *eidPayload; - if(!sceneGraph.GetChild(dropEID, eid)) - sceneGraph.SetParent(dropEID, eid); //Set dropEID parent to eid (belonging to current Node) + std::vector const droppedEIDs = *eidPayload; + for(auto const& dropEID : droppedEIDs) + { + if(!sceneGraph.GetChild(dropEID, eid)) + sceneGraph.SetParent(dropEID, eid); + } + //if(!sceneGraph.GetChild(dropEID, eid)) + // sceneGraph.SetParent(dropEID, eid); //Set dropEID parent to eid (belonging to current Node) SHDragDrop::EndTarget(); } } From ec6772657f965e5983c4a77d00bbad3621db038a Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Mon, 17 Oct 2022 21:25:59 +0800 Subject: [PATCH 05/32] Hierarchy QOL Undo/Redo Parent Child Shift select Undo/Redo create entity --- SHADE_Engine/src/Editor/Command/SHCommand.hpp | 1 + .../src/Editor/Command/SHCommandManager.cpp | 6 +- .../src/Editor/Command/SHCommandManager.h | 10 +- .../HierarchyPanel/SHHierarchyPanel.cpp | 125 ++++++++++++++++-- .../HierarchyPanel/SHHierarchyPanel.h | 34 ++++- 5 files changed, 155 insertions(+), 21 deletions(-) diff --git a/SHADE_Engine/src/Editor/Command/SHCommand.hpp b/SHADE_Engine/src/Editor/Command/SHCommand.hpp index 7a526506..149c8986 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommand.hpp +++ b/SHADE_Engine/src/Editor/Command/SHCommand.hpp @@ -24,6 +24,7 @@ namespace SHADE class SHCommand : SHBaseCommand { public: + using SHCommandPtr = std::unique_ptr; typedef std::function SetterFunction; SHCommand(T const& oldVal, T const& value, SetterFunction setFnc) diff --git a/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp b/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp index ee2d316d..7a965644 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp +++ b/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp @@ -13,7 +13,7 @@ namespace SHADE SHCommandManager::CommandStack SHCommandManager::undoStack{}; SHCommandManager::CommandStack SHCommandManager::redoStack{}; - void SHCommandManager::PerformCommand(CommandPtr commandPtr, bool const& overrideValue) + void SHCommandManager::PerformCommand(BaseCommandPtr commandPtr, bool const& overrideValue) { redoStack = CommandStack(); commandPtr->Execute(); @@ -27,9 +27,9 @@ namespace SHADE } } - void SHCommandManager::RegisterCommand(CommandPtr commandPtr) + void SHCommandManager::RegisterCommand(BaseCommandPtr commandPtr) { - undoStack.push(commandPtr); + undoStack.push(commandPtr); } void SHCommandManager::UndoCommand() diff --git a/SHADE_Engine/src/Editor/Command/SHCommandManager.h b/SHADE_Engine/src/Editor/Command/SHCommandManager.h index 9152c3cb..c1ceb4b0 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommandManager.h +++ b/SHADE_Engine/src/Editor/Command/SHCommandManager.h @@ -19,11 +19,13 @@ namespace SHADE //#==============================================================# //|| Type Aliases || //#==============================================================# - using CommandPtr = std::shared_ptr; - using CommandStack = std::stack; + using BaseCommandPtr = std::shared_ptr; + template + using SHCommandPtr = std::shared_ptr>; + using CommandStack = std::stack; - static void PerformCommand(CommandPtr commandPtr, bool const& overrideValue = false); - static void RegisterCommand(CommandPtr commandPtr); + static void PerformCommand(BaseCommandPtr commandPtr, bool const& overrideValue = false); + static void RegisterCommand(BaseCommandPtr commandPtr); static void UndoCommand(); static void RedoCommand(); static std::size_t GetUndoStackSize(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 13f3b679..c2316006 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -50,6 +50,7 @@ namespace SHADE if(const auto root = sceneGraph.GetRoot()) { auto const& children = root->GetChildren(); + for (const auto child : children) { RecursivelyDrawEntityNode(child); @@ -102,7 +103,7 @@ namespace SHADE } if (ImGui::SmallButton(ICON_MD_ADD_CIRCLE)) { - SHEntityManager::CreateEntity(); + SHCommandManager::PerformCommand(std::make_shared()); } if (ImGui::IsItemHovered()) { @@ -142,13 +143,24 @@ namespace SHADE auto* entity = SHEntityManager::GetEntityByID(currentNode->GetEntityID()); //Draw Node - bool isNodeOpen = ImGui::TreeNodeEx(reinterpret_cast(entity), nodeFlags, "%u: %s", EntityHandleGenerator::GetIndex(eid), entity->name.c_str()); + bool isNodeOpen = ImGui::TreeNodeEx(reinterpret_cast(entity), nodeFlags, "%u: %s", SHEntityManager::GetEntityIndex(eid), entity->name.c_str()); const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); //Check For Begin Drag if (SHDragDrop::BeginSource()) { - ImGui::Text("Moving EID: %zu", eid); + std::string moveLabel = "Moving EID: "; + if(!isSelected) + editor->selectedEntities.push_back(eid); + for(int i = 0; i < static_cast(editor->selectedEntities.size()); ++i) + { + moveLabel.append(std::to_string(editor->selectedEntities[i])); + if(i + 1 < static_cast(editor->selectedEntities.size())) + { + moveLabel.append(", "); + } + } + ImGui::Text(moveLabel.c_str()); SHDragDrop::SetPayload>(DRAG_EID, &editor->selectedEntities); SHDragDrop::EndSource(); } @@ -156,14 +168,7 @@ namespace SHADE { if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(DRAG_EID)) //If payload is valid { - std::vector const droppedEIDs = *eidPayload; - for(auto const& dropEID : droppedEIDs) - { - if(!sceneGraph.GetChild(dropEID, eid)) - sceneGraph.SetParent(dropEID, eid); - } - //if(!sceneGraph.GetChild(dropEID, eid)) - // sceneGraph.SetParent(dropEID, eid); //Set dropEID parent to eid (belonging to current Node) + ParentSelectedEntities(eid); SHDragDrop::EndTarget(); } } @@ -183,7 +188,7 @@ namespace SHADE if((currentNode->GetParent() != sceneGraph.GetRoot()) && ImGui::Selectable(std::format("{} Unparent Selected", ICON_MD_NORTH_WEST).data())) { - sceneGraph.SetParent(currentNode->GetEntityID(), nullptr); + ParentSelectedEntities(MAX_EID); } ImGui::EndPopup(); } @@ -195,7 +200,15 @@ namespace SHADE { if (!isSelected) { - if (!ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + if(ImGui::IsKeyDown(ImGuiKey_LeftShift)) + { + if(editor->selectedEntities.size() >= 1) + { + SelectRangeOfEntities(editor->selectedEntities[0], eid); + } + else editor->selectedEntities.clear(); + } + else if (!ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) editor->selectedEntities.clear(); editor->selectedEntities.push_back(eid); }//if not selected @@ -242,4 +255,90 @@ namespace SHADE { SHEntityManager::CreateEntity(MAX_EID, "DefaultChild", parentEID); } + + void SHHierarchyPanel::ParentSelectedEntities(EntityID parentEID) const noexcept + { + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + auto const editor = SHSystemManager::GetSystem(); + SHEntityParentCommand::EntityParentData entityParentData; + std::vector parentedEIDS; + for(auto const& eid : editor->selectedEntities) + { + if(sceneGraph.GetChild(eid, parentEID) == nullptr) + { + parentedEIDS.push_back(eid); + if(auto parent = sceneGraph.GetParent(eid)) + entityParentData[eid].oldParentEID = parent->GetEntityID(); + entityParentData[eid].newParentEID = parentEID; + } + } + SHCommandManager::PerformCommand(std::make_shared(parentedEIDS, entityParentData)); + } + + void SHHierarchyPanel::SelectRangeOfEntities(EntityID beginEID, EntityID endEID) + { + bool startSelecting = false; bool endSelecting = false; + auto const editor = SHSystemManager::GetSystem(); + editor->selectedEntities.clear(); + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + sceneGraph.Traverse([&](SHSceneNode* nodePtr) + { + auto eid = nodePtr->GetEntityID(); + if(!startSelecting) + { + if(eid == beginEID || eid == endEID) + { + startSelecting = true; + editor->selectedEntities.push_back(eid); + } + } + else + { + if(!endSelecting) + { + editor->selectedEntities.push_back(eid); + if(eid == endEID || eid == beginEID) + { + endSelecting = true; + } + } + } + }); + } + + void SHCreateEntityCommand::Execute() + { + EntityID newEID = SHEntityManager::CreateEntity(eid); + if(eid == MAX_EID) + eid = newEID; + } + + void SHCreateEntityCommand::Undo() + { + SHEntityManager::DestroyEntity(eid); + } + + void SHEntityParentCommand::Execute() + { + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + for(auto const& eid : entities) + { + if(entityParentData[eid].newParentEID == MAX_EID) + sceneGraph.SetParent(eid, nullptr); + else + sceneGraph.SetParent(eid, entityParentData[eid].newParentEID); + } + } + + void SHEntityParentCommand::Undo() + { + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + for(auto const& eid : entities) + { + if(entityParentData[eid].oldParentEID == MAX_EID) + sceneGraph.SetParent(eid, nullptr); + else + sceneGraph.SetParent(eid, entityParentData[eid].oldParentEID); + } + } }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index 88983ca9..0cfe6474 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -10,7 +10,7 @@ #include "imgui_internal.h" #include "ECS_Base/SHECSMacros.h" #include "Editor/EditorWindow/SHEditorWindow.h" - +#include "Editor/Command/SHCommand.hpp" namespace SHADE { class SHSceneNode; @@ -28,8 +28,40 @@ namespace SHADE void DrawMenuBar() const noexcept; ImRect RecursivelyDrawEntityNode(SHSceneNode*); void CreateChildEntity(EntityID parentEID) const noexcept; + void ParentSelectedEntities(EntityID parentEID) const noexcept; + void SelectRangeOfEntities(EntityID beginEID, EntityID EndEID); std::string filter; bool isAnyNodeSelected = false; EntityID scrollTo = MAX_EID; };//class SHHierarchyPanel + + //Might move to a different file + class SHCreateEntityCommand final : public SHBaseCommand + { + public: + void Execute() override; + void Undo() override; + private: + EntityID eid = MAX_EID; + }; + + class SHEntityParentCommand final : public SHBaseCommand + { + public: + struct Data + { + EntityID oldParentEID = MAX_EID; + EntityID newParentEID = MAX_EID; + }; + using EntityParentData = std::unordered_map; + + SHEntityParentCommand(std::vector entityIDs, EntityParentData inEntityParentData):entities(entityIDs),entityParentData(inEntityParentData){} + + void Execute() override; + void Undo() override; + private: + std::vector entities; + std::unordered_map entityParentData; + }; + }//namespace SHADE From 3a3f7efb29db2c8d629e6285e3922e63560043d9 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Wed, 19 Oct 2022 01:31:48 +0800 Subject: [PATCH 06/32] [WIP] Transform Gizmo --- .../src/Editor/Gizmos/SHTransformGizmo.cpp | 24 +++++++++++++++++++ .../src/Editor/Gizmos/SHTransformGizmo.h | 13 ++++++++++ 2 files changed, 37 insertions(+) create mode 100644 SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp create mode 100644 SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp new file mode 100644 index 00000000..a9276b6d --- /dev/null +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp @@ -0,0 +1,24 @@ +#include "SHpch.h" +#include "SHTransformGizmo.h" + +#include "ECS_Base/Managers/SHComponentManager.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Editor/SHEditor.hpp" + +namespace SHADE +{ + void SHTransformGizmo::Draw() + { + if(selectedEntityTranformComponent == nullptr) + { + SHEditor* editor = SHSystemManager::GetSystem(); + if(editor->selectedEntities.empty()) + return; + EntityID eid = editor->selectedEntities.back(); + selectedEntityTranformComponent = SHComponentManager::GetComponent_s(eid); + } + + + + } +} diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h new file mode 100644 index 00000000..d034eef9 --- /dev/null +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h @@ -0,0 +1,13 @@ +#pragma once +#include "Math/Transform/SHTransformComponent.h" + +namespace SHADE +{ + class SHTransformGizmo + { + public: + void Draw(); + private: + SHTransformComponent* selectedEntityTranformComponent{nullptr}; + }; +} From efe8ba4f115f7432bd39ea8a8fa6c893c6a4638c Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Wed, 19 Oct 2022 10:32:52 +0800 Subject: [PATCH 07/32] Added some editor camera controls --- SHADE_Engine/src/Camera/SHCameraComponent.cpp | 12 +++--- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 43 ++++++++++++++++++- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.cpp b/SHADE_Engine/src/Camera/SHCameraComponent.cpp index 9b84081f..a206d39a 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.cpp +++ b/SHADE_Engine/src/Camera/SHCameraComponent.cpp @@ -37,24 +37,24 @@ namespace SHADE } void SHCameraComponent::SetPositionX(float x) noexcept { - position[0] = x; + position.x = x; dirtyView = true; } void SHCameraComponent::SetPositionY(float y) noexcept { - position[1] = y; + position.y = y; dirtyView = true; } void SHCameraComponent::SetPositionZ(float z) noexcept { - position[2] = z; + position.z = z; dirtyView = true; } void SHCameraComponent::SetPosition(float x,float y, float z) noexcept { - position[0] = x; - position[1] = y; - position[2] = z; + position.x = x; + position.y = y; + position.z = z; dirtyView = true; } void SHCameraComponent::SetPosition(SHVec3& pos) noexcept diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 24d96028..6be26133 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -12,10 +12,49 @@ namespace SHADE { SHCameraSystem* system = static_cast(GetSystem()); + SHVec3 target{ 0.0f,0.0f,1.0f }; + SHVec3 up = { 0.0f,1.0f,0.0f }; + + + SHCameraComponent& camera = system->editorCamera; + SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); + SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); + //SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); + + target = SHVec3::Normalise(target); + + SHVec3::RotateZ(up, camera.roll); + up = SHVec3::Normalise(up); + + + SHVec3 view = target - camera.position; view = SHVec3::Normalise(view); + SHVec3 right = SHVec3::Cross(view, up); right = SHVec3::Normalise(right); + const SHVec3 UP = SHVec3::Cross(right, view); + + if (SHInputManagerSystem::GetKey(SHInputManagerSystem::SH_KEYCODE::A)) { - system->editorCamera.SetPositionX(system->editorCamera.position[0] - dt * system->editorCamera.movementSpeed); - + system->editorCamera.position -= right * dt * camera.movementSpeed; + } + if (SHInputManagerSystem::GetKey(SHInputManagerSystem::SH_KEYCODE::D)) + { + system->editorCamera.position += right * dt * camera.movementSpeed; + } + if (SHInputManagerSystem::GetKey(SHInputManagerSystem::SH_KEYCODE::W)) + { + system->editorCamera.position += view * dt * camera.movementSpeed; + } + if (SHInputManagerSystem::GetKey(SHInputManagerSystem::SH_KEYCODE::S)) + { + system->editorCamera.position -= view * dt * camera.movementSpeed; + } + if (SHInputManagerSystem::GetKey(SHInputManagerSystem::SH_KEYCODE::Q)) + { + system->editorCamera.position += UP * dt * camera.movementSpeed; + } + if (SHInputManagerSystem::GetKey(SHInputManagerSystem::SH_KEYCODE::E)) + { + system->editorCamera.position -= UP * dt * camera.movementSpeed; } system->UpdateCameraComponent(system->editorCamera); } From a7e8320008c8d658d45a955aa0f4759792ae7f10 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Wed, 19 Oct 2022 17:23:25 +0800 Subject: [PATCH 08/32] Copy/Paste Entities --- .../HierarchyPanel/SHHierarchyPanel.cpp | 13 ++++- .../src/Serialization/SHSerialization.cpp | 47 +++++++++++++++--- .../src/Serialization/SHSerialization.h | 6 ++- .../src/Tools/SHClipboardUtilities.cpp | 49 +++++++++++++++++++ SHADE_Engine/src/Tools/SHClipboardUtilities.h | 13 +++++ 5 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 SHADE_Engine/src/Tools/SHClipboardUtilities.cpp create mode 100644 SHADE_Engine/src/Tools/SHClipboardUtilities.h diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index c2666a0d..27e46d98 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -22,6 +22,7 @@ #include #include "Serialization/SHSerialization.h" +#include "Tools/SHClipboardUtilities.h" namespace SHADE @@ -80,6 +81,8 @@ namespace SHADE void SHHierarchyPanel::SetScrollTo(EntityID eid) { + if(eid == MAX_EID) + return; scrollTo = eid; } @@ -185,7 +188,15 @@ namespace SHADE } if(ImGui::Selectable("Copy")) { - SHLOG_INFO(SHSerialization::SerializeEntitiesToString(editor->selectedEntities)) + SHClipboardUtilities::WriteToClipboard(SHSerialization::SerializeEntitiesToString(editor->selectedEntities)); + } + if(ImGui::Selectable("Paste")) + { + SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard())); + } + if(ImGui::Selectable("Paste as Child")) + { + SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard(), eid)); } if(ImGui::Selectable(std::format("{} Delete", ICON_MD_DELETE).data())) { diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index e259fbbc..f6d29066 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -52,11 +52,13 @@ namespace SHADE out << YAML::EndSeq; } - static void DeserializeEntity(YAML::iterator& it, YAML::Node const& node, std::vector& createdEntities, EntityID parentEID = MAX_EID) + static EntityID DeserializeEntity(YAML::iterator& it, YAML::Node const& node, std::vector& createdEntities, EntityID parentEID = MAX_EID) { - if (!node[EIDNode]) - return; - EntityID eid = node[EIDNode].as(); + EntityID eid = MAX_EID; + if(!node) + return eid; + if (node[EIDNode]) + eid = node[EIDNode].as(); std::string name = "Default"; if (node[EntityNameNode]) name = node[EntityNameNode].as(); @@ -77,6 +79,7 @@ namespace SHADE } } } + return eid; } void SHSerialization::DeserializeSceneFromFile(std::filesystem::path const& path) @@ -111,7 +114,11 @@ namespace SHADE { DeserializeEntity(it, (*it), createdEntities); } - + if(createdEntities.empty()) + { + SHLOG_ERROR("Failed to create entities from deserializaiton") + return; + } //Initialize Entity auto entityVecIt = createdEntities.begin(); for (auto it = entities.begin(); it != entities.end(); ++it) @@ -130,17 +137,19 @@ namespace SHADE } } - std::string SHSerialization::SerializeEntitiesToString(std::vector const& entities) + std::string SHSerialization::SerializeEntitiesToString(std::vector const& entities) noexcept { YAML::Emitter out; YAML::Node node; auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + out << YAML::BeginSeq; for (auto const& eid : entities) { auto entityNode = sceneGraph.GetNode(eid); EmitEntity(entityNode, out); } - return std::basic_string(out.c_str()); + out << YAML::EndSeq; + return std::string(out.c_str()); } void SHSerialization::SerializeEntityToFile(std::filesystem::path const& path) @@ -182,6 +191,30 @@ namespace SHADE return node; } + EntityID SHSerialization::DeserializeEntitiesFromString(std::string const& data, EntityID const& parentEID) noexcept + { + if(data.empty()) + return MAX_EID; + YAML::Node entities = YAML::Load(data.c_str()); + EntityID eid{MAX_EID}; + std::vector createdEntities; + for(auto it = entities.begin(); it != entities.end(); ++it) + { + eid = DeserializeEntity(it, *it, createdEntities, parentEID); + } + if(createdEntities.empty()) + { + SHLOG_ERROR("Failed to create entities from deserializaiton") + return MAX_EID; + } + auto entityVecIt = createdEntities.begin(); + for(auto it = entities.begin(); it != entities.end(); ++it) + { + InitializeEntity(*it, *entityVecIt++); + } + return eid; + } + template, bool> = true> std::optional GetComponentID(YAML::Node const& componentNode) { diff --git a/SHADE_Engine/src/Serialization/SHSerialization.h b/SHADE_Engine/src/Serialization/SHSerialization.h index d247de7a..6b078dba 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.h +++ b/SHADE_Engine/src/Serialization/SHSerialization.h @@ -28,14 +28,18 @@ namespace SHADE static void SerializeSceneToFile(std::filesystem::path const& path); static std::string SerializeSceneToString(); static void SerializeSceneToEmitter(YAML::Emitter& out); + static void DeserializeSceneFromFile(std::filesystem::path const& path); static void EmitEntity(SHSceneNode* entityNode, YAML::Emitter& out); - static std::string SerializeEntitiesToString(std::vector const& entities); + + static std::string SerializeEntitiesToString(std::vector const& entities) noexcept; static void SerializeEntityToFile(std::filesystem::path const& path); static YAML::Node SerializeEntityToNode(SHSceneNode* sceneNode); + static EntityID DeserializeEntitiesFromString(std::string const& data, EntityID const& parentEID = MAX_EID) noexcept; + static std::vector GetComponentIDList(YAML::Node const& componentsNode); private: static void InitializeEntity(YAML::Node const& entityNode, EntityID const& eid); diff --git a/SHADE_Engine/src/Tools/SHClipboardUtilities.cpp b/SHADE_Engine/src/Tools/SHClipboardUtilities.cpp new file mode 100644 index 00000000..061a5ec6 --- /dev/null +++ b/SHADE_Engine/src/Tools/SHClipboardUtilities.cpp @@ -0,0 +1,49 @@ +#include "SHpch.h" +#include "SHClipboardUtilities.h" + +namespace SHADE +{ + void SHClipboardUtilities::WriteToClipboard(std::string const& str) noexcept + { + if(str.empty()) + return; + HWND const hwnd = GetDesktopWindow(); + OpenClipboard(hwnd); + EmptyClipboard(); + + auto const size = str.size() + 1; + const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, size); + if(hGlobal) + { + std::memcpy(GlobalLock(hGlobal), str.c_str(), size); + GlobalUnlock(hGlobal); + SetClipboardData(CF_TEXT, hGlobal); + } + else + { + SHLOG_ERROR("Failed to write to clipboard: {}", str.c_str()) + } + CloseClipboard(); + GlobalFree(hGlobal); + } + + std::string const SHClipboardUtilities::GetDataFromClipboard() noexcept + { + HWND const hwnd = GetDesktopWindow(); + if(!OpenClipboard(hwnd)) + { + SHLOG_ERROR("Failed to open clipboard") + return std::string(); + } + + if(HANDLE const dataHandle = GetClipboardData(CF_TEXT); dataHandle) + { + std::string data(static_cast(GlobalLock(dataHandle))); + GlobalUnlock(dataHandle); + CloseClipboard(); + return data; + } + CloseClipboard(); + return std::string(); + } +} diff --git a/SHADE_Engine/src/Tools/SHClipboardUtilities.h b/SHADE_Engine/src/Tools/SHClipboardUtilities.h new file mode 100644 index 00000000..7f3acdbe --- /dev/null +++ b/SHADE_Engine/src/Tools/SHClipboardUtilities.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace SHADE +{ + class SHClipboardUtilities + { + public: + static void WriteToClipboard(std::string const& str) noexcept; + static std::string const GetDataFromClipboard() noexcept; + }; +} \ No newline at end of file From 6051b7ded55027c071afa7dd976fe31fd939a143 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Wed, 19 Oct 2022 20:42:38 +0800 Subject: [PATCH 09/32] Tfm gizmo wip --- Assets/Editor/Layouts/UserLayout.ini | 48 ------------------- .../src/Editor/Gizmos/SHTransformGizmo.cpp | 18 +++++++ .../src/Editor/Gizmos/SHTransformGizmo.h | 2 + SHADE_Engine/src/Editor/SHEditor.cpp | 5 ++ SHADE_Engine/src/Editor/SHEditor.hpp | 4 ++ 5 files changed, 29 insertions(+), 48 deletions(-) delete mode 100644 Assets/Editor/Layouts/UserLayout.ini diff --git a/Assets/Editor/Layouts/UserLayout.ini b/Assets/Editor/Layouts/UserLayout.ini deleted file mode 100644 index 33b4ccfd..00000000 --- a/Assets/Editor/Layouts/UserLayout.ini +++ /dev/null @@ -1,48 +0,0 @@ -[Window][MainStatusBar] -Pos=0,1389 -Size=2547,20 -Collapsed=0 - -[Window][SHEditorMenuBar] -Pos=0,48 -Size=2547,1341 -Collapsed=0 - -[Window][Hierarchy Panel] -Pos=0,172 -Size=571,1217 -Collapsed=0 -DockId=0x00000004,0 - -[Window][Debug##Default] -Pos=60,60 -Size=400,400 -Collapsed=0 - -[Window][Inspector] -Pos=2276,48 -Size=271,1341 -Collapsed=0 -DockId=0x00000006,0 - -[Window][Profiler] -Pos=0,48 -Size=571,122 -Collapsed=0 -DockId=0x00000003,0 - -[Window][Viewport] -Pos=573,48 -Size=1701,1341 -Collapsed=0 -DockId=0x00000002,0 - -[Docking][Data] -DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=2547,1341 Split=X - DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1992,1036 Split=X - DockNode ID=0x00000001 Parent=0x00000005 SizeRef=571,1036 Split=Y Selected=0x1E6EB881 - DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 - DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,940 Selected=0xE096E5AE - DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1074,1036 CentralNode=1 Selected=0x13926F0B - DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=271,1036 Selected=0xE7039252 - diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp index a9276b6d..5c3a0ce0 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp @@ -4,6 +4,10 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.hpp" +#include +#include + +#include "Camera/SHCameraSystem.h" namespace SHADE { @@ -18,7 +22,21 @@ namespace SHADE selectedEntityTranformComponent = SHComponentManager::GetComponent_s(eid); } + if(!editorCamera) + { + auto const cameraSystem = SHSystemManager::GetSystem(); + editorCamera = cameraSystem->GetEditorCamera(); + } + ImGuizmo::SetOrthographic(false); + SHMatrix view = editorCamera->GetViewMatrix(); + SHMatrix proj = editorCamera->GetProjMatrix(); + SHMatrix mat = selectedEntityTranformComponent->GetTRS(); + ImGuizmo::DrawGrid(view.m[0], proj.m[0], mat.m[0], 10.f); + if(ImGuizmo::Manipulate(view.m[0], proj.m[0], ImGuizmo::OPERATION::UNIVERSAL, ImGuizmo::MODE::WORLD, mat.m[0])) + { + + } } } diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h index d034eef9..4ddbeb66 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h @@ -1,4 +1,5 @@ #pragma once +#include "Camera/SHCameraComponent.h" #include "Math/Transform/SHTransformComponent.h" namespace SHADE @@ -9,5 +10,6 @@ namespace SHADE void Draw(); private: SHTransformComponent* selectedEntityTranformComponent{nullptr}; + SHCameraComponent* editorCamera{nullptr}; }; } diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 6b3c3a93..7399665f 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -36,6 +36,7 @@ #include #include #include +#include //#==============================================================# //|| ImGui Backend Includes || @@ -129,6 +130,9 @@ namespace SHADE if(window->isOpen) window->Update(); } + + ImGuizmo::SetDrawlist(ImGui::GetForegroundDrawList()); + transformGizmo.Draw(); if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z)) { @@ -366,6 +370,7 @@ namespace SHADE ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); + ImGuizmo::BeginFrame(); } diff --git a/SHADE_Engine/src/Editor/SHEditor.hpp b/SHADE_Engine/src/Editor/SHEditor.hpp index baa6324e..4f8b32e2 100644 --- a/SHADE_Engine/src/Editor/SHEditor.hpp +++ b/SHADE_Engine/src/Editor/SHEditor.hpp @@ -16,6 +16,8 @@ #include "Resource/Handle.h" #include "EditorWindow/SHEditorWindow.h" #include "Tools/SHLogger.h" +#include "Gizmos/SHTransformGizmo.h" + //#==============================================================# //|| Library Includes || @@ -200,5 +202,7 @@ namespace SHADE SDL_Window* sdlWindow {nullptr}; ImGuiIO* io{nullptr}; + + SHTransformGizmo transformGizmo; };//class SHEditor }//namespace SHADE From 4f177bc45512b34efa7b000189d84449607c9a21 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 19 Oct 2022 20:43:22 +0800 Subject: [PATCH 10/32] Reworked script serialization and deserialization functions (WIP) --- SHADE_Application/premake5.lua | 4 +- SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 44 ++------- SHADE_Engine/src/Scripting/SHScriptEngine.h | 37 ++++---- .../src/Serialization/SHSerialization.cpp | 12 ++- .../src/Serialization/SHSerialization.h | 1 + SHADE_Managed/src/Scripts/ScriptStore.cxx | 91 ++++++++++--------- SHADE_Managed/src/Scripts/ScriptStore.hxx | 23 ++--- .../src/Serialisation/ReflectionUtilities.cxx | 61 +++++++------ .../src/Serialisation/ReflectionUtilities.h++ | 4 +- .../src/Serialisation/ReflectionUtilities.hxx | 8 +- 10 files changed, 145 insertions(+), 140 deletions(-) diff --git a/SHADE_Application/premake5.lua b/SHADE_Application/premake5.lua index 35ea2c10..395c3a48 100644 --- a/SHADE_Application/premake5.lua +++ b/SHADE_Application/premake5.lua @@ -37,7 +37,8 @@ project "SHADE_Application" "%{IncludeDir.VULKAN}/include", "%{IncludeDir.spdlog}/include", "%{IncludeDir.tinyddsloader}", - "%{IncludeDir.reactphysics3d}\\include" + "%{IncludeDir.reactphysics3d}\\include", + "%{IncludeDir.yamlcpp}" } externalwarnings "Off" @@ -51,6 +52,7 @@ project "SHADE_Application" { "SHADE_Engine", "SHADE_Managed", + "yaml-cpp", "SDL2.lib", "SDL2main.lib" } diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index d7a090a4..6585414d 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -115,34 +115,22 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Script Serialisation Functions */ /*---------------------------------------------------------------------------------*/ - std::string SHScriptEngine::SerialiseScripts(EntityID entity) const + bool SHScriptEngine::SerialiseScripts(EntityID entity, YAML::Node& scriptsNode) const { - // Create buffer needed to store serialised script data - constexpr int BUFFER_SIZE = 10240; - std::unique_ptr buffer { new char[BUFFER_SIZE] }; - std::memset(buffer.get(), 0, BUFFER_SIZE); - // Attempt to serialise the script - std::string result; - if (csScriptsSerialise(entity, buffer.get(), BUFFER_SIZE)) - { - result = std::string(buffer.get()); - } - else - { - SHLOG_ERROR("[ScriptEngine] Failed to serialise scripts as string buffer is too small!"); - } + if (csScriptsSerialiseYaml(entity, &scriptsNode)) + return true; - // Return an empty string since we failed to serialise - return result; + SHLOG_ERROR("[ScriptEngine] Failed to serialise scripts for entity #{}.", entity); + return false; } /*-----------------------------------------------------------------------------------*/ /* Script Serialisation Functions */ /*-----------------------------------------------------------------------------------*/ - void SHScriptEngine::DeserialiseScript(EntityID entity, const std::string& yaml) const + bool SHScriptEngine::DeserialiseScript(EntityID entity, YAML::Node& scriptsNode) const { - csScriptDeserialise(entity, yaml.c_str()); + return csScriptsDeserialiseYaml(entity, &scriptsNode); } /*-----------------------------------------------------------------------------------*/ @@ -380,30 +368,18 @@ namespace SHADE DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", "RemoveAllScriptsImmediately" ); - /*csScriptsSerialise = dotNet.GetFunctionPtr + csScriptsSerialiseYaml = dotNet.GetFunctionPtr ( DEFAULT_CSHARP_LIB_NAME, DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", "SerialiseScripts" ); - csScriptsSerialiseYaml = dotNet.GetFunctionPtr + csScriptsDeserialiseYaml = dotNet.GetFunctionPtr ( DEFAULT_CSHARP_LIB_NAME, DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", - "SerialiseScriptsYaml" + "DeserialiseScripts" ); - csScriptDeserialise = dotNet.GetFunctionPtr - ( - DEFAULT_CSHARP_LIB_NAME, - DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", - "DeserialiseScript" - ); - csScriptDeserialiseYaml = dotNet.GetFunctionPtr - ( - DEFAULT_CSHARP_LIB_NAME, - DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", - "SerialiseScriptsYaml" - );*/ csEditorRenderScripts = dotNet.GetFunctionPtr ( DEFAULT_CSHARP_LIB_NAME, diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h index 60723425..58dbe0b3 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.h +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h @@ -13,7 +13,8 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include - +// External Dependencies +#include // Project Headers #include "SH_API.h" #include "SHDotNetRuntime.h" @@ -141,23 +142,26 @@ namespace SHADE /* Script Serialisation Functions */ /*-----------------------------------------------------------------------------*/ /// - /// Generates a JSON string that represents the set of Scripts attached to the - /// specified Entity. + /// Performs serialization of all scripts for the specified entity into the + /// YAML::Node specified. This node will contain all serialised scripts after + /// calling this function. /// - /// The Entity to Serialise. - /// - /// String that represents the set of scripts attached to the specified Entity. - /// - std::string SerialiseScripts(EntityID entity) const; + /// The Entity to Serialise. + /// + /// YAML Node that will store the serialised scripts. + /// + /// True if successfully serialised. + bool SerialiseScripts(EntityID entity, YAML::Node& scriptsNode) const; /// - /// Loads the specified JSON string and creates a Script for the specified Entity - /// based on the specified JSON string. + /// Creates scripts and sets fields for the specified Entity based on the specified + /// YAML node. /// /// The Entity to deserialise a Script on to. - /// - /// The YAML string that represents the Script to load into the Entity. + /// + /// YAML Node that contains the serialised script data. /// - void DeserialiseScript(EntityID entity, const std::string& yaml) const; + /// True if successfully deserialised. + bool DeserialiseScript(EntityID entity, YAML::Node& scriptsNode) const; /*-----------------------------------------------------------------------------*/ /* Script Editor Functions */ @@ -211,8 +215,7 @@ namespace SHADE using CsScriptManipFuncPtr = bool(*)(EntityID, const char*); using CsScriptBasicFuncPtr = void(*)(EntityID); using CsScriptOptionalFuncPtr = void(*)(EntityID, bool); - using CsScriptSerialiseFuncPtr = bool(*)(EntityID, char*, int); - using CsScriptDeserialiseFuncPtr = bool(*)(EntityID, const char*); + using CsScriptSerialiseYamlFuncPtr = bool(*)(EntityID, void*); using CsScriptSerialiseYamlFuncPtr = bool(*)(EntityID, void*); using CsScriptEditorFuncPtr = void(*)(EntityID); @@ -245,10 +248,8 @@ namespace SHADE CsScriptManipFuncPtr csScriptsAdd = nullptr; CsScriptBasicFuncPtr csScriptsRemoveAll = nullptr; CsScriptOptionalFuncPtr csScriptsRemoveAllImmediately = nullptr; - CsScriptSerialiseFuncPtr csScriptsSerialise = nullptr; - CsScriptDeserialiseFuncPtr csScriptDeserialise = nullptr; CsScriptSerialiseYamlFuncPtr csScriptsSerialiseYaml = nullptr; - CsScriptSerialiseYamlFuncPtr csScriptDeserialiseYaml = nullptr; + CsScriptSerialiseYamlFuncPtr csScriptsDeserialiseYaml = nullptr; // - Editor CsScriptEditorFuncPtr csEditorRenderScripts = nullptr; CsFuncPtr csEditorUndo = nullptr; diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index e259fbbc..d0023107 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -13,6 +13,8 @@ #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Math/Transform/SHTransformComponent.h" #include "Physics/Components/SHRigidBodyComponent.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Scripting/SHScriptEngine.h" namespace SHADE { @@ -157,7 +159,7 @@ namespace SHADE node = YAML::Null; return node; } - node.SetStyle(YAML::EmitterStyle::Block); + node.SetStyle(YAML::EmitterStyle::Block); node[EIDNode] = eid; node[EntityNameNode] = entity->name; node[IsActiveNode] = sceneNode->IsActive(); @@ -179,6 +181,14 @@ namespace SHADE components[rttr::type::get().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(rigidbody); } node[ComponentsNode] = components; + + YAML::Node scripts; + SHSystemManager::GetSystem()->SerialiseScripts(eid, scripts); + node[ScriptsNode] = scripts; + + //YAML::Emitter emitter; + //emitter << node; + //SHLOG_TRACE(emitter.c_str()) return node; } diff --git a/SHADE_Engine/src/Serialization/SHSerialization.h b/SHADE_Engine/src/Serialization/SHSerialization.h index d247de7a..e02db819 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.h +++ b/SHADE_Engine/src/Serialization/SHSerialization.h @@ -21,6 +21,7 @@ namespace SHADE constexpr const char* EIDNode = "EID"; constexpr const char* IsActiveNode = "IsActive"; constexpr const char* NumberOfChildrenNode = "NumberOfChildren"; + constexpr const char* ScriptsNode = "Scripts"; struct SH_API SHSerialization { diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index 252ab071..3f366c36 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -26,6 +26,7 @@ of DigiPen Institute of Technology is prohibited. #include "Utility/Convert.hxx" #include "Script.hxx" #include "Engine/Entity.hxx" +#include "Serialisation/ReflectionUtilities.hxx" namespace SHADE { @@ -470,70 +471,78 @@ namespace SHADE } SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") } - bool ScriptStore::SerialiseScripts(Entity entity, System::Text::StringBuilder^ buffer, int bufferSize) + + bool ScriptStore::SerialiseScripts(Entity entity, System::IntPtr yamlNodePtr) { SAFE_NATIVE_CALL_BEGIN - // Create a buffer that we can work with temporarily - System::Text::StringBuilder^ jsonString = gcnew System::Text::StringBuilder(); + // Convert to pointer + YAML::Node* yamlNode = reinterpret_cast(yamlNodePtr.ToPointer()); + // Check if yamlNode is valid + if (yamlNode == nullptr) + { + Debug::LogWarning("Attempted to serialise scripts with an invalid YAML Node! Skipping."); + return false; + } + // Check if entity exists, otherwise nothing if (!EntityUtils::IsValid(entity)) - return true; - + { + Debug::LogWarning("Attempted to serialise scripts for an invalid Entity! Skipping."); + return false; + } + // Check if entity exists in the script storage - if (!scripts.ContainsKey(entity)) + if (!scripts.ContainsKey(entity)) return true; // Serialise each script + //yamlNode->SetStyle(YAML::EmitterStyle::Block); System::Collections::Generic::List^ scriptList = scripts[entity]; - for (int i = 0; i < scriptList->Count; ++i) + for each (Script^ script in scriptList) { - throw gcnew System::NotImplementedException; - //jsonString->Append(ReflectionUtilities::Serialise(scriptList[i])); - - // Only add separator if is not last script - if (i != scriptList->Count - 1) - { - jsonString->Append(",\r\n"); - } + ReflectionUtilities::Serialise(script, *yamlNode); } - // Check if the size is too big - if (jsonString->Length > bufferSize) - return false; - - // Otherwise we copy it over - buffer->Clear(); - buffer->Append(jsonString->ToString()); return true; SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") return false; } - bool ScriptStore::DeserialiseScript(Entity entity, System::String^ yaml) + bool ScriptStore::DeserialiseScripts(Entity entity, System::IntPtr yamlNodePtr) { SAFE_NATIVE_CALL_BEGIN + // Convert to pointer + YAML::Node* yamlNode = reinterpret_cast(yamlNodePtr.ToPointer()); + + // Check if yamlNode is valid + if (yamlNode == nullptr) + { + Debug::LogWarning("Attempted to deserialise scripts with an invalid YAML Node! Skipping."); + return false; + } + // Check if entity exists, otherwise nothing if (!EntityUtils::IsValid(entity)) - return false; - - // Get the name of the script - const int FIRST_QUOTE = yaml->IndexOf('\"'); - const int FIRST_COLON = yaml->IndexOf(':'); - if (FIRST_QUOTE < 0 || FIRST_COLON < 0) // No script name, it's invalid - return false; - const int SCRIPT_NAME_START = FIRST_QUOTE + 1; - const int SCRIPT_NAME_END = FIRST_COLON - 1; - System::String^ typeName = yaml->Substring(SCRIPT_NAME_START, SCRIPT_NAME_END - SCRIPT_NAME_START); - - // Create the script - Script^ script; - if (AddScriptViaNameWithRef(entity, typeName, script)) { - // Copy the data in - throw gcnew System::NotImplementedException; - //ReflectionUtilities::Deserialise(json, script); - return true; + Debug::LogWarning("Attempted to deserialise scripts for an invalid Entity! Skipping."); + return false; + } + + // Go through all elements in the node + for (YAML::Node& node : *yamlNode) + { + // Get the name of the script + const std::string typeName = ""; // TODO + + // Create + Script^ script; + if (AddScriptViaNameWithRef(entity, Convert::ToCLI(typeName), script)) + { + // Copy the data in + ReflectionUtilities::Deserialise(script, node); + return true; + } } SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") diff --git a/SHADE_Managed/src/Scripts/ScriptStore.hxx b/SHADE_Managed/src/Scripts/ScriptStore.hxx index cc0c1db5..4a9be721 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.hxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.hxx @@ -17,6 +17,7 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "Engine/Entity.hxx" #include "Script.hxx" +#include "Serialization/SHSerialization.h" namespace SHADE { @@ -237,27 +238,23 @@ namespace SHADE /* Serialisation Functions */ /*-----------------------------------------------------------------------------*/ /// - /// Generates a JSON string that represents the set of Scripts attached - /// to the specified Entity. + /// Populates a YAML node with the scripts for a specified Entity. ///

/// This function should only be called from native unmanaged code. ///
/// The Entity to Serialise. - /// - /// StringBuilder handle that maps to a native char array that will contain the - /// serialised string. - /// - /// - /// The size of the char array. + /// + /// Pointer to a YAML::Node that will be populated with all of the serialised + /// scripts and their associated fields. /// /// /// True if serialisation is successful. False if the buffer is too small for /// the serialised output. /// - static bool SerialiseScripts(Entity entity, System::Text::StringBuilder^ buffer, int bufferSize); + static bool SerialiseScripts(Entity entity, System::IntPtr yamlNode); /// - /// Processes a JSON string that represents a single Script and attaches - /// it onto the specified Entity. + /// Processes a YAML node that contains a list of multiple scripts to be loaded + /// into the specified Entity. ///

/// This function should only be called from native unmanaged code. ///
@@ -265,10 +262,10 @@ namespace SHADE /// The Entity to attach the deserialised Scripts to. /// /// - /// JSON string that describes the Script to serialise. + /// Pointer to the YAML::Node that contains serialized script data. /// /// - static bool DeserialiseScript(Entity entity, System::String^ yaml); + static bool DeserialiseScripts(Entity entity, System::IntPtr yamlNode); private: /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx index 2a9cc57c..dc03a95c 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx @@ -61,13 +61,12 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Serialisation Functions */ /*---------------------------------------------------------------------------------*/ - void ReflectionUtilities::Serialise(System::Object^ object, YAML::Emitter& yaml) + void ReflectionUtilities::Serialise(System::Object^ object, YAML::Node& yamlNode) { using namespace System::Reflection; // Create YAML object - yaml << YAML::Key << Convert::ToNative(object->GetType()->FullName); - yaml << YAML::BeginMap; + YAML::Node scriptNode; // Get all fields System::Collections::Generic::IEnumerable^ fields = GetInstanceFields(object); @@ -78,12 +77,12 @@ namespace SHADE continue; // Serialise - writeFieldIntoYaml(field, object, yaml); + writeFieldIntoYaml(field, object, scriptNode); } - yaml << YAML::EndMap; + yamlNode[Convert::ToNative(object->GetType()->FullName)] = scriptNode; } - void ReflectionUtilities::Deserialise(YAML::Node& yamlNode, Object^ object) + void ReflectionUtilities::Deserialise(Object^ object, YAML::Node& yamlNode) { using namespace System::Reflection; @@ -117,44 +116,50 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Serialization Helper Functions */ /*---------------------------------------------------------------------------------*/ - void ReflectionUtilities::writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Emitter& yaml) + void ReflectionUtilities::writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode) { - // Field Name - yaml << YAML::Key << Convert::ToNative(fieldInfo->Name); - - // Field Value - yaml << YAML::Value; - if (fieldInsertYaml (fieldInfo, object, yaml) || - fieldInsertYaml (fieldInfo, object, yaml) || - fieldInsertYaml (fieldInfo, object, yaml) || - fieldInsertYaml(fieldInfo, object, yaml) || - fieldInsertYaml(fieldInfo, object, yaml) || - fieldInsertYaml(fieldInfo, object, yaml) || - fieldInsertYaml (fieldInfo, object, yaml) || - fieldInsertYaml (fieldInfo, object, yaml) || - fieldInsertYaml (fieldInfo, object, yaml) || - fieldInsertYaml (fieldInfo, object, yaml)) + // Field YAML Node + YAML::Node fieldNode; + fieldNode.SetStyle(YAML::EmitterStyle::Block); + fieldNode["Name"] = Convert::ToNative(fieldInfo->Name); + + // Retrieve string for the YAML + if (fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode)) { return; } else if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid)) { - yaml << safe_cast(fieldInfo->GetValue(object)); + fieldNode = std::to_string(safe_cast(fieldInfo->GetValue(object))); } else if (fieldInfo->FieldType == System::String::typeid) { System::String^ str = safe_cast(fieldInfo->GetValue(object)); - yaml << Convert::ToNative(str); + fieldNode = Convert::ToNative(str); } else if (fieldInfo->FieldType == Vector2::typeid) { Vector2 vec = safe_cast(fieldInfo->GetValue(object)); - yaml << YAML::BeginSeq << YAML::Flow << vec.x << vec.y << YAML::EndSeq; + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(vec.x); + fieldNode.push_back(vec.y); } else if (fieldInfo->FieldType == Vector3::typeid) { Vector3 vec = safe_cast(fieldInfo->GetValue(object)); - yaml << YAML::BeginSeq << YAML::Flow << vec.x << vec.y << vec.z << YAML::EndSeq; + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(vec.x); + fieldNode.push_back(vec.y); + fieldNode.push_back(vec.z); } else // Not any of the supported types { @@ -163,7 +168,11 @@ namespace SHADE "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialization.", fieldInfo->Name, fieldInfo->FieldType) )); + return; } + + // Store the field into YAML + yamlNode.push_back(fieldNode); } void ReflectionUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ index 88469b34..a3d4c33b 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ @@ -23,11 +23,11 @@ namespace SHADE /* Serialization Helper Functions */ /*---------------------------------------------------------------------------------*/ template - bool ReflectionUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Emitter& emitter) + bool ReflectionUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode) { if (fieldInfo->FieldType == FieldType::typeid) { - emitter << safe_cast(fieldInfo->GetValue(object)); + fieldNode = 0.0;//static_cast(safe_cast(fieldInfo->GetValue(object))); return true; } diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx index 53f8fa1d..0709f667 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx @@ -52,7 +52,7 @@ namespace SHADE /// attribute will be serialised. /// /// The object to serialise. - static void Serialise(System::Object^ object, YAML::Emitter& yaml); + static void Serialise(System::Object^ object, YAML::Node& yamlNode); /// /// Deserialises a YAML node that contains a map of Scripts and copies the /// deserialised data into the specified object if there are matching fields. @@ -62,14 +62,14 @@ namespace SHADE /// object. /// /// The object to copy deserialised data into. - static void Deserialise(YAML::Node& yamlNode, Object^ object); + static void Deserialise(System::Object^ object, YAML::Node& yamlNode); /*-----------------------------------------------------------------------------*/ /* Serialization Helper Functions */ /*-----------------------------------------------------------------------------*/ - static void writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Emitter& yaml); + static void writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode); template - static bool fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Emitter& emitter); + static bool fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode); static void writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); template static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); From 736d7b652bcbb9b9e16e9c106daca1d2c4dbe030 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Thu, 20 Oct 2022 03:21:42 +0800 Subject: [PATCH 11/32] Added camera turning --- Assets/Editor/Layouts/UserLayout.ini | 20 +-- SHADE_Engine/src/Camera/SHCameraComponent.cpp | 2 +- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 114 +++++++++--------- SHADE_Engine/src/Camera/SHCameraSystem.h | 2 + 4 files changed, 67 insertions(+), 71 deletions(-) diff --git a/Assets/Editor/Layouts/UserLayout.ini b/Assets/Editor/Layouts/UserLayout.ini index 33b4ccfd..530ee770 100644 --- a/Assets/Editor/Layouts/UserLayout.ini +++ b/Assets/Editor/Layouts/UserLayout.ini @@ -1,16 +1,16 @@ [Window][MainStatusBar] -Pos=0,1389 -Size=2547,20 +Pos=0,1060 +Size=1920,20 Collapsed=0 [Window][SHEditorMenuBar] Pos=0,48 -Size=2547,1341 +Size=1920,1012 Collapsed=0 [Window][Hierarchy Panel] -Pos=0,172 -Size=571,1217 +Pos=0,142 +Size=571,918 Collapsed=0 DockId=0x00000004,0 @@ -20,25 +20,25 @@ Size=400,400 Collapsed=0 [Window][Inspector] -Pos=2276,48 -Size=271,1341 +Pos=1649,48 +Size=271,1012 Collapsed=0 DockId=0x00000006,0 [Window][Profiler] Pos=0,48 -Size=571,122 +Size=571,92 Collapsed=0 DockId=0x00000003,0 [Window][Viewport] Pos=573,48 -Size=1701,1341 +Size=1074,1012 Collapsed=0 DockId=0x00000002,0 [Docking][Data] -DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=2547,1341 Split=X +DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1992,1036 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=571,1036 Split=Y Selected=0x1E6EB881 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.cpp b/SHADE_Engine/src/Camera/SHCameraComponent.cpp index 650ed3c5..7ba6855c 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.cpp +++ b/SHADE_Engine/src/Camera/SHCameraComponent.cpp @@ -7,7 +7,7 @@ namespace SHADE { SHCameraComponent::SHCameraComponent() :yaw(0.0f), pitch(0.0f), roll(0.0f) - , width(1920.0f), height(1080.0f), zNear(0.01f), zFar(10000.0f), fov(90.0f), movementSpeed(1.0f), turnSpeed(1.0f) + , width(1920.0f), height(1080.0f), zNear(0.01f), zFar(10000.0f), fov(90.0f), movementSpeed(1.0f), turnSpeed(0.5f) , perspProj(true), dirtyView(true), dirtyProj(true) , viewMatrix(), projMatrix() , position() diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 9c97131a..130aab09 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -2,6 +2,7 @@ #include "SHCameraSystem.h" #include "Math/SHMathHelpers.h" #include "Input/SHInputManager.h" +#include "Math/Vector/SHVec2.h" @@ -12,25 +13,8 @@ namespace SHADE { SHCameraSystem* system = static_cast(GetSystem()); auto& camera = system->editorCamera; - SHVec3 target{ 0.0f,0.0f,-1.0f }; - SHVec3 up = { 0.0f,1.0f,0.0f }; - - - SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); - SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); - target += camera.position; - ////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); - - //target = SHVec3::Normalise(target); - - SHVec3::RotateZ(up, camera.roll); - up = SHVec3::Normalise(up); - - - SHVec3 view = target - camera.position; view = SHVec3::Normalise(view); - SHVec3 right = SHVec3::Cross(view, up); right = SHVec3::Normalise(right); - const SHVec3 UP = SHVec3::Cross(view, right); - + SHVec3 view, right, UP; + system->GetCameraAxis(camera, view, right, UP); if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) { @@ -62,6 +46,18 @@ namespace SHADE system->editorCamera.position -= UP * dt * camera.movementSpeed; system->editorCamera.dirtyView = true; } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::RMB)) + { + double mouseX, mouseY; + SHInputManager::GetMouseVelocity(&mouseX,&mouseY); + + //std::cout << camera.yaw << std::endl; + + system->editorCamera.pitch -= mouseY * dt * camera.turnSpeed.x; + system->editorCamera.yaw -= mouseX * dt * camera.turnSpeed.y; + system->editorCamera.dirtyView = true; + } + system->UpdateCameraComponent(system->editorCamera); } @@ -89,24 +85,9 @@ namespace SHADE { if (camera.dirtyView) { - SHVec3 target{ 0.0f,0.0f,-1.0f }; - SHVec3 up = { 0.0f,1.0f,0.0f }; - - - SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); - SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); - target += camera.position; - ////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); - - //target = SHVec3::Normalise(target); - - SHVec3::RotateZ(up, camera.roll); - up = SHVec3::Normalise(up); - - - SHVec3 view = target - camera.position; view = SHVec3::Normalise(view); - SHVec3 right = SHVec3::Cross(view, up); right = SHVec3::Normalise(right); - const SHVec3 UP = SHVec3::Cross(view, right); + + SHVec3 view, right, UP; + GetCameraAxis(camera, view, right, UP); camera.viewMatrix = SHMatrix::Identity; camera.viewMatrix(0, 0) = right[0]; @@ -142,38 +123,51 @@ namespace SHADE camera.projMatrix(3, 2) = 1.0f; camera.projMatrix(2, 3) = -(camera.zFar * camera.zNear) / (camera.zFar - camera.zNear); - //const float fov_rad = SHMath::DegreesToRadians(camera.fov); - //const float focal_length = 1.0f / tan(fov_rad * 0.5f); - - //camera.projMatrix(0,0) = focal_length / camera.GetAspectRatio(); - //camera.projMatrix(1,1) = -focal_length; - //camera.projMatrix(2,2) = camera.zNear / (camera.zFar - camera.zNear); - //camera.projMatrix(2,3) = camera.zFar * (camera.zNear / (camera.zFar - camera.zNear)); - //camera.projMatrix(3,2) = -1.0f; - //camera.projMatrix(3,3) = 0.0f; - - //camera.projMatrix = SHMatrix::Inverse(camera.projMatrix); - + camera.dirtyProj = false; } else { - const float R = camera.width * 0.5f; - const float L = -R; - const float T = camera.height * 0.5f; - const float B = -T; + //const float R = camera.width * 0.5f; + //const float L = -R; + //const float T = camera.height * 0.5f; + //const float B = -T; - camera.projMatrix = SHMatrix::Identity; - camera.projMatrix(0, 0) = 2.0f / (R - L); - camera.projMatrix(1, 1) = 2.0f / (B - T); - camera.projMatrix(2, 2) = 1.0f / (camera.zFar - camera.zNear); - camera.projMatrix(3, 0) = -(R + L) / (R - L); - camera.projMatrix(3, 1) = -(B + T) / (B - T); - camera.projMatrix(3, 2) = -camera.zNear / (camera.zFar - camera.zNear); + //camera.projMatrix = SHMatrix::Identity; + //camera.projMatrix(0, 0) = 2.0f / (R - L); + //camera.projMatrix(1, 1) = 2.0f / (B - T); + //camera.projMatrix(2, 2) = 1.0f / (camera.zFar - camera.zNear); + //camera.projMatrix(3, 0) = -(R + L) / (R - L); + //camera.projMatrix(3, 1) = -(B + T) / (B - T); + //camera.projMatrix(3, 2) = -camera.zNear / (camera.zFar - camera.zNear); camera.dirtyProj = false; } } } + void SHCameraSystem::GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& upVec) const noexcept + { + SHVec3 target{ 0.0f,0.0f,-1.0f }; + SHVec3 up = { 0.0f,1.0f,0.0f }; + + + target = SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); + target =SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); + std::cout << "Target vec: " << target.x<<", "< Date: Thu, 20 Oct 2022 09:34:33 +0800 Subject: [PATCH 12/32] Some clean up --- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 130aab09..a9b356de 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -18,33 +18,33 @@ namespace SHADE if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) { - system->editorCamera.position -= right * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position -= right * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::D)) { - system->editorCamera.position += right * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position += right * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::W)) { - system->editorCamera.position += view * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position += view * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::S)) { - system->editorCamera.position -= view * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position -= view * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::Q)) { - system->editorCamera.position += UP * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position += UP * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::E)) { - system->editorCamera.position -= UP * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position -= UP * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::RMB)) { @@ -53,9 +53,9 @@ namespace SHADE //std::cout << camera.yaw << std::endl; - system->editorCamera.pitch -= mouseY * dt * camera.turnSpeed.x; - system->editorCamera.yaw -= mouseX * dt * camera.turnSpeed.y; - system->editorCamera.dirtyView = true; + camera.pitch -= mouseY * dt * camera.turnSpeed.x; + camera.yaw -= mouseX * dt * camera.turnSpeed.y; + camera.dirtyView = true; } system->UpdateCameraComponent(system->editorCamera); From cfed342f9c7571f303e0143d47373b91b3ac2b78 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Thu, 20 Oct 2022 09:54:51 +0800 Subject: [PATCH 13/32] Modified script serialization to use a sequence of scripts instead of a map --- SHADE_Managed/src/Scripts/ScriptStore.cxx | 2 +- .../src/Serialisation/ReflectionUtilities.cxx | 15 ++++++++++----- .../src/Serialisation/ReflectionUtilities.hxx | 2 ++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index 3f366c36..b9423795 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -497,7 +497,7 @@ namespace SHADE return true; // Serialise each script - //yamlNode->SetStyle(YAML::EmitterStyle::Block); + yamlNode->SetStyle(YAML::EmitterStyle::Block); System::Collections::Generic::List^ scriptList = scripts[entity]; for each (Script^ script in scriptList) { diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx index dc03a95c..8e5b1d38 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx @@ -38,6 +38,11 @@ if (iter != jsonValue.MemberEnd()) \ vec.MEMBER = iter->value.GetDouble(); \ } \ +/*-------------------------------------------------------------------------------------*/ +/* File-Level Constants */ +/*-------------------------------------------------------------------------------------*/ +static const std::string_view SCRIPT_TYPE_YAMLTAG = "Type"; + /*-------------------------------------------------------------------------------------*/ /* Function Definitions */ /*-------------------------------------------------------------------------------------*/ @@ -61,12 +66,14 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Serialisation Functions */ /*---------------------------------------------------------------------------------*/ - void ReflectionUtilities::Serialise(System::Object^ object, YAML::Node& yamlNode) + void ReflectionUtilities::Serialise(System::Object^ object, YAML::Node& scriptListNode) { using namespace System::Reflection; // Create YAML object YAML::Node scriptNode; + scriptNode.SetStyle(YAML::EmitterStyle::Block); + scriptNode[SCRIPT_TYPE_YAMLTAG] = Convert::ToNative(object->GetType()->FullName); // Get all fields System::Collections::Generic::IEnumerable^ fields = GetInstanceFields(object); @@ -80,7 +87,7 @@ namespace SHADE writeFieldIntoYaml(field, object, scriptNode); } - yamlNode[Convert::ToNative(object->GetType()->FullName)] = scriptNode; + scriptListNode.push_back(scriptNode); } void ReflectionUtilities::Deserialise(Object^ object, YAML::Node& yamlNode) { @@ -120,8 +127,6 @@ namespace SHADE { // Field YAML Node YAML::Node fieldNode; - fieldNode.SetStyle(YAML::EmitterStyle::Block); - fieldNode["Name"] = Convert::ToNative(fieldInfo->Name); // Retrieve string for the YAML if (fieldInsertYaml (fieldInfo, object, fieldNode) || @@ -172,7 +177,7 @@ namespace SHADE } // Store the field into YAML - yamlNode.push_back(fieldNode); + yamlNode[Convert::ToNative(fieldInfo->Name)] = fieldNode; } void ReflectionUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx index 0709f667..4742c7ff 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx @@ -64,6 +64,8 @@ namespace SHADE /// The object to copy deserialised data into. static void Deserialise(System::Object^ object, YAML::Node& yamlNode); + + private: /*-----------------------------------------------------------------------------*/ /* Serialization Helper Functions */ /*-----------------------------------------------------------------------------*/ From 4bc91283c8448dc9ae84066340ce5bfd8be5877e Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Thu, 20 Oct 2022 10:10:43 +0800 Subject: [PATCH 14/32] Fixed primitive fields not being serialised --- .../src/Serialisation/ReflectionUtilities.cxx | 86 ++++++++++--------- .../src/Serialisation/ReflectionUtilities.hxx | 3 +- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx index 8e5b1d38..3e963818 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx @@ -73,7 +73,7 @@ namespace SHADE // Create YAML object YAML::Node scriptNode; scriptNode.SetStyle(YAML::EmitterStyle::Block); - scriptNode[SCRIPT_TYPE_YAMLTAG] = Convert::ToNative(object->GetType()->FullName); + scriptNode[SCRIPT_TYPE_YAMLTAG.data()] = Convert::ToNative(object->GetType()->FullName); // Get all fields System::Collections::Generic::IEnumerable^ fields = GetInstanceFields(object); @@ -129,51 +129,53 @@ namespace SHADE YAML::Node fieldNode; // Retrieve string for the YAML - if (fieldInsertYaml (fieldInfo, object, fieldNode) || - fieldInsertYaml (fieldInfo, object, fieldNode) || - fieldInsertYaml (fieldInfo, object, fieldNode) || + const bool PRIMITIVE_SERIALIZED = fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || fieldInsertYaml(fieldInfo, object, fieldNode) || fieldInsertYaml(fieldInfo, object, fieldNode) || fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml (fieldInfo, object, fieldNode) || - fieldInsertYaml (fieldInfo, object, fieldNode) || - fieldInsertYaml (fieldInfo, object, fieldNode) || - fieldInsertYaml (fieldInfo, object, fieldNode)) + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode); + + // Serialization of more complex types + if (!PRIMITIVE_SERIALIZED) { - return; - } - else if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid)) - { - fieldNode = std::to_string(safe_cast(fieldInfo->GetValue(object))); - } - else if (fieldInfo->FieldType == System::String::typeid) - { - System::String^ str = safe_cast(fieldInfo->GetValue(object)); - fieldNode = Convert::ToNative(str); - } - else if (fieldInfo->FieldType == Vector2::typeid) - { - Vector2 vec = safe_cast(fieldInfo->GetValue(object)); - fieldNode.SetStyle(YAML::EmitterStyle::Flow); - fieldNode.push_back(vec.x); - fieldNode.push_back(vec.y); - } - else if (fieldInfo->FieldType == Vector3::typeid) - { - Vector3 vec = safe_cast(fieldInfo->GetValue(object)); - fieldNode.SetStyle(YAML::EmitterStyle::Flow); - fieldNode.push_back(vec.x); - fieldNode.push_back(vec.y); - fieldNode.push_back(vec.z); - } - else // Not any of the supported types - { - Debug::LogWarning(Convert::ToNative(System::String::Format - ( - "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialization.", - fieldInfo->Name, fieldInfo->FieldType) - )); - return; + if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid)) + { + fieldNode = std::to_string(safe_cast(fieldInfo->GetValue(object))); + } + else if (fieldInfo->FieldType == System::String::typeid) + { + System::String^ str = safe_cast(fieldInfo->GetValue(object)); + fieldNode = Convert::ToNative(str); + } + else if (fieldInfo->FieldType == Vector2::typeid) + { + Vector2 vec = safe_cast(fieldInfo->GetValue(object)); + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(vec.x); + fieldNode.push_back(vec.y); + } + else if (fieldInfo->FieldType == Vector3::typeid) + { + Vector3 vec = safe_cast(fieldInfo->GetValue(object)); + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(vec.x); + fieldNode.push_back(vec.y); + fieldNode.push_back(vec.z); + } + else // Not any of the supported types + { + Debug::LogWarning(Convert::ToNative(System::String::Format + ( + "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialization.", + fieldInfo->Name, fieldInfo->FieldType) + )); + return; + } } // Store the field into YAML diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx index 4742c7ff..fbae5ee0 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx @@ -58,8 +58,7 @@ namespace SHADE /// deserialised data into the specified object if there are matching fields. /// /// - /// The JSON string that contains the data to copy into this PlushieScript - /// object. + /// The JSON string that contains the data to copy into this Script object. /// /// The object to copy deserialised data into. static void Deserialise(System::Object^ object, YAML::Node& yamlNode); From 166a036142bfad809663775c107d5e686449d79f Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Thu, 20 Oct 2022 11:08:20 +0800 Subject: [PATCH 15/32] Completed script serialization in YAML --- SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 4 +- SHADE_Engine/src/Scripting/SHScriptEngine.h | 54 ++++++++-------- .../src/Serialization/SHSerialization.cpp | 7 +- SHADE_Managed/src/Scripts/ScriptStore.cxx | 24 +++++-- .../src/Serialisation/ReflectionUtilities.h++ | 5 +- .../src/Serialisation/ReflectionUtilities.hxx | 1 - SHADE_Managed/src/Utility/Convert.hxx | 64 +++++++++++++++++++ 7 files changed, 117 insertions(+), 42 deletions(-) diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index 6585414d..fea59353 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -128,7 +128,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Script Serialisation Functions */ /*-----------------------------------------------------------------------------------*/ - bool SHScriptEngine::DeserialiseScript(EntityID entity, YAML::Node& scriptsNode) const + bool SHScriptEngine::DeserialiseScripts(EntityID entity, const YAML::Node& scriptsNode) const { return csScriptsDeserialiseYaml(entity, &scriptsNode); } @@ -374,7 +374,7 @@ namespace SHADE DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", "SerialiseScripts" ); - csScriptsDeserialiseYaml = dotNet.GetFunctionPtr + csScriptsDeserialiseYaml = dotNet.GetFunctionPtr ( DEFAULT_CSHARP_LIB_NAME, DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h index 58dbe0b3..137d978f 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.h +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h @@ -161,7 +161,7 @@ namespace SHADE /// YAML Node that contains the serialised script data. /// /// True if successfully deserialised. - bool DeserialiseScript(EntityID entity, YAML::Node& scriptsNode) const; + bool DeserialiseScripts(EntityID entity, const YAML::Node& scriptsNode) const; /*-----------------------------------------------------------------------------*/ /* Script Editor Functions */ @@ -211,13 +211,13 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------*/ - using CsFuncPtr = void(*)(void); - using CsScriptManipFuncPtr = bool(*)(EntityID, const char*); - using CsScriptBasicFuncPtr = void(*)(EntityID); - using CsScriptOptionalFuncPtr = void(*)(EntityID, bool); - using CsScriptSerialiseYamlFuncPtr = bool(*)(EntityID, void*); - using CsScriptSerialiseYamlFuncPtr = bool(*)(EntityID, void*); - using CsScriptEditorFuncPtr = void(*)(EntityID); + using CsFuncPtr = void(*)(void); + using CsScriptManipFuncPtr = bool(*)(EntityID, const char*); + using CsScriptBasicFuncPtr = void(*)(EntityID); + using CsScriptOptionalFuncPtr = void(*)(EntityID, bool); + using CsScriptSerialiseYamlFuncPtr = bool(*)(EntityID, void*); + using CsScriptDeserialiseYamlFuncPtr = bool(*)(EntityID, const void*); + using CsScriptEditorFuncPtr = void(*)(EntityID); /*-----------------------------------------------------------------------------*/ /* Constants */ @@ -231,29 +231,29 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------*/ - SHDotNetRuntime dotNet { false }; + SHDotNetRuntime dotNet { false }; // Function Pointers to CLR Code // - Engine Lifecycle - CsFuncPtr csEngineInit = nullptr; - CsFuncPtr csEngineLoadScripts = nullptr; - CsFuncPtr csEngineUnloadScripts = nullptr; - CsFuncPtr csEngineReloadScripts = nullptr; - CsFuncPtr csEngineExit = nullptr; + CsFuncPtr csEngineInit = nullptr; + CsFuncPtr csEngineLoadScripts = nullptr; + CsFuncPtr csEngineUnloadScripts = nullptr; + CsFuncPtr csEngineReloadScripts = nullptr; + CsFuncPtr csEngineExit = nullptr; // - Scripts Store - CsFuncPtr csScriptsFrameSetUp = nullptr; - CsFuncPtr csScriptsExecuteFixedUpdate = nullptr; - CsFuncPtr csScriptsExecuteUpdate = nullptr; - CsFuncPtr csScriptsExecuteLateUpdate = nullptr; - CsFuncPtr csScriptsFrameCleanUp = nullptr; - CsScriptManipFuncPtr csScriptsAdd = nullptr; - CsScriptBasicFuncPtr csScriptsRemoveAll = nullptr; - CsScriptOptionalFuncPtr csScriptsRemoveAllImmediately = nullptr; - CsScriptSerialiseYamlFuncPtr csScriptsSerialiseYaml = nullptr; - CsScriptSerialiseYamlFuncPtr csScriptsDeserialiseYaml = nullptr; + CsFuncPtr csScriptsFrameSetUp = nullptr; + CsFuncPtr csScriptsExecuteFixedUpdate = nullptr; + CsFuncPtr csScriptsExecuteUpdate = nullptr; + CsFuncPtr csScriptsExecuteLateUpdate = nullptr; + CsFuncPtr csScriptsFrameCleanUp = nullptr; + CsScriptManipFuncPtr csScriptsAdd = nullptr; + CsScriptBasicFuncPtr csScriptsRemoveAll = nullptr; + CsScriptOptionalFuncPtr csScriptsRemoveAllImmediately = nullptr; + CsScriptSerialiseYamlFuncPtr csScriptsSerialiseYaml = nullptr; + CsScriptDeserialiseYamlFuncPtr csScriptsDeserialiseYaml = nullptr; // - Editor - CsScriptEditorFuncPtr csEditorRenderScripts = nullptr; - CsFuncPtr csEditorUndo = nullptr; - CsFuncPtr csEditorRedo = nullptr; + CsScriptEditorFuncPtr csEditorRenderScripts = nullptr; + CsFuncPtr csEditorUndo = nullptr; + CsFuncPtr csEditorRedo = nullptr; /*-----------------------------------------------------------------------------*/ /* Event Handler Functions */ diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index d0023107..63583cf2 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -79,6 +79,10 @@ namespace SHADE } } } + + // Deserialise scripts + if (node[ScriptsNode]) + SHSystemManager::GetSystem()->DeserialiseScripts(eid, node[ScriptsNode]); } void SHSerialization::DeserializeSceneFromFile(std::filesystem::path const& path) @@ -186,9 +190,6 @@ namespace SHADE SHSystemManager::GetSystem()->SerialiseScripts(eid, scripts); node[ScriptsNode] = scripts; - //YAML::Emitter emitter; - //emitter << node; - //SHLOG_TRACE(emitter.c_str()) return node; } diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index b9423795..b6bc1815 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -481,14 +481,14 @@ namespace SHADE // Check if yamlNode is valid if (yamlNode == nullptr) { - Debug::LogWarning("Attempted to serialise scripts with an invalid YAML Node! Skipping."); + Debug::LogWarning("[ScriptStore] Attempted to serialise scripts with an invalid YAML Node! Skipping."); return false; } // Check if entity exists, otherwise nothing if (!EntityUtils::IsValid(entity)) { - Debug::LogWarning("Attempted to serialise scripts for an invalid Entity! Skipping."); + Debug::LogWarning("[ScriptStore] Attempted to serialise scripts for an invalid Entity! Skipping."); return false; } @@ -518,14 +518,14 @@ namespace SHADE // Check if yamlNode is valid if (yamlNode == nullptr) { - Debug::LogWarning("Attempted to deserialise scripts with an invalid YAML Node! Skipping."); + Debug::LogWarning("[ScriptStore] Attempted to deserialise scripts with an invalid YAML Node! Skipping."); return false; } // Check if entity exists, otherwise nothing if (!EntityUtils::IsValid(entity)) { - Debug::LogWarning("Attempted to deserialise scripts for an invalid Entity! Skipping."); + Debug::LogWarning("[ScriptStore] Attempted to deserialise scripts for an invalid Entity! Skipping."); return false; } @@ -533,17 +533,27 @@ namespace SHADE for (YAML::Node& node : *yamlNode) { // Get the name of the script - const std::string typeName = ""; // TODO + if (!node["Type"]) + { + Debug::LogWarning("[ScriptStore] Script with no type detected, skipping."); + continue; + } + + System::String^ typeName = Convert::ToCLI(node["Type"].as()); // Create Script^ script; - if (AddScriptViaNameWithRef(entity, Convert::ToCLI(typeName), script)) + if (AddScriptViaNameWithRef(entity, typeName, script)) { // Copy the data in ReflectionUtilities::Deserialise(script, node); - return true; + } + else + { + Debug::LogWarning("[ScriptStore] Script with unloaded type detected, skipping."); } } + return true; SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") return false; diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ index a3d4c33b..7c39232a 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ @@ -27,7 +27,8 @@ namespace SHADE { if (fieldInfo->FieldType == FieldType::typeid) { - fieldNode = 0.0;//static_cast(safe_cast(fieldInfo->GetValue(object))); + const FieldType VALUE = safe_cast(fieldInfo->GetValue(object)); + fieldNode = static_cast(VALUE); return true; } @@ -37,7 +38,7 @@ namespace SHADE template bool ReflectionUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) { - return fieldAssignYaml(fieldInfo, object, node); + return fieldAssignYaml>(fieldInfo, object, node); } template diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx index fbae5ee0..403c913c 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx @@ -63,7 +63,6 @@ namespace SHADE /// The object to copy deserialised data into. static void Deserialise(System::Object^ object, YAML::Node& yamlNode); - private: /*-----------------------------------------------------------------------------*/ /* Serialization Helper Functions */ diff --git a/SHADE_Managed/src/Utility/Convert.hxx b/SHADE_Managed/src/Utility/Convert.hxx index b41ffef4..f04cbf4b 100644 --- a/SHADE_Managed/src/Utility/Convert.hxx +++ b/SHADE_Managed/src/Utility/Convert.hxx @@ -91,4 +91,68 @@ namespace SHADE /// Managed copy of a native std::string. static System::String^ ToCLI(const std::string& str); }; + + /// + /// Type Transformer for managed types to native types. + /// + /// + /// Managed type to get the native type for. + /// + template + struct ToNativeType + { + public: + using Value = void; + }; + template<> struct ToNativeType { using Value = int16_t; }; + template<> struct ToNativeType { using Value = int32_t; }; + template<> struct ToNativeType { using Value = int64_t; }; + template<> struct ToNativeType { using Value = uint16_t; }; + template<> struct ToNativeType { using Value = uint32_t; }; + template<> struct ToNativeType { using Value = uint64_t; }; + template<> struct ToNativeType { using Value = int8_t; }; + template<> struct ToNativeType { using Value = bool; }; + template<> struct ToNativeType { using Value = double; }; + template<> struct ToNativeType { using Value = float; }; + + /// + /// Alias for ToNativeType::Value + /// + /// + /// Managed type to get the native type for. + /// + template + using ToNativeType_T = typename ToNativeType::Value; + + /// + /// Type Transformer for native types to managed types. + /// + /// + /// Managed type to get the native type for. + /// + template + struct ToManagedType + { + public: + using Value = void; + }; + template<> struct ToManagedType { using Value = System::Byte; }; + template<> struct ToManagedType { using Value = System::Int16; }; + template<> struct ToManagedType { using Value = System::Int32; }; + template<> struct ToManagedType { using Value = System::Int64; }; + template<> struct ToManagedType { using Value = System::UInt16; }; + template<> struct ToManagedType { using Value = System::UInt32; }; + template<> struct ToManagedType { using Value = System::UInt64; }; + template<> struct ToManagedType { using Value = bool; }; + template<> struct ToManagedType { using Value = double; }; + template<> struct ToManagedType { using Value = float; }; + + /// + /// Alias for ToManagedType::Value + /// + /// + /// Managed type to get the native type for. + /// + template + using ToManagedType_T = typename ToManagedType::Value; } From 0fadbc8a9d71c5cec685bbcc4db6ba6b40ab8102 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Thu, 20 Oct 2022 19:00:36 +0800 Subject: [PATCH 16/32] Renamed Handle, ResourceManager and ResourceLibrary to SHHandle, SHResourceHub and SHResourceLibrary --- SHADE_Engine/src/Editor/SHEditor.hpp | 2 +- .../src/Graphics/Buffers/SHVkBuffer.h | 2 +- .../src/Graphics/Commands/SHVkCommandBuffer.h | 4 +-- .../src/Graphics/Commands/SHVkCommandPool.cpp | 2 +- .../src/Graphics/Commands/SHVkCommandPool.h | 2 +- .../Graphics/Descriptors/SHVkDescriptorPool.h | 2 +- .../Descriptors/SHVkDescriptorSetGroup.h | 2 +- .../Descriptors/SHVkDescriptorSetLayout.h | 2 +- .../src/Graphics/Devices/SHVkLogicalDevice.h | 4 +-- .../Graphics/Framebuffer/SHVkFramebuffer.h | 2 +- SHADE_Engine/src/Graphics/Images/SHVkImage.h | 2 +- .../src/Graphics/Images/SHVkImageView.h | 2 +- .../src/Graphics/Images/SHVkSampler.h | 2 +- .../src/Graphics/Instance/SHVkInstance.cpp | 4 +-- .../src/Graphics/Instance/SHVkInstance.h | 6 ++-- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 2 +- .../Graphics/MiddleEnd/Batching/SHBatcher.h | 2 +- .../MiddleEnd/Batching/SHSuperBatch.h | 2 +- .../MiddleEnd/Interface/SHGraphicsSystem.h | 4 +-- .../Graphics/MiddleEnd/Interface/SHMaterial.h | 2 +- .../MiddleEnd/Interface/SHMaterialInstance.h | 2 +- .../MiddleEnd/Interface/SHMeshLibrary.h | 6 ++-- .../Interface/SHPostOffscreenRenderSystem.h | 2 +- .../MiddleEnd/Interface/SHRenderable.h | 2 +- .../Graphics/MiddleEnd/Interface/SHRenderer.h | 2 +- .../MiddleEnd/Interface/SHViewport.cpp | 4 +-- .../Graphics/MiddleEnd/Interface/SHViewport.h | 6 ++-- .../Materials/SHMaterialInstanceCache.cpp | 4 +-- .../Materials/SHMaterialInstanceCache.h | 6 ++-- .../MiddleEnd/Textures/SHTextureLibrary.h | 6 ++-- .../MiddleEnd/Textures/SHVkSamplerCache.h | 2 +- .../Pipeline/SHPipelineLayoutParams.h | 2 +- .../src/Graphics/Pipeline/SHVkPipeline.h | 2 +- SHADE_Engine/src/Graphics/Queues/SHVkQueue.h | 2 +- .../RenderGraph/SHAttachmentDescInitParams.h | 2 +- .../Graphics/RenderGraph/SHRenderGraph.cpp | 2 +- .../src/Graphics/RenderGraph/SHRenderGraph.h | 4 +-- .../RenderGraph/SHRenderGraphNode.cpp | 2 +- .../Graphics/RenderGraph/SHRenderGraphNode.h | 6 ++-- .../RenderGraph/SHRenderGraphResource.h | 2 +- .../src/Graphics/RenderGraph/SHSubpass.cpp | 2 +- .../src/Graphics/RenderGraph/SHSubpass.h | 4 +-- .../Graphics/RenderGraph/SHSubpassCompute.h | 2 +- .../Graphics/Renderpass/SHVkAttachDescGen.h | 2 +- .../src/Graphics/Renderpass/SHVkRenderpass.h | 2 +- .../Graphics/Renderpass/SHVkSubpassParams.h | 2 +- SHADE_Engine/src/Graphics/SHVkUtil.h | 2 +- .../src/Graphics/Shaders/SHVkShaderModule.h | 2 +- .../src/Graphics/Swapchain/SHVkSwapchain.h | 2 +- .../src/Graphics/Synchronization/SHVkFence.h | 2 +- .../Graphics/Synchronization/SHVkSemaphore.h | 2 +- .../Graphics/Windowing/Surface/SHVkSurface.h | 2 +- .../src/Resource/{Handle.h => SHHandle.h} | 10 +++--- .../src/Resource/{Handle.hpp => SHHandle.hpp} | 6 ++-- ...ourceLibrary.cpp => SHResourceLibrary.cpp} | 4 +-- ...{ResourceLibrary.h => SHResourceLibrary.h} | 16 ++++----- ...ourceLibrary.hpp => SHResourceLibrary.hpp} | 34 +++++++++---------- 57 files changed, 107 insertions(+), 107 deletions(-) rename SHADE_Engine/src/Resource/{Handle.h => SHHandle.h} (97%) rename SHADE_Engine/src/Resource/{Handle.hpp => SHHandle.hpp} (96%) rename SHADE_Engine/src/Resource/{ResourceLibrary.cpp => SHResourceLibrary.cpp} (87%) rename SHADE_Engine/src/Resource/{ResourceLibrary.h => SHResourceLibrary.h} (96%) rename SHADE_Engine/src/Resource/{ResourceLibrary.hpp => SHResourceLibrary.hpp} (83%) diff --git a/SHADE_Engine/src/Editor/SHEditor.hpp b/SHADE_Engine/src/Editor/SHEditor.hpp index baa6324e..557c60c1 100644 --- a/SHADE_Engine/src/Editor/SHEditor.hpp +++ b/SHADE_Engine/src/Editor/SHEditor.hpp @@ -13,7 +13,7 @@ #include "ECS_Base/SHECSMacros.h" #include "ECS_Base/System/SHSystem.h" #include "ECS_Base/System/SHSystemRoutine.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "EditorWindow/SHEditorWindow.h" #include "Tools/SHLogger.h" diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h index e931fec5..aeda7d18 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h @@ -3,7 +3,7 @@ #include "Graphics/SHVulkanIncludes.h" #include "vk_mem_alloc.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h index 9416a1aa..deb1131c 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h @@ -4,7 +4,7 @@ #include "Graphics/SHVulkanIncludes.h" #include "Graphics/SHVulkanDefines.h" #include "SHCommandPoolResetMode.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" #include "Graphics/Pipeline/SHVkPipelineLayout.h" namespace SHADE @@ -36,7 +36,7 @@ namespace SHADE class SHVkCommandBuffer { friend class SHVkCommandPool; - friend class ResourceLibrary; + friend class SHResourceLibrary; static constexpr uint16_t PUSH_CONSTANT_SIZE = 512; private: diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.cpp b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.cpp index e1470898..375ece4d 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.cpp +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.cpp @@ -2,7 +2,7 @@ #include "SHVkCommandPool.h" #include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/Instance/SHVkInstance.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" #include "Tools/SHLogger.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.h index 2bb290a7..fcc0ce41 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.h +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.h @@ -6,7 +6,7 @@ #include "Graphics/Queues/SHVkQueue.h" #include "SHCommandPoolResetMode.h" #include "SHVkCommandBuffer.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.h b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.h index 9a533d06..c822829a 100644 --- a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.h +++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.h @@ -2,7 +2,7 @@ // Project Includes #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.h b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.h index f2b886e8..b2536de8 100644 --- a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.h +++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.h @@ -4,7 +4,7 @@ // Project Includes #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/Shaders/SHShaderReflected.h" #include "SHDescriptorSetUpdater.h" diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h index 4fffbc96..1639901d 100644 --- a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h +++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h @@ -2,7 +2,7 @@ // Project Includes #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h index 6f7048b8..3d59d5c6 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h @@ -8,8 +8,8 @@ #include "Graphics/SHVulkanIncludes.h" #include "Graphics/Devices/SHVkPhysicalDevice.h" #include "Graphics/Queues/SHVkQueue.h" -#include "Resource/Handle.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHHandle.h" +#include "Resource/SHResourceLibrary.h" #include "Graphics/Swapchain/SHSwapchainParams.h" #include "Graphics/Commands/SHCommandPoolResetMode.h" #include "Graphics/Commands/SHVkCommandPool.h" diff --git a/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.h b/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.h index 47bfcf99..4a02408c 100644 --- a/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.h +++ b/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.h @@ -2,7 +2,7 @@ #define SH_VK_FRAMEBUFFER_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Images/SHVkImage.h b/SHADE_Engine/src/Graphics/Images/SHVkImage.h index 51b71b26..4fb16017 100644 --- a/SHADE_Engine/src/Graphics/Images/SHVkImage.h +++ b/SHADE_Engine/src/Graphics/Images/SHVkImage.h @@ -3,7 +3,7 @@ #include "SHImageViewDetails.h" #include "Graphics/SHVulkanDefines.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" #include "vk_mem_alloc.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Images/SHVkImageView.h b/SHADE_Engine/src/Graphics/Images/SHVkImageView.h index ae23d7a7..79bb5dca 100644 --- a/SHADE_Engine/src/Graphics/Images/SHVkImageView.h +++ b/SHADE_Engine/src/Graphics/Images/SHVkImageView.h @@ -2,7 +2,7 @@ #define SH_VK_IMAGE_VIEW_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "SHImageViewDetails.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Images/SHVkSampler.h b/SHADE_Engine/src/Graphics/Images/SHVkSampler.h index bb878a69..3a989553 100644 --- a/SHADE_Engine/src/Graphics/Images/SHVkSampler.h +++ b/SHADE_Engine/src/Graphics/Images/SHVkSampler.h @@ -15,7 +15,7 @@ of DigiPen Institute of Technology is prohibited. #include // Project Includes #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp b/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp index 6688f9a6..191bc563 100644 --- a/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp +++ b/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp @@ -15,7 +15,7 @@ namespace SHADE bool SHVkInstance::validationLayersOn; vk::Instance SHVkInstance::vkInstance; SHVkDebugMessenger SHVkInstance::debugMessenger; - ResourceManager SHVkInstance::resourceManager; + SHResourceHub SHVkInstance::resourceManager; /***************************************************************************/ /*! @@ -258,7 +258,7 @@ namespace SHADE return vkInstance; } - ResourceManager& SHVkInstance::GetResourceManager(void) noexcept + SHResourceHub& SHVkInstance::GetResourceManager(void) noexcept { return resourceManager; } diff --git a/SHADE_Engine/src/Graphics/Instance/SHVkInstance.h b/SHADE_Engine/src/Graphics/Instance/SHVkInstance.h index a6fb4f0f..55adc774 100644 --- a/SHADE_Engine/src/Graphics/Instance/SHVkInstance.h +++ b/SHADE_Engine/src/Graphics/Instance/SHVkInstance.h @@ -18,7 +18,7 @@ written consent of DigiPen Institute of Technology is prohibited. #include "Graphics/Debugging/SHVkDebugMessenger.h" #include "Graphics/Devices/SHVkPhysicalDevice.h" #include "Graphics/Devices/SHVkLogicalDevice.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" namespace SHADE @@ -61,7 +61,7 @@ namespace SHADE static SHVkDebugMessenger debugMessenger; //! Resource management for vulkan project - static ResourceManager resourceManager; + static SHResourceHub resourceManager; /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER FUNCTIONS */ @@ -85,7 +85,7 @@ namespace SHADE /* Getters and Setters */ /*-----------------------------------------------------------------------*/ static vk::Instance const& GetVkInstance (void) noexcept; - static ResourceManager& GetResourceManager(void) noexcept; + static SHResourceHub& GetResourceManager(void) noexcept; }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 37c57396..193fff0d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -18,7 +18,7 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include "Graphics/SHVulkanIncludes.h" // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/MiddleEnd/Interface/SHMaterial.h" #include "Math/SHMatrix.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatcher.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatcher.h index 8074899d..f299906c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatcher.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatcher.h @@ -15,7 +15,7 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.h index 9743e7dc..9877e04e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.h @@ -15,7 +15,7 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include "Graphics/SHVulkanIncludes.h" // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "SHBatch.h" #include "Graphics/Pipeline/SHVkPipeline.h" diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 7d0fa3ab..29429703 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -17,7 +17,7 @@ of DigiPen Institute of Technology is prohibited. #include // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/SHVulkanIncludes.h" #include "Graphics/MiddleEnd/PerFrame/SHRenderContext.h" #include "Graphics/RenderGraph/SHRenderGraph.h" @@ -313,7 +313,7 @@ namespace SHADE SHWindow* window = nullptr; // Middle End Resources - ResourceManager resourceManager; + SHResourceHub resourceManager; SHMeshLibrary meshLibrary; SHTextureLibrary texLibrary; SHSamplerCache samplerCache; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.h index 348efb07..ec546fd5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.h @@ -15,7 +15,7 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "SHCommonTypes.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.h index d111acb9..b6fcc830 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.h @@ -13,7 +13,7 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h" #include "SH_API.h" diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h index e9923385..72ac1878 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h @@ -15,8 +15,8 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include // Project Includes -#include "Resource/Handle.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHHandle.h" +#include "Resource/SHResourceLibrary.h" #include "Math/SHMath.h" namespace SHADE @@ -167,7 +167,7 @@ namespace SHADE std::vector meshAddJobs; std::vector> meshRemoveJobs; // Tracking - ResourceLibrary meshes{}; + SHResourceLibrary meshes{}; std::vector> meshOrder; // CPU Storage std::vector vertPosStorage; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h index d3360b87..7a236eaf 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h @@ -1,6 +1,6 @@ #pragma once -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h index 3bb7cfda..bc885ca2 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h @@ -12,7 +12,7 @@ of DigiPen Institute of Technology is prohibited. #pragma once // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" //#include "SHTransform.h" #include "ECS_Base/Components/SHComponent.h" #include "Math/SHMatrix.h" diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h index 57c63e7f..dbd1e415 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h @@ -17,7 +17,7 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "SHCamera.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/RenderGraph/SHRenderGraph.h" #include "Math/SHMath.h" #include diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.cpp index f47920e0..df9e244e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.cpp @@ -17,7 +17,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Instance/SHVkInstance.h" #include "Tools/SHLogger.h" #include "SHRenderer.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" #include "Graphics/RenderGraph/SHRenderGraph.h" namespace SHADE @@ -49,7 +49,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Renderer Registration Functions */ /*---------------------------------------------------------------------------------*/ - Handle SHViewport::AddRenderer(ResourceManager& resourceManager, uint32_t numFrames, std::vector>& cmdPools, Handle descriptorPool, Handle cameraDescLayout, Handle renderGraph) + Handle SHViewport::AddRenderer(SHResourceHub& resourceManager, uint32_t numFrames, std::vector>& cmdPools, Handle descriptorPool, Handle cameraDescLayout, Handle renderGraph) { // Create the renderer auto renderer = resourceManager.Create(device, numFrames, cmdPools, descriptorPool, cameraDescLayout, GetHandle(), renderGraph); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.h index 04b39f00..26c0a6bd 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHViewport.h @@ -17,7 +17,7 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include "Graphics/SHVulkanIncludes.h" // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { @@ -28,7 +28,7 @@ namespace SHADE class SHVkCommandBuffer; class SHVkLogicalDevice; class SHVkImageView; - class ResourceManager; + class SHResourceHub; class SHRenderGraph; class SHVkDescriptorPool; class SHVkDescriptorSetLayout; @@ -59,7 +59,7 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ /* Renderers Registration Functions */ /*-----------------------------------------------------------------------------*/ - Handle AddRenderer(ResourceManager& resourceManager, uint32_t numFrames, std::vector>& cmdPools, Handle descriptorPool, Handle cameraDescLayout, Handle renderGraph); + Handle AddRenderer(SHResourceHub& resourceManager, uint32_t numFrames, std::vector>& cmdPools, Handle descriptorPool, Handle cameraDescLayout, Handle renderGraph); void RemoveRenderer(Handle renderer); /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.cpp index ed3d27c3..624956da 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.cpp @@ -14,7 +14,7 @@ of DigiPen Institute of Technology is prohibited. #include "SHMaterialInstanceCache.h" #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" namespace SHADE @@ -22,7 +22,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Usage Functions */ /*---------------------------------------------------------------------------------*/ - SHADE::Handle SHMaterialInstanceCache::CreateOrGet(ResourceManager& manager, Handle material) + SHADE::Handle SHMaterialInstanceCache::CreateOrGet(SHResourceHub& manager, Handle material) { // Check if there is already an existing instance auto matInst = cache.find(material); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.h b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.h index d85cb553..6612faf6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.h @@ -15,7 +15,7 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { @@ -24,7 +24,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ class SHMaterial; class SHMaterialInstance; - class ResourceManager; + class SHResourceHub; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -56,7 +56,7 @@ namespace SHADE */ /***********************************************************************************/ - Handle CreateOrGet(ResourceManager& manager, Handle material); + Handle CreateOrGet(SHResourceHub& manager, Handle material); /***********************************************************************************/ /*! diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h index c2eb85ec..935a7a4d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h @@ -17,8 +17,8 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include "tinyddsloader.h" // Project Includes -#include "Resource/Handle.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHHandle.h" +#include "Resource/SHResourceLibrary.h" #include "Math/SHMath.h" #include "Graphics/SHVulkanIncludes.h" @@ -156,7 +156,7 @@ namespace SHADE std::vector addJobs; std::vector> removeJobs; // Tracking - ResourceManager resourceManager; + SHResourceHub resourceManager; std::vector> texOrder; // CPU Storage std::vector, Handle, vk::ImageLayout>> combinedImageSamplers; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHVkSamplerCache.h b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHVkSamplerCache.h index c1529577..1adbcb8c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHVkSamplerCache.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHVkSamplerCache.h @@ -16,7 +16,7 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include "Graphics/SHVulkanIncludes.h" // Project Includes -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h b/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h index 093e03d4..d696e4a3 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h +++ b/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h @@ -2,7 +2,7 @@ #define SH_PIPELINE_LAYOUT_PARAMS_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/Descriptors/SHVkDescriptorSetLayout.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h index fe55a41e..1103d3d0 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h +++ b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h @@ -3,7 +3,7 @@ #include "SHPipelineState.h" #include "SHPipelineType.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/Pipeline/SHVkPipelineLayout.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h b/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h index 9d865ee9..a647cb72 100644 --- a/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h +++ b/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h @@ -3,7 +3,7 @@ #include "Graphics/SHVulkanIncludes.h" #include "Graphics/SHVulkanDefines.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHAttachmentDescInitParams.h b/SHADE_Engine/src/Graphics/RenderGraph/SHAttachmentDescInitParams.h index 20e924cb..845a19a4 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHAttachmentDescInitParams.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHAttachmentDescInitParams.h @@ -1,6 +1,6 @@ #pragma once -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp index 98cb6709..0bc2c98b 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp @@ -367,7 +367,7 @@ namespace SHADE , graphResources{} , resourceManager{nullptr} { - resourceManager = std::make_shared(); + resourceManager = std::make_shared(); } SHRenderGraph::SHRenderGraph(SHRenderGraph&& rhs) noexcept diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h index 952c6d8f..a919c4ff 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h @@ -2,7 +2,7 @@ #define SH_RENDER_GRAPH_H #include "Graphics/Renderpass/SHVkRenderpass.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" #include "SH_API.h" #include "Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.h" #include "Graphics/MiddleEnd/Batching/SHSuperBatch.h" @@ -71,7 +71,7 @@ namespace SHADE std::unordered_map> graphResources; //! Resource library for graph handles - std::shared_ptr resourceManager; + std::shared_ptr resourceManager; public: /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp index ec184386..5950426e 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp @@ -104,7 +104,7 @@ namespace SHADE */ /***************************************************************************/ - SHRenderGraphNode::SHRenderGraphNode(std::shared_ptr rm, Handle const& logicalDevice, Handle const& swapchain, std::vector attDescInitParams, std::vector> predecessors, std::unordered_map> const* resources) noexcept + SHRenderGraphNode::SHRenderGraphNode(std::shared_ptr rm, Handle const& logicalDevice, Handle const& swapchain, std::vector attDescInitParams, std::vector> predecessors, std::unordered_map> const* resources) noexcept : logicalDeviceHdl{ logicalDevice } , renderpass{} , framebuffers{} diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h index 77861108..3cb1c4ee 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h @@ -13,7 +13,7 @@ namespace SHADE { - class ResourceManager; + class SHResourceHub; class SHVkFramebuffer; class SHRenderGraphResource; class SHVkLogicalDevice; @@ -26,7 +26,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER VARIABLES */ /*-----------------------------------------------------------------------*/ - std::shared_ptr resourceManager; + std::shared_ptr resourceManager; //! For Vulkan object creation Handle logicalDeviceHdl; @@ -88,7 +88,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* CTORS AND DTORS */ /*-----------------------------------------------------------------------*/ - SHRenderGraphNode(std::shared_ptr rm, Handle const& logicalDevice, Handle const& swapchain, std::vector attDescInitParams, std::vector> predecessors, std::unordered_map> const* resources) noexcept; + SHRenderGraphNode(std::shared_ptr rm, Handle const& logicalDevice, Handle const& swapchain, std::vector attDescInitParams, std::vector> predecessors, std::unordered_map> const* resources) noexcept; SHRenderGraphNode(SHRenderGraphNode&& rhs) noexcept; SHRenderGraphNode& operator= (SHRenderGraphNode&& rhs) noexcept; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.h index efaf9bf5..fc8b05f7 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.h @@ -3,7 +3,7 @@ #include #include "SHAttachmentDescriptionType.h" #include -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/SHVulkanIncludes.h" #include "SH_API.h" diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp index ffbe4ff0..7c5021f8 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp @@ -182,7 +182,7 @@ namespace SHADE exteriorDrawCalls.push_back(newDrawCall); } - void SHSubpass::Init(ResourceManager& resourceManager) noexcept + void SHSubpass::Init(SHResourceHub& resourceManager) noexcept { superBatch = resourceManager.Create(GetHandle()); diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.h b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.h index c567a897..e5094708 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.h @@ -3,7 +3,7 @@ #include "SHAttachmentDescriptionType.h" #include #include "SH_API.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "Graphics/SHVulkanIncludes.h" @@ -78,7 +78,7 @@ namespace SHADE void Execute(Handle& commandBuffer, Handle descPool, uint32_t frameIndex) noexcept; void AddExteriorDrawCalls(std::function&)> const& newDrawCall) noexcept; - void Init(ResourceManager& resourceManager) noexcept; + void Init(SHResourceHub& resourceManager) noexcept; /*-----------------------------------------------------------------------*/ /* GETTERS AND SETTERS */ diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpassCompute.h b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpassCompute.h index 3ebc5676..7bbbe051 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpassCompute.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpassCompute.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Renderpass/SHVkAttachDescGen.h b/SHADE_Engine/src/Graphics/Renderpass/SHVkAttachDescGen.h index a44f4f27..8cbf72af 100644 --- a/SHADE_Engine/src/Graphics/Renderpass/SHVkAttachDescGen.h +++ b/SHADE_Engine/src/Graphics/Renderpass/SHVkAttachDescGen.h @@ -2,7 +2,7 @@ #define SH_VK_ATTACHMENT_DESC_GEN_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Renderpass/SHVkRenderpass.h b/SHADE_Engine/src/Graphics/Renderpass/SHVkRenderpass.h index 70a04bbf..c265e603 100644 --- a/SHADE_Engine/src/Graphics/Renderpass/SHVkRenderpass.h +++ b/SHADE_Engine/src/Graphics/Renderpass/SHVkRenderpass.h @@ -3,7 +3,7 @@ #include "SHVkAttachDescGen.h" #include "SHVkSubpassParams.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Renderpass/SHVkSubpassParams.h b/SHADE_Engine/src/Graphics/Renderpass/SHVkSubpassParams.h index bf789c54..6f722f5a 100644 --- a/SHADE_Engine/src/Graphics/Renderpass/SHVkSubpassParams.h +++ b/SHADE_Engine/src/Graphics/Renderpass/SHVkSubpassParams.h @@ -2,7 +2,7 @@ #define SH_VK_SUBPASS_PARAMS_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include namespace SHADE diff --git a/SHADE_Engine/src/Graphics/SHVkUtil.h b/SHADE_Engine/src/Graphics/SHVkUtil.h index ca3b6f83..dbf17826 100644 --- a/SHADE_Engine/src/Graphics/SHVkUtil.h +++ b/SHADE_Engine/src/Graphics/SHVkUtil.h @@ -3,7 +3,7 @@ #include "SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.h b/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.h index 2973d734..492710f2 100644 --- a/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.h +++ b/SHADE_Engine/src/Graphics/Shaders/SHVkShaderModule.h @@ -2,7 +2,7 @@ #define SH_VK_SHADER_MODULE_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" #include "SHShaderReflected.h" #include diff --git a/SHADE_Engine/src/Graphics/Swapchain/SHVkSwapchain.h b/SHADE_Engine/src/Graphics/Swapchain/SHVkSwapchain.h index 28bd7a4e..384665d3 100644 --- a/SHADE_Engine/src/Graphics/Swapchain/SHVkSwapchain.h +++ b/SHADE_Engine/src/Graphics/Swapchain/SHVkSwapchain.h @@ -4,7 +4,7 @@ #include #include #include "Graphics/SHVulkanIncludes.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" #include "Graphics/Swapchain/SHSwapchainParams.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h b/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h index 02445d42..7c48a5f6 100644 --- a/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h +++ b/SHADE_Engine/src/Graphics/Synchronization/SHVkFence.h @@ -2,7 +2,7 @@ #define SH_VK_FENCE_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Synchronization/SHVkSemaphore.h b/SHADE_Engine/src/Graphics/Synchronization/SHVkSemaphore.h index 57f7d7df..f03cad65 100644 --- a/SHADE_Engine/src/Graphics/Synchronization/SHVkSemaphore.h +++ b/SHADE_Engine/src/Graphics/Synchronization/SHVkSemaphore.h @@ -2,7 +2,7 @@ #define SH_VK_SEMAPHORE_H #include "Graphics/SHVulkanIncludes.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE { diff --git a/SHADE_Engine/src/Graphics/Windowing/Surface/SHVkSurface.h b/SHADE_Engine/src/Graphics/Windowing/Surface/SHVkSurface.h index 0328fecc..bf01dc9c 100644 --- a/SHADE_Engine/src/Graphics/Windowing/Surface/SHVkSurface.h +++ b/SHADE_Engine/src/Graphics/Windowing/Surface/SHVkSurface.h @@ -3,7 +3,7 @@ #include #include "Graphics/SHVulkanIncludes.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" namespace SHADE { diff --git a/SHADE_Engine/src/Resource/Handle.h b/SHADE_Engine/src/Resource/SHHandle.h similarity index 97% rename from SHADE_Engine/src/Resource/Handle.h rename to SHADE_Engine/src/Resource/SHHandle.h index 9579063c..ffc8ae6b 100644 --- a/SHADE_Engine/src/Resource/Handle.h +++ b/SHADE_Engine/src/Resource/SHHandle.h @@ -9,7 +9,7 @@ namespace SHADE /* Forward Declarations */ /*---------------------------------------------------------------------------------*/ template - class ResourceLibrary; + class SHResourceLibrary; /*---------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -140,12 +140,12 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------*/ - ResourceLibrary* library = nullptr; + SHResourceLibrary* library = nullptr; /*-----------------------------------------------------------------------------*/ /* Friend Declarations */ /*-----------------------------------------------------------------------------*/ - friend class ResourceLibrary; + friend class SHResourceLibrary; }; /// @@ -183,7 +183,7 @@ namespace SHADE /// /// Required to lock usage to ResourceLibrary only. /// Handle to set. - void SetHandle(const ResourceLibrary& rscLib, Handle hdl); + void SetHandle(const SHResourceLibrary& rscLib, Handle hdl); private: /*-----------------------------------------------------------------------------*/ @@ -220,4 +220,4 @@ namespace std }; } -#include "Handle.hpp" \ No newline at end of file +#include "SHHandle.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Resource/Handle.hpp b/SHADE_Engine/src/Resource/SHHandle.hpp similarity index 96% rename from SHADE_Engine/src/Resource/Handle.hpp rename to SHADE_Engine/src/Resource/SHHandle.hpp index 45468ec1..6f6ea31f 100644 --- a/SHADE_Engine/src/Resource/Handle.hpp +++ b/SHADE_Engine/src/Resource/SHHandle.hpp @@ -1,7 +1,7 @@ #pragma once // Primary Header -#include "Handle.h" -#include "ResourceLibrary.h" +#include "SHHandle.h" +#include "SHResourceLibrary.h" namespace SHADE { @@ -96,7 +96,7 @@ namespace SHADE return handle; } template - inline void ISelfHandle::SetHandle(const ResourceLibrary&, Handle hdl) + inline void ISelfHandle::SetHandle(const SHResourceLibrary&, Handle hdl) { handle = hdl; } diff --git a/SHADE_Engine/src/Resource/ResourceLibrary.cpp b/SHADE_Engine/src/Resource/SHResourceLibrary.cpp similarity index 87% rename from SHADE_Engine/src/Resource/ResourceLibrary.cpp rename to SHADE_Engine/src/Resource/SHResourceLibrary.cpp index b215b591..db4a8f48 100644 --- a/SHADE_Engine/src/Resource/ResourceLibrary.cpp +++ b/SHADE_Engine/src/Resource/SHResourceLibrary.cpp @@ -1,11 +1,11 @@ #include "SHPch.h" -#include "ResourceLibrary.h" +#include "SHResourceLibrary.h" namespace SHADE { /*-----------------------------------------------------------------------------*/ /* Constructors/Destructors */ /*-----------------------------------------------------------------------------*/ - ResourceManager::~ResourceManager() + SHResourceHub::~SHResourceHub() { // Delete all resources libraries for (auto iter = deleters.rbegin(); iter != deleters.rend(); ++iter) diff --git a/SHADE_Engine/src/Resource/ResourceLibrary.h b/SHADE_Engine/src/Resource/SHResourceLibrary.h similarity index 96% rename from SHADE_Engine/src/Resource/ResourceLibrary.h rename to SHADE_Engine/src/Resource/SHResourceLibrary.h index a5bf0a67..c83cf321 100644 --- a/SHADE_Engine/src/Resource/ResourceLibrary.h +++ b/SHADE_Engine/src/Resource/SHResourceLibrary.h @@ -6,7 +6,7 @@ #include // Project Headers -#include "Handle.h" +#include "SHHandle.h" #include "Resource/SparseSet.h" namespace SHADE @@ -17,13 +17,13 @@ namespace SHADE /// /// Type of resources that this library stores. template - class ResourceLibrary + class SHResourceLibrary { public: /*-----------------------------------------------------------------------------*/ /* Constructor */ /*-----------------------------------------------------------------------------*/ - ResourceLibrary(); + SHResourceLibrary(); /*-----------------------------------------------------------------------------*/ /* Usage Functions */ @@ -75,13 +75,13 @@ namespace SHADE /// /// Manages all resources in multiple ResourceLibraries. /// - class ResourceManager final + class SHResourceHub final { public: /*-----------------------------------------------------------------------------*/ /* Constructors/Destructors */ /*-----------------------------------------------------------------------------*/ - ~ResourceManager(); + ~SHResourceHub(); /*-----------------------------------------------------------------------------*/ /* Usage Functions */ @@ -136,10 +136,10 @@ namespace SHADE /* Helper Functions */ /*-----------------------------------------------------------------------------*/ template - ResourceLibrary& getLibrary(); + SHResourceLibrary& getLibrary(); template - const ResourceLibrary& getLibrary() const; + const SHResourceLibrary& getLibrary() const; }; } -#include "ResourceLibrary.hpp" \ No newline at end of file +#include "SHResourceLibrary.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Resource/ResourceLibrary.hpp b/SHADE_Engine/src/Resource/SHResourceLibrary.hpp similarity index 83% rename from SHADE_Engine/src/Resource/ResourceLibrary.hpp rename to SHADE_Engine/src/Resource/SHResourceLibrary.hpp index 9aa72ead..54120a94 100644 --- a/SHADE_Engine/src/Resource/ResourceLibrary.hpp +++ b/SHADE_Engine/src/Resource/SHResourceLibrary.hpp @@ -1,6 +1,6 @@ #pragma once // Primary Header -#include "ResourceLibrary.h" +#include "SHResourceLibrary.h" // Standard Library #include @@ -10,7 +10,7 @@ namespace SHADE /* ResourceLibrary - Constructor */ /*---------------------------------------------------------------------------------*/ template - ResourceLibrary::ResourceLibrary() + SHResourceLibrary::SHResourceLibrary() { // Type Checking //static_assert(std::is_copy_assignable_v, "Resource Library's resources must be copy assignable."); @@ -24,7 +24,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ template template - Handle ResourceLibrary::Create(Args&&... args) + Handle SHResourceLibrary::Create(Args&&... args) { // Create the handle Handle handle; @@ -55,7 +55,7 @@ namespace SHADE } template - void ResourceLibrary::Free(Handle handle) + void SHResourceLibrary::Free(Handle handle) { assertHandleValid(handle); @@ -63,7 +63,7 @@ namespace SHADE } template - T& ResourceLibrary::Get(Handle handle) + T& SHResourceLibrary::Get(Handle handle) { assertHandleValid(handle); @@ -71,7 +71,7 @@ namespace SHADE } template - const T& ResourceLibrary::Get(Handle handle) const + const T& SHResourceLibrary::Get(Handle handle) const { assertHandleValid(handle); @@ -82,14 +82,14 @@ namespace SHADE /* ResourceLibrary - Helper Functions */ /*---------------------------------------------------------------------------------*/ template - void ResourceLibrary::assertHandleValid(Handle handle) const + void SHResourceLibrary::assertHandleValid(Handle handle) const { if (!handle || handle.id.Data.Version != versionCounts[handle.id.Data.Index]) throw std::invalid_argument("Invalid handle provided!"); } template - inline uint32_t ResourceLibrary::getAvailableFreeIndex() + inline uint32_t SHResourceLibrary::getAvailableFreeIndex() { // Get from the free list if present if (!freeList.empty()) @@ -107,25 +107,25 @@ namespace SHADE /* ResourceManager - Usage Functions */ /*---------------------------------------------------------------------------------*/ template - Handle ResourceManager::Create(Args&&... args) + Handle SHResourceHub::Create(Args&&... args) { return getLibrary().Create(std::forward(args) ...); } template - void ResourceManager::Free(Handle handle) + void SHResourceHub::Free(Handle handle) { getLibrary().Free(handle); } template - T& ResourceManager::Get(Handle handle) + T& SHResourceHub::Get(Handle handle) { return getLibrary().Get(handle); } template - const T& ResourceManager::Get(Handle handle) const + const T& SHResourceHub::Get(Handle handle) const { return getLibrary().Get(handle); } @@ -134,18 +134,18 @@ namespace SHADE /* ResourceManager - Helper Functions */ /*-----------------------------------------------------------------------------*/ template - ResourceLibrary& ResourceManager::getLibrary() + SHResourceLibrary& SHResourceHub::getLibrary() { // Attempt to retrieve the library const std::type_index RSC_TYPE = typeid(T); if (resourceLibs.contains(RSC_TYPE)) { - return *static_cast*>(resourceLibs.at(RSC_TYPE)); + return *static_cast*>(resourceLibs.at(RSC_TYPE)); } else { // Construct library if doesn't exist - ResourceLibrary* lib = new ResourceLibrary(); + SHResourceLibrary* lib = new SHResourceLibrary(); resourceLibs.emplace(RSC_TYPE, static_cast(lib)); // Construct deleter to properly delete objects with void* @@ -156,8 +156,8 @@ namespace SHADE } template - const ResourceLibrary& ResourceManager::getLibrary() const + const SHResourceLibrary& SHResourceHub::getLibrary() const { - return const_cast(this).getLibrary(); + return const_cast(this).getLibrary(); } } From e01a608d8c3e5ce6112cac7d1620a2bf8baf0ca4 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Thu, 20 Oct 2022 19:16:03 +0800 Subject: [PATCH 17/32] Transform gizmo finally rendering correctly --- .../Inspector/SHEditorInspector.cpp | 6 +++- .../Editor/EditorWindow/SHEditorWindow.cpp | 5 ++- .../src/Editor/EditorWindow/SHEditorWindow.h | 8 +++-- .../ViewportWindow/SHEditorViewport.cpp | 32 ++++++++----------- .../ViewportWindow/SHEditorViewport.h | 3 ++ .../src/Editor/Gizmos/SHTransformGizmo.cpp | 16 +++++++--- SHADE_Engine/src/Editor/SHEditor.cpp | 7 ++-- SHADE_Engine/src/Editor/SHEditor.hpp | 2 +- 8 files changed, 46 insertions(+), 33 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index da09f345..b59ce9cc 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -55,7 +55,11 @@ namespace SHADE { EntityID const& eid = editor->selectedEntities[0]; SHEntity* entity = SHEntityManager::GetEntityByID(eid); - + if(!entity) + { + ImGui::End(); + return; + } ImGui::TextColored(ImGuiColors::green, "EID: %zu", eid); SHEditorWidgets::CheckBox("##IsActive", [entity]()->bool {return entity->GetActive(); }, [entity](bool const& active) {entity->SetActive(active); }); ImGui::SameLine(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp index edbe0faa..b5a691d8 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp @@ -43,9 +43,12 @@ namespace SHADE bool result = ImGui::Begin(windowName.data(), &isOpen, windowFlags); auto wndSize = ImGui::GetWindowSize(); - if(windowSize.x != wndSize.x || windowSize.y != wndSize.y) + auto contentRegionAvail = ImGui::GetContentRegionAvail(); + if( beginContentRegionAvailable.x != contentRegionAvail.x || beginContentRegionAvailable.y != contentRegionAvail.y || windowSize.x != wndSize.x || windowSize.y != wndSize.y) { windowSize = {wndSize.x, wndSize.y}; + beginContentRegionAvailable = {contentRegionAvail.x, contentRegionAvail.y}; + OnResize(); } auto wndPos = ImGui::GetWindowPos(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h index f46cd880..239d8223 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h @@ -26,6 +26,10 @@ namespace SHADE bool isOpen; bool isWindowHovered; std::string_view windowName; + SHVec2 windowSize; + SHVec2 windowPos; + SHVec2 viewportMousePos; + SHVec2 beginContentRegionAvailable; protected: virtual bool Begin(); virtual void OnResize(); @@ -33,8 +37,6 @@ namespace SHADE ImGuiWindowFlags windowFlags = 0; ImGuiIO& io; - SHVec2 windowSize; - SHVec2 windowPos; - SHVec2 viewportMousePos; + };//class SHEditorWindow }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp index 420443ae..f4403ecb 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp @@ -1,6 +1,8 @@ #include "SHpch.h" +#include "Editor/SHImGuiHelpers.hpp" #include "SHEditorViewport.h" +#include "ImGuizmo.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.hpp" #include "Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h" @@ -24,31 +26,21 @@ namespace SHADE void SHEditorViewport::Update() { SHEditorWindow::Update(); + //ImGuizmo::SetDrawlist(ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())); + + ImGuizmo::SetRect(beginCursorPos.x, beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); + transfromGizmo.Draw(); if(Begin()) { DrawMenuBar(); auto gfxSystem = SHSystemManager::GetSystem(); auto const& descriptorSet = gfxSystem->GetPostOffscreenRenderSystem()->GetDescriptorSetGroup()->GetVkHandle()[0]; auto mousePos = ImGui::GetMousePos(); - auto cursorPos = ImGui::GetCursorScreenPos(); - viewportMousePos = {mousePos.x - cursorPos.x, mousePos.y - cursorPos.y}; + beginCursorPos = ImGui::GetCursorScreenPos(); + viewportMousePos = {mousePos.x - beginCursorPos.x, mousePos.y - beginCursorPos.y}; gfxSystem->GetMousePickSystem ()->SetViewportMousePos (viewportMousePos); - //if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - //{ - // auto eid = gfxSystem->GetMousePickSystem ()->GetPickedEntity(); - // if(eid != MAX_EID) - // { - // auto editor = SHSystemManager::GetSystem(); - // editor->selectedEntities.clear(); - // editor->selectedEntities.push_back(eid); - // if (const auto hierarchyPanel = SHEditorWindowManager::GetEditorWindow()) - // { - // hierarchyPanel->SetScrollTo(eid); - // } - // } - //} - ImGui::Image((ImTextureID)descriptorSet, ImGui::GetWindowSize()); + ImGui::Image((ImTextureID)descriptorSet, {beginContentRegionAvailable.x, beginContentRegionAvailable.y}); } ImGui::End(); } @@ -66,7 +58,11 @@ namespace SHADE //auto pos = ImGui::GetCursorPos(); //windowCursorPos = {} - gfxSystem->PrepareResize(static_cast(windowSize.x), static_cast(windowSize.y)); + if(beginContentRegionAvailable.x == 0 || beginContentRegionAvailable.y == 0) + { + beginContentRegionAvailable = windowSize; + } + gfxSystem->PrepareResize(static_cast(beginContentRegionAvailable.x), static_cast(beginContentRegionAvailable.y)); } void SHEditorViewport::OnPosChange() diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h index 5f4a5919..7bcfed82 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h @@ -10,6 +10,7 @@ #include "imgui_internal.h" #include "ECS_Base/SHECSMacros.h" #include "Editor/EditorWindow/SHEditorWindow.h" +#include "Editor/Gizmos/SHTransformGizmo.h" namespace SHADE { @@ -25,5 +26,7 @@ namespace SHADE void OnPosChange() override; private: void DrawMenuBar() const noexcept; + SHTransformGizmo transfromGizmo; + SHVec2 beginCursorPos; };//class SHEditorViewport }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp index 5c3a0ce0..70f98eb1 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp @@ -8,6 +8,7 @@ #include #include "Camera/SHCameraSystem.h" +#include "Editor/EditorWindow/ViewportWindow/SHEditorViewport.h" namespace SHADE { @@ -21,20 +22,25 @@ namespace SHADE EntityID eid = editor->selectedEntities.back(); selectedEntityTranformComponent = SHComponentManager::GetComponent_s(eid); } - + if(selectedEntityTranformComponent == nullptr) + return; if(!editorCamera) { auto const cameraSystem = SHSystemManager::GetSystem(); editorCamera = cameraSystem->GetEditorCamera(); } + auto viewportWindow = SHEditorWindowManager::GetEditorWindow(); ImGuizmo::SetOrthographic(false); - SHMatrix view = editorCamera->GetViewMatrix(); - SHMatrix proj = editorCamera->GetProjMatrix(); + SHMatrix view = SHMatrix::Transpose(editorCamera->GetViewMatrix()); + view(3, 1) = -view(3, 1); + SHMatrix proj = SHMatrix::Transpose(editorCamera->GetProjMatrix()); SHMatrix mat = selectedEntityTranformComponent->GetTRS(); - ImGuizmo::DrawGrid(view.m[0], proj.m[0], mat.m[0], 10.f); - if(ImGuizmo::Manipulate(view.m[0], proj.m[0], ImGuizmo::OPERATION::UNIVERSAL, ImGuizmo::MODE::WORLD, mat.m[0])) + SHMatrix gridMat = SHMatrix::Identity; + ImGuizmo::DrawGrid(&view._11, &proj._11, &gridMat._11, 0.1f); + //ImGuizmo::ViewManipulate() + if(ImGuizmo::Manipulate(&view._11, &proj._11, ImGuizmo::OPERATION::UNIVERSAL, ImGuizmo::MODE::WORLD, &mat._11)) { } diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 7399665f..162102b3 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -88,10 +88,10 @@ namespace SHADE //Add editor windows SHEditorWindowManager::CreateEditorWindow(); - SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); io = &ImGui::GetIO(); @@ -128,11 +128,10 @@ namespace SHADE for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values) { if(window->isOpen) + { window->Update(); + } } - - ImGuizmo::SetDrawlist(ImGui::GetForegroundDrawList()); - transformGizmo.Draw(); if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z)) { diff --git a/SHADE_Engine/src/Editor/SHEditor.hpp b/SHADE_Engine/src/Editor/SHEditor.hpp index 4f8b32e2..5603e9f4 100644 --- a/SHADE_Engine/src/Editor/SHEditor.hpp +++ b/SHADE_Engine/src/Editor/SHEditor.hpp @@ -203,6 +203,6 @@ namespace SHADE ImGuiIO* io{nullptr}; - SHTransformGizmo transformGizmo; + //SHTransformGizmo transformGizmo; };//class SHEditor }//namespace SHADE From 29b6e7050d742c4a104417f33f2dfb1e269fb237 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Fri, 21 Oct 2022 08:11:12 +0800 Subject: [PATCH 18/32] camera director WIP --- SHADE_Engine/src/Camera/SHCameraDirector.cpp | 50 ++++++++++++++++++++ SHADE_Engine/src/Camera/SHCameraDirector.h | 36 ++++++++++++++ SHADE_Engine/src/Camera/SHCameraSystem.cpp | 10 ++++ SHADE_Engine/src/Camera/SHCameraSystem.h | 18 +++++-- 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 SHADE_Engine/src/Camera/SHCameraDirector.cpp create mode 100644 SHADE_Engine/src/Camera/SHCameraDirector.h diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.cpp b/SHADE_Engine/src/Camera/SHCameraDirector.cpp new file mode 100644 index 00000000..4ade9dbd --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraDirector.cpp @@ -0,0 +1,50 @@ +#include "SHpch.h" +#include "SHCameraDirector.h" +#include "SHCameraComponent.h" +#include "ECS_Base/Managers/SHComponentManager.h" +#include "ECS_Base/SHECSMacros.h" +#include "Tools/SHLog.h" + +namespace SHADE +{ + SHCameraDirector::SHCameraDirector() + :mainCameraEID(MAX_EID), transitionCameraEID(MAX_EID) + { + } + + + SHMatrix SHCameraDirector::GetViewMatrix() const noexcept + { + return viewMatrix; + } + SHMatrix SHCameraDirector::GetProjMatrix() const noexcept + { + return projMatrix; + } + SHMatrix SHCameraDirector::GetVPMatrix() const noexcept + { + return projMatrix * viewMatrix; + } + + void SHCameraDirector::UpdateMatrix() noexcept + { + if (mainCameraEID == MAX_EID) + { + return; + } + SHCameraComponent* camComponent = SHComponentManager::GetComponent_s(mainCameraEID); + if (!camComponent) + { + SHLOG_WARNING("Camera Director warning: Entity does not have a camera"); + } + else + { + viewMatrix = camComponent->GetViewMatrix(); + projMatrix = camComponent->GetProjMatrix(); + } + + + } + + +} diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.h b/SHADE_Engine/src/Camera/SHCameraDirector.h new file mode 100644 index 00000000..00e2d0c2 --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraDirector.h @@ -0,0 +1,36 @@ +#pragma once + +#include "SH_API.h" +#include "ECS_Base/Entity/SHEntity.h" +#include "Math/SHMatrix.h" + + +namespace SHADE +{ + class SH_API SHCameraDirector + { + public: + SHCameraDirector(); + ~SHCameraDirector() = default; + + + EntityID mainCameraEID; + EntityID transitionCameraEID; + + SHMatrix GetViewMatrix() const noexcept; + SHMatrix GetProjMatrix() const noexcept; + SHMatrix GetVPMatrix() const noexcept; + void UpdateMatrix() noexcept; + + + private: + + + protected: + SHMatrix viewMatrix; + SHMatrix projMatrix; + + }; + + +} diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index a9b356de..e0d983b2 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -3,6 +3,7 @@ #include "Math/SHMathHelpers.h" #include "Input/SHInputManager.h" #include "Math/Vector/SHVec2.h" +#include "ECS_Base/Managers/SHComponentManager.h" @@ -169,5 +170,14 @@ namespace SHADE upVec = SHVec3::Cross(forward, right); } + void SHCameraSystem::CameraSystemUpdate::Execute(double dt) noexcept + { + SHCameraSystem* system = static_cast(GetSystem()); + auto& dense = SHComponentManager::GetDense(); + for (auto& cam : dense) + { + system->UpdateCameraComponent(cam); + } + } } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index 43e386e7..2f8e6142 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -3,8 +3,9 @@ #include "ECS_Base/System/SHSystem.h" #include "SHCameraComponent.h" #include "ECS_Base/System/SHSystemRoutine.h" -#include "SH_API.h" +#include "Resource/ResourceLibrary.h" +#include "SH_API.h" namespace SHADE { @@ -14,7 +15,7 @@ namespace SHADE //A camera component that represents editor camera. //This is not tied to any entity. Hence this EID should not be used. SHCameraComponent editorCamera; - + public: @@ -34,13 +35,22 @@ namespace SHADE }; friend class EditorCameraUpdate; - SHCameraComponent* GetEditorCamera (void) noexcept; + class SH_API CameraSystemUpdate final: public SHSystemRoutine + { + public: + CameraSystemUpdate() : SHSystemRoutine("Camera System Update", false) {}; + virtual void Execute(double dt)noexcept override final; + }; + friend class CameraSystemUpdate; + + SHCameraComponent* GetEditorCamera (void) noexcept; + void GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& up) const noexcept; protected: void UpdateCameraComponent(SHCameraComponent& camera) noexcept; - void GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& up) const noexcept; + }; From 31ad8b8c87398b96bb6ca55c2cc18af89d7b61db Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Fri, 21 Oct 2022 09:29:13 +0800 Subject: [PATCH 19/32] Added Resource Lib and handle for CameraDirector into camera system --- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 14 ++++++++++++++ SHADE_Engine/src/Camera/SHCameraSystem.h | 7 +++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index e0d983b2..02e6cd1c 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -178,6 +178,20 @@ namespace SHADE { system->UpdateCameraComponent(cam); } + for (auto& handle : system->directorHandleList) + { + handle->UpdateMatrix(); + } + + + } + + + Handle SHCameraSystem::CreateDirector() noexcept + { + auto handle = directorLibrary.Create(); + directorHandleList.emplace_back(handle); + return handle; } } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index 2f8e6142..5b946389 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -4,7 +4,8 @@ #include "SHCameraComponent.h" #include "ECS_Base/System/SHSystemRoutine.h" #include "Resource/ResourceLibrary.h" - +#include "SHCameraDirector.h" +#include "Resource/Handle.h" #include "SH_API.h" namespace SHADE @@ -16,7 +17,8 @@ namespace SHADE //This is not tied to any entity. Hence this EID should not be used. SHCameraComponent editorCamera; - + ResourceLibrary directorLibrary; + std::vector> directorHandleList; public: SHCameraSystem(void) = default; @@ -46,6 +48,7 @@ namespace SHADE SHCameraComponent* GetEditorCamera (void) noexcept; void GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& up) const noexcept; + Handle CreateDirector() noexcept; protected: void UpdateCameraComponent(SHCameraComponent& camera) noexcept; From 267ad5f8c6a82ddb422ab7a00671581e12d67272 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Fri, 21 Oct 2022 09:53:19 +0800 Subject: [PATCH 20/32] Set up the CameraDirector creation and setting of main camera --- SHADE_Engine/src/Camera/SHCameraComponent.cpp | 11 ++++++++++- SHADE_Engine/src/Camera/SHCameraComponent.h | 2 ++ SHADE_Engine/src/Camera/SHCameraDirector.cpp | 12 +++++++++++- SHADE_Engine/src/Camera/SHCameraDirector.h | 7 +++++++ SHADE_Engine/src/Camera/SHCameraSystem.cpp | 14 +++++++++++++- SHADE_Engine/src/Camera/SHCameraSystem.h | 6 +++--- 6 files changed, 46 insertions(+), 6 deletions(-) diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.cpp b/SHADE_Engine/src/Camera/SHCameraComponent.cpp index 7ba6855c..755311a0 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.cpp +++ b/SHADE_Engine/src/Camera/SHCameraComponent.cpp @@ -1,7 +1,8 @@ #include "SHpch.h" #include "SHCameraComponent.h" #include "ECS_Base/Managers/SHComponentManager.h" - +#include "SHCameraSystem.h" +#include "ECS_Base/Managers/SHSystemManager.h" namespace SHADE { @@ -128,4 +129,12 @@ namespace SHADE return projMatrix; } + void SHCameraComponent::SetMainCamera(size_t directorCameraIndex) noexcept + { + auto system = SHSystemManager::GetSystem(); + system->GetDirector(directorCameraIndex)->SetMainCamera(*this); + } + + + } diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.h b/SHADE_Engine/src/Camera/SHCameraComponent.h index c86fa160..1149b1e1 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.h +++ b/SHADE_Engine/src/Camera/SHCameraComponent.h @@ -70,6 +70,8 @@ namespace SHADE const SHMatrix& GetViewMatrix() const noexcept; const SHMatrix& GetProjMatrix() const noexcept; + void SetMainCamera(size_t cameraDirectorIndex = 0) noexcept; + float movementSpeed; SHVec3 turnSpeed; diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.cpp b/SHADE_Engine/src/Camera/SHCameraDirector.cpp index 4ade9dbd..5e49fdfe 100644 --- a/SHADE_Engine/src/Camera/SHCameraDirector.cpp +++ b/SHADE_Engine/src/Camera/SHCameraDirector.cpp @@ -3,6 +3,7 @@ #include "SHCameraComponent.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/SHECSMacros.h" +#include "ECS_Base/Managers/SHEntityManager.h" #include "Tools/SHLog.h" namespace SHADE @@ -42,9 +43,18 @@ namespace SHADE viewMatrix = camComponent->GetViewMatrix(); projMatrix = camComponent->GetProjMatrix(); } + } - + void SHCameraDirector::SetMainCamera(SHCameraComponent& camera) noexcept + { + if (SHEntityManager::IsValidEID(camera.GetEID()) == false) + { + SHLOG_WARNING("Camera Director Warning: Attempting to set an invalid entity as main camera.") + return; + } + mainCameraEID = camera.GetEID(); } + } diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.h b/SHADE_Engine/src/Camera/SHCameraDirector.h index 00e2d0c2..b1311147 100644 --- a/SHADE_Engine/src/Camera/SHCameraDirector.h +++ b/SHADE_Engine/src/Camera/SHCameraDirector.h @@ -3,10 +3,15 @@ #include "SH_API.h" #include "ECS_Base/Entity/SHEntity.h" #include "Math/SHMatrix.h" +#include "Resource/Handle.h" namespace SHADE { + class SHCameraComponent; + + + class SH_API SHCameraDirector { public: @@ -21,6 +26,7 @@ namespace SHADE SHMatrix GetProjMatrix() const noexcept; SHMatrix GetVPMatrix() const noexcept; void UpdateMatrix() noexcept; + void SetMainCamera(SHCameraComponent& cam) noexcept; private: @@ -32,5 +38,6 @@ namespace SHADE }; + typedef Handle DirectorHandle; } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 02e6cd1c..04c017c0 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -187,11 +187,23 @@ namespace SHADE } - Handle SHCameraSystem::CreateDirector() noexcept + DirectorHandle SHCameraSystem::CreateDirector() noexcept { auto handle = directorLibrary.Create(); directorHandleList.emplace_back(handle); return handle; } + DirectorHandle SHCameraSystem::GetDirector(size_t index) noexcept + { + if (index < directorHandleList.size()) + { + return directorHandleList[index]; + } + else + { + return CreateDirector(); + } + } + } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index 5b946389..0d89b842 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -5,7 +5,6 @@ #include "ECS_Base/System/SHSystemRoutine.h" #include "Resource/ResourceLibrary.h" #include "SHCameraDirector.h" -#include "Resource/Handle.h" #include "SH_API.h" namespace SHADE @@ -18,7 +17,7 @@ namespace SHADE SHCameraComponent editorCamera; ResourceLibrary directorLibrary; - std::vector> directorHandleList; + std::vector directorHandleList; public: SHCameraSystem(void) = default; @@ -48,7 +47,8 @@ namespace SHADE SHCameraComponent* GetEditorCamera (void) noexcept; void GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& up) const noexcept; - Handle CreateDirector() noexcept; + DirectorHandle CreateDirector() noexcept; + DirectorHandle GetDirector(size_t index) noexcept; protected: void UpdateCameraComponent(SHCameraComponent& camera) noexcept; From 7e04bee8d6a7ef341430a054017177f64d9788c1 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Fri, 21 Oct 2022 10:17:32 +0800 Subject: [PATCH 21/32] Defaults main camera if the mainCamera is still unset --- SHADE_Engine/src/Camera/SHCameraDirector.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.cpp b/SHADE_Engine/src/Camera/SHCameraDirector.cpp index 5e49fdfe..559897c0 100644 --- a/SHADE_Engine/src/Camera/SHCameraDirector.cpp +++ b/SHADE_Engine/src/Camera/SHCameraDirector.cpp @@ -31,7 +31,12 @@ namespace SHADE { if (mainCameraEID == MAX_EID) { - return; + auto& dense = SHComponentManager::GetDense(); + if (dense.size() == 0) + { + return; + } + mainCameraEID = dense[0].GetEID(); } SHCameraComponent* camComponent = SHComponentManager::GetComponent_s(mainCameraEID); if (!camComponent) From 7e15edb052391fcd1f6ad35ef32771a42fd96c60 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 21 Oct 2022 13:58:27 +0800 Subject: [PATCH 22/32] Completed initial implementation of SHResourceManager --- SHADE_Application/src/Scenes/SBTestScene.cpp | 2 +- SHADE_Engine/src/Assets/SHAssetManager.cpp | 7 +- SHADE_Engine/src/Assets/SHAssetManager.h | 1 + .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 6 +- .../MiddleEnd/Interface/SHGraphicsSystem.h | 6 +- .../src/Resource/SHResourceManager.cpp | 59 +++++++ SHADE_Engine/src/Resource/SHResourceManager.h | 99 ++++++++++++ .../src/Resource/SHResourceManager.hpp | 147 ++++++++++++++++++ 8 files changed, 319 insertions(+), 8 deletions(-) create mode 100644 SHADE_Engine/src/Resource/SHResourceManager.cpp create mode 100644 SHADE_Engine/src/Resource/SHResourceManager.h create mode 100644 SHADE_Engine/src/Resource/SHResourceManager.hpp diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index f1d656ee..bd812933 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -64,7 +64,7 @@ namespace Sandbox std::vector> texHandles; for (const auto& tex : textures) { - auto texture = graphicsSystem->Add(tex); + auto texture = graphicsSystem->AddTexture(tex); texHandles.push_back(texture); } graphicsSystem->BuildTextures(); diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index aa9772dd..956d52e8 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -62,7 +62,12 @@ namespace SHADE } } - AssetPath SHAssetManager::GenerateLocalPath(AssetPath path) noexcept + void SHAssetManager::Unload(AssetID assetId) noexcept + { + // TODO + } + + AssetPath SHAssetManager::GenerateLocalPath(AssetPath path) noexcept { if (!IsRecognised(path.extension().string().c_str())) { diff --git a/SHADE_Engine/src/Assets/SHAssetManager.h b/SHADE_Engine/src/Assets/SHAssetManager.h index 50549e01..2469165a 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.h +++ b/SHADE_Engine/src/Assets/SHAssetManager.h @@ -32,6 +32,7 @@ namespace SHADE * \brief Deallocate all memory used by resource data ****************************************************************************/ static void Unload() noexcept; + static void Unload(AssetID assetId) noexcept; /**************************************************************************** * \brief Load all resources that are in the folder diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 06762db8..44404bc7 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -585,19 +585,19 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Texture Registration Functions */ /*---------------------------------------------------------------------------------*/ - Handle SHGraphicsSystem::Add(const SHTextureAsset& texAsset) + Handle SHGraphicsSystem::AddTexture(const SHTextureAsset& texAsset) { auto sampler = samplerCache.GetSampler(device, SHVkSamplerParams { .maxLod = static_cast(texAsset.mipOffsets.size()) }); return texLibrary.Add(texAsset, sampler); } - SHADE::Handle SHGraphicsSystem::Add(uint32_t pixelCount, const SHTexture::PixelChannel* const pixelData, uint32_t width, uint32_t height, SHTexture::TextureFormat format, std::vector mipOffsets) + SHADE::Handle SHGraphicsSystem::AddTexture(uint32_t pixelCount, const SHTexture::PixelChannel* const pixelData, uint32_t width, uint32_t height, SHTexture::TextureFormat format, std::vector mipOffsets) { auto sampler = samplerCache.GetSampler(device, SHVkSamplerParams{ .maxLod = static_cast(mipOffsets.size()) }); return texLibrary.Add(pixelCount, pixelData, width, height, format, mipOffsets, sampler); } - void SHGraphicsSystem::Remove(Handle tex) + void SHGraphicsSystem::RemoveTexture(Handle tex) { texLibrary.Remove(tex); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 29429703..870325ac 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -231,8 +231,8 @@ namespace SHADE */ /*******************************************************************************/ - Handle Add(const SHTextureAsset& texAsset); - Handle Add(uint32_t pixelCount, const SHTexture::PixelChannel* const pixelData, uint32_t width, uint32_t height, SHTexture::TextureFormat format, std::vector mipOffsets); + Handle AddTexture(const SHTextureAsset& texAsset); + Handle AddTexture(uint32_t pixelCount, const SHTexture::PixelChannel* const pixelData, uint32_t width, uint32_t height, SHTexture::TextureFormat format, std::vector mipOffsets); /*******************************************************************************/ /*! @@ -246,7 +246,7 @@ namespace SHADE */ /*******************************************************************************/ - void Remove(Handle tex); + void RemoveTexture(Handle tex); /***************************************************************************/ /*! diff --git a/SHADE_Engine/src/Resource/SHResourceManager.cpp b/SHADE_Engine/src/Resource/SHResourceManager.cpp new file mode 100644 index 00000000..cd5c7abd --- /dev/null +++ b/SHADE_Engine/src/Resource/SHResourceManager.cpp @@ -0,0 +1,59 @@ +/************************************************************************************//*! +\file SHResourceManager.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 21, 2022 +\brief Contains the definition of the functions of the SHResourceManager static + class. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#include "SHpch.h" +// Primary Include +#include "SHResourceManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Static Data Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + SHResourceHub SHResourceManager::resourceHub; + std::unordered_map> SHResourceManager::handlesMap; + std::unordered_map> SHResourceManager::typedFreeFuncMap; + std::vector SHResourceManager::loadedAssetData; + + /*-----------------------------------------------------------------------------------*/ + /* Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + void SHResourceManager::Unload(AssetID assetId) + { + // Search each library for the asset ID and try to free it + for (auto& typedHandleMap : handlesMap) + { + if (typedHandleMap.second.contains(assetId)) + { + // Dispose + typedFreeFuncMap[typedHandleMap.first](assetId); + typedHandleMap.second.erase(assetId); + } + } + } + + void SHResourceManager::FinaliseChanges() + { + SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem == nullptr) + throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); + gfxSystem->BuildMeshBuffers(); + gfxSystem->BuildTextures(); + + // Free CPU Resources + for (auto assetId : loadedAssetData) + { + SHAssetManager::Unload(assetId); + } + loadedAssetData.clear(); + } +} diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h new file mode 100644 index 00000000..8c41f778 --- /dev/null +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -0,0 +1,99 @@ +/************************************************************************************//*! +\file SHResourceManager.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 21, 2022 +\brief Contains the definition of the SHResourceManager static class. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// STL Includes +#include +// Project Includes +#include "SH_API.h" +#include "SHResourceLibrary.h" +#include "Assets/SHAssetMacros.h" + +namespace SHADE +{ + /// + /// Static class responsible for loading and caching runtime resources from their + /// serialised Asset IDs. + /// + class SH_API SHResourceManager + { + public: + /*---------------------------------------------------------------------------------*/ + /* Loading Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Loads or retrieves an existing loaded object of the specified type with the + /// specified asset ID. + /// Note that for specific types, the retrieved Handle may not be valid until after + /// FinaliseChanges() is called. + /// + /// + /// Type of resource to load. + /// + /// Asset ID of the resource to load. + /// Handle to a loaded runtime asset. + template + static Handle LoadOrGet(AssetID assetId); + /// + /// Unloads an existing loaded asset. Attempting to unload an invalid Handle will + /// simply do nothing except emit a warning. + /// Faster than the untemplated version. + /// + /// Type of resource to unload. + /// Handle to the resource to unload. + template + static void Unload(Handle assetId); + /// + /// Unloads an existing loaded asset. Attempting to unload an invalid Handle will + /// simply do nothing except emit a warning. + /// Compared to the templated version, this function is slower as it requires + /// searching through the storage of all resource types. + /// + /// Handle to the resource to unload. + static void Unload(AssetID assetId); + /// + /// Needs to be called to finalise all changes to loads. + /// + static void FinaliseChanges(); + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + using AssetHandleMap = std::unordered_map; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + // Handles + static SHResourceHub resourceHub; + static std::unordered_map handlesMap; + static std::unordered_map> typedFreeFuncMap; + // Pointers to temp CPU resources + static std::vector loadedAssetData; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Retrieves or creates the AssetHandleMap for the specific type if it doesn't exist + /// + /// + /// The type of AssetHandleMap to retrieve. + /// + /// Reference to the AssetHandleMap of the specified type. + template + static AssetHandleMap& getAssetHandleMap(); + }; +} + +#include "SHResourceManager.hpp" diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp new file mode 100644 index 00000000..7e90f499 --- /dev/null +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -0,0 +1,147 @@ +/************************************************************************************//*! +\file SHResourceManager.hpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 21, 2022 +\brief Contains the definition of the function templates of the + SHResourceManager static class. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once +// Primary Include +#include "SHResourceManager.h" +// Project Includes +#include "Assets/SHAssetManager.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Tools/SHLog.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Loading Functions */ + /*-----------------------------------------------------------------------------------*/ + template + Handle SHResourceManager::LoadOrGet(AssetID assetId) + { + // Check if it is an unsupported type + if (!std::is_same_v && !std::is_same_v) + { + static_assert(true, "Unsupported Resource Type specified for SHResourceManager."); + } + + /* Attempt to get existing loaded asset */ + AssetHandleMap& typedHandleMap = getAssetHandleMap(); + if (typedHandleMap.contains(assetId)) + return typedHandleMap[assetId]; + + /* Otherwise, we need to load it! */ + // Meshes + if constexpr (std::is_same_v) + { + // Get system + SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem == nullptr) + throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); + + // Load + SHMeshAsset* assetData = SHAssetManager::GetMesh(assetId); + if (assetData == nullptr) + { + SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); + return {}; + } + loadedAssetData.emplace_back(assetId); + + Handle meshHandle = gfxSystem->AddMesh + ( + assetData->vertexPosition.size(), + assetData->vertexPosition.data(), + assetData->texCoords.data(), + assetData->vertexTangent.data(), + assetData->vertexNormal.data(), + assetData->indices.size(), + assetData->indices.data() + ); + typedHandleMap.insert(assetId, meshHandle); + return meshHandle; + } + // Textures + else if constexpr (std::is_same_v) + { + // Get system + SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem == nullptr) + throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); + + // Load + SHTextureAsset* assetData = SHAssetManager::GetTexture(assetId); + if (assetData == nullptr) + { + SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); + return {}; + } + loadedAssetData.emplace_back(assetId); + + Handle texHandle = gfxSystem->AddTexture + ( + assetData->numBytes, + assetData->pixelData, + assetData->width, + assetData->height, + assetData->format, + assetData->mipOffsets + ); + typedHandleMap.insert(assetId, texHandle); + return texHandle; + } + } + + template + void SHResourceManager::Unload(Handle assetId) + { + // Check if it is an usupported type + if (!std::is_same_v && !std::is_same_v) + { + static_assert(true, "Unsupported Resource Type specified for SHResourceManager."); + } + + /* Attempt to get existing loaded asset */ + AssetHandleMap& typedHandleMap = getAssetHandleMap(); + if (typedHandleMap.contains(assetId)) + { + // Dispose + static_cast>(typedHandleMap[assetId]).Free(); + typedHandleMap.erase(assetId); + } + else + { + // There's nothing to remove + SHLog::Warning("[SHResourceManager] Attempted to unload an invalid resource. Ignoring."); + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + template + SHResourceManager::AssetHandleMap& SHResourceManager::getAssetHandleMap() + { + if (!handlesMap.contains(typeid(ResourceType))) + { + handlesMap.insert(typeid(ResourceType)); + typedFreeFuncMap.insert + ( + typeid(ResourceType), + [](AssetID assetId) + { + static_cast>(SHResourceManager::handlesMap[typeid(ResourceType)][assetId]).Free(); + } + ); + } + return handlesMap[typeid(ResourceType)]; + } +} From 43c2680a8243bc0bd7955ca6f69989b39de5808d Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 21 Oct 2022 15:38:39 +0800 Subject: [PATCH 23/32] Added generic Handles and fixed bugs with SHResourceManager --- SHADE_Application/src/Scenes/SBTestScene.cpp | 41 +++++------- SHADE_Engine/src/Resource/SHHandle.h | 66 ++++++++++++++++++- SHADE_Engine/src/Resource/SHHandle.hpp | 41 +++++++++++- SHADE_Engine/src/Resource/SHResourceLibrary.h | 20 +++++- .../src/Resource/SHResourceLibrary.hpp | 5 +- .../src/Resource/SHResourceManager.cpp | 2 +- SHADE_Engine/src/Resource/SHResourceManager.h | 2 +- .../src/Resource/SHResourceManager.hpp | 14 ++-- 8 files changed, 149 insertions(+), 42 deletions(-) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index bd812933..41326174 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -1,4 +1,4 @@ -#include "SBpch.h" + #include "SBpch.h" #include "SBTestScene.h" #include "ECS_Base/Managers/SHSystemManager.h" @@ -14,6 +14,7 @@ #include "Physics/Components/SHColliderComponent.h" #include "Assets/SHAssetManager.h" +#include "Resource/SHResourceManager.h" using namespace SHADE; @@ -40,34 +41,22 @@ namespace Sandbox const auto CUBE_MESH = SHADE::SHPrimitiveGenerator::Cube(*graphicsSystem); //Test Racoon mesh - auto meshes = SHADE::SHAssetManager::GetAllMeshes(); std::vector> handles; - for (auto const& mesh : meshes) + std::vector> texHandles; + for (const auto& asset : SHAssetManager::GetAllAssets()) { - if (mesh.header.meshName == "Cube.012") + switch (asset.type) { - handles.push_back(graphicsSystem->AddMesh( - mesh.header.vertexCount, - mesh.vertexPosition.data(), - mesh.texCoords.data(), - mesh.vertexTangent.data(), - mesh.vertexNormal.data(), - mesh.header.indexCount, - mesh.indices.data() - )); + case AssetType::MESH: + if (asset.name == "Cube.012") + handles.emplace_back(SHResourceManager::LoadOrGet(asset.id)); + break; + case AssetType::TEXTURE: + texHandles.emplace_back(SHResourceManager::LoadOrGet(asset.id)); + break; } } - graphicsSystem->BuildMeshBuffers(); - - // Load Textures - auto textures = SHADE::SHAssetManager::GetAllTextures(); - std::vector> texHandles; - for (const auto& tex : textures) - { - auto texture = graphicsSystem->AddTexture(tex); - texHandles.push_back(texture); - } - graphicsSystem->BuildTextures(); + SHResourceManager::FinaliseChanges(); // Create Materials auto matInst = graphicsSystem->AddOrGetBaseMaterialInstance(); @@ -78,8 +67,8 @@ namespace Sandbox // Create Stress Test Objects static const SHVec3 TEST_OBJ_SCALE = SHVec3::One * 0.5f; - constexpr int NUM_ROWS = 10; - constexpr int NUM_COLS = 10; + constexpr int NUM_ROWS = 0; + constexpr int NUM_COLS = 0; static const SHVec3 TEST_OBJ_SPACING = { 0.1f, 0.1f, 0.1f }; static const SHVec3 TEST_OBJ_START_POS = { -(NUM_COLS / 2 * TEST_OBJ_SPACING.x) + 1.0f, -2.0f, -1.0f }; diff --git a/SHADE_Engine/src/Resource/SHHandle.h b/SHADE_Engine/src/Resource/SHHandle.h index ffc8ae6b..6acc85ed 100644 --- a/SHADE_Engine/src/Resource/SHHandle.h +++ b/SHADE_Engine/src/Resource/SHHandle.h @@ -8,6 +8,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Forward Declarations */ /*---------------------------------------------------------------------------------*/ + class SHResourceLibraryBase; template class SHResourceLibrary; @@ -25,6 +26,20 @@ namespace SHADE using std::runtime_error::runtime_error; }; + /// + /// Exception thrown when a generic Handle is being casted to the wrong type. + /// + class BadHandleCastException : std::runtime_error + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + BadHandleCastException() + : std::runtime_error("Attempted to cast a generic Handle to the wrong type. ") + {} + }; + /// /// Base implementation of the Handle that is not templated to allow for holding /// generic non-type-specific Handles. @@ -80,7 +95,7 @@ namespace SHADE /// Generic implementation of a Handle object /// /// Type of the handle. - template + template class Handle : public HandleBase { public: @@ -88,6 +103,16 @@ namespace SHADE /* Constructors */ /*-----------------------------------------------------------------------------*/ Handle() = default; + /// + /// Converts a generic/void Handle to a specific type. + /// Runtime type checking is enabled to ensure that Handles are only being casted + /// to the correct type. + /// + /// Generic handle to convert. + /// + /// Thrown if an invalid conversion is made. + /// + explicit Handle(const Handle& genericHandle); ~Handle() = default; /*-----------------------------------------------------------------------------*/ @@ -146,7 +171,42 @@ namespace SHADE /* Friend Declarations */ /*-----------------------------------------------------------------------------*/ friend class SHResourceLibrary; - }; + friend class Handle; + }; + + /// + /// Template Specialization for Handle that represents a type-less Handle. + /// + template<> + class Handle : public HandleBase + { + public: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + Handle() = default; + template + explicit Handle(const Handle& handle); + ~Handle() = default; + + /*-----------------------------------------------------------------------------*/ + /* Overloaded Operators */ + /*-----------------------------------------------------------------------------*/ + template + inline bool operator==(const Handle& rhs) const noexcept; + + protected: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + SHResourceLibraryBase* library = nullptr; + + /*-----------------------------------------------------------------------------*/ + /* Friend Declarations */ + /*-----------------------------------------------------------------------------*/ + template + friend class Handle; + }; /// /// Interface that needs to be implemented by classes that want to store a Handle to @@ -155,7 +215,7 @@ namespace SHADE template class ISelfHandle { - public: + public: /*-----------------------------------------------------------------------------*/ /* Constructors */ /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHHandle.hpp b/SHADE_Engine/src/Resource/SHHandle.hpp index 6f6ea31f..53061ac7 100644 --- a/SHADE_Engine/src/Resource/SHHandle.hpp +++ b/SHADE_Engine/src/Resource/SHHandle.hpp @@ -20,7 +20,21 @@ namespace SHADE { return id.Raw != INVALID_ID.Raw; } - + + /*---------------------------------------------------------------------------------*/ + /* Handle - Constructors */ + /*---------------------------------------------------------------------------------*/ + template + Handle::Handle(const Handle& genericHandle) + : library { reinterpret_cast*>(genericHandle.library) } + { + id = genericHandle.id; + + // Check if valid + if (library != nullptr && library->GetType() != typeid(T)) + throw BadHandleCastException(); + } + /*---------------------------------------------------------------------------------*/ /* Handle - Usage Functions */ /*---------------------------------------------------------------------------------*/ @@ -63,6 +77,28 @@ namespace SHADE return &library->Get(*this); } + /*---------------------------------------------------------------------------------*/ + /* Handle - Constructors */ + /*---------------------------------------------------------------------------------*/ + template + Handle::Handle(const Handle& handle) + : library{ static_cast(handle.library) } + { + id = handle.id; + } + + /*---------------------------------------------------------------------------------*/ + /* Handle - Overloaded Operators */ + /*---------------------------------------------------------------------------------*/ + template + bool SHADE::Handle::operator==(const Handle& rhs) const noexcept + { + return id.Raw == rhs.id.Raw && library == static_cast(rhs.library); + } + + /*---------------------------------------------------------------------------------*/ + /* ISelfHandle - Constructors */ + /*---------------------------------------------------------------------------------*/ template inline ISelfHandle::ISelfHandle(const ISelfHandle& rhs) : handle { rhs.handle } @@ -73,6 +109,9 @@ namespace SHADE : handle { rhs.handle } {} + /*---------------------------------------------------------------------------------*/ + /* ISelfHandle - Overloaded Operators */ + /*---------------------------------------------------------------------------------*/ template inline ISelfHandle& ISelfHandle::operator=(const ISelfHandle& rhs) { diff --git a/SHADE_Engine/src/Resource/SHResourceLibrary.h b/SHADE_Engine/src/Resource/SHResourceLibrary.h index c83cf321..46ae4572 100644 --- a/SHADE_Engine/src/Resource/SHResourceLibrary.h +++ b/SHADE_Engine/src/Resource/SHResourceLibrary.h @@ -11,13 +11,31 @@ namespace SHADE { + /// + /// Base class for SHResourceLibrary that holds information about the library type. + /// + class SHResourceLibraryBase + { + public: + /*-----------------------------------------------------------------------------*/ + /* Getter Functions */ + /*-----------------------------------------------------------------------------*/ + inline std::type_index GetType() { return libraryType; } + + protected: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + std::type_index libraryType = typeid(void); + }; + /// /// Generic Resource Library for a specified type of Resource. This object will own /// any resources created using it. /// /// Type of resources that this library stores. template - class SHResourceLibrary + class SHResourceLibrary : public SHResourceLibraryBase { public: /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHResourceLibrary.hpp b/SHADE_Engine/src/Resource/SHResourceLibrary.hpp index 54120a94..411f6bf5 100644 --- a/SHADE_Engine/src/Resource/SHResourceLibrary.hpp +++ b/SHADE_Engine/src/Resource/SHResourceLibrary.hpp @@ -13,10 +13,11 @@ namespace SHADE SHResourceLibrary::SHResourceLibrary() { // Type Checking - //static_assert(std::is_copy_assignable_v, "Resource Library's resources must be copy assignable."); - //static_assert(std::is_copy_constructible_v, "Resource Library's resources must be copy constructible."); static_assert(std::is_move_assignable_v, "Resource Library's resources must be move assignable."); static_assert(std::is_move_constructible_v, "Resource Library's resources must be move constructible."); + + // Keep track of the type for conversions + libraryType = typeid(T); } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHResourceManager.cpp b/SHADE_Engine/src/Resource/SHResourceManager.cpp index cd5c7abd..a82f7890 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.cpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.cpp @@ -20,7 +20,7 @@ namespace SHADE /* Static Data Member Definitions */ /*-----------------------------------------------------------------------------------*/ SHResourceHub SHResourceManager::resourceHub; - std::unordered_map> SHResourceManager::handlesMap; + std::unordered_map>> SHResourceManager::handlesMap; std::unordered_map> SHResourceManager::typedFreeFuncMap; std::vector SHResourceManager::loadedAssetData; diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index 8c41f778..f43b4a77 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -69,7 +69,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using AssetHandleMap = std::unordered_map; + using AssetHandleMap = std::unordered_map>; /*---------------------------------------------------------------------------------*/ /* Data Members */ diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 7e90f499..593f8bb7 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -36,7 +36,7 @@ namespace SHADE /* Attempt to get existing loaded asset */ AssetHandleMap& typedHandleMap = getAssetHandleMap(); if (typedHandleMap.contains(assetId)) - return typedHandleMap[assetId]; + return Handle(typedHandleMap[assetId]); /* Otherwise, we need to load it! */ // Meshes @@ -48,7 +48,7 @@ namespace SHADE throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); // Load - SHMeshAsset* assetData = SHAssetManager::GetMesh(assetId); + const SHMeshAsset* assetData = SHAssetManager::GetMesh(assetId); if (assetData == nullptr) { SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); @@ -66,7 +66,7 @@ namespace SHADE assetData->indices.size(), assetData->indices.data() ); - typedHandleMap.insert(assetId, meshHandle); + typedHandleMap.emplace(assetId, Handle(meshHandle)); return meshHandle; } // Textures @@ -78,7 +78,7 @@ namespace SHADE throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); // Load - SHTextureAsset* assetData = SHAssetManager::GetTexture(assetId); + const SHTextureAsset* assetData = SHAssetManager::GetTexture(assetId); if (assetData == nullptr) { SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); @@ -95,7 +95,7 @@ namespace SHADE assetData->format, assetData->mipOffsets ); - typedHandleMap.insert(assetId, texHandle); + typedHandleMap.emplace(assetId, Handle(texHandle)); return texHandle; } } @@ -132,8 +132,8 @@ namespace SHADE { if (!handlesMap.contains(typeid(ResourceType))) { - handlesMap.insert(typeid(ResourceType)); - typedFreeFuncMap.insert + handlesMap.emplace(typeid(ResourceType), AssetHandleMap{}); + typedFreeFuncMap.emplace ( typeid(ResourceType), [](AssetID assetId) From e5628fbed881ba391206d1ebecb62422a99806c1 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 21 Oct 2022 17:09:50 +0800 Subject: [PATCH 24/32] Added functionality to retrieve AssetID from SHResourceManager using a Handle --- .../src/Resource/SHResourceManager.cpp | 54 ++++++++++++++--- SHADE_Engine/src/Resource/SHResourceManager.h | 33 ++++++++++- .../src/Resource/SHResourceManager.hpp | 58 +++++++++++++------ 3 files changed, 118 insertions(+), 27 deletions(-) diff --git a/SHADE_Engine/src/Resource/SHResourceManager.cpp b/SHADE_Engine/src/Resource/SHResourceManager.cpp index a82f7890..156c31c7 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.cpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.cpp @@ -21,6 +21,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHResourceHub SHResourceManager::resourceHub; std::unordered_map>> SHResourceManager::handlesMap; + std::unordered_map SHResourceManager::assetIdMap; std::unordered_map> SHResourceManager::typedFreeFuncMap; std::vector SHResourceManager::loadedAssetData; @@ -30,30 +31,65 @@ namespace SHADE void SHResourceManager::Unload(AssetID assetId) { // Search each library for the asset ID and try to free it + Handle handle; for (auto& typedHandleMap : handlesMap) { if (typedHandleMap.second.contains(assetId)) { + // Save handle for later + handle = typedHandleMap.second[assetId]; // Dispose typedFreeFuncMap[typedHandleMap.first](assetId); typedHandleMap.second.erase(assetId); } } + + // No handles were found + if (!handle) + return; + + for (auto& typedAssetIdsMap : assetIdMap) + { + if (typedAssetIdsMap.second.contains(handle)) + { + // Dispose + typedAssetIdsMap.second.erase(handle); + } + } } void SHResourceManager::FinaliseChanges() { - SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); - if (gfxSystem == nullptr) - throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); - gfxSystem->BuildMeshBuffers(); - gfxSystem->BuildTextures(); + SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem == nullptr) + throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); + gfxSystem->BuildMeshBuffers(); + gfxSystem->BuildTextures(); - // Free CPU Resources - for (auto assetId : loadedAssetData) + // Free CPU Resources + for (auto assetId : loadedAssetData) + { + SHAssetManager::Unload(assetId); + } + loadedAssetData.clear(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Query Functions */ + /*-----------------------------------------------------------------------------------*/ + std::optional SHResourceManager::GetAssetID(Handle handle) + { + const Handle GENERIC_HANDLE = Handle(handle); + + // Search each library for the asset ID and try to free it + for (auto& typedAssetIdsMap : assetIdMap) + { + if (typedAssetIdsMap.second.contains(GENERIC_HANDLE)) { - SHAssetManager::Unload(assetId); + return typedAssetIdsMap.second[GENERIC_HANDLE]; } - loadedAssetData.clear(); + } + + return {}; } } diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index f43b4a77..84f93b10 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -65,11 +65,41 @@ namespace SHADE /// static void FinaliseChanges(); + /*---------------------------------------------------------------------------------*/ + /* Query Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Retrieves the AssetID associated with a specified Handle. + /// Faster than the untemplated version. + /// + /// Type of resource to get the ID of. + /// Handle to get the AssetID of. + /// + /// AssetID for the specified Handle. If the Handle is invalid, there will be no + /// value. + /// + template + static std::optional GetAssetID(Handle handle); + /// + /// Retrieves the AssetID associated with a specified Handle. + /// Compared to the templated version, this function is slower as it requires + /// searching through the storage of all resource types. + /// + /// Handle to get the AssetID of. + /// + /// AssetID for the specified Handle. If the Handle is invalid, there will be no + /// value. + /// + static std::optional GetAssetID(Handle handle); + private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ using AssetHandleMap = std::unordered_map>; + using HandleAssetMap = std::unordered_map, AssetID>; + using AssetHandleMapRef = std::reference_wrapper; + using HandleAssetMapRef = std::reference_wrapper; /*---------------------------------------------------------------------------------*/ /* Data Members */ @@ -77,6 +107,7 @@ namespace SHADE // Handles static SHResourceHub resourceHub; static std::unordered_map handlesMap; + static std::unordered_map assetIdMap; static std::unordered_map> typedFreeFuncMap; // Pointers to temp CPU resources static std::vector loadedAssetData; @@ -92,7 +123,7 @@ namespace SHADE /// /// Reference to the AssetHandleMap of the specified type. template - static AssetHandleMap& getAssetHandleMap(); + static std::pair getAssetHandleMap(); }; } diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 593f8bb7..54ced715 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -34,9 +34,9 @@ namespace SHADE } /* Attempt to get existing loaded asset */ - AssetHandleMap& typedHandleMap = getAssetHandleMap(); - if (typedHandleMap.contains(assetId)) - return Handle(typedHandleMap[assetId]); + auto [typedHandleMap, typedAssetIdMap] = getAssetHandleMap(); + if (typedHandleMap.get().contains(assetId)) + return Handle(typedHandleMap.get()[assetId]); /* Otherwise, we need to load it! */ // Meshes @@ -66,7 +66,9 @@ namespace SHADE assetData->indices.size(), assetData->indices.data() ); - typedHandleMap.emplace(assetId, Handle(meshHandle)); + Handle genericHandle = Handle(meshHandle); + typedHandleMap.get().emplace(assetId, genericHandle); + typedAssetIdMap.get().emplace(genericHandle, assetId); return meshHandle; } // Textures @@ -95,7 +97,7 @@ namespace SHADE assetData->format, assetData->mipOffsets ); - typedHandleMap.emplace(assetId, Handle(texHandle)); + typedHandleMap.get().emplace(assetId, Handle(texHandle)); return texHandle; } } @@ -103,19 +105,22 @@ namespace SHADE template void SHResourceManager::Unload(Handle assetId) { - // Check if it is an usupported type + // Check if it is an unsupported type if (!std::is_same_v && !std::is_same_v) { static_assert(true, "Unsupported Resource Type specified for SHResourceManager."); } /* Attempt to get existing loaded asset */ - AssetHandleMap& typedHandleMap = getAssetHandleMap(); - if (typedHandleMap.contains(assetId)) + auto [typedHandleMap, typedAssetIdMap] = getAssetHandleMap(); + if (typedHandleMap.get().contains(assetId)) { // Dispose - static_cast>(typedHandleMap[assetId]).Free(); - typedHandleMap.erase(assetId); + Handle handle = typedHandleMap.get()[assetId]; + Handle typedHandle = static_cast>(handle); + typedHandle.Free(); + typedAssetIdMap.get().erase(handle); + typedHandleMap.get().erase(assetId); } else { @@ -124,24 +129,43 @@ namespace SHADE } } + /*-----------------------------------------------------------------------------------*/ + /* Query Functions */ + /*-----------------------------------------------------------------------------------*/ + template + static std::optional SHResourceManager::GetAssetID(Handle handle) + { + const Handle GENERIC_HANDLE = Handle(handle); + auto [typedHandleMap, typedAssetIdMap] = getAssetHandleMap(); + if (typedAssetIdMap.get().contains(GENERIC_HANDLE)) + { + return typedAssetIdMap.GetId()[GENERIC_HANDLE]; + } + + return {}; + } + /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ template - SHResourceManager::AssetHandleMap& SHResourceManager::getAssetHandleMap() + std::pair SHResourceManager::getAssetHandleMap() { - if (!handlesMap.contains(typeid(ResourceType))) + const std::type_index TYPE = typeid(ResourceType); + + if (!handlesMap.contains(TYPE)) { - handlesMap.emplace(typeid(ResourceType), AssetHandleMap{}); + handlesMap.emplace(TYPE, AssetHandleMap{}); + assetIdMap.emplace(TYPE, HandleAssetMap{}); typedFreeFuncMap.emplace ( - typeid(ResourceType), - [](AssetID assetId) + TYPE, + [TYPE](AssetID assetId) { - static_cast>(SHResourceManager::handlesMap[typeid(ResourceType)][assetId]).Free(); + static_cast>(SHResourceManager::handlesMap[TYPE][assetId]).Free(); } ); } - return handlesMap[typeid(ResourceType)]; + return std::make_pair(std::ref(handlesMap[TYPE]), std::ref(assetIdMap[TYPE])); } } From b3ed320f9b8ead15f0d8ef10281ad5fd1d7b0d43 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Fri, 21 Oct 2022 18:39:50 +0800 Subject: [PATCH 25/32] Transform Gizmo done. (Commands for it are wonky) --- .../src/Application/SBApplication.cpp | 2 +- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 1 - .../src/Editor/Command/SHCommandManager.cpp | 10 +++ .../src/Editor/Command/SHCommandManager.h | 3 + .../Profiling/SHEditorProfiler.cpp | 6 ++ .../ViewportWindow/SHEditorViewport.cpp | 45 +++++++++-- .../ViewportWindow/SHEditorViewport.h | 4 +- .../src/Editor/Gizmos/SHTransformGizmo.cpp | 79 ++++++++++++++----- .../src/Editor/Gizmos/SHTransformGizmo.h | 35 +++++++- SHADE_Engine/src/Editor/SHEditor.cpp | 4 +- SHADE_Engine/src/Editor/SHImGuiHelpers.hpp | 6 +- 11 files changed, 162 insertions(+), 33 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 9e793fba..a9f632da 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -149,7 +149,7 @@ namespace Sandbox SHSceneManager::SceneUpdate(0.016f); #endif SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, 0.016f); - editor->PollPicking(); + //editor->PollPicking(); } // Finish all graphics jobs first graphicsSystem->AwaitGraphicsExecution(); diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index a9b356de..7c6ea176 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -154,7 +154,6 @@ namespace SHADE target = SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); target =SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); - std::cout << "Target vec: " << target.x<<", "< #include "ECS_Base/Managers/SHSystemManager.h" +#include "Editor/Command/SHCommandManager.h" #include "FRC/SHFramerateController.h" namespace SHADE @@ -38,6 +39,11 @@ namespace SHADE { ImGui::PlotLines("DT", frames.data(), static_cast(frames.size()), 0, nullptr, 0.0f, 16.0f); } + if(ImGui::CollapsingHeader("Command Manager")) + { + ImGui::Text("Undo: %zu", SHCommandManager::GetUndoStackSize()); + ImGui::Text("Redo: %zu", SHCommandManager::GetRedoStackSize()); + } ImGui::End(); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp index f4403ecb..8a18757f 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp @@ -4,6 +4,7 @@ #include "ImGuizmo.h" #include "ECS_Base/Managers/SHSystemManager.h" +#include "Editor/IconsMaterialDesign.h" #include "Editor/SHEditor.hpp" #include "Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" @@ -26,12 +27,9 @@ namespace SHADE void SHEditorViewport::Update() { SHEditorWindow::Update(); - //ImGuizmo::SetDrawlist(ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())); - - ImGuizmo::SetRect(beginCursorPos.x, beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); - transfromGizmo.Draw(); if(Begin()) { + ImGuizmo::SetDrawlist(); DrawMenuBar(); auto gfxSystem = SHSystemManager::GetSystem(); auto const& descriptorSet = gfxSystem->GetPostOffscreenRenderSystem()->GetDescriptorSetGroup()->GetVkHandle()[0]; @@ -42,6 +40,8 @@ namespace SHADE ImGui::Image((ImTextureID)descriptorSet, {beginContentRegionAvailable.x, beginContentRegionAvailable.y}); } + ImGuizmo::SetRect(beginCursorPos.x , beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); + transformGizmo.Draw(); ImGui::End(); } @@ -70,10 +70,45 @@ namespace SHADE SHEditorWindow::OnPosChange(); } - void SHEditorViewport::DrawMenuBar() const noexcept + void SHEditorViewport::DrawMenuBar() noexcept { if(ImGui::BeginMenuBar()) { + bool const isTranslate = transformGizmo.operation == SHTransformGizmo::Operation::TRANSLATE; + ImGui::BeginDisabled(isTranslate); + if(isTranslate) + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); + if(ImGui::SmallButton(ICON_MD_OPEN_WITH)) + { + transformGizmo.operation = SHTransformGizmo::Operation::TRANSLATE; + } + ImGui::EndDisabled(); + if(isTranslate) + ImGui::PopStyleColor(); + + bool const isRotate = transformGizmo.operation == SHTransformGizmo::Operation::ROTATE; + ImGui::BeginDisabled(isRotate); + if(isRotate) + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); + if(ImGui::SmallButton(ICON_MD_AUTORENEW)) + { + transformGizmo.operation = SHTransformGizmo::Operation::ROTATE; + } + ImGui::EndDisabled(); + if(isRotate) + ImGui::PopStyleColor(); + + bool const isScale = transformGizmo.operation == SHTransformGizmo::Operation::SCALE; + ImGui::BeginDisabled(isScale); + if(isScale) + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); + if(ImGui::SmallButton(ICON_MD_OPEN_IN_FULL)) + { + transformGizmo.operation = SHTransformGizmo::Operation::SCALE; + } + ImGui::EndDisabled(); + if(isScale) + ImGui::PopStyleColor(); ImGui::EndMenuBar(); } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h index 7bcfed82..80b13285 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h @@ -21,12 +21,12 @@ namespace SHADE void Init() override; void Update() override; void Exit() override; + SHTransformGizmo transformGizmo; protected: void OnResize() override; void OnPosChange() override; private: - void DrawMenuBar() const noexcept; - SHTransformGizmo transfromGizmo; + void DrawMenuBar() noexcept; SHVec2 beginCursorPos; };//class SHEditorViewport }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp index 70f98eb1..35f8c4cb 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp @@ -4,45 +4,82 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.hpp" +#include "Editor/SHImGuiHelpers.hpp" #include #include #include "Camera/SHCameraSystem.h" +#include "Editor/Command/SHCommandManager.h" #include "Editor/EditorWindow/ViewportWindow/SHEditorViewport.h" - namespace SHADE { void SHTransformGizmo::Draw() { - if(selectedEntityTranformComponent == nullptr) - { - SHEditor* editor = SHSystemManager::GetSystem(); - if(editor->selectedEntities.empty()) - return; - EntityID eid = editor->selectedEntities.back(); - selectedEntityTranformComponent = SHComponentManager::GetComponent_s(eid); - } - if(selectedEntityTranformComponent == nullptr) - return; - if(!editorCamera) + bool justChangedTfm = false; + if (!editorCamera) { auto const cameraSystem = SHSystemManager::GetSystem(); editorCamera = cameraSystem->GetEditorCamera(); } - auto viewportWindow = SHEditorWindowManager::GetEditorWindow(); - ImGuizmo::SetOrthographic(false); + ImGuizmo::SetOrthographic(true); SHMatrix view = SHMatrix::Transpose(editorCamera->GetViewMatrix()); - view(3, 1) = -view(3, 1); SHMatrix proj = SHMatrix::Transpose(editorCamera->GetProjMatrix()); - SHMatrix mat = selectedEntityTranformComponent->GetTRS(); - SHMatrix gridMat = SHMatrix::Identity; - ImGuizmo::DrawGrid(&view._11, &proj._11, &gridMat._11, 0.1f); - //ImGuizmo::ViewManipulate() - if(ImGuizmo::Manipulate(&view._11, &proj._11, ImGuizmo::OPERATION::UNIVERSAL, ImGuizmo::MODE::WORLD, &mat._11)) + proj(1, 1) *= -1; + static SHMatrix gridMat = SHMatrix::Translate(0, -0.5f, 0.f) * SHMatrix::Identity; + //ImGuizmo::DrawGrid(&view._11, &proj._11, &gridMat._11, 100.f); + if (selectedEntityTransformComponent == nullptr) { - + SHEditor* editor = SHSystemManager::GetSystem(); + if (editor->selectedEntities.empty()) + return; + EntityID eid = editor->selectedEntities.back(); + selectedEntityTransformComponent = SHComponentManager::GetComponent_s(eid); + justChangedTfm = true; + } + else + { + SHEditor* editor = SHSystemManager::GetSystem(); + if (editor->selectedEntities.empty()) + return; + EntityID eid = editor->selectedEntities.back(); + auto tfmComponent = SHComponentManager::GetComponent_s(eid); + if (selectedEntityTransformComponent != tfmComponent) + { + selectedEntityTransformComponent = tfmComponent; + justChangedTfm = true; + } + } + if (selectedEntityTransformComponent == nullptr) + return; + + SHMatrix mat = selectedEntityTransformComponent->GetTRS(); + isManipulating = ImGuizmo::Manipulate(&view._11, &proj._11, static_cast(operation), ImGuizmo::MODE::WORLD, &mat._11); + if (!justChangedTfm) + { + if (ImGui::IsItemClicked()) + SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(selectedEntityTransformComponent->GetTRS(), mat, [tfm = std::move(selectedEntityTransformComponent)](SHMatrix const& mtx) + { + if (!tfm) + return; + SHVec3 translate{}, rotate{}, scale{}; + mtx.Decompose(translate, rotate, scale); + tfm->SetWorldPosition(translate); + tfm->SetWorldRotation(rotate); + tfm->SetWorldScale(scale); + }))); + else if (ImGui::IsItemHovered(ImGuiMouseButton_Left) && ImGui::IsMouseDown(ImGuiMouseButton_Left) && isManipulating) + SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(selectedEntityTransformComponent->GetTRS(), mat, [tfm = std::move(selectedEntityTransformComponent)](SHMatrix const& mtx) + { + if (!tfm) + return; + SHVec3 translate{}, rotate{}, scale{}; + mtx.Decompose(translate, rotate, scale); + tfm->SetWorldPosition(translate); + tfm->SetWorldRotation(rotate); + tfm->SetWorldScale(scale); + })), true); } } } diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h index 4ddbeb66..2565575f 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.h @@ -7,9 +7,42 @@ namespace SHADE class SHTransformGizmo { public: + enum class Mode + { + WORLD, + LOCAL + }; + + enum class Operation + { + TRANSLATE_X = (1u << 0), + TRANSLATE_Y = (1u << 1), + TRANSLATE_Z = (1u << 2), + ROTATE_X = (1u << 3), + ROTATE_Y = (1u << 4), + ROTATE_Z = (1u << 5), + ROTATE_SCREEN = (1u << 6), + SCALE_X = (1u << 7), + SCALE_Y = (1u << 8), + SCALE_Z = (1u << 9), + BOUNDS = (1u << 10), + SCALE_XU = (1u << 11), + SCALE_YU = (1u << 12), + SCALE_ZU = (1u << 13), + + TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z, + ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN, + SCALE = SCALE_X | SCALE_Y | SCALE_Z, + SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal + UNIVERSAL = TRANSLATE | ROTATE | SCALEU + }; + void Draw(); + bool isManipulating = false; + Mode mode = Mode::WORLD; + Operation operation = Operation::TRANSLATE; private: - SHTransformComponent* selectedEntityTranformComponent{nullptr}; + SHTransformComponent* selectedEntityTransformComponent{nullptr}; SHCameraComponent* editorCamera{nullptr}; }; } diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 162102b3..e938ae22 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -133,6 +133,8 @@ namespace SHADE } } + PollPicking(); + if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z)) { SHCommandManager::RedoCommand(); @@ -341,7 +343,7 @@ namespace SHADE if (auto gfxSystem = SHSystemManager::GetSystem()) { auto viewportWindow = SHEditorWindowManager::GetEditorWindow(); - if (viewportWindow->isWindowHovered && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) + if (viewportWindow->isWindowHovered && !viewportWindow->transformGizmo.isManipulating && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { EntityID pickedEID = gfxSystem->GetMousePickSystem()->GetPickedEntity(); if(pickedEID == MAX_EID) diff --git a/SHADE_Engine/src/Editor/SHImGuiHelpers.hpp b/SHADE_Engine/src/Editor/SHImGuiHelpers.hpp index 66eeb8c4..0c7c1ecf 100644 --- a/SHADE_Engine/src/Editor/SHImGuiHelpers.hpp +++ b/SHADE_Engine/src/Editor/SHImGuiHelpers.hpp @@ -15,7 +15,7 @@ //#==============================================================# #ifndef SH_IM_MATH #define IM_VEC2_CLASS_EXTRA \ - ImVec2(const SHADE::SHVec2& vec) {x = vec.x; y = vec.y;} \ + ImVec2(const SHADE::SHVec2& vec) {x = vec.x; y = vec.y;} \ operator SHADE::SHVec2() const {return SHADE::SHVec2(x,y);} #define IM_VEC3_CLASS_EXTRA \ ImVec3(const SHADE::SHVec3& vec) {x = vec.x; y = vec.y; z = vec.z;} \ @@ -43,6 +43,10 @@ namespace SHADE constexpr ImVec4 blue = {0.0f, 0.0f, 1.0f, 1.f}; constexpr ImVec4 white = {1.0f, 1.0f, 1.0f, 1.f}; + constexpr int colors_red = 0; + constexpr int colors_green = 1; + constexpr int colors_blue = 2; + constexpr int colors_white = 3; constexpr ImU32 colors[] = { 0xBB0000FF, // red 0xBB00FF00, // green From 99a69b00206e7fe46faf87ab2c28570f8d6912c7 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Fri, 21 Oct 2022 20:45:03 +0800 Subject: [PATCH 26/32] tweaks + add new ui font --- Assets/Editor/Fonts/fa-solid-900.ttf | Bin 0 -> 397728 bytes .../Inspector/SHEditorComponentView.hpp | 3 +- .../ViewportWindow/SHEditorViewport.cpp | 22 +- .../src/Editor/Gizmos/SHTransformGizmo.cpp | 2 +- SHADE_Engine/src/Editor/IconsFontAwesome6.h | 1400 +++++++++++++++++ SHADE_Engine/src/Editor/SHEditor.cpp | 10 +- 6 files changed, 1427 insertions(+), 10 deletions(-) create mode 100644 Assets/Editor/Fonts/fa-solid-900.ttf create mode 100644 SHADE_Engine/src/Editor/IconsFontAwesome6.h diff --git a/Assets/Editor/Fonts/fa-solid-900.ttf b/Assets/Editor/Fonts/fa-solid-900.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f89fc9f854f31dfa1481d31d1b19c25816da4947 GIT binary patch literal 397728 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%0AJm^cz`MgMZ3nFE%a*tJy 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 rZHV{(LAGJk3@xiFsZ^8&U?LGew6kkZh literal 0 HcmV?d00001 diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 47fec730..08f5695b 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -10,6 +10,7 @@ //|| SHADE Includes || //#==============================================================# #include "Editor/IconsMaterialDesign.h" +#include "Editor/IconsFontAwesome6.h" #include "ECS_Base/Components/SHComponent.h" #include "Editor/SHEditorWidgets.hpp" #include "Physics/Components/SHColliderComponent.h" @@ -216,7 +217,7 @@ namespace SHADE if (collider.GetType() == SHCollider::Type::BOX) { - SHEditorWidgets::BeginPanel( std::format("{} Box Collider #{}", ICON_MD_VIEW_IN_AR, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + SHEditorWidgets::BeginPanel( std::format("{} Box Collider #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); auto box = reinterpret_cast(collider.GetShape()); SHEditorWidgets::DragVec3("Half Extents", { "X", "Y", "Z" }, [box] {return box->GetHalfExtents(); }, [box](SHVec3 const& vec) {box->SetHalfExtents(vec);}); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp index 8a18757f..b06c37c7 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp @@ -10,11 +10,14 @@ #include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" #include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" +#include + +constexpr std::string_view windowName = "\xef\x80\x95 Viewport"; namespace SHADE { SHEditorViewport::SHEditorViewport() - :SHEditorWindow("Viewport", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar) + :SHEditorWindow("\xee\x90\x8b Viewport", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar) { } @@ -39,10 +42,20 @@ namespace SHADE gfxSystem->GetMousePickSystem ()->SetViewportMousePos (viewportMousePos); ImGui::Image((ImTextureID)descriptorSet, {beginContentRegionAvailable.x, beginContentRegionAvailable.y}); + + if(ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right)) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_None); + ImGui::SetCursorScreenPos(ImGui::GetMousePos()); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiColors::green); + ImGui::Text(ICON_FA_EYE); + ImGui::PopStyleColor(); + } } ImGuizmo::SetRect(beginCursorPos.x , beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); transformGizmo.Draw(); ImGui::End(); + } void SHEditorViewport::Exit() @@ -78,7 +91,7 @@ namespace SHADE ImGui::BeginDisabled(isTranslate); if(isTranslate) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::SmallButton(ICON_MD_OPEN_WITH)) + if(ImGui::Button(ICON_MD_OPEN_WITH)) { transformGizmo.operation = SHTransformGizmo::Operation::TRANSLATE; } @@ -90,7 +103,7 @@ namespace SHADE ImGui::BeginDisabled(isRotate); if(isRotate) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::SmallButton(ICON_MD_AUTORENEW)) + if(ImGui::Button(ICON_MD_AUTORENEW)) { transformGizmo.operation = SHTransformGizmo::Operation::ROTATE; } @@ -102,13 +115,14 @@ namespace SHADE ImGui::BeginDisabled(isScale); if(isScale) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::SmallButton(ICON_MD_OPEN_IN_FULL)) + if(ImGui::Button(ICON_MD_EXPAND)) { transformGizmo.operation = SHTransformGizmo::Operation::SCALE; } ImGui::EndDisabled(); if(isScale) ImGui::PopStyleColor(); + ImGui::EndMenuBar(); } } diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp index 35f8c4cb..a53b8c10 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp @@ -22,7 +22,7 @@ namespace SHADE editorCamera = cameraSystem->GetEditorCamera(); } auto viewportWindow = SHEditorWindowManager::GetEditorWindow(); - ImGuizmo::SetOrthographic(true); + ImGuizmo::SetOrthographic(false); SHMatrix view = SHMatrix::Transpose(editorCamera->GetViewMatrix()); SHMatrix proj = SHMatrix::Transpose(editorCamera->GetProjMatrix()); diff --git a/SHADE_Engine/src/Editor/IconsFontAwesome6.h b/SHADE_Engine/src/Editor/IconsFontAwesome6.h new file mode 100644 index 00000000..77831d94 --- /dev/null +++ b/SHADE_Engine/src/Editor/IconsFontAwesome6.h @@ -0,0 +1,1400 @@ +#pragma once +// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++ +// from https://github.com/FortAwesome/Font-Awesome/raw/6.x/metadata/icons.yml +// for use with https://github.com/FortAwesome/Font-Awesome/blob/6.x/webfonts/fa-regular-400.ttf, https://github.com/FortAwesome/Font-Awesome/blob/6.x/webfonts/fa-solid-900.ttf +#pragma once + +#define FONT_ICON_FILE_NAME_FAR "fa-regular-400.ttf" +#define FONT_ICON_FILE_NAME_FAS "fa-solid-900.ttf" + +#define ICON_MIN_FA 0xe005 +#define ICON_MAX_16_FA 0xf8ff +#define ICON_MAX_FA 0xf8ff +#define ICON_FA_0 "0" // U+0030 +#define ICON_FA_1 "1" // U+0031 +#define ICON_FA_2 "2" // U+0032 +#define ICON_FA_3 "3" // U+0033 +#define ICON_FA_4 "4" // U+0034 +#define ICON_FA_5 "5" // U+0035 +#define ICON_FA_6 "6" // U+0036 +#define ICON_FA_7 "7" // U+0037 +#define ICON_FA_8 "8" // U+0038 +#define ICON_FA_9 "9" // U+0039 +#define ICON_FA_A "A" // U+0041 +#define ICON_FA_ADDRESS_BOOK "\xef\x8a\xb9" // U+f2b9 +#define ICON_FA_ADDRESS_CARD "\xef\x8a\xbb" // U+f2bb +#define ICON_FA_ALIGN_CENTER "\xef\x80\xb7" // U+f037 +#define ICON_FA_ALIGN_JUSTIFY "\xef\x80\xb9" // U+f039 +#define ICON_FA_ALIGN_LEFT "\xef\x80\xb6" // U+f036 +#define ICON_FA_ALIGN_RIGHT "\xef\x80\xb8" // U+f038 +#define ICON_FA_ANCHOR "\xef\x84\xbd" // U+f13d +#define ICON_FA_ANCHOR_CIRCLE_CHECK "\xee\x92\xaa" // U+e4aa +#define ICON_FA_ANCHOR_CIRCLE_EXCLAMATION "\xee\x92\xab" // U+e4ab +#define ICON_FA_ANCHOR_CIRCLE_XMARK "\xee\x92\xac" // U+e4ac +#define ICON_FA_ANCHOR_LOCK "\xee\x92\xad" // U+e4ad +#define ICON_FA_ANGLE_DOWN "\xef\x84\x87" // U+f107 +#define ICON_FA_ANGLE_LEFT "\xef\x84\x84" // U+f104 +#define ICON_FA_ANGLE_RIGHT "\xef\x84\x85" // U+f105 +#define ICON_FA_ANGLE_UP "\xef\x84\x86" // U+f106 +#define ICON_FA_ANGLES_DOWN "\xef\x84\x83" // U+f103 +#define ICON_FA_ANGLES_LEFT "\xef\x84\x80" // U+f100 +#define ICON_FA_ANGLES_RIGHT "\xef\x84\x81" // U+f101 +#define ICON_FA_ANGLES_UP "\xef\x84\x82" // U+f102 +#define ICON_FA_ANKH "\xef\x99\x84" // U+f644 +#define ICON_FA_APPLE_WHOLE "\xef\x97\x91" // U+f5d1 +#define ICON_FA_ARCHWAY "\xef\x95\x97" // U+f557 +#define ICON_FA_ARROW_DOWN "\xef\x81\xa3" // U+f063 +#define ICON_FA_ARROW_DOWN_1_9 "\xef\x85\xa2" // U+f162 +#define ICON_FA_ARROW_DOWN_9_1 "\xef\xa2\x86" // U+f886 +#define ICON_FA_ARROW_DOWN_A_Z "\xef\x85\x9d" // U+f15d +#define ICON_FA_ARROW_DOWN_LONG "\xef\x85\xb5" // U+f175 +#define ICON_FA_ARROW_DOWN_SHORT_WIDE "\xef\xa2\x84" // U+f884 +#define ICON_FA_ARROW_DOWN_UP_ACROSS_LINE "\xee\x92\xaf" // U+e4af +#define ICON_FA_ARROW_DOWN_UP_LOCK "\xee\x92\xb0" // U+e4b0 +#define ICON_FA_ARROW_DOWN_WIDE_SHORT "\xef\x85\xa0" // U+f160 +#define ICON_FA_ARROW_DOWN_Z_A "\xef\xa2\x81" // U+f881 +#define ICON_FA_ARROW_LEFT "\xef\x81\xa0" // U+f060 +#define ICON_FA_ARROW_LEFT_LONG "\xef\x85\xb7" // U+f177 +#define ICON_FA_ARROW_POINTER "\xef\x89\x85" // U+f245 +#define ICON_FA_ARROW_RIGHT "\xef\x81\xa1" // U+f061 +#define ICON_FA_ARROW_RIGHT_ARROW_LEFT "\xef\x83\xac" // U+f0ec +#define ICON_FA_ARROW_RIGHT_FROM_BRACKET "\xef\x82\x8b" // U+f08b +#define ICON_FA_ARROW_RIGHT_LONG "\xef\x85\xb8" // U+f178 +#define ICON_FA_ARROW_RIGHT_TO_BRACKET "\xef\x82\x90" // U+f090 +#define ICON_FA_ARROW_RIGHT_TO_CITY "\xee\x92\xb3" // U+e4b3 +#define ICON_FA_ARROW_ROTATE_LEFT "\xef\x83\xa2" // U+f0e2 +#define ICON_FA_ARROW_ROTATE_RIGHT "\xef\x80\x9e" // U+f01e +#define ICON_FA_ARROW_TREND_DOWN "\xee\x82\x97" // U+e097 +#define ICON_FA_ARROW_TREND_UP "\xee\x82\x98" // U+e098 +#define ICON_FA_ARROW_TURN_DOWN "\xef\x85\x89" // U+f149 +#define ICON_FA_ARROW_TURN_UP "\xef\x85\x88" // U+f148 +#define ICON_FA_ARROW_UP "\xef\x81\xa2" // U+f062 +#define ICON_FA_ARROW_UP_1_9 "\xef\x85\xa3" // U+f163 +#define ICON_FA_ARROW_UP_9_1 "\xef\xa2\x87" // U+f887 +#define ICON_FA_ARROW_UP_A_Z "\xef\x85\x9e" // U+f15e +#define ICON_FA_ARROW_UP_FROM_BRACKET "\xee\x82\x9a" // U+e09a +#define ICON_FA_ARROW_UP_FROM_GROUND_WATER "\xee\x92\xb5" // U+e4b5 +#define ICON_FA_ARROW_UP_FROM_WATER_PUMP "\xee\x92\xb6" // U+e4b6 +#define ICON_FA_ARROW_UP_LONG "\xef\x85\xb6" // U+f176 +#define ICON_FA_ARROW_UP_RIGHT_DOTS "\xee\x92\xb7" // U+e4b7 +#define ICON_FA_ARROW_UP_RIGHT_FROM_SQUARE "\xef\x82\x8e" // U+f08e +#define ICON_FA_ARROW_UP_SHORT_WIDE "\xef\xa2\x85" // U+f885 +#define ICON_FA_ARROW_UP_WIDE_SHORT "\xef\x85\xa1" // U+f161 +#define ICON_FA_ARROW_UP_Z_A "\xef\xa2\x82" // U+f882 +#define ICON_FA_ARROWS_DOWN_TO_LINE "\xee\x92\xb8" // U+e4b8 +#define ICON_FA_ARROWS_DOWN_TO_PEOPLE "\xee\x92\xb9" // U+e4b9 +#define ICON_FA_ARROWS_LEFT_RIGHT "\xef\x81\xbe" // U+f07e +#define ICON_FA_ARROWS_LEFT_RIGHT_TO_LINE "\xee\x92\xba" // U+e4ba +#define ICON_FA_ARROWS_ROTATE "\xef\x80\xa1" // U+f021 +#define ICON_FA_ARROWS_SPIN "\xee\x92\xbb" // U+e4bb +#define ICON_FA_ARROWS_SPLIT_UP_AND_LEFT "\xee\x92\xbc" // U+e4bc +#define ICON_FA_ARROWS_TO_CIRCLE "\xee\x92\xbd" // U+e4bd +#define ICON_FA_ARROWS_TO_DOT "\xee\x92\xbe" // U+e4be +#define ICON_FA_ARROWS_TO_EYE "\xee\x92\xbf" // U+e4bf +#define ICON_FA_ARROWS_TURN_RIGHT "\xee\x93\x80" // U+e4c0 +#define ICON_FA_ARROWS_TURN_TO_DOTS "\xee\x93\x81" // U+e4c1 +#define ICON_FA_ARROWS_UP_DOWN "\xef\x81\xbd" // U+f07d +#define ICON_FA_ARROWS_UP_DOWN_LEFT_RIGHT "\xef\x81\x87" // U+f047 +#define ICON_FA_ARROWS_UP_TO_LINE "\xee\x93\x82" // U+e4c2 +#define ICON_FA_ASTERISK "*" // U+002a +#define ICON_FA_AT "@" // U+0040 +#define ICON_FA_ATOM "\xef\x97\x92" // U+f5d2 +#define ICON_FA_AUDIO_DESCRIPTION "\xef\x8a\x9e" // U+f29e +#define ICON_FA_AUSTRAL_SIGN "\xee\x82\xa9" // U+e0a9 +#define ICON_FA_AWARD "\xef\x95\x99" // U+f559 +#define ICON_FA_B "B" // U+0042 +#define ICON_FA_BABY "\xef\x9d\xbc" // U+f77c +#define ICON_FA_BABY_CARRIAGE "\xef\x9d\xbd" // U+f77d +#define ICON_FA_BACKWARD "\xef\x81\x8a" // U+f04a +#define ICON_FA_BACKWARD_FAST "\xef\x81\x89" // U+f049 +#define ICON_FA_BACKWARD_STEP "\xef\x81\x88" // U+f048 +#define ICON_FA_BACON "\xef\x9f\xa5" // U+f7e5 +#define ICON_FA_BACTERIA "\xee\x81\x99" // U+e059 +#define ICON_FA_BACTERIUM "\xee\x81\x9a" // U+e05a +#define ICON_FA_BAG_SHOPPING "\xef\x8a\x90" // U+f290 +#define ICON_FA_BAHAI "\xef\x99\xa6" // U+f666 +#define ICON_FA_BAHT_SIGN "\xee\x82\xac" // U+e0ac +#define ICON_FA_BAN "\xef\x81\x9e" // U+f05e +#define ICON_FA_BAN_SMOKING "\xef\x95\x8d" // U+f54d +#define ICON_FA_BANDAGE "\xef\x91\xa2" // U+f462 +#define ICON_FA_BARCODE "\xef\x80\xaa" // U+f02a +#define ICON_FA_BARS "\xef\x83\x89" // U+f0c9 +#define ICON_FA_BARS_PROGRESS "\xef\xa0\xa8" // U+f828 +#define ICON_FA_BARS_STAGGERED "\xef\x95\x90" // U+f550 +#define ICON_FA_BASEBALL "\xef\x90\xb3" // U+f433 +#define ICON_FA_BASEBALL_BAT_BALL "\xef\x90\xb2" // U+f432 +#define ICON_FA_BASKET_SHOPPING "\xef\x8a\x91" // U+f291 +#define ICON_FA_BASKETBALL "\xef\x90\xb4" // U+f434 +#define ICON_FA_BATH "\xef\x8b\x8d" // U+f2cd +#define ICON_FA_BATTERY_EMPTY "\xef\x89\x84" // U+f244 +#define ICON_FA_BATTERY_FULL "\xef\x89\x80" // U+f240 +#define ICON_FA_BATTERY_HALF "\xef\x89\x82" // U+f242 +#define ICON_FA_BATTERY_QUARTER "\xef\x89\x83" // U+f243 +#define ICON_FA_BATTERY_THREE_QUARTERS "\xef\x89\x81" // U+f241 +#define ICON_FA_BED "\xef\x88\xb6" // U+f236 +#define ICON_FA_BED_PULSE "\xef\x92\x87" // U+f487 +#define ICON_FA_BEER_MUG_EMPTY "\xef\x83\xbc" // U+f0fc +#define ICON_FA_BELL "\xef\x83\xb3" // U+f0f3 +#define ICON_FA_BELL_CONCIERGE "\xef\x95\xa2" // U+f562 +#define ICON_FA_BELL_SLASH "\xef\x87\xb6" // U+f1f6 +#define ICON_FA_BEZIER_CURVE "\xef\x95\x9b" // U+f55b +#define ICON_FA_BICYCLE "\xef\x88\x86" // U+f206 +#define ICON_FA_BINOCULARS "\xef\x87\xa5" // U+f1e5 +#define ICON_FA_BIOHAZARD "\xef\x9e\x80" // U+f780 +#define ICON_FA_BITCOIN_SIGN "\xee\x82\xb4" // U+e0b4 +#define ICON_FA_BLENDER "\xef\x94\x97" // U+f517 +#define ICON_FA_BLENDER_PHONE "\xef\x9a\xb6" // U+f6b6 +#define ICON_FA_BLOG "\xef\x9e\x81" // U+f781 +#define ICON_FA_BOLD "\xef\x80\xb2" // U+f032 +#define ICON_FA_BOLT "\xef\x83\xa7" // U+f0e7 +#define ICON_FA_BOLT_LIGHTNING "\xee\x82\xb7" // U+e0b7 +#define ICON_FA_BOMB "\xef\x87\xa2" // U+f1e2 +#define ICON_FA_BONE "\xef\x97\x97" // U+f5d7 +#define ICON_FA_BONG "\xef\x95\x9c" // U+f55c +#define ICON_FA_BOOK "\xef\x80\xad" // U+f02d +#define ICON_FA_BOOK_ATLAS "\xef\x95\x98" // U+f558 +#define ICON_FA_BOOK_BIBLE "\xef\x99\x87" // U+f647 +#define ICON_FA_BOOK_BOOKMARK "\xee\x82\xbb" // U+e0bb +#define ICON_FA_BOOK_JOURNAL_WHILLS "\xef\x99\xaa" // U+f66a +#define ICON_FA_BOOK_MEDICAL "\xef\x9f\xa6" // U+f7e6 +#define ICON_FA_BOOK_OPEN "\xef\x94\x98" // U+f518 +#define ICON_FA_BOOK_OPEN_READER "\xef\x97\x9a" // U+f5da +#define ICON_FA_BOOK_QURAN "\xef\x9a\x87" // U+f687 +#define ICON_FA_BOOK_SKULL "\xef\x9a\xb7" // U+f6b7 +#define ICON_FA_BOOK_TANAKH "\xef\xa0\xa7" // U+f827 +#define ICON_FA_BOOKMARK "\xef\x80\xae" // U+f02e +#define ICON_FA_BORDER_ALL "\xef\xa1\x8c" // U+f84c +#define ICON_FA_BORDER_NONE "\xef\xa1\x90" // U+f850 +#define ICON_FA_BORDER_TOP_LEFT "\xef\xa1\x93" // U+f853 +#define ICON_FA_BORE_HOLE "\xee\x93\x83" // U+e4c3 +#define ICON_FA_BOTTLE_DROPLET "\xee\x93\x84" // U+e4c4 +#define ICON_FA_BOTTLE_WATER "\xee\x93\x85" // U+e4c5 +#define ICON_FA_BOWL_FOOD "\xee\x93\x86" // U+e4c6 +#define ICON_FA_BOWL_RICE "\xee\x8b\xab" // U+e2eb +#define ICON_FA_BOWLING_BALL "\xef\x90\xb6" // U+f436 +#define ICON_FA_BOX "\xef\x91\xa6" // U+f466 +#define ICON_FA_BOX_ARCHIVE "\xef\x86\x87" // U+f187 +#define ICON_FA_BOX_OPEN "\xef\x92\x9e" // U+f49e +#define ICON_FA_BOX_TISSUE "\xee\x81\x9b" // U+e05b +#define ICON_FA_BOXES_PACKING "\xee\x93\x87" // U+e4c7 +#define ICON_FA_BOXES_STACKED "\xef\x91\xa8" // U+f468 +#define ICON_FA_BRAILLE "\xef\x8a\xa1" // U+f2a1 +#define ICON_FA_BRAIN "\xef\x97\x9c" // U+f5dc +#define ICON_FA_BRAZILIAN_REAL_SIGN "\xee\x91\xac" // U+e46c +#define ICON_FA_BREAD_SLICE "\xef\x9f\xac" // U+f7ec +#define ICON_FA_BRIDGE "\xee\x93\x88" // U+e4c8 +#define ICON_FA_BRIDGE_CIRCLE_CHECK "\xee\x93\x89" // U+e4c9 +#define ICON_FA_BRIDGE_CIRCLE_EXCLAMATION "\xee\x93\x8a" // U+e4ca +#define ICON_FA_BRIDGE_CIRCLE_XMARK "\xee\x93\x8b" // U+e4cb +#define ICON_FA_BRIDGE_LOCK "\xee\x93\x8c" // U+e4cc +#define ICON_FA_BRIDGE_WATER "\xee\x93\x8e" // U+e4ce +#define ICON_FA_BRIEFCASE "\xef\x82\xb1" // U+f0b1 +#define ICON_FA_BRIEFCASE_MEDICAL "\xef\x91\xa9" // U+f469 +#define ICON_FA_BROOM "\xef\x94\x9a" // U+f51a +#define ICON_FA_BROOM_BALL "\xef\x91\x98" // U+f458 +#define ICON_FA_BRUSH "\xef\x95\x9d" // U+f55d +#define ICON_FA_BUCKET "\xee\x93\x8f" // U+e4cf +#define ICON_FA_BUG "\xef\x86\x88" // U+f188 +#define ICON_FA_BUG_SLASH "\xee\x92\x90" // U+e490 +#define ICON_FA_BUGS "\xee\x93\x90" // U+e4d0 +#define ICON_FA_BUILDING "\xef\x86\xad" // U+f1ad +#define ICON_FA_BUILDING_CIRCLE_ARROW_RIGHT "\xee\x93\x91" // U+e4d1 +#define ICON_FA_BUILDING_CIRCLE_CHECK "\xee\x93\x92" // U+e4d2 +#define ICON_FA_BUILDING_CIRCLE_EXCLAMATION "\xee\x93\x93" // U+e4d3 +#define ICON_FA_BUILDING_CIRCLE_XMARK "\xee\x93\x94" // U+e4d4 +#define ICON_FA_BUILDING_COLUMNS "\xef\x86\x9c" // U+f19c +#define ICON_FA_BUILDING_FLAG "\xee\x93\x95" // U+e4d5 +#define ICON_FA_BUILDING_LOCK "\xee\x93\x96" // U+e4d6 +#define ICON_FA_BUILDING_NGO "\xee\x93\x97" // U+e4d7 +#define ICON_FA_BUILDING_SHIELD "\xee\x93\x98" // U+e4d8 +#define ICON_FA_BUILDING_UN "\xee\x93\x99" // U+e4d9 +#define ICON_FA_BUILDING_USER "\xee\x93\x9a" // U+e4da +#define ICON_FA_BUILDING_WHEAT "\xee\x93\x9b" // U+e4db +#define ICON_FA_BULLHORN "\xef\x82\xa1" // U+f0a1 +#define ICON_FA_BULLSEYE "\xef\x85\x80" // U+f140 +#define ICON_FA_BURGER "\xef\xa0\x85" // U+f805 +#define ICON_FA_BURST "\xee\x93\x9c" // U+e4dc +#define ICON_FA_BUS "\xef\x88\x87" // U+f207 +#define ICON_FA_BUS_SIMPLE "\xef\x95\x9e" // U+f55e +#define ICON_FA_BUSINESS_TIME "\xef\x99\x8a" // U+f64a +#define ICON_FA_C "C" // U+0043 +#define ICON_FA_CABLE_CAR "\xef\x9f\x9a" // U+f7da +#define ICON_FA_CAKE_CANDLES "\xef\x87\xbd" // U+f1fd +#define ICON_FA_CALCULATOR "\xef\x87\xac" // U+f1ec +#define ICON_FA_CALENDAR "\xef\x84\xb3" // U+f133 +#define ICON_FA_CALENDAR_CHECK "\xef\x89\xb4" // U+f274 +#define ICON_FA_CALENDAR_DAY "\xef\x9e\x83" // U+f783 +#define ICON_FA_CALENDAR_DAYS "\xef\x81\xb3" // U+f073 +#define ICON_FA_CALENDAR_MINUS "\xef\x89\xb2" // U+f272 +#define ICON_FA_CALENDAR_PLUS "\xef\x89\xb1" // U+f271 +#define ICON_FA_CALENDAR_WEEK "\xef\x9e\x84" // U+f784 +#define ICON_FA_CALENDAR_XMARK "\xef\x89\xb3" // U+f273 +#define ICON_FA_CAMERA "\xef\x80\xb0" // U+f030 +#define ICON_FA_CAMERA_RETRO "\xef\x82\x83" // U+f083 +#define ICON_FA_CAMERA_ROTATE "\xee\x83\x98" // U+e0d8 +#define ICON_FA_CAMPGROUND "\xef\x9a\xbb" // U+f6bb +#define ICON_FA_CANDY_CANE "\xef\x9e\x86" // U+f786 +#define ICON_FA_CANNABIS "\xef\x95\x9f" // U+f55f +#define ICON_FA_CAPSULES "\xef\x91\xab" // U+f46b +#define ICON_FA_CAR "\xef\x86\xb9" // U+f1b9 +#define ICON_FA_CAR_BATTERY "\xef\x97\x9f" // U+f5df +#define ICON_FA_CAR_BURST "\xef\x97\xa1" // U+f5e1 +#define ICON_FA_CAR_ON "\xee\x93\x9d" // U+e4dd +#define ICON_FA_CAR_REAR "\xef\x97\x9e" // U+f5de +#define ICON_FA_CAR_SIDE "\xef\x97\xa4" // U+f5e4 +#define ICON_FA_CAR_TUNNEL "\xee\x93\x9e" // U+e4de +#define ICON_FA_CARAVAN "\xef\xa3\xbf" // U+f8ff +#define ICON_FA_CARET_DOWN "\xef\x83\x97" // U+f0d7 +#define ICON_FA_CARET_LEFT "\xef\x83\x99" // U+f0d9 +#define ICON_FA_CARET_RIGHT "\xef\x83\x9a" // U+f0da +#define ICON_FA_CARET_UP "\xef\x83\x98" // U+f0d8 +#define ICON_FA_CARROT "\xef\x9e\x87" // U+f787 +#define ICON_FA_CART_ARROW_DOWN "\xef\x88\x98" // U+f218 +#define ICON_FA_CART_FLATBED "\xef\x91\xb4" // U+f474 +#define ICON_FA_CART_FLATBED_SUITCASE "\xef\x96\x9d" // U+f59d +#define ICON_FA_CART_PLUS "\xef\x88\x97" // U+f217 +#define ICON_FA_CART_SHOPPING "\xef\x81\xba" // U+f07a +#define ICON_FA_CASH_REGISTER "\xef\x9e\x88" // U+f788 +#define ICON_FA_CAT "\xef\x9a\xbe" // U+f6be +#define ICON_FA_CEDI_SIGN "\xee\x83\x9f" // U+e0df +#define ICON_FA_CENT_SIGN "\xee\x8f\xb5" // U+e3f5 +#define ICON_FA_CERTIFICATE "\xef\x82\xa3" // U+f0a3 +#define ICON_FA_CHAIR "\xef\x9b\x80" // U+f6c0 +#define ICON_FA_CHALKBOARD "\xef\x94\x9b" // U+f51b +#define ICON_FA_CHALKBOARD_USER "\xef\x94\x9c" // U+f51c +#define ICON_FA_CHAMPAGNE_GLASSES "\xef\x9e\x9f" // U+f79f +#define ICON_FA_CHARGING_STATION "\xef\x97\xa7" // U+f5e7 +#define ICON_FA_CHART_AREA "\xef\x87\xbe" // U+f1fe +#define ICON_FA_CHART_BAR "\xef\x82\x80" // U+f080 +#define ICON_FA_CHART_COLUMN "\xee\x83\xa3" // U+e0e3 +#define ICON_FA_CHART_GANTT "\xee\x83\xa4" // U+e0e4 +#define ICON_FA_CHART_LINE "\xef\x88\x81" // U+f201 +#define ICON_FA_CHART_PIE "\xef\x88\x80" // U+f200 +#define ICON_FA_CHART_SIMPLE "\xee\x91\xb3" // U+e473 +#define ICON_FA_CHECK "\xef\x80\x8c" // U+f00c +#define ICON_FA_CHECK_DOUBLE "\xef\x95\xa0" // U+f560 +#define ICON_FA_CHECK_TO_SLOT "\xef\x9d\xb2" // U+f772 +#define ICON_FA_CHEESE "\xef\x9f\xaf" // U+f7ef +#define ICON_FA_CHESS "\xef\x90\xb9" // U+f439 +#define ICON_FA_CHESS_BISHOP "\xef\x90\xba" // U+f43a +#define ICON_FA_CHESS_BOARD "\xef\x90\xbc" // U+f43c +#define ICON_FA_CHESS_KING "\xef\x90\xbf" // U+f43f +#define ICON_FA_CHESS_KNIGHT "\xef\x91\x81" // U+f441 +#define ICON_FA_CHESS_PAWN "\xef\x91\x83" // U+f443 +#define ICON_FA_CHESS_QUEEN "\xef\x91\x85" // U+f445 +#define ICON_FA_CHESS_ROOK "\xef\x91\x87" // U+f447 +#define ICON_FA_CHEVRON_DOWN "\xef\x81\xb8" // U+f078 +#define ICON_FA_CHEVRON_LEFT "\xef\x81\x93" // U+f053 +#define ICON_FA_CHEVRON_RIGHT "\xef\x81\x94" // U+f054 +#define ICON_FA_CHEVRON_UP "\xef\x81\xb7" // U+f077 +#define ICON_FA_CHILD "\xef\x86\xae" // U+f1ae +#define ICON_FA_CHILD_DRESS "\xee\x96\x9c" // U+e59c +#define ICON_FA_CHILD_REACHING "\xee\x96\x9d" // U+e59d +#define ICON_FA_CHILD_RIFLE "\xee\x93\xa0" // U+e4e0 +#define ICON_FA_CHILDREN "\xee\x93\xa1" // U+e4e1 +#define ICON_FA_CHURCH "\xef\x94\x9d" // U+f51d +#define ICON_FA_CIRCLE "\xef\x84\x91" // U+f111 +#define ICON_FA_CIRCLE_ARROW_DOWN "\xef\x82\xab" // U+f0ab +#define ICON_FA_CIRCLE_ARROW_LEFT "\xef\x82\xa8" // U+f0a8 +#define ICON_FA_CIRCLE_ARROW_RIGHT "\xef\x82\xa9" // U+f0a9 +#define ICON_FA_CIRCLE_ARROW_UP "\xef\x82\xaa" // U+f0aa +#define ICON_FA_CIRCLE_CHECK "\xef\x81\x98" // U+f058 +#define ICON_FA_CIRCLE_CHEVRON_DOWN "\xef\x84\xba" // U+f13a +#define ICON_FA_CIRCLE_CHEVRON_LEFT "\xef\x84\xb7" // U+f137 +#define ICON_FA_CIRCLE_CHEVRON_RIGHT "\xef\x84\xb8" // U+f138 +#define ICON_FA_CIRCLE_CHEVRON_UP "\xef\x84\xb9" // U+f139 +#define ICON_FA_CIRCLE_DOLLAR_TO_SLOT "\xef\x92\xb9" // U+f4b9 +#define ICON_FA_CIRCLE_DOT "\xef\x86\x92" // U+f192 +#define ICON_FA_CIRCLE_DOWN "\xef\x8d\x98" // U+f358 +#define ICON_FA_CIRCLE_EXCLAMATION "\xef\x81\xaa" // U+f06a +#define ICON_FA_CIRCLE_H "\xef\x91\xbe" // U+f47e +#define ICON_FA_CIRCLE_HALF_STROKE "\xef\x81\x82" // U+f042 +#define ICON_FA_CIRCLE_INFO "\xef\x81\x9a" // U+f05a +#define ICON_FA_CIRCLE_LEFT "\xef\x8d\x99" // U+f359 +#define ICON_FA_CIRCLE_MINUS "\xef\x81\x96" // U+f056 +#define ICON_FA_CIRCLE_NODES "\xee\x93\xa2" // U+e4e2 +#define ICON_FA_CIRCLE_NOTCH "\xef\x87\x8e" // U+f1ce +#define ICON_FA_CIRCLE_PAUSE "\xef\x8a\x8b" // U+f28b +#define ICON_FA_CIRCLE_PLAY "\xef\x85\x84" // U+f144 +#define ICON_FA_CIRCLE_PLUS "\xef\x81\x95" // U+f055 +#define ICON_FA_CIRCLE_QUESTION "\xef\x81\x99" // U+f059 +#define ICON_FA_CIRCLE_RADIATION "\xef\x9e\xba" // U+f7ba +#define ICON_FA_CIRCLE_RIGHT "\xef\x8d\x9a" // U+f35a +#define ICON_FA_CIRCLE_STOP "\xef\x8a\x8d" // U+f28d +#define ICON_FA_CIRCLE_UP "\xef\x8d\x9b" // U+f35b +#define ICON_FA_CIRCLE_USER "\xef\x8a\xbd" // U+f2bd +#define ICON_FA_CIRCLE_XMARK "\xef\x81\x97" // U+f057 +#define ICON_FA_CITY "\xef\x99\x8f" // U+f64f +#define ICON_FA_CLAPPERBOARD "\xee\x84\xb1" // U+e131 +#define ICON_FA_CLIPBOARD "\xef\x8c\xa8" // U+f328 +#define ICON_FA_CLIPBOARD_CHECK "\xef\x91\xac" // U+f46c +#define ICON_FA_CLIPBOARD_LIST "\xef\x91\xad" // U+f46d +#define ICON_FA_CLIPBOARD_QUESTION "\xee\x93\xa3" // U+e4e3 +#define ICON_FA_CLIPBOARD_USER "\xef\x9f\xb3" // U+f7f3 +#define ICON_FA_CLOCK "\xef\x80\x97" // U+f017 +#define ICON_FA_CLOCK_ROTATE_LEFT "\xef\x87\x9a" // U+f1da +#define ICON_FA_CLONE "\xef\x89\x8d" // U+f24d +#define ICON_FA_CLOSED_CAPTIONING "\xef\x88\x8a" // U+f20a +#define ICON_FA_CLOUD "\xef\x83\x82" // U+f0c2 +#define ICON_FA_CLOUD_ARROW_DOWN "\xef\x83\xad" // U+f0ed +#define ICON_FA_CLOUD_ARROW_UP "\xef\x83\xae" // U+f0ee +#define ICON_FA_CLOUD_BOLT "\xef\x9d\xac" // U+f76c +#define ICON_FA_CLOUD_MEATBALL "\xef\x9c\xbb" // U+f73b +#define ICON_FA_CLOUD_MOON "\xef\x9b\x83" // U+f6c3 +#define ICON_FA_CLOUD_MOON_RAIN "\xef\x9c\xbc" // U+f73c +#define ICON_FA_CLOUD_RAIN "\xef\x9c\xbd" // U+f73d +#define ICON_FA_CLOUD_SHOWERS_HEAVY "\xef\x9d\x80" // U+f740 +#define ICON_FA_CLOUD_SHOWERS_WATER "\xee\x93\xa4" // U+e4e4 +#define ICON_FA_CLOUD_SUN "\xef\x9b\x84" // U+f6c4 +#define ICON_FA_CLOUD_SUN_RAIN "\xef\x9d\x83" // U+f743 +#define ICON_FA_CLOVER "\xee\x84\xb9" // U+e139 +#define ICON_FA_CODE "\xef\x84\xa1" // U+f121 +#define ICON_FA_CODE_BRANCH "\xef\x84\xa6" // U+f126 +#define ICON_FA_CODE_COMMIT "\xef\x8e\x86" // U+f386 +#define ICON_FA_CODE_COMPARE "\xee\x84\xba" // U+e13a +#define ICON_FA_CODE_FORK "\xee\x84\xbb" // U+e13b +#define ICON_FA_CODE_MERGE "\xef\x8e\x87" // U+f387 +#define ICON_FA_CODE_PULL_REQUEST "\xee\x84\xbc" // U+e13c +#define ICON_FA_COINS "\xef\x94\x9e" // U+f51e +#define ICON_FA_COLON_SIGN "\xee\x85\x80" // U+e140 +#define ICON_FA_COMMENT "\xef\x81\xb5" // U+f075 +#define ICON_FA_COMMENT_DOLLAR "\xef\x99\x91" // U+f651 +#define ICON_FA_COMMENT_DOTS "\xef\x92\xad" // U+f4ad +#define ICON_FA_COMMENT_MEDICAL "\xef\x9f\xb5" // U+f7f5 +#define ICON_FA_COMMENT_SLASH "\xef\x92\xb3" // U+f4b3 +#define ICON_FA_COMMENT_SMS "\xef\x9f\x8d" // U+f7cd +#define ICON_FA_COMMENTS "\xef\x82\x86" // U+f086 +#define ICON_FA_COMMENTS_DOLLAR "\xef\x99\x93" // U+f653 +#define ICON_FA_COMPACT_DISC "\xef\x94\x9f" // U+f51f +#define ICON_FA_COMPASS "\xef\x85\x8e" // U+f14e +#define ICON_FA_COMPASS_DRAFTING "\xef\x95\xa8" // U+f568 +#define ICON_FA_COMPRESS "\xef\x81\xa6" // U+f066 +#define ICON_FA_COMPUTER "\xee\x93\xa5" // U+e4e5 +#define ICON_FA_COMPUTER_MOUSE "\xef\xa3\x8c" // U+f8cc +#define ICON_FA_COOKIE "\xef\x95\xa3" // U+f563 +#define ICON_FA_COOKIE_BITE "\xef\x95\xa4" // U+f564 +#define ICON_FA_COPY "\xef\x83\x85" // U+f0c5 +#define ICON_FA_COPYRIGHT "\xef\x87\xb9" // U+f1f9 +#define ICON_FA_COUCH "\xef\x92\xb8" // U+f4b8 +#define ICON_FA_COW "\xef\x9b\x88" // U+f6c8 +#define ICON_FA_CREDIT_CARD "\xef\x82\x9d" // U+f09d +#define ICON_FA_CROP "\xef\x84\xa5" // U+f125 +#define ICON_FA_CROP_SIMPLE "\xef\x95\xa5" // U+f565 +#define ICON_FA_CROSS "\xef\x99\x94" // U+f654 +#define ICON_FA_CROSSHAIRS "\xef\x81\x9b" // U+f05b +#define ICON_FA_CROW "\xef\x94\xa0" // U+f520 +#define ICON_FA_CROWN "\xef\x94\xa1" // U+f521 +#define ICON_FA_CRUTCH "\xef\x9f\xb7" // U+f7f7 +#define ICON_FA_CRUZEIRO_SIGN "\xee\x85\x92" // U+e152 +#define ICON_FA_CUBE "\xef\x86\xb2" // U+f1b2 +#define ICON_FA_CUBES "\xef\x86\xb3" // U+f1b3 +#define ICON_FA_CUBES_STACKED "\xee\x93\xa6" // U+e4e6 +#define ICON_FA_D "D" // U+0044 +#define ICON_FA_DATABASE "\xef\x87\x80" // U+f1c0 +#define ICON_FA_DELETE_LEFT "\xef\x95\x9a" // U+f55a +#define ICON_FA_DEMOCRAT "\xef\x9d\x87" // U+f747 +#define ICON_FA_DESKTOP "\xef\x8e\x90" // U+f390 +#define ICON_FA_DHARMACHAKRA "\xef\x99\x95" // U+f655 +#define ICON_FA_DIAGRAM_NEXT "\xee\x91\xb6" // U+e476 +#define ICON_FA_DIAGRAM_PREDECESSOR "\xee\x91\xb7" // U+e477 +#define ICON_FA_DIAGRAM_PROJECT "\xef\x95\x82" // U+f542 +#define ICON_FA_DIAGRAM_SUCCESSOR "\xee\x91\xba" // U+e47a +#define ICON_FA_DIAMOND "\xef\x88\x99" // U+f219 +#define ICON_FA_DIAMOND_TURN_RIGHT "\xef\x97\xab" // U+f5eb +#define ICON_FA_DICE "\xef\x94\xa2" // U+f522 +#define ICON_FA_DICE_D20 "\xef\x9b\x8f" // U+f6cf +#define ICON_FA_DICE_D6 "\xef\x9b\x91" // U+f6d1 +#define ICON_FA_DICE_FIVE "\xef\x94\xa3" // U+f523 +#define ICON_FA_DICE_FOUR "\xef\x94\xa4" // U+f524 +#define ICON_FA_DICE_ONE "\xef\x94\xa5" // U+f525 +#define ICON_FA_DICE_SIX "\xef\x94\xa6" // U+f526 +#define ICON_FA_DICE_THREE "\xef\x94\xa7" // U+f527 +#define ICON_FA_DICE_TWO "\xef\x94\xa8" // U+f528 +#define ICON_FA_DISEASE "\xef\x9f\xba" // U+f7fa +#define ICON_FA_DISPLAY "\xee\x85\xa3" // U+e163 +#define ICON_FA_DIVIDE "\xef\x94\xa9" // U+f529 +#define ICON_FA_DNA "\xef\x91\xb1" // U+f471 +#define ICON_FA_DOG "\xef\x9b\x93" // U+f6d3 +#define ICON_FA_DOLLAR_SIGN "$" // U+0024 +#define ICON_FA_DOLLY "\xef\x91\xb2" // U+f472 +#define ICON_FA_DONG_SIGN "\xee\x85\xa9" // U+e169 +#define ICON_FA_DOOR_CLOSED "\xef\x94\xaa" // U+f52a +#define ICON_FA_DOOR_OPEN "\xef\x94\xab" // U+f52b +#define ICON_FA_DOVE "\xef\x92\xba" // U+f4ba +#define ICON_FA_DOWN_LEFT_AND_UP_RIGHT_TO_CENTER "\xef\x90\xa2" // U+f422 +#define ICON_FA_DOWN_LONG "\xef\x8c\x89" // U+f309 +#define ICON_FA_DOWNLOAD "\xef\x80\x99" // U+f019 +#define ICON_FA_DRAGON "\xef\x9b\x95" // U+f6d5 +#define ICON_FA_DRAW_POLYGON "\xef\x97\xae" // U+f5ee +#define ICON_FA_DROPLET "\xef\x81\x83" // U+f043 +#define ICON_FA_DROPLET_SLASH "\xef\x97\x87" // U+f5c7 +#define ICON_FA_DRUM "\xef\x95\xa9" // U+f569 +#define ICON_FA_DRUM_STEELPAN "\xef\x95\xaa" // U+f56a +#define ICON_FA_DRUMSTICK_BITE "\xef\x9b\x97" // U+f6d7 +#define ICON_FA_DUMBBELL "\xef\x91\x8b" // U+f44b +#define ICON_FA_DUMPSTER "\xef\x9e\x93" // U+f793 +#define ICON_FA_DUMPSTER_FIRE "\xef\x9e\x94" // U+f794 +#define ICON_FA_DUNGEON "\xef\x9b\x99" // U+f6d9 +#define ICON_FA_E "E" // U+0045 +#define ICON_FA_EAR_DEAF "\xef\x8a\xa4" // U+f2a4 +#define ICON_FA_EAR_LISTEN "\xef\x8a\xa2" // U+f2a2 +#define ICON_FA_EARTH_AFRICA "\xef\x95\xbc" // U+f57c +#define ICON_FA_EARTH_AMERICAS "\xef\x95\xbd" // U+f57d +#define ICON_FA_EARTH_ASIA "\xef\x95\xbe" // U+f57e +#define ICON_FA_EARTH_EUROPE "\xef\x9e\xa2" // U+f7a2 +#define ICON_FA_EARTH_OCEANIA "\xee\x91\xbb" // U+e47b +#define ICON_FA_EGG "\xef\x9f\xbb" // U+f7fb +#define ICON_FA_EJECT "\xef\x81\x92" // U+f052 +#define ICON_FA_ELEVATOR "\xee\x85\xad" // U+e16d +#define ICON_FA_ELLIPSIS "\xef\x85\x81" // U+f141 +#define ICON_FA_ELLIPSIS_VERTICAL "\xef\x85\x82" // U+f142 +#define ICON_FA_ENVELOPE "\xef\x83\xa0" // U+f0e0 +#define ICON_FA_ENVELOPE_CIRCLE_CHECK "\xee\x93\xa8" // U+e4e8 +#define ICON_FA_ENVELOPE_OPEN "\xef\x8a\xb6" // U+f2b6 +#define ICON_FA_ENVELOPE_OPEN_TEXT "\xef\x99\x98" // U+f658 +#define ICON_FA_ENVELOPES_BULK "\xef\x99\xb4" // U+f674 +#define ICON_FA_EQUALS "=" // U+003d +#define ICON_FA_ERASER "\xef\x84\xad" // U+f12d +#define ICON_FA_ETHERNET "\xef\x9e\x96" // U+f796 +#define ICON_FA_EURO_SIGN "\xef\x85\x93" // U+f153 +#define ICON_FA_EXCLAMATION "!" // U+0021 +#define ICON_FA_EXPAND "\xef\x81\xa5" // U+f065 +#define ICON_FA_EXPLOSION "\xee\x93\xa9" // U+e4e9 +#define ICON_FA_EYE "\xef\x81\xae" // U+f06e +#define ICON_FA_EYE_DROPPER "\xef\x87\xbb" // U+f1fb +#define ICON_FA_EYE_LOW_VISION "\xef\x8a\xa8" // U+f2a8 +#define ICON_FA_EYE_SLASH "\xef\x81\xb0" // U+f070 +#define ICON_FA_F "F" // U+0046 +#define ICON_FA_FACE_ANGRY "\xef\x95\x96" // U+f556 +#define ICON_FA_FACE_DIZZY "\xef\x95\xa7" // U+f567 +#define ICON_FA_FACE_FLUSHED "\xef\x95\xb9" // U+f579 +#define ICON_FA_FACE_FROWN "\xef\x84\x99" // U+f119 +#define ICON_FA_FACE_FROWN_OPEN "\xef\x95\xba" // U+f57a +#define ICON_FA_FACE_GRIMACE "\xef\x95\xbf" // U+f57f +#define ICON_FA_FACE_GRIN "\xef\x96\x80" // U+f580 +#define ICON_FA_FACE_GRIN_BEAM "\xef\x96\x82" // U+f582 +#define ICON_FA_FACE_GRIN_BEAM_SWEAT "\xef\x96\x83" // U+f583 +#define ICON_FA_FACE_GRIN_HEARTS "\xef\x96\x84" // U+f584 +#define ICON_FA_FACE_GRIN_SQUINT "\xef\x96\x85" // U+f585 +#define ICON_FA_FACE_GRIN_SQUINT_TEARS "\xef\x96\x86" // U+f586 +#define ICON_FA_FACE_GRIN_STARS "\xef\x96\x87" // U+f587 +#define ICON_FA_FACE_GRIN_TEARS "\xef\x96\x88" // U+f588 +#define ICON_FA_FACE_GRIN_TONGUE "\xef\x96\x89" // U+f589 +#define ICON_FA_FACE_GRIN_TONGUE_SQUINT "\xef\x96\x8a" // U+f58a +#define ICON_FA_FACE_GRIN_TONGUE_WINK "\xef\x96\x8b" // U+f58b +#define ICON_FA_FACE_GRIN_WIDE "\xef\x96\x81" // U+f581 +#define ICON_FA_FACE_GRIN_WINK "\xef\x96\x8c" // U+f58c +#define ICON_FA_FACE_KISS "\xef\x96\x96" // U+f596 +#define ICON_FA_FACE_KISS_BEAM "\xef\x96\x97" // U+f597 +#define ICON_FA_FACE_KISS_WINK_HEART "\xef\x96\x98" // U+f598 +#define ICON_FA_FACE_LAUGH "\xef\x96\x99" // U+f599 +#define ICON_FA_FACE_LAUGH_BEAM "\xef\x96\x9a" // U+f59a +#define ICON_FA_FACE_LAUGH_SQUINT "\xef\x96\x9b" // U+f59b +#define ICON_FA_FACE_LAUGH_WINK "\xef\x96\x9c" // U+f59c +#define ICON_FA_FACE_MEH "\xef\x84\x9a" // U+f11a +#define ICON_FA_FACE_MEH_BLANK "\xef\x96\xa4" // U+f5a4 +#define ICON_FA_FACE_ROLLING_EYES "\xef\x96\xa5" // U+f5a5 +#define ICON_FA_FACE_SAD_CRY "\xef\x96\xb3" // U+f5b3 +#define ICON_FA_FACE_SAD_TEAR "\xef\x96\xb4" // U+f5b4 +#define ICON_FA_FACE_SMILE "\xef\x84\x98" // U+f118 +#define ICON_FA_FACE_SMILE_BEAM "\xef\x96\xb8" // U+f5b8 +#define ICON_FA_FACE_SMILE_WINK "\xef\x93\x9a" // U+f4da +#define ICON_FA_FACE_SURPRISE "\xef\x97\x82" // U+f5c2 +#define ICON_FA_FACE_TIRED "\xef\x97\x88" // U+f5c8 +#define ICON_FA_FAN "\xef\xa1\xa3" // U+f863 +#define ICON_FA_FAUCET "\xee\x80\x85" // U+e005 +#define ICON_FA_FAUCET_DRIP "\xee\x80\x86" // U+e006 +#define ICON_FA_FAX "\xef\x86\xac" // U+f1ac +#define ICON_FA_FEATHER "\xef\x94\xad" // U+f52d +#define ICON_FA_FEATHER_POINTED "\xef\x95\xab" // U+f56b +#define ICON_FA_FERRY "\xee\x93\xaa" // U+e4ea +#define ICON_FA_FILE "\xef\x85\x9b" // U+f15b +#define ICON_FA_FILE_ARROW_DOWN "\xef\x95\xad" // U+f56d +#define ICON_FA_FILE_ARROW_UP "\xef\x95\xb4" // U+f574 +#define ICON_FA_FILE_AUDIO "\xef\x87\x87" // U+f1c7 +#define ICON_FA_FILE_CIRCLE_CHECK "\xee\x96\xa0" // U+e5a0 +#define ICON_FA_FILE_CIRCLE_EXCLAMATION "\xee\x93\xab" // U+e4eb +#define ICON_FA_FILE_CIRCLE_MINUS "\xee\x93\xad" // U+e4ed +#define ICON_FA_FILE_CIRCLE_PLUS "\xee\x92\x94" // U+e494 +#define ICON_FA_FILE_CIRCLE_QUESTION "\xee\x93\xaf" // U+e4ef +#define ICON_FA_FILE_CIRCLE_XMARK "\xee\x96\xa1" // U+e5a1 +#define ICON_FA_FILE_CODE "\xef\x87\x89" // U+f1c9 +#define ICON_FA_FILE_CONTRACT "\xef\x95\xac" // U+f56c +#define ICON_FA_FILE_CSV "\xef\x9b\x9d" // U+f6dd +#define ICON_FA_FILE_EXCEL "\xef\x87\x83" // U+f1c3 +#define ICON_FA_FILE_EXPORT "\xef\x95\xae" // U+f56e +#define ICON_FA_FILE_IMAGE "\xef\x87\x85" // U+f1c5 +#define ICON_FA_FILE_IMPORT "\xef\x95\xaf" // U+f56f +#define ICON_FA_FILE_INVOICE "\xef\x95\xb0" // U+f570 +#define ICON_FA_FILE_INVOICE_DOLLAR "\xef\x95\xb1" // U+f571 +#define ICON_FA_FILE_LINES "\xef\x85\x9c" // U+f15c +#define ICON_FA_FILE_MEDICAL "\xef\x91\xb7" // U+f477 +#define ICON_FA_FILE_PDF "\xef\x87\x81" // U+f1c1 +#define ICON_FA_FILE_PEN "\xef\x8c\x9c" // U+f31c +#define ICON_FA_FILE_POWERPOINT "\xef\x87\x84" // U+f1c4 +#define ICON_FA_FILE_PRESCRIPTION "\xef\x95\xb2" // U+f572 +#define ICON_FA_FILE_SHIELD "\xee\x93\xb0" // U+e4f0 +#define ICON_FA_FILE_SIGNATURE "\xef\x95\xb3" // U+f573 +#define ICON_FA_FILE_VIDEO "\xef\x87\x88" // U+f1c8 +#define ICON_FA_FILE_WAVEFORM "\xef\x91\xb8" // U+f478 +#define ICON_FA_FILE_WORD "\xef\x87\x82" // U+f1c2 +#define ICON_FA_FILE_ZIPPER "\xef\x87\x86" // U+f1c6 +#define ICON_FA_FILL "\xef\x95\xb5" // U+f575 +#define ICON_FA_FILL_DRIP "\xef\x95\xb6" // U+f576 +#define ICON_FA_FILM "\xef\x80\x88" // U+f008 +#define ICON_FA_FILTER "\xef\x82\xb0" // U+f0b0 +#define ICON_FA_FILTER_CIRCLE_DOLLAR "\xef\x99\xa2" // U+f662 +#define ICON_FA_FILTER_CIRCLE_XMARK "\xee\x85\xbb" // U+e17b +#define ICON_FA_FINGERPRINT "\xef\x95\xb7" // U+f577 +#define ICON_FA_FIRE "\xef\x81\xad" // U+f06d +#define ICON_FA_FIRE_BURNER "\xee\x93\xb1" // U+e4f1 +#define ICON_FA_FIRE_EXTINGUISHER "\xef\x84\xb4" // U+f134 +#define ICON_FA_FIRE_FLAME_CURVED "\xef\x9f\xa4" // U+f7e4 +#define ICON_FA_FIRE_FLAME_SIMPLE "\xef\x91\xaa" // U+f46a +#define ICON_FA_FISH "\xef\x95\xb8" // U+f578 +#define ICON_FA_FISH_FINS "\xee\x93\xb2" // U+e4f2 +#define ICON_FA_FLAG "\xef\x80\xa4" // U+f024 +#define ICON_FA_FLAG_CHECKERED "\xef\x84\x9e" // U+f11e +#define ICON_FA_FLAG_USA "\xef\x9d\x8d" // U+f74d +#define ICON_FA_FLASK "\xef\x83\x83" // U+f0c3 +#define ICON_FA_FLASK_VIAL "\xee\x93\xb3" // U+e4f3 +#define ICON_FA_FLOPPY_DISK "\xef\x83\x87" // U+f0c7 +#define ICON_FA_FLORIN_SIGN "\xee\x86\x84" // U+e184 +#define ICON_FA_FOLDER "\xef\x81\xbb" // U+f07b +#define ICON_FA_FOLDER_CLOSED "\xee\x86\x85" // U+e185 +#define ICON_FA_FOLDER_MINUS "\xef\x99\x9d" // U+f65d +#define ICON_FA_FOLDER_OPEN "\xef\x81\xbc" // U+f07c +#define ICON_FA_FOLDER_PLUS "\xef\x99\x9e" // U+f65e +#define ICON_FA_FOLDER_TREE "\xef\xa0\x82" // U+f802 +#define ICON_FA_FONT "\xef\x80\xb1" // U+f031 +#define ICON_FA_FONT_AWESOME "\xef\x8a\xb4" // U+f2b4 +#define ICON_FA_FOOTBALL "\xef\x91\x8e" // U+f44e +#define ICON_FA_FORWARD "\xef\x81\x8e" // U+f04e +#define ICON_FA_FORWARD_FAST "\xef\x81\x90" // U+f050 +#define ICON_FA_FORWARD_STEP "\xef\x81\x91" // U+f051 +#define ICON_FA_FRANC_SIGN "\xee\x86\x8f" // U+e18f +#define ICON_FA_FROG "\xef\x94\xae" // U+f52e +#define ICON_FA_FUTBOL "\xef\x87\xa3" // U+f1e3 +#define ICON_FA_G "G" // U+0047 +#define ICON_FA_GAMEPAD "\xef\x84\x9b" // U+f11b +#define ICON_FA_GAS_PUMP "\xef\x94\xaf" // U+f52f +#define ICON_FA_GAUGE "\xef\x98\xa4" // U+f624 +#define ICON_FA_GAUGE_HIGH "\xef\x98\xa5" // U+f625 +#define ICON_FA_GAUGE_SIMPLE "\xef\x98\xa9" // U+f629 +#define ICON_FA_GAUGE_SIMPLE_HIGH "\xef\x98\xaa" // U+f62a +#define ICON_FA_GAVEL "\xef\x83\xa3" // U+f0e3 +#define ICON_FA_GEAR "\xef\x80\x93" // U+f013 +#define ICON_FA_GEARS "\xef\x82\x85" // U+f085 +#define ICON_FA_GEM "\xef\x8e\xa5" // U+f3a5 +#define ICON_FA_GENDERLESS "\xef\x88\xad" // U+f22d +#define ICON_FA_GHOST "\xef\x9b\xa2" // U+f6e2 +#define ICON_FA_GIFT "\xef\x81\xab" // U+f06b +#define ICON_FA_GIFTS "\xef\x9e\x9c" // U+f79c +#define ICON_FA_GLASS_WATER "\xee\x93\xb4" // U+e4f4 +#define ICON_FA_GLASS_WATER_DROPLET "\xee\x93\xb5" // U+e4f5 +#define ICON_FA_GLASSES "\xef\x94\xb0" // U+f530 +#define ICON_FA_GLOBE "\xef\x82\xac" // U+f0ac +#define ICON_FA_GOLF_BALL_TEE "\xef\x91\x90" // U+f450 +#define ICON_FA_GOPURAM "\xef\x99\xa4" // U+f664 +#define ICON_FA_GRADUATION_CAP "\xef\x86\x9d" // U+f19d +#define ICON_FA_GREATER_THAN ">" // U+003e +#define ICON_FA_GREATER_THAN_EQUAL "\xef\x94\xb2" // U+f532 +#define ICON_FA_GRIP "\xef\x96\x8d" // U+f58d +#define ICON_FA_GRIP_LINES "\xef\x9e\xa4" // U+f7a4 +#define ICON_FA_GRIP_LINES_VERTICAL "\xef\x9e\xa5" // U+f7a5 +#define ICON_FA_GRIP_VERTICAL "\xef\x96\x8e" // U+f58e +#define ICON_FA_GROUP_ARROWS_ROTATE "\xee\x93\xb6" // U+e4f6 +#define ICON_FA_GUARANI_SIGN "\xee\x86\x9a" // U+e19a +#define ICON_FA_GUITAR "\xef\x9e\xa6" // U+f7a6 +#define ICON_FA_GUN "\xee\x86\x9b" // U+e19b +#define ICON_FA_H "H" // U+0048 +#define ICON_FA_HAMMER "\xef\x9b\xa3" // U+f6e3 +#define ICON_FA_HAMSA "\xef\x99\xa5" // U+f665 +#define ICON_FA_HAND "\xef\x89\x96" // U+f256 +#define ICON_FA_HAND_BACK_FIST "\xef\x89\x95" // U+f255 +#define ICON_FA_HAND_DOTS "\xef\x91\xa1" // U+f461 +#define ICON_FA_HAND_FIST "\xef\x9b\x9e" // U+f6de +#define ICON_FA_HAND_HOLDING "\xef\x92\xbd" // U+f4bd +#define ICON_FA_HAND_HOLDING_DOLLAR "\xef\x93\x80" // U+f4c0 +#define ICON_FA_HAND_HOLDING_DROPLET "\xef\x93\x81" // U+f4c1 +#define ICON_FA_HAND_HOLDING_HAND "\xee\x93\xb7" // U+e4f7 +#define ICON_FA_HAND_HOLDING_HEART "\xef\x92\xbe" // U+f4be +#define ICON_FA_HAND_HOLDING_MEDICAL "\xee\x81\x9c" // U+e05c +#define ICON_FA_HAND_LIZARD "\xef\x89\x98" // U+f258 +#define ICON_FA_HAND_MIDDLE_FINGER "\xef\xa0\x86" // U+f806 +#define ICON_FA_HAND_PEACE "\xef\x89\x9b" // U+f25b +#define ICON_FA_HAND_POINT_DOWN "\xef\x82\xa7" // U+f0a7 +#define ICON_FA_HAND_POINT_LEFT "\xef\x82\xa5" // U+f0a5 +#define ICON_FA_HAND_POINT_RIGHT "\xef\x82\xa4" // U+f0a4 +#define ICON_FA_HAND_POINT_UP "\xef\x82\xa6" // U+f0a6 +#define ICON_FA_HAND_POINTER "\xef\x89\x9a" // U+f25a +#define ICON_FA_HAND_SCISSORS "\xef\x89\x97" // U+f257 +#define ICON_FA_HAND_SPARKLES "\xee\x81\x9d" // U+e05d +#define ICON_FA_HAND_SPOCK "\xef\x89\x99" // U+f259 +#define ICON_FA_HANDCUFFS "\xee\x93\xb8" // U+e4f8 +#define ICON_FA_HANDS "\xef\x8a\xa7" // U+f2a7 +#define ICON_FA_HANDS_ASL_INTERPRETING "\xef\x8a\xa3" // U+f2a3 +#define ICON_FA_HANDS_BOUND "\xee\x93\xb9" // U+e4f9 +#define ICON_FA_HANDS_BUBBLES "\xee\x81\x9e" // U+e05e +#define ICON_FA_HANDS_CLAPPING "\xee\x86\xa8" // U+e1a8 +#define ICON_FA_HANDS_HOLDING "\xef\x93\x82" // U+f4c2 +#define ICON_FA_HANDS_HOLDING_CHILD "\xee\x93\xba" // U+e4fa +#define ICON_FA_HANDS_HOLDING_CIRCLE "\xee\x93\xbb" // U+e4fb +#define ICON_FA_HANDS_PRAYING "\xef\x9a\x84" // U+f684 +#define ICON_FA_HANDSHAKE "\xef\x8a\xb5" // U+f2b5 +#define ICON_FA_HANDSHAKE_ANGLE "\xef\x93\x84" // U+f4c4 +#define ICON_FA_HANDSHAKE_SIMPLE "\xef\x93\x86" // U+f4c6 +#define ICON_FA_HANDSHAKE_SIMPLE_SLASH "\xee\x81\x9f" // U+e05f +#define ICON_FA_HANDSHAKE_SLASH "\xee\x81\xa0" // U+e060 +#define ICON_FA_HANUKIAH "\xef\x9b\xa6" // U+f6e6 +#define ICON_FA_HARD_DRIVE "\xef\x82\xa0" // U+f0a0 +#define ICON_FA_HASHTAG "#" // U+0023 +#define ICON_FA_HAT_COWBOY "\xef\xa3\x80" // U+f8c0 +#define ICON_FA_HAT_COWBOY_SIDE "\xef\xa3\x81" // U+f8c1 +#define ICON_FA_HAT_WIZARD "\xef\x9b\xa8" // U+f6e8 +#define ICON_FA_HEAD_SIDE_COUGH "\xee\x81\xa1" // U+e061 +#define ICON_FA_HEAD_SIDE_COUGH_SLASH "\xee\x81\xa2" // U+e062 +#define ICON_FA_HEAD_SIDE_MASK "\xee\x81\xa3" // U+e063 +#define ICON_FA_HEAD_SIDE_VIRUS "\xee\x81\xa4" // U+e064 +#define ICON_FA_HEADING "\xef\x87\x9c" // U+f1dc +#define ICON_FA_HEADPHONES "\xef\x80\xa5" // U+f025 +#define ICON_FA_HEADPHONES_SIMPLE "\xef\x96\x8f" // U+f58f +#define ICON_FA_HEADSET "\xef\x96\x90" // U+f590 +#define ICON_FA_HEART "\xef\x80\x84" // U+f004 +#define ICON_FA_HEART_CIRCLE_BOLT "\xee\x93\xbc" // U+e4fc +#define ICON_FA_HEART_CIRCLE_CHECK "\xee\x93\xbd" // U+e4fd +#define ICON_FA_HEART_CIRCLE_EXCLAMATION "\xee\x93\xbe" // U+e4fe +#define ICON_FA_HEART_CIRCLE_MINUS "\xee\x93\xbf" // U+e4ff +#define ICON_FA_HEART_CIRCLE_PLUS "\xee\x94\x80" // U+e500 +#define ICON_FA_HEART_CIRCLE_XMARK "\xee\x94\x81" // U+e501 +#define ICON_FA_HEART_CRACK "\xef\x9e\xa9" // U+f7a9 +#define ICON_FA_HEART_PULSE "\xef\x88\x9e" // U+f21e +#define ICON_FA_HELICOPTER "\xef\x94\xb3" // U+f533 +#define ICON_FA_HELICOPTER_SYMBOL "\xee\x94\x82" // U+e502 +#define ICON_FA_HELMET_SAFETY "\xef\xa0\x87" // U+f807 +#define ICON_FA_HELMET_UN "\xee\x94\x83" // U+e503 +#define ICON_FA_HIGHLIGHTER "\xef\x96\x91" // U+f591 +#define ICON_FA_HILL_AVALANCHE "\xee\x94\x87" // U+e507 +#define ICON_FA_HILL_ROCKSLIDE "\xee\x94\x88" // U+e508 +#define ICON_FA_HIPPO "\xef\x9b\xad" // U+f6ed +#define ICON_FA_HOCKEY_PUCK "\xef\x91\x93" // U+f453 +#define ICON_FA_HOLLY_BERRY "\xef\x9e\xaa" // U+f7aa +#define ICON_FA_HORSE "\xef\x9b\xb0" // U+f6f0 +#define ICON_FA_HORSE_HEAD "\xef\x9e\xab" // U+f7ab +#define ICON_FA_HOSPITAL "\xef\x83\xb8" // U+f0f8 +#define ICON_FA_HOSPITAL_USER "\xef\xa0\x8d" // U+f80d +#define ICON_FA_HOT_TUB_PERSON "\xef\x96\x93" // U+f593 +#define ICON_FA_HOTDOG "\xef\xa0\x8f" // U+f80f +#define ICON_FA_HOTEL "\xef\x96\x94" // U+f594 +#define ICON_FA_HOURGLASS "\xef\x89\x94" // U+f254 +#define ICON_FA_HOURGLASS_END "\xef\x89\x93" // U+f253 +#define ICON_FA_HOURGLASS_HALF "\xef\x89\x92" // U+f252 +#define ICON_FA_HOURGLASS_START "\xef\x89\x91" // U+f251 +#define ICON_FA_HOUSE "\xef\x80\x95" // U+f015 +#define ICON_FA_HOUSE_CHIMNEY "\xee\x8e\xaf" // U+e3af +#define ICON_FA_HOUSE_CHIMNEY_CRACK "\xef\x9b\xb1" // U+f6f1 +#define ICON_FA_HOUSE_CHIMNEY_MEDICAL "\xef\x9f\xb2" // U+f7f2 +#define ICON_FA_HOUSE_CHIMNEY_USER "\xee\x81\xa5" // U+e065 +#define ICON_FA_HOUSE_CHIMNEY_WINDOW "\xee\x80\x8d" // U+e00d +#define ICON_FA_HOUSE_CIRCLE_CHECK "\xee\x94\x89" // U+e509 +#define ICON_FA_HOUSE_CIRCLE_EXCLAMATION "\xee\x94\x8a" // U+e50a +#define ICON_FA_HOUSE_CIRCLE_XMARK "\xee\x94\x8b" // U+e50b +#define ICON_FA_HOUSE_CRACK "\xee\x8e\xb1" // U+e3b1 +#define ICON_FA_HOUSE_FIRE "\xee\x94\x8c" // U+e50c +#define ICON_FA_HOUSE_FLAG "\xee\x94\x8d" // U+e50d +#define ICON_FA_HOUSE_FLOOD_WATER "\xee\x94\x8e" // U+e50e +#define ICON_FA_HOUSE_FLOOD_WATER_CIRCLE_ARROW_RIGHT "\xee\x94\x8f" // U+e50f +#define ICON_FA_HOUSE_LAPTOP "\xee\x81\xa6" // U+e066 +#define ICON_FA_HOUSE_LOCK "\xee\x94\x90" // U+e510 +#define ICON_FA_HOUSE_MEDICAL "\xee\x8e\xb2" // U+e3b2 +#define ICON_FA_HOUSE_MEDICAL_CIRCLE_CHECK "\xee\x94\x91" // U+e511 +#define ICON_FA_HOUSE_MEDICAL_CIRCLE_EXCLAMATION "\xee\x94\x92" // U+e512 +#define ICON_FA_HOUSE_MEDICAL_CIRCLE_XMARK "\xee\x94\x93" // U+e513 +#define ICON_FA_HOUSE_MEDICAL_FLAG "\xee\x94\x94" // U+e514 +#define ICON_FA_HOUSE_SIGNAL "\xee\x80\x92" // U+e012 +#define ICON_FA_HOUSE_TSUNAMI "\xee\x94\x95" // U+e515 +#define ICON_FA_HOUSE_USER "\xee\x86\xb0" // U+e1b0 +#define ICON_FA_HRYVNIA_SIGN "\xef\x9b\xb2" // U+f6f2 +#define ICON_FA_HURRICANE "\xef\x9d\x91" // U+f751 +#define ICON_FA_I "I" // U+0049 +#define ICON_FA_I_CURSOR "\xef\x89\x86" // U+f246 +#define ICON_FA_ICE_CREAM "\xef\xa0\x90" // U+f810 +#define ICON_FA_ICICLES "\xef\x9e\xad" // U+f7ad +#define ICON_FA_ICONS "\xef\xa1\xad" // U+f86d +#define ICON_FA_ID_BADGE "\xef\x8b\x81" // U+f2c1 +#define ICON_FA_ID_CARD "\xef\x8b\x82" // U+f2c2 +#define ICON_FA_ID_CARD_CLIP "\xef\x91\xbf" // U+f47f +#define ICON_FA_IGLOO "\xef\x9e\xae" // U+f7ae +#define ICON_FA_IMAGE "\xef\x80\xbe" // U+f03e +#define ICON_FA_IMAGE_PORTRAIT "\xef\x8f\xa0" // U+f3e0 +#define ICON_FA_IMAGES "\xef\x8c\x82" // U+f302 +#define ICON_FA_INBOX "\xef\x80\x9c" // U+f01c +#define ICON_FA_INDENT "\xef\x80\xbc" // U+f03c +#define ICON_FA_INDIAN_RUPEE_SIGN "\xee\x86\xbc" // U+e1bc +#define ICON_FA_INDUSTRY "\xef\x89\xb5" // U+f275 +#define ICON_FA_INFINITY "\xef\x94\xb4" // U+f534 +#define ICON_FA_INFO "\xef\x84\xa9" // U+f129 +#define ICON_FA_ITALIC "\xef\x80\xb3" // U+f033 +#define ICON_FA_J "J" // U+004a +#define ICON_FA_JAR "\xee\x94\x96" // U+e516 +#define ICON_FA_JAR_WHEAT "\xee\x94\x97" // U+e517 +#define ICON_FA_JEDI "\xef\x99\xa9" // U+f669 +#define ICON_FA_JET_FIGHTER "\xef\x83\xbb" // U+f0fb +#define ICON_FA_JET_FIGHTER_UP "\xee\x94\x98" // U+e518 +#define ICON_FA_JOINT "\xef\x96\x95" // U+f595 +#define ICON_FA_JUG_DETERGENT "\xee\x94\x99" // U+e519 +#define ICON_FA_K "K" // U+004b +#define ICON_FA_KAABA "\xef\x99\xab" // U+f66b +#define ICON_FA_KEY "\xef\x82\x84" // U+f084 +#define ICON_FA_KEYBOARD "\xef\x84\x9c" // U+f11c +#define ICON_FA_KHANDA "\xef\x99\xad" // U+f66d +#define ICON_FA_KIP_SIGN "\xee\x87\x84" // U+e1c4 +#define ICON_FA_KIT_MEDICAL "\xef\x91\xb9" // U+f479 +#define ICON_FA_KITCHEN_SET "\xee\x94\x9a" // U+e51a +#define ICON_FA_KIWI_BIRD "\xef\x94\xb5" // U+f535 +#define ICON_FA_L "L" // U+004c +#define ICON_FA_LAND_MINE_ON "\xee\x94\x9b" // U+e51b +#define ICON_FA_LANDMARK "\xef\x99\xaf" // U+f66f +#define ICON_FA_LANDMARK_DOME "\xef\x9d\x92" // U+f752 +#define ICON_FA_LANDMARK_FLAG "\xee\x94\x9c" // U+e51c +#define ICON_FA_LANGUAGE "\xef\x86\xab" // U+f1ab +#define ICON_FA_LAPTOP "\xef\x84\x89" // U+f109 +#define ICON_FA_LAPTOP_CODE "\xef\x97\xbc" // U+f5fc +#define ICON_FA_LAPTOP_FILE "\xee\x94\x9d" // U+e51d +#define ICON_FA_LAPTOP_MEDICAL "\xef\xa0\x92" // U+f812 +#define ICON_FA_LARI_SIGN "\xee\x87\x88" // U+e1c8 +#define ICON_FA_LAYER_GROUP "\xef\x97\xbd" // U+f5fd +#define ICON_FA_LEAF "\xef\x81\xac" // U+f06c +#define ICON_FA_LEFT_LONG "\xef\x8c\x8a" // U+f30a +#define ICON_FA_LEFT_RIGHT "\xef\x8c\xb7" // U+f337 +#define ICON_FA_LEMON "\xef\x82\x94" // U+f094 +#define ICON_FA_LESS_THAN "<" // U+003c +#define ICON_FA_LESS_THAN_EQUAL "\xef\x94\xb7" // U+f537 +#define ICON_FA_LIFE_RING "\xef\x87\x8d" // U+f1cd +#define ICON_FA_LIGHTBULB "\xef\x83\xab" // U+f0eb +#define ICON_FA_LINES_LEANING "\xee\x94\x9e" // U+e51e +#define ICON_FA_LINK "\xef\x83\x81" // U+f0c1 +#define ICON_FA_LINK_SLASH "\xef\x84\xa7" // U+f127 +#define ICON_FA_LIRA_SIGN "\xef\x86\x95" // U+f195 +#define ICON_FA_LIST "\xef\x80\xba" // U+f03a +#define ICON_FA_LIST_CHECK "\xef\x82\xae" // U+f0ae +#define ICON_FA_LIST_OL "\xef\x83\x8b" // U+f0cb +#define ICON_FA_LIST_UL "\xef\x83\x8a" // U+f0ca +#define ICON_FA_LITECOIN_SIGN "\xee\x87\x93" // U+e1d3 +#define ICON_FA_LOCATION_ARROW "\xef\x84\xa4" // U+f124 +#define ICON_FA_LOCATION_CROSSHAIRS "\xef\x98\x81" // U+f601 +#define ICON_FA_LOCATION_DOT "\xef\x8f\x85" // U+f3c5 +#define ICON_FA_LOCATION_PIN "\xef\x81\x81" // U+f041 +#define ICON_FA_LOCATION_PIN_LOCK "\xee\x94\x9f" // U+e51f +#define ICON_FA_LOCK "\xef\x80\xa3" // U+f023 +#define ICON_FA_LOCK_OPEN "\xef\x8f\x81" // U+f3c1 +#define ICON_FA_LOCUST "\xee\x94\xa0" // U+e520 +#define ICON_FA_LUNGS "\xef\x98\x84" // U+f604 +#define ICON_FA_LUNGS_VIRUS "\xee\x81\xa7" // U+e067 +#define ICON_FA_M "M" // U+004d +#define ICON_FA_MAGNET "\xef\x81\xb6" // U+f076 +#define ICON_FA_MAGNIFYING_GLASS "\xef\x80\x82" // U+f002 +#define ICON_FA_MAGNIFYING_GLASS_ARROW_RIGHT "\xee\x94\xa1" // U+e521 +#define ICON_FA_MAGNIFYING_GLASS_CHART "\xee\x94\xa2" // U+e522 +#define ICON_FA_MAGNIFYING_GLASS_DOLLAR "\xef\x9a\x88" // U+f688 +#define ICON_FA_MAGNIFYING_GLASS_LOCATION "\xef\x9a\x89" // U+f689 +#define ICON_FA_MAGNIFYING_GLASS_MINUS "\xef\x80\x90" // U+f010 +#define ICON_FA_MAGNIFYING_GLASS_PLUS "\xef\x80\x8e" // U+f00e +#define ICON_FA_MANAT_SIGN "\xee\x87\x95" // U+e1d5 +#define ICON_FA_MAP "\xef\x89\xb9" // U+f279 +#define ICON_FA_MAP_LOCATION "\xef\x96\x9f" // U+f59f +#define ICON_FA_MAP_LOCATION_DOT "\xef\x96\xa0" // U+f5a0 +#define ICON_FA_MAP_PIN "\xef\x89\xb6" // U+f276 +#define ICON_FA_MARKER "\xef\x96\xa1" // U+f5a1 +#define ICON_FA_MARS "\xef\x88\xa2" // U+f222 +#define ICON_FA_MARS_AND_VENUS "\xef\x88\xa4" // U+f224 +#define ICON_FA_MARS_AND_VENUS_BURST "\xee\x94\xa3" // U+e523 +#define ICON_FA_MARS_DOUBLE "\xef\x88\xa7" // U+f227 +#define ICON_FA_MARS_STROKE "\xef\x88\xa9" // U+f229 +#define ICON_FA_MARS_STROKE_RIGHT "\xef\x88\xab" // U+f22b +#define ICON_FA_MARS_STROKE_UP "\xef\x88\xaa" // U+f22a +#define ICON_FA_MARTINI_GLASS "\xef\x95\xbb" // U+f57b +#define ICON_FA_MARTINI_GLASS_CITRUS "\xef\x95\xa1" // U+f561 +#define ICON_FA_MARTINI_GLASS_EMPTY "\xef\x80\x80" // U+f000 +#define ICON_FA_MASK "\xef\x9b\xba" // U+f6fa +#define ICON_FA_MASK_FACE "\xee\x87\x97" // U+e1d7 +#define ICON_FA_MASK_VENTILATOR "\xee\x94\xa4" // U+e524 +#define ICON_FA_MASKS_THEATER "\xef\x98\xb0" // U+f630 +#define ICON_FA_MATTRESS_PILLOW "\xee\x94\xa5" // U+e525 +#define ICON_FA_MAXIMIZE "\xef\x8c\x9e" // U+f31e +#define ICON_FA_MEDAL "\xef\x96\xa2" // U+f5a2 +#define ICON_FA_MEMORY "\xef\x94\xb8" // U+f538 +#define ICON_FA_MENORAH "\xef\x99\xb6" // U+f676 +#define ICON_FA_MERCURY "\xef\x88\xa3" // U+f223 +#define ICON_FA_MESSAGE "\xef\x89\xba" // U+f27a +#define ICON_FA_METEOR "\xef\x9d\x93" // U+f753 +#define ICON_FA_MICROCHIP "\xef\x8b\x9b" // U+f2db +#define ICON_FA_MICROPHONE "\xef\x84\xb0" // U+f130 +#define ICON_FA_MICROPHONE_LINES "\xef\x8f\x89" // U+f3c9 +#define ICON_FA_MICROPHONE_LINES_SLASH "\xef\x94\xb9" // U+f539 +#define ICON_FA_MICROPHONE_SLASH "\xef\x84\xb1" // U+f131 +#define ICON_FA_MICROSCOPE "\xef\x98\x90" // U+f610 +#define ICON_FA_MILL_SIGN "\xee\x87\xad" // U+e1ed +#define ICON_FA_MINIMIZE "\xef\x9e\x8c" // U+f78c +#define ICON_FA_MINUS "\xef\x81\xa8" // U+f068 +#define ICON_FA_MITTEN "\xef\x9e\xb5" // U+f7b5 +#define ICON_FA_MOBILE "\xef\x8f\x8e" // U+f3ce +#define ICON_FA_MOBILE_BUTTON "\xef\x84\x8b" // U+f10b +#define ICON_FA_MOBILE_RETRO "\xee\x94\xa7" // U+e527 +#define ICON_FA_MOBILE_SCREEN "\xef\x8f\x8f" // U+f3cf +#define ICON_FA_MOBILE_SCREEN_BUTTON "\xef\x8f\x8d" // U+f3cd +#define ICON_FA_MONEY_BILL "\xef\x83\x96" // U+f0d6 +#define ICON_FA_MONEY_BILL_1 "\xef\x8f\x91" // U+f3d1 +#define ICON_FA_MONEY_BILL_1_WAVE "\xef\x94\xbb" // U+f53b +#define ICON_FA_MONEY_BILL_TRANSFER "\xee\x94\xa8" // U+e528 +#define ICON_FA_MONEY_BILL_TREND_UP "\xee\x94\xa9" // U+e529 +#define ICON_FA_MONEY_BILL_WAVE "\xef\x94\xba" // U+f53a +#define ICON_FA_MONEY_BILL_WHEAT "\xee\x94\xaa" // U+e52a +#define ICON_FA_MONEY_BILLS "\xee\x87\xb3" // U+e1f3 +#define ICON_FA_MONEY_CHECK "\xef\x94\xbc" // U+f53c +#define ICON_FA_MONEY_CHECK_DOLLAR "\xef\x94\xbd" // U+f53d +#define ICON_FA_MONUMENT "\xef\x96\xa6" // U+f5a6 +#define ICON_FA_MOON "\xef\x86\x86" // U+f186 +#define ICON_FA_MORTAR_PESTLE "\xef\x96\xa7" // U+f5a7 +#define ICON_FA_MOSQUE "\xef\x99\xb8" // U+f678 +#define ICON_FA_MOSQUITO "\xee\x94\xab" // U+e52b +#define ICON_FA_MOSQUITO_NET "\xee\x94\xac" // U+e52c +#define ICON_FA_MOTORCYCLE "\xef\x88\x9c" // U+f21c +#define ICON_FA_MOUND "\xee\x94\xad" // U+e52d +#define ICON_FA_MOUNTAIN "\xef\x9b\xbc" // U+f6fc +#define ICON_FA_MOUNTAIN_CITY "\xee\x94\xae" // U+e52e +#define ICON_FA_MOUNTAIN_SUN "\xee\x94\xaf" // U+e52f +#define ICON_FA_MUG_HOT "\xef\x9e\xb6" // U+f7b6 +#define ICON_FA_MUG_SAUCER "\xef\x83\xb4" // U+f0f4 +#define ICON_FA_MUSIC "\xef\x80\x81" // U+f001 +#define ICON_FA_N "N" // U+004e +#define ICON_FA_NAIRA_SIGN "\xee\x87\xb6" // U+e1f6 +#define ICON_FA_NETWORK_WIRED "\xef\x9b\xbf" // U+f6ff +#define ICON_FA_NEUTER "\xef\x88\xac" // U+f22c +#define ICON_FA_NEWSPAPER "\xef\x87\xaa" // U+f1ea +#define ICON_FA_NOT_EQUAL "\xef\x94\xbe" // U+f53e +#define ICON_FA_NOTE_STICKY "\xef\x89\x89" // U+f249 +#define ICON_FA_NOTES_MEDICAL "\xef\x92\x81" // U+f481 +#define ICON_FA_O "O" // U+004f +#define ICON_FA_OBJECT_GROUP "\xef\x89\x87" // U+f247 +#define ICON_FA_OBJECT_UNGROUP "\xef\x89\x88" // U+f248 +#define ICON_FA_OIL_CAN "\xef\x98\x93" // U+f613 +#define ICON_FA_OIL_WELL "\xee\x94\xb2" // U+e532 +#define ICON_FA_OM "\xef\x99\xb9" // U+f679 +#define ICON_FA_OTTER "\xef\x9c\x80" // U+f700 +#define ICON_FA_OUTDENT "\xef\x80\xbb" // U+f03b +#define ICON_FA_P "P" // U+0050 +#define ICON_FA_PAGER "\xef\xa0\x95" // U+f815 +#define ICON_FA_PAINT_ROLLER "\xef\x96\xaa" // U+f5aa +#define ICON_FA_PAINTBRUSH "\xef\x87\xbc" // U+f1fc +#define ICON_FA_PALETTE "\xef\x94\xbf" // U+f53f +#define ICON_FA_PALLET "\xef\x92\x82" // U+f482 +#define ICON_FA_PANORAMA "\xee\x88\x89" // U+e209 +#define ICON_FA_PAPER_PLANE "\xef\x87\x98" // U+f1d8 +#define ICON_FA_PAPERCLIP "\xef\x83\x86" // U+f0c6 +#define ICON_FA_PARACHUTE_BOX "\xef\x93\x8d" // U+f4cd +#define ICON_FA_PARAGRAPH "\xef\x87\x9d" // U+f1dd +#define ICON_FA_PASSPORT "\xef\x96\xab" // U+f5ab +#define ICON_FA_PASTE "\xef\x83\xaa" // U+f0ea +#define ICON_FA_PAUSE "\xef\x81\x8c" // U+f04c +#define ICON_FA_PAW "\xef\x86\xb0" // U+f1b0 +#define ICON_FA_PEACE "\xef\x99\xbc" // U+f67c +#define ICON_FA_PEN "\xef\x8c\x84" // U+f304 +#define ICON_FA_PEN_CLIP "\xef\x8c\x85" // U+f305 +#define ICON_FA_PEN_FANCY "\xef\x96\xac" // U+f5ac +#define ICON_FA_PEN_NIB "\xef\x96\xad" // U+f5ad +#define ICON_FA_PEN_RULER "\xef\x96\xae" // U+f5ae +#define ICON_FA_PEN_TO_SQUARE "\xef\x81\x84" // U+f044 +#define ICON_FA_PENCIL "\xef\x8c\x83" // U+f303 +#define ICON_FA_PEOPLE_ARROWS "\xee\x81\xa8" // U+e068 +#define ICON_FA_PEOPLE_CARRY_BOX "\xef\x93\x8e" // U+f4ce +#define ICON_FA_PEOPLE_GROUP "\xee\x94\xb3" // U+e533 +#define ICON_FA_PEOPLE_LINE "\xee\x94\xb4" // U+e534 +#define ICON_FA_PEOPLE_PULLING "\xee\x94\xb5" // U+e535 +#define ICON_FA_PEOPLE_ROBBERY "\xee\x94\xb6" // U+e536 +#define ICON_FA_PEOPLE_ROOF "\xee\x94\xb7" // U+e537 +#define ICON_FA_PEPPER_HOT "\xef\xa0\x96" // U+f816 +#define ICON_FA_PERCENT "%" // U+0025 +#define ICON_FA_PERSON "\xef\x86\x83" // U+f183 +#define ICON_FA_PERSON_ARROW_DOWN_TO_LINE "\xee\x94\xb8" // U+e538 +#define ICON_FA_PERSON_ARROW_UP_FROM_LINE "\xee\x94\xb9" // U+e539 +#define ICON_FA_PERSON_BIKING "\xef\xa1\x8a" // U+f84a +#define ICON_FA_PERSON_BOOTH "\xef\x9d\x96" // U+f756 +#define ICON_FA_PERSON_BREASTFEEDING "\xee\x94\xba" // U+e53a +#define ICON_FA_PERSON_BURST "\xee\x94\xbb" // U+e53b +#define ICON_FA_PERSON_CANE "\xee\x94\xbc" // U+e53c +#define ICON_FA_PERSON_CHALKBOARD "\xee\x94\xbd" // U+e53d +#define ICON_FA_PERSON_CIRCLE_CHECK "\xee\x94\xbe" // U+e53e +#define ICON_FA_PERSON_CIRCLE_EXCLAMATION "\xee\x94\xbf" // U+e53f +#define ICON_FA_PERSON_CIRCLE_MINUS "\xee\x95\x80" // U+e540 +#define ICON_FA_PERSON_CIRCLE_PLUS "\xee\x95\x81" // U+e541 +#define ICON_FA_PERSON_CIRCLE_QUESTION "\xee\x95\x82" // U+e542 +#define ICON_FA_PERSON_CIRCLE_XMARK "\xee\x95\x83" // U+e543 +#define ICON_FA_PERSON_DIGGING "\xef\xa1\x9e" // U+f85e +#define ICON_FA_PERSON_DOTS_FROM_LINE "\xef\x91\xb0" // U+f470 +#define ICON_FA_PERSON_DRESS "\xef\x86\x82" // U+f182 +#define ICON_FA_PERSON_DRESS_BURST "\xee\x95\x84" // U+e544 +#define ICON_FA_PERSON_DROWNING "\xee\x95\x85" // U+e545 +#define ICON_FA_PERSON_FALLING "\xee\x95\x86" // U+e546 +#define ICON_FA_PERSON_FALLING_BURST "\xee\x95\x87" // U+e547 +#define ICON_FA_PERSON_HALF_DRESS "\xee\x95\x88" // U+e548 +#define ICON_FA_PERSON_HARASSING "\xee\x95\x89" // U+e549 +#define ICON_FA_PERSON_HIKING "\xef\x9b\xac" // U+f6ec +#define ICON_FA_PERSON_MILITARY_POINTING "\xee\x95\x8a" // U+e54a +#define ICON_FA_PERSON_MILITARY_RIFLE "\xee\x95\x8b" // U+e54b +#define ICON_FA_PERSON_MILITARY_TO_PERSON "\xee\x95\x8c" // U+e54c +#define ICON_FA_PERSON_PRAYING "\xef\x9a\x83" // U+f683 +#define ICON_FA_PERSON_PREGNANT "\xee\x8c\x9e" // U+e31e +#define ICON_FA_PERSON_RAYS "\xee\x95\x8d" // U+e54d +#define ICON_FA_PERSON_RIFLE "\xee\x95\x8e" // U+e54e +#define ICON_FA_PERSON_RUNNING "\xef\x9c\x8c" // U+f70c +#define ICON_FA_PERSON_SHELTER "\xee\x95\x8f" // U+e54f +#define ICON_FA_PERSON_SKATING "\xef\x9f\x85" // U+f7c5 +#define ICON_FA_PERSON_SKIING "\xef\x9f\x89" // U+f7c9 +#define ICON_FA_PERSON_SKIING_NORDIC "\xef\x9f\x8a" // U+f7ca +#define ICON_FA_PERSON_SNOWBOARDING "\xef\x9f\x8e" // U+f7ce +#define ICON_FA_PERSON_SWIMMING "\xef\x97\x84" // U+f5c4 +#define ICON_FA_PERSON_THROUGH_WINDOW "\xee\x96\xa9" // U+e5a9 +#define ICON_FA_PERSON_WALKING "\xef\x95\x94" // U+f554 +#define ICON_FA_PERSON_WALKING_ARROW_LOOP_LEFT "\xee\x95\x91" // U+e551 +#define ICON_FA_PERSON_WALKING_ARROW_RIGHT "\xee\x95\x92" // U+e552 +#define ICON_FA_PERSON_WALKING_DASHED_LINE_ARROW_RIGHT "\xee\x95\x93" // U+e553 +#define ICON_FA_PERSON_WALKING_LUGGAGE "\xee\x95\x94" // U+e554 +#define ICON_FA_PERSON_WALKING_WITH_CANE "\xef\x8a\x9d" // U+f29d +#define ICON_FA_PESETA_SIGN "\xee\x88\xa1" // U+e221 +#define ICON_FA_PESO_SIGN "\xee\x88\xa2" // U+e222 +#define ICON_FA_PHONE "\xef\x82\x95" // U+f095 +#define ICON_FA_PHONE_FLIP "\xef\xa1\xb9" // U+f879 +#define ICON_FA_PHONE_SLASH "\xef\x8f\x9d" // U+f3dd +#define ICON_FA_PHONE_VOLUME "\xef\x8a\xa0" // U+f2a0 +#define ICON_FA_PHOTO_FILM "\xef\xa1\xbc" // U+f87c +#define ICON_FA_PIGGY_BANK "\xef\x93\x93" // U+f4d3 +#define ICON_FA_PILLS "\xef\x92\x84" // U+f484 +#define ICON_FA_PIZZA_SLICE "\xef\xa0\x98" // U+f818 +#define ICON_FA_PLACE_OF_WORSHIP "\xef\x99\xbf" // U+f67f +#define ICON_FA_PLANE "\xef\x81\xb2" // U+f072 +#define ICON_FA_PLANE_ARRIVAL "\xef\x96\xaf" // U+f5af +#define ICON_FA_PLANE_CIRCLE_CHECK "\xee\x95\x95" // U+e555 +#define ICON_FA_PLANE_CIRCLE_EXCLAMATION "\xee\x95\x96" // U+e556 +#define ICON_FA_PLANE_CIRCLE_XMARK "\xee\x95\x97" // U+e557 +#define ICON_FA_PLANE_DEPARTURE "\xef\x96\xb0" // U+f5b0 +#define ICON_FA_PLANE_LOCK "\xee\x95\x98" // U+e558 +#define ICON_FA_PLANE_SLASH "\xee\x81\xa9" // U+e069 +#define ICON_FA_PLANE_UP "\xee\x88\xad" // U+e22d +#define ICON_FA_PLANT_WILT "\xee\x96\xaa" // U+e5aa +#define ICON_FA_PLATE_WHEAT "\xee\x95\x9a" // U+e55a +#define ICON_FA_PLAY "\xef\x81\x8b" // U+f04b +#define ICON_FA_PLUG "\xef\x87\xa6" // U+f1e6 +#define ICON_FA_PLUG_CIRCLE_BOLT "\xee\x95\x9b" // U+e55b +#define ICON_FA_PLUG_CIRCLE_CHECK "\xee\x95\x9c" // U+e55c +#define ICON_FA_PLUG_CIRCLE_EXCLAMATION "\xee\x95\x9d" // U+e55d +#define ICON_FA_PLUG_CIRCLE_MINUS "\xee\x95\x9e" // U+e55e +#define ICON_FA_PLUG_CIRCLE_PLUS "\xee\x95\x9f" // U+e55f +#define ICON_FA_PLUG_CIRCLE_XMARK "\xee\x95\xa0" // U+e560 +#define ICON_FA_PLUS "+" // U+002b +#define ICON_FA_PLUS_MINUS "\xee\x90\xbc" // U+e43c +#define ICON_FA_PODCAST "\xef\x8b\x8e" // U+f2ce +#define ICON_FA_POO "\xef\x8b\xbe" // U+f2fe +#define ICON_FA_POO_STORM "\xef\x9d\x9a" // U+f75a +#define ICON_FA_POOP "\xef\x98\x99" // U+f619 +#define ICON_FA_POWER_OFF "\xef\x80\x91" // U+f011 +#define ICON_FA_PRESCRIPTION "\xef\x96\xb1" // U+f5b1 +#define ICON_FA_PRESCRIPTION_BOTTLE "\xef\x92\x85" // U+f485 +#define ICON_FA_PRESCRIPTION_BOTTLE_MEDICAL "\xef\x92\x86" // U+f486 +#define ICON_FA_PRINT "\xef\x80\xaf" // U+f02f +#define ICON_FA_PUMP_MEDICAL "\xee\x81\xaa" // U+e06a +#define ICON_FA_PUMP_SOAP "\xee\x81\xab" // U+e06b +#define ICON_FA_PUZZLE_PIECE "\xef\x84\xae" // U+f12e +#define ICON_FA_Q "Q" // U+0051 +#define ICON_FA_QRCODE "\xef\x80\xa9" // U+f029 +#define ICON_FA_QUESTION "?" // U+003f +#define ICON_FA_QUOTE_LEFT "\xef\x84\x8d" // U+f10d +#define ICON_FA_QUOTE_RIGHT "\xef\x84\x8e" // U+f10e +#define ICON_FA_R "R" // U+0052 +#define ICON_FA_RADIATION "\xef\x9e\xb9" // U+f7b9 +#define ICON_FA_RADIO "\xef\xa3\x97" // U+f8d7 +#define ICON_FA_RAINBOW "\xef\x9d\x9b" // U+f75b +#define ICON_FA_RANKING_STAR "\xee\x95\xa1" // U+e561 +#define ICON_FA_RECEIPT "\xef\x95\x83" // U+f543 +#define ICON_FA_RECORD_VINYL "\xef\xa3\x99" // U+f8d9 +#define ICON_FA_RECTANGLE_AD "\xef\x99\x81" // U+f641 +#define ICON_FA_RECTANGLE_LIST "\xef\x80\xa2" // U+f022 +#define ICON_FA_RECTANGLE_XMARK "\xef\x90\x90" // U+f410 +#define ICON_FA_RECYCLE "\xef\x86\xb8" // U+f1b8 +#define ICON_FA_REGISTERED "\xef\x89\x9d" // U+f25d +#define ICON_FA_REPEAT "\xef\x8d\xa3" // U+f363 +#define ICON_FA_REPLY "\xef\x8f\xa5" // U+f3e5 +#define ICON_FA_REPLY_ALL "\xef\x84\xa2" // U+f122 +#define ICON_FA_REPUBLICAN "\xef\x9d\x9e" // U+f75e +#define ICON_FA_RESTROOM "\xef\x9e\xbd" // U+f7bd +#define ICON_FA_RETWEET "\xef\x81\xb9" // U+f079 +#define ICON_FA_RIBBON "\xef\x93\x96" // U+f4d6 +#define ICON_FA_RIGHT_FROM_BRACKET "\xef\x8b\xb5" // U+f2f5 +#define ICON_FA_RIGHT_LEFT "\xef\x8d\xa2" // U+f362 +#define ICON_FA_RIGHT_LONG "\xef\x8c\x8b" // U+f30b +#define ICON_FA_RIGHT_TO_BRACKET "\xef\x8b\xb6" // U+f2f6 +#define ICON_FA_RING "\xef\x9c\x8b" // U+f70b +#define ICON_FA_ROAD "\xef\x80\x98" // U+f018 +#define ICON_FA_ROAD_BARRIER "\xee\x95\xa2" // U+e562 +#define ICON_FA_ROAD_BRIDGE "\xee\x95\xa3" // U+e563 +#define ICON_FA_ROAD_CIRCLE_CHECK "\xee\x95\xa4" // U+e564 +#define ICON_FA_ROAD_CIRCLE_EXCLAMATION "\xee\x95\xa5" // U+e565 +#define ICON_FA_ROAD_CIRCLE_XMARK "\xee\x95\xa6" // U+e566 +#define ICON_FA_ROAD_LOCK "\xee\x95\xa7" // U+e567 +#define ICON_FA_ROAD_SPIKES "\xee\x95\xa8" // U+e568 +#define ICON_FA_ROBOT "\xef\x95\x84" // U+f544 +#define ICON_FA_ROCKET "\xef\x84\xb5" // U+f135 +#define ICON_FA_ROTATE "\xef\x8b\xb1" // U+f2f1 +#define ICON_FA_ROTATE_LEFT "\xef\x8b\xaa" // U+f2ea +#define ICON_FA_ROTATE_RIGHT "\xef\x8b\xb9" // U+f2f9 +#define ICON_FA_ROUTE "\xef\x93\x97" // U+f4d7 +#define ICON_FA_RSS "\xef\x82\x9e" // U+f09e +#define ICON_FA_RUBLE_SIGN "\xef\x85\x98" // U+f158 +#define ICON_FA_RUG "\xee\x95\xa9" // U+e569 +#define ICON_FA_RULER "\xef\x95\x85" // U+f545 +#define ICON_FA_RULER_COMBINED "\xef\x95\x86" // U+f546 +#define ICON_FA_RULER_HORIZONTAL "\xef\x95\x87" // U+f547 +#define ICON_FA_RULER_VERTICAL "\xef\x95\x88" // U+f548 +#define ICON_FA_RUPEE_SIGN "\xef\x85\x96" // U+f156 +#define ICON_FA_RUPIAH_SIGN "\xee\x88\xbd" // U+e23d +#define ICON_FA_S "S" // U+0053 +#define ICON_FA_SACK_DOLLAR "\xef\xa0\x9d" // U+f81d +#define ICON_FA_SACK_XMARK "\xee\x95\xaa" // U+e56a +#define ICON_FA_SAILBOAT "\xee\x91\x85" // U+e445 +#define ICON_FA_SATELLITE "\xef\x9e\xbf" // U+f7bf +#define ICON_FA_SATELLITE_DISH "\xef\x9f\x80" // U+f7c0 +#define ICON_FA_SCALE_BALANCED "\xef\x89\x8e" // U+f24e +#define ICON_FA_SCALE_UNBALANCED "\xef\x94\x95" // U+f515 +#define ICON_FA_SCALE_UNBALANCED_FLIP "\xef\x94\x96" // U+f516 +#define ICON_FA_SCHOOL "\xef\x95\x89" // U+f549 +#define ICON_FA_SCHOOL_CIRCLE_CHECK "\xee\x95\xab" // U+e56b +#define ICON_FA_SCHOOL_CIRCLE_EXCLAMATION "\xee\x95\xac" // U+e56c +#define ICON_FA_SCHOOL_CIRCLE_XMARK "\xee\x95\xad" // U+e56d +#define ICON_FA_SCHOOL_FLAG "\xee\x95\xae" // U+e56e +#define ICON_FA_SCHOOL_LOCK "\xee\x95\xaf" // U+e56f +#define ICON_FA_SCISSORS "\xef\x83\x84" // U+f0c4 +#define ICON_FA_SCREWDRIVER "\xef\x95\x8a" // U+f54a +#define ICON_FA_SCREWDRIVER_WRENCH "\xef\x9f\x99" // U+f7d9 +#define ICON_FA_SCROLL "\xef\x9c\x8e" // U+f70e +#define ICON_FA_SCROLL_TORAH "\xef\x9a\xa0" // U+f6a0 +#define ICON_FA_SD_CARD "\xef\x9f\x82" // U+f7c2 +#define ICON_FA_SECTION "\xee\x91\x87" // U+e447 +#define ICON_FA_SEEDLING "\xef\x93\x98" // U+f4d8 +#define ICON_FA_SERVER "\xef\x88\xb3" // U+f233 +#define ICON_FA_SHAPES "\xef\x98\x9f" // U+f61f +#define ICON_FA_SHARE "\xef\x81\xa4" // U+f064 +#define ICON_FA_SHARE_FROM_SQUARE "\xef\x85\x8d" // U+f14d +#define ICON_FA_SHARE_NODES "\xef\x87\xa0" // U+f1e0 +#define ICON_FA_SHEET_PLASTIC "\xee\x95\xb1" // U+e571 +#define ICON_FA_SHEKEL_SIGN "\xef\x88\x8b" // U+f20b +#define ICON_FA_SHIELD "\xef\x84\xb2" // U+f132 +#define ICON_FA_SHIELD_CAT "\xee\x95\xb2" // U+e572 +#define ICON_FA_SHIELD_DOG "\xee\x95\xb3" // U+e573 +#define ICON_FA_SHIELD_HALVED "\xef\x8f\xad" // U+f3ed +#define ICON_FA_SHIELD_HEART "\xee\x95\xb4" // U+e574 +#define ICON_FA_SHIELD_VIRUS "\xee\x81\xac" // U+e06c +#define ICON_FA_SHIP "\xef\x88\x9a" // U+f21a +#define ICON_FA_SHIRT "\xef\x95\x93" // U+f553 +#define ICON_FA_SHOE_PRINTS "\xef\x95\x8b" // U+f54b +#define ICON_FA_SHOP "\xef\x95\x8f" // U+f54f +#define ICON_FA_SHOP_LOCK "\xee\x92\xa5" // U+e4a5 +#define ICON_FA_SHOP_SLASH "\xee\x81\xb0" // U+e070 +#define ICON_FA_SHOWER "\xef\x8b\x8c" // U+f2cc +#define ICON_FA_SHRIMP "\xee\x91\x88" // U+e448 +#define ICON_FA_SHUFFLE "\xef\x81\xb4" // U+f074 +#define ICON_FA_SHUTTLE_SPACE "\xef\x86\x97" // U+f197 +#define ICON_FA_SIGN_HANGING "\xef\x93\x99" // U+f4d9 +#define ICON_FA_SIGNAL "\xef\x80\x92" // U+f012 +#define ICON_FA_SIGNATURE "\xef\x96\xb7" // U+f5b7 +#define ICON_FA_SIGNS_POST "\xef\x89\xb7" // U+f277 +#define ICON_FA_SIM_CARD "\xef\x9f\x84" // U+f7c4 +#define ICON_FA_SINK "\xee\x81\xad" // U+e06d +#define ICON_FA_SITEMAP "\xef\x83\xa8" // U+f0e8 +#define ICON_FA_SKULL "\xef\x95\x8c" // U+f54c +#define ICON_FA_SKULL_CROSSBONES "\xef\x9c\x94" // U+f714 +#define ICON_FA_SLASH "\xef\x9c\x95" // U+f715 +#define ICON_FA_SLEIGH "\xef\x9f\x8c" // U+f7cc +#define ICON_FA_SLIDERS "\xef\x87\x9e" // U+f1de +#define ICON_FA_SMOG "\xef\x9d\x9f" // U+f75f +#define ICON_FA_SMOKING "\xef\x92\x8d" // U+f48d +#define ICON_FA_SNOWFLAKE "\xef\x8b\x9c" // U+f2dc +#define ICON_FA_SNOWMAN "\xef\x9f\x90" // U+f7d0 +#define ICON_FA_SNOWPLOW "\xef\x9f\x92" // U+f7d2 +#define ICON_FA_SOAP "\xee\x81\xae" // U+e06e +#define ICON_FA_SOCKS "\xef\x9a\x96" // U+f696 +#define ICON_FA_SOLAR_PANEL "\xef\x96\xba" // U+f5ba +#define ICON_FA_SORT "\xef\x83\x9c" // U+f0dc +#define ICON_FA_SORT_DOWN "\xef\x83\x9d" // U+f0dd +#define ICON_FA_SORT_UP "\xef\x83\x9e" // U+f0de +#define ICON_FA_SPA "\xef\x96\xbb" // U+f5bb +#define ICON_FA_SPAGHETTI_MONSTER_FLYING "\xef\x99\xbb" // U+f67b +#define ICON_FA_SPELL_CHECK "\xef\xa2\x91" // U+f891 +#define ICON_FA_SPIDER "\xef\x9c\x97" // U+f717 +#define ICON_FA_SPINNER "\xef\x84\x90" // U+f110 +#define ICON_FA_SPLOTCH "\xef\x96\xbc" // U+f5bc +#define ICON_FA_SPOON "\xef\x8b\xa5" // U+f2e5 +#define ICON_FA_SPRAY_CAN "\xef\x96\xbd" // U+f5bd +#define ICON_FA_SPRAY_CAN_SPARKLES "\xef\x97\x90" // U+f5d0 +#define ICON_FA_SQUARE "\xef\x83\x88" // U+f0c8 +#define ICON_FA_SQUARE_ARROW_UP_RIGHT "\xef\x85\x8c" // U+f14c +#define ICON_FA_SQUARE_CARET_DOWN "\xef\x85\x90" // U+f150 +#define ICON_FA_SQUARE_CARET_LEFT "\xef\x86\x91" // U+f191 +#define ICON_FA_SQUARE_CARET_RIGHT "\xef\x85\x92" // U+f152 +#define ICON_FA_SQUARE_CARET_UP "\xef\x85\x91" // U+f151 +#define ICON_FA_SQUARE_CHECK "\xef\x85\x8a" // U+f14a +#define ICON_FA_SQUARE_ENVELOPE "\xef\x86\x99" // U+f199 +#define ICON_FA_SQUARE_FULL "\xef\x91\x9c" // U+f45c +#define ICON_FA_SQUARE_H "\xef\x83\xbd" // U+f0fd +#define ICON_FA_SQUARE_MINUS "\xef\x85\x86" // U+f146 +#define ICON_FA_SQUARE_NFI "\xee\x95\xb6" // U+e576 +#define ICON_FA_SQUARE_PARKING "\xef\x95\x80" // U+f540 +#define ICON_FA_SQUARE_PEN "\xef\x85\x8b" // U+f14b +#define ICON_FA_SQUARE_PERSON_CONFINED "\xee\x95\xb7" // U+e577 +#define ICON_FA_SQUARE_PHONE "\xef\x82\x98" // U+f098 +#define ICON_FA_SQUARE_PHONE_FLIP "\xef\xa1\xbb" // U+f87b +#define ICON_FA_SQUARE_PLUS "\xef\x83\xbe" // U+f0fe +#define ICON_FA_SQUARE_POLL_HORIZONTAL "\xef\x9a\x82" // U+f682 +#define ICON_FA_SQUARE_POLL_VERTICAL "\xef\x9a\x81" // U+f681 +#define ICON_FA_SQUARE_ROOT_VARIABLE "\xef\x9a\x98" // U+f698 +#define ICON_FA_SQUARE_RSS "\xef\x85\x83" // U+f143 +#define ICON_FA_SQUARE_SHARE_NODES "\xef\x87\xa1" // U+f1e1 +#define ICON_FA_SQUARE_UP_RIGHT "\xef\x8d\xa0" // U+f360 +#define ICON_FA_SQUARE_VIRUS "\xee\x95\xb8" // U+e578 +#define ICON_FA_SQUARE_XMARK "\xef\x8b\x93" // U+f2d3 +#define ICON_FA_STAFF_SNAKE "\xee\x95\xb9" // U+e579 +#define ICON_FA_STAIRS "\xee\x8a\x89" // U+e289 +#define ICON_FA_STAMP "\xef\x96\xbf" // U+f5bf +#define ICON_FA_STAPLER "\xee\x96\xaf" // U+e5af +#define ICON_FA_STAR "\xef\x80\x85" // U+f005 +#define ICON_FA_STAR_AND_CRESCENT "\xef\x9a\x99" // U+f699 +#define ICON_FA_STAR_HALF "\xef\x82\x89" // U+f089 +#define ICON_FA_STAR_HALF_STROKE "\xef\x97\x80" // U+f5c0 +#define ICON_FA_STAR_OF_DAVID "\xef\x9a\x9a" // U+f69a +#define ICON_FA_STAR_OF_LIFE "\xef\x98\xa1" // U+f621 +#define ICON_FA_STERLING_SIGN "\xef\x85\x94" // U+f154 +#define ICON_FA_STETHOSCOPE "\xef\x83\xb1" // U+f0f1 +#define ICON_FA_STOP "\xef\x81\x8d" // U+f04d +#define ICON_FA_STOPWATCH "\xef\x8b\xb2" // U+f2f2 +#define ICON_FA_STOPWATCH_20 "\xee\x81\xaf" // U+e06f +#define ICON_FA_STORE "\xef\x95\x8e" // U+f54e +#define ICON_FA_STORE_SLASH "\xee\x81\xb1" // U+e071 +#define ICON_FA_STREET_VIEW "\xef\x88\x9d" // U+f21d +#define ICON_FA_STRIKETHROUGH "\xef\x83\x8c" // U+f0cc +#define ICON_FA_STROOPWAFEL "\xef\x95\x91" // U+f551 +#define ICON_FA_SUBSCRIPT "\xef\x84\xac" // U+f12c +#define ICON_FA_SUITCASE "\xef\x83\xb2" // U+f0f2 +#define ICON_FA_SUITCASE_MEDICAL "\xef\x83\xba" // U+f0fa +#define ICON_FA_SUITCASE_ROLLING "\xef\x97\x81" // U+f5c1 +#define ICON_FA_SUN "\xef\x86\x85" // U+f185 +#define ICON_FA_SUN_PLANT_WILT "\xee\x95\xba" // U+e57a +#define ICON_FA_SUPERSCRIPT "\xef\x84\xab" // U+f12b +#define ICON_FA_SWATCHBOOK "\xef\x97\x83" // U+f5c3 +#define ICON_FA_SYNAGOGUE "\xef\x9a\x9b" // U+f69b +#define ICON_FA_SYRINGE "\xef\x92\x8e" // U+f48e +#define ICON_FA_T "T" // U+0054 +#define ICON_FA_TABLE "\xef\x83\x8e" // U+f0ce +#define ICON_FA_TABLE_CELLS "\xef\x80\x8a" // U+f00a +#define ICON_FA_TABLE_CELLS_LARGE "\xef\x80\x89" // U+f009 +#define ICON_FA_TABLE_COLUMNS "\xef\x83\x9b" // U+f0db +#define ICON_FA_TABLE_LIST "\xef\x80\x8b" // U+f00b +#define ICON_FA_TABLE_TENNIS_PADDLE_BALL "\xef\x91\x9d" // U+f45d +#define ICON_FA_TABLET "\xef\x8f\xbb" // U+f3fb +#define ICON_FA_TABLET_BUTTON "\xef\x84\x8a" // U+f10a +#define ICON_FA_TABLET_SCREEN_BUTTON "\xef\x8f\xba" // U+f3fa +#define ICON_FA_TABLETS "\xef\x92\x90" // U+f490 +#define ICON_FA_TACHOGRAPH_DIGITAL "\xef\x95\xa6" // U+f566 +#define ICON_FA_TAG "\xef\x80\xab" // U+f02b +#define ICON_FA_TAGS "\xef\x80\xac" // U+f02c +#define ICON_FA_TAPE "\xef\x93\x9b" // U+f4db +#define ICON_FA_TARP "\xee\x95\xbb" // U+e57b +#define ICON_FA_TARP_DROPLET "\xee\x95\xbc" // U+e57c +#define ICON_FA_TAXI "\xef\x86\xba" // U+f1ba +#define ICON_FA_TEETH "\xef\x98\xae" // U+f62e +#define ICON_FA_TEETH_OPEN "\xef\x98\xaf" // U+f62f +#define ICON_FA_TEMPERATURE_ARROW_DOWN "\xee\x80\xbf" // U+e03f +#define ICON_FA_TEMPERATURE_ARROW_UP "\xee\x81\x80" // U+e040 +#define ICON_FA_TEMPERATURE_EMPTY "\xef\x8b\x8b" // U+f2cb +#define ICON_FA_TEMPERATURE_FULL "\xef\x8b\x87" // U+f2c7 +#define ICON_FA_TEMPERATURE_HALF "\xef\x8b\x89" // U+f2c9 +#define ICON_FA_TEMPERATURE_HIGH "\xef\x9d\xa9" // U+f769 +#define ICON_FA_TEMPERATURE_LOW "\xef\x9d\xab" // U+f76b +#define ICON_FA_TEMPERATURE_QUARTER "\xef\x8b\x8a" // U+f2ca +#define ICON_FA_TEMPERATURE_THREE_QUARTERS "\xef\x8b\x88" // U+f2c8 +#define ICON_FA_TENGE_SIGN "\xef\x9f\x97" // U+f7d7 +#define ICON_FA_TENT "\xee\x95\xbd" // U+e57d +#define ICON_FA_TENT_ARROW_DOWN_TO_LINE "\xee\x95\xbe" // U+e57e +#define ICON_FA_TENT_ARROW_LEFT_RIGHT "\xee\x95\xbf" // U+e57f +#define ICON_FA_TENT_ARROW_TURN_LEFT "\xee\x96\x80" // U+e580 +#define ICON_FA_TENT_ARROWS_DOWN "\xee\x96\x81" // U+e581 +#define ICON_FA_TENTS "\xee\x96\x82" // U+e582 +#define ICON_FA_TERMINAL "\xef\x84\xa0" // U+f120 +#define ICON_FA_TEXT_HEIGHT "\xef\x80\xb4" // U+f034 +#define ICON_FA_TEXT_SLASH "\xef\xa1\xbd" // U+f87d +#define ICON_FA_TEXT_WIDTH "\xef\x80\xb5" // U+f035 +#define ICON_FA_THERMOMETER "\xef\x92\x91" // U+f491 +#define ICON_FA_THUMBS_DOWN "\xef\x85\xa5" // U+f165 +#define ICON_FA_THUMBS_UP "\xef\x85\xa4" // U+f164 +#define ICON_FA_THUMBTACK "\xef\x82\x8d" // U+f08d +#define ICON_FA_TICKET "\xef\x85\x85" // U+f145 +#define ICON_FA_TICKET_SIMPLE "\xef\x8f\xbf" // U+f3ff +#define ICON_FA_TIMELINE "\xee\x8a\x9c" // U+e29c +#define ICON_FA_TOGGLE_OFF "\xef\x88\x84" // U+f204 +#define ICON_FA_TOGGLE_ON "\xef\x88\x85" // U+f205 +#define ICON_FA_TOILET "\xef\x9f\x98" // U+f7d8 +#define ICON_FA_TOILET_PAPER "\xef\x9c\x9e" // U+f71e +#define ICON_FA_TOILET_PAPER_SLASH "\xee\x81\xb2" // U+e072 +#define ICON_FA_TOILET_PORTABLE "\xee\x96\x83" // U+e583 +#define ICON_FA_TOILETS_PORTABLE "\xee\x96\x84" // U+e584 +#define ICON_FA_TOOLBOX "\xef\x95\x92" // U+f552 +#define ICON_FA_TOOTH "\xef\x97\x89" // U+f5c9 +#define ICON_FA_TORII_GATE "\xef\x9a\xa1" // U+f6a1 +#define ICON_FA_TORNADO "\xef\x9d\xaf" // U+f76f +#define ICON_FA_TOWER_BROADCAST "\xef\x94\x99" // U+f519 +#define ICON_FA_TOWER_CELL "\xee\x96\x85" // U+e585 +#define ICON_FA_TOWER_OBSERVATION "\xee\x96\x86" // U+e586 +#define ICON_FA_TRACTOR "\xef\x9c\xa2" // U+f722 +#define ICON_FA_TRADEMARK "\xef\x89\x9c" // U+f25c +#define ICON_FA_TRAFFIC_LIGHT "\xef\x98\xb7" // U+f637 +#define ICON_FA_TRAILER "\xee\x81\x81" // U+e041 +#define ICON_FA_TRAIN "\xef\x88\xb8" // U+f238 +#define ICON_FA_TRAIN_SUBWAY "\xef\x88\xb9" // U+f239 +#define ICON_FA_TRAIN_TRAM "\xee\x96\xb4" // U+e5b4 +#define ICON_FA_TRANSGENDER "\xef\x88\xa5" // U+f225 +#define ICON_FA_TRASH "\xef\x87\xb8" // U+f1f8 +#define ICON_FA_TRASH_ARROW_UP "\xef\xa0\xa9" // U+f829 +#define ICON_FA_TRASH_CAN "\xef\x8b\xad" // U+f2ed +#define ICON_FA_TRASH_CAN_ARROW_UP "\xef\xa0\xaa" // U+f82a +#define ICON_FA_TREE "\xef\x86\xbb" // U+f1bb +#define ICON_FA_TREE_CITY "\xee\x96\x87" // U+e587 +#define ICON_FA_TRIANGLE_EXCLAMATION "\xef\x81\xb1" // U+f071 +#define ICON_FA_TROPHY "\xef\x82\x91" // U+f091 +#define ICON_FA_TROWEL "\xee\x96\x89" // U+e589 +#define ICON_FA_TROWEL_BRICKS "\xee\x96\x8a" // U+e58a +#define ICON_FA_TRUCK "\xef\x83\x91" // U+f0d1 +#define ICON_FA_TRUCK_ARROW_RIGHT "\xee\x96\x8b" // U+e58b +#define ICON_FA_TRUCK_DROPLET "\xee\x96\x8c" // U+e58c +#define ICON_FA_TRUCK_FAST "\xef\x92\x8b" // U+f48b +#define ICON_FA_TRUCK_FIELD "\xee\x96\x8d" // U+e58d +#define ICON_FA_TRUCK_FIELD_UN "\xee\x96\x8e" // U+e58e +#define ICON_FA_TRUCK_FRONT "\xee\x8a\xb7" // U+e2b7 +#define ICON_FA_TRUCK_MEDICAL "\xef\x83\xb9" // U+f0f9 +#define ICON_FA_TRUCK_MONSTER "\xef\x98\xbb" // U+f63b +#define ICON_FA_TRUCK_MOVING "\xef\x93\x9f" // U+f4df +#define ICON_FA_TRUCK_PICKUP "\xef\x98\xbc" // U+f63c +#define ICON_FA_TRUCK_PLANE "\xee\x96\x8f" // U+e58f +#define ICON_FA_TRUCK_RAMP_BOX "\xef\x93\x9e" // U+f4de +#define ICON_FA_TTY "\xef\x87\xa4" // U+f1e4 +#define ICON_FA_TURKISH_LIRA_SIGN "\xee\x8a\xbb" // U+e2bb +#define ICON_FA_TURN_DOWN "\xef\x8e\xbe" // U+f3be +#define ICON_FA_TURN_UP "\xef\x8e\xbf" // U+f3bf +#define ICON_FA_TV "\xef\x89\xac" // U+f26c +#define ICON_FA_U "U" // U+0055 +#define ICON_FA_UMBRELLA "\xef\x83\xa9" // U+f0e9 +#define ICON_FA_UMBRELLA_BEACH "\xef\x97\x8a" // U+f5ca +#define ICON_FA_UNDERLINE "\xef\x83\x8d" // U+f0cd +#define ICON_FA_UNIVERSAL_ACCESS "\xef\x8a\x9a" // U+f29a +#define ICON_FA_UNLOCK "\xef\x82\x9c" // U+f09c +#define ICON_FA_UNLOCK_KEYHOLE "\xef\x84\xbe" // U+f13e +#define ICON_FA_UP_DOWN "\xef\x8c\xb8" // U+f338 +#define ICON_FA_UP_DOWN_LEFT_RIGHT "\xef\x82\xb2" // U+f0b2 +#define ICON_FA_UP_LONG "\xef\x8c\x8c" // U+f30c +#define ICON_FA_UP_RIGHT_AND_DOWN_LEFT_FROM_CENTER "\xef\x90\xa4" // U+f424 +#define ICON_FA_UP_RIGHT_FROM_SQUARE "\xef\x8d\x9d" // U+f35d +#define ICON_FA_UPLOAD "\xef\x82\x93" // U+f093 +#define ICON_FA_USER "\xef\x80\x87" // U+f007 +#define ICON_FA_USER_ASTRONAUT "\xef\x93\xbb" // U+f4fb +#define ICON_FA_USER_CHECK "\xef\x93\xbc" // U+f4fc +#define ICON_FA_USER_CLOCK "\xef\x93\xbd" // U+f4fd +#define ICON_FA_USER_DOCTOR "\xef\x83\xb0" // U+f0f0 +#define ICON_FA_USER_GEAR "\xef\x93\xbe" // U+f4fe +#define ICON_FA_USER_GRADUATE "\xef\x94\x81" // U+f501 +#define ICON_FA_USER_GROUP "\xef\x94\x80" // U+f500 +#define ICON_FA_USER_INJURED "\xef\x9c\xa8" // U+f728 +#define ICON_FA_USER_LARGE "\xef\x90\x86" // U+f406 +#define ICON_FA_USER_LARGE_SLASH "\xef\x93\xba" // U+f4fa +#define ICON_FA_USER_LOCK "\xef\x94\x82" // U+f502 +#define ICON_FA_USER_MINUS "\xef\x94\x83" // U+f503 +#define ICON_FA_USER_NINJA "\xef\x94\x84" // U+f504 +#define ICON_FA_USER_NURSE "\xef\xa0\xaf" // U+f82f +#define ICON_FA_USER_PEN "\xef\x93\xbf" // U+f4ff +#define ICON_FA_USER_PLUS "\xef\x88\xb4" // U+f234 +#define ICON_FA_USER_SECRET "\xef\x88\x9b" // U+f21b +#define ICON_FA_USER_SHIELD "\xef\x94\x85" // U+f505 +#define ICON_FA_USER_SLASH "\xef\x94\x86" // U+f506 +#define ICON_FA_USER_TAG "\xef\x94\x87" // U+f507 +#define ICON_FA_USER_TIE "\xef\x94\x88" // U+f508 +#define ICON_FA_USER_XMARK "\xef\x88\xb5" // U+f235 +#define ICON_FA_USERS "\xef\x83\x80" // U+f0c0 +#define ICON_FA_USERS_BETWEEN_LINES "\xee\x96\x91" // U+e591 +#define ICON_FA_USERS_GEAR "\xef\x94\x89" // U+f509 +#define ICON_FA_USERS_LINE "\xee\x96\x92" // U+e592 +#define ICON_FA_USERS_RAYS "\xee\x96\x93" // U+e593 +#define ICON_FA_USERS_RECTANGLE "\xee\x96\x94" // U+e594 +#define ICON_FA_USERS_SLASH "\xee\x81\xb3" // U+e073 +#define ICON_FA_USERS_VIEWFINDER "\xee\x96\x95" // U+e595 +#define ICON_FA_UTENSILS "\xef\x8b\xa7" // U+f2e7 +#define ICON_FA_V "V" // U+0056 +#define ICON_FA_VAN_SHUTTLE "\xef\x96\xb6" // U+f5b6 +#define ICON_FA_VAULT "\xee\x8b\x85" // U+e2c5 +#define ICON_FA_VECTOR_SQUARE "\xef\x97\x8b" // U+f5cb +#define ICON_FA_VENUS "\xef\x88\xa1" // U+f221 +#define ICON_FA_VENUS_DOUBLE "\xef\x88\xa6" // U+f226 +#define ICON_FA_VENUS_MARS "\xef\x88\xa8" // U+f228 +#define ICON_FA_VEST "\xee\x82\x85" // U+e085 +#define ICON_FA_VEST_PATCHES "\xee\x82\x86" // U+e086 +#define ICON_FA_VIAL "\xef\x92\x92" // U+f492 +#define ICON_FA_VIAL_CIRCLE_CHECK "\xee\x96\x96" // U+e596 +#define ICON_FA_VIAL_VIRUS "\xee\x96\x97" // U+e597 +#define ICON_FA_VIALS "\xef\x92\x93" // U+f493 +#define ICON_FA_VIDEO "\xef\x80\xbd" // U+f03d +#define ICON_FA_VIDEO_SLASH "\xef\x93\xa2" // U+f4e2 +#define ICON_FA_VIHARA "\xef\x9a\xa7" // U+f6a7 +#define ICON_FA_VIRUS "\xee\x81\xb4" // U+e074 +#define ICON_FA_VIRUS_COVID "\xee\x92\xa8" // U+e4a8 +#define ICON_FA_VIRUS_COVID_SLASH "\xee\x92\xa9" // U+e4a9 +#define ICON_FA_VIRUS_SLASH "\xee\x81\xb5" // U+e075 +#define ICON_FA_VIRUSES "\xee\x81\xb6" // U+e076 +#define ICON_FA_VOICEMAIL "\xef\xa2\x97" // U+f897 +#define ICON_FA_VOLCANO "\xef\x9d\xb0" // U+f770 +#define ICON_FA_VOLLEYBALL "\xef\x91\x9f" // U+f45f +#define ICON_FA_VOLUME_HIGH "\xef\x80\xa8" // U+f028 +#define ICON_FA_VOLUME_LOW "\xef\x80\xa7" // U+f027 +#define ICON_FA_VOLUME_OFF "\xef\x80\xa6" // U+f026 +#define ICON_FA_VOLUME_XMARK "\xef\x9a\xa9" // U+f6a9 +#define ICON_FA_VR_CARDBOARD "\xef\x9c\xa9" // U+f729 +#define ICON_FA_W "W" // U+0057 +#define ICON_FA_WALKIE_TALKIE "\xef\xa3\xaf" // U+f8ef +#define ICON_FA_WALLET "\xef\x95\x95" // U+f555 +#define ICON_FA_WAND_MAGIC "\xef\x83\x90" // U+f0d0 +#define ICON_FA_WAND_MAGIC_SPARKLES "\xee\x8b\x8a" // U+e2ca +#define ICON_FA_WAND_SPARKLES "\xef\x9c\xab" // U+f72b +#define ICON_FA_WAREHOUSE "\xef\x92\x94" // U+f494 +#define ICON_FA_WATER "\xef\x9d\xb3" // U+f773 +#define ICON_FA_WATER_LADDER "\xef\x97\x85" // U+f5c5 +#define ICON_FA_WAVE_SQUARE "\xef\xa0\xbe" // U+f83e +#define ICON_FA_WEIGHT_HANGING "\xef\x97\x8d" // U+f5cd +#define ICON_FA_WEIGHT_SCALE "\xef\x92\x96" // U+f496 +#define ICON_FA_WHEAT_AWN "\xee\x8b\x8d" // U+e2cd +#define ICON_FA_WHEAT_AWN_CIRCLE_EXCLAMATION "\xee\x96\x98" // U+e598 +#define ICON_FA_WHEELCHAIR "\xef\x86\x93" // U+f193 +#define ICON_FA_WHEELCHAIR_MOVE "\xee\x8b\x8e" // U+e2ce +#define ICON_FA_WHISKEY_GLASS "\xef\x9e\xa0" // U+f7a0 +#define ICON_FA_WIFI "\xef\x87\xab" // U+f1eb +#define ICON_FA_WIND "\xef\x9c\xae" // U+f72e +#define ICON_FA_WINDOW_MAXIMIZE "\xef\x8b\x90" // U+f2d0 +#define ICON_FA_WINDOW_MINIMIZE "\xef\x8b\x91" // U+f2d1 +#define ICON_FA_WINDOW_RESTORE "\xef\x8b\x92" // U+f2d2 +#define ICON_FA_WINE_BOTTLE "\xef\x9c\xaf" // U+f72f +#define ICON_FA_WINE_GLASS "\xef\x93\xa3" // U+f4e3 +#define ICON_FA_WINE_GLASS_EMPTY "\xef\x97\x8e" // U+f5ce +#define ICON_FA_WON_SIGN "\xef\x85\x99" // U+f159 +#define ICON_FA_WORM "\xee\x96\x99" // U+e599 +#define ICON_FA_WRENCH "\xef\x82\xad" // U+f0ad +#define ICON_FA_X "X" // U+0058 +#define ICON_FA_X_RAY "\xef\x92\x97" // U+f497 +#define ICON_FA_XMARK "\xef\x80\x8d" // U+f00d +#define ICON_FA_XMARKS_LINES "\xee\x96\x9a" // U+e59a +#define ICON_FA_Y "Y" // U+0059 +#define ICON_FA_YEN_SIGN "\xef\x85\x97" // U+f157 +#define ICON_FA_YIN_YANG "\xef\x9a\xad" // U+f6ad +#define ICON_FA_Z "Z" // U+005a \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index e938ae22..aff1eaad 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -4,6 +4,7 @@ #include "SHpch.h" #include "IconsMaterialDesign.h" +#include "IconsFontAwesome6.h" #include "DragDrop/SHDragDrop.hpp" //#==============================================================# @@ -99,7 +100,7 @@ namespace SHADE io->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //Enable for Multi-Viewports io->ConfigFlags |= ImGuiConfigFlags_DockingEnable; //Enable docking io->IniFilename = "../../Assets/Editor/Layouts/UserLayout.ini"; - + io->ConfigWindowsMoveFromTitleBarOnly = true; InitLayout(); InitFonts(); @@ -170,10 +171,11 @@ namespace SHADE { ImFont* mainFont = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path - constexpr ImWchar icon_ranges[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 }; ImFontConfig icons_config{}; icons_config.MergeMode = true; icons_config.GlyphOffset.y = 5.f; - ImFont* UIFont = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges); //TODO: Change to config based assets path - + constexpr ImWchar icon_ranges_fa[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; + ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/fa-solid-900.ttf", 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path + constexpr ImWchar icon_ranges_md[] = { ICON_MIN_MD, ICON_MAX_MD, 0 }; + ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path io->Fonts->Build(); } From 77aa5b8c7d6146e242e789118ee90da8fcb57863 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Sat, 22 Oct 2022 23:27:43 +0800 Subject: [PATCH 27/32] Bindings and Controllers --- SHADE_Engine/src/Input/SHInputManager.cpp | 657 ++++++++++++++++++++++ SHADE_Engine/src/Input/SHInputManager.h | 409 +++++++++++++- 2 files changed, 1055 insertions(+), 11 deletions(-) diff --git a/SHADE_Engine/src/Input/SHInputManager.cpp b/SHADE_Engine/src/Input/SHInputManager.cpp index 18d9e3e2..c665a9c9 100644 --- a/SHADE_Engine/src/Input/SHInputManager.cpp +++ b/SHADE_Engine/src/Input/SHInputManager.cpp @@ -19,6 +19,10 @@ namespace SHADE /*------------------------------------------------------------------------*/ /* Static defines */ /*------------------------------------------------------------------------*/ + + bool SHInputManager::controllerInUse = false; + + std::map SHInputManager::bindings; unsigned SHInputManager::keyCount = 0; bool SHInputManager::keys[MAX_KEYS]; @@ -41,6 +45,60 @@ namespace SHADE int SHInputManager::mouseWheelVerticalDelta = 0; int SHInputManager::mouseWheelVerticalDeltaPoll = 0; + unsigned char SHInputManager::controllersConnectedCount = 0; + unsigned SHInputManager::controllersInputCount[XUSER_MAX_COUNT]; + unsigned SHInputManager::controllersButtonCount[XUSER_MAX_COUNT]; + short SHInputManager::controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + short SHInputManager::controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + double SHInputManager::controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + double SHInputManager::controllersReleasedTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //Internal helper function for splitting between inputs + bool SHInputManager::controllerConsideredHeld(size_t inputIdx, short value) noexcept + { + if (inputIdx >= MAX_CONTROLLER_INPUT) return false; //Bounds check + else + { + if (inputIdx < NUM_CONTROLLER_BUTTON) + { + return static_cast(value); + } + else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER) + { + return (value > XINPUT_GAMEPAD_TRIGGER_THRESHOLD); + } + else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER + NUM_CONTROLLER_TRIGGER) + { + return (std::abs(value) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + } + else + { + return (std::abs(value) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); + } + } + } + + //Internal helper function for getting normalised input value + double SHInputManager::controllerNormalisedValue(size_t inputIdx, short value) noexcept + { + if (inputIdx >= MAX_CONTROLLER_INPUT) return 0.0; //Bounds check + else + { + if (inputIdx < NUM_CONTROLLER_BUTTON) + { + return static_cast(value); + } + else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER) //8-bit triggers, 0 to 255 + { + return static_cast(value) / static_cast(UCHAR_MAX); + } + else //16-bit thumbsticks, -32768 to 32767 + { + return static_cast(value) / static_cast(SHRT_MAX); + } + } + } + void SHInputManager::UpdateInput(double dt) noexcept { //Keyboard and Mouse Buttons//////////////////////////////////////////////// @@ -120,6 +178,273 @@ namespace SHADE mouseWheelVerticalDelta = 0; mouseWheelVerticalDelta = mouseWheelVerticalDeltaPoll; mouseWheelVerticalDeltaPoll = 0; + + //Controllers////////////////////////////////////////////////////////////// + + controllersConnectedCount = 0; + + //Set last controller states + memcpy(controllersLast, controllers, sizeof(controllers)); + + //Reset controller states + SecureZeroMemory(&controllers, sizeof(controllers)); + + //https://learn.microsoft.com/en-us/windows/win32/xinput/getting-started-with-xinput#getting-controller-state + for (DWORD c = 0; c < XUSER_MAX_COUNT; ++c) + { + controllersInputCount[c] = 0; + controllersButtonCount[c] = 0; + + XINPUT_STATE state; + SecureZeroMemory(&state, sizeof(XINPUT_STATE)); + + //Get the state of controller from XInput + DWORD result = XInputGetState(c, &state); + + //Write gamepad data + if (result == ERROR_SUCCESS) + { + ++controllersConnectedCount; + + //DIGITAL BUTTONS/////////////////////////////////////// + + //DPAD UP + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_UP)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_UP)] = 0; + } + + //DPAD DOWN + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_DOWN)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_DOWN)] = 0; + } + + //DPAD LEFT + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_LEFT)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_LEFT)] = 0; + } + + //DPAD RIGHT + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_RIGHT)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_RIGHT)] = 0; + } + + //START + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) + { + controllers[c][static_cast(SH_CONTROLLERCODE::START)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::START)] = 0; + } + + //BACK + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) + { + controllers[c][static_cast(SH_CONTROLLERCODE::BACK)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::BACK)] = 0; + } + + //LEFT THUMBSTICK BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK)] = 0; + } + + //RIGHT THUMBSTICK BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK)] = 0; + } + + //LEFT SHOULDER BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_SHOULDER)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_SHOULDER)] = 0; + } + + //RIGHT SHOULDER BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_SHOULDER)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_SHOULDER)] = 0; + } + + //A + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) + { + controllers[c][static_cast(SH_CONTROLLERCODE::A)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::A)] = 0; + } + + //B + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) + { + controllers[c][static_cast(SH_CONTROLLERCODE::B)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::B)] = 0; + } + + //X + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) + { + controllers[c][static_cast(SH_CONTROLLERCODE::X)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::X)] = 0; + } + + //Y + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) + { + controllers[c][static_cast(SH_CONTROLLERCODE::Y)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::Y)] = 0; + } + + //8 BIT VALUES (0 - 255)/////////////////////////////////// + + //LEFT TRIGGER + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_TRIGGER)] = state.Gamepad.bLeftTrigger; + if (state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + { + ++controllersInputCount[c]; //Registered as held + } + + //RIGHT TRIGGER + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_TRIGGER)] = state.Gamepad.bRightTrigger; + if (state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + { + ++controllersInputCount[c]; //Registered as held + } + + //16 BIT VALUES (0 - 65535)//////////////////////////////// + + //LEFT THUMBSTICK X + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK_X)] = state.Gamepad.sThumbLX; + if (std::abs(state.Gamepad.sThumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + + //LEFT THUMBSTICK Y + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK_Y)] = state.Gamepad.sThumbLY; + if (std::abs(state.Gamepad.sThumbLY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + + //RIGHT THUMBSTICK X + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK_X)] = state.Gamepad.sThumbRX; + if (std::abs(state.Gamepad.sThumbRX) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + + //RIGHT THUMBSTICK Y + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK_Y)] = state.Gamepad.sThumbRY; + if (std::abs(state.Gamepad.sThumbRY) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + } + + //Timers and updating if controller is presently in use + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (controllerConsideredHeld(i, controllers[c][i])) //Considered on + { + controllerInUse = true; + if (!controllerConsideredHeld(i, controllersLast[c][i])) //Just on + { + controllersHeldTime[c][i] = 0.0; //Reset timer + } + controllersHeldTime[c][i] += dt; //Tick up + } + else //Considered off + { + if (controllerConsideredHeld(i, controllersLast[c][i])) //Just off + { + controllersReleasedTime[c][i] = 0.0; //Reset timer + } + controllersReleasedTime[c][i] += dt; //Tick up + } + } + } } bool SHInputManager::AnyKeyDown(SH_KEYCODE* firstDetected) noexcept @@ -161,4 +486,336 @@ namespace SHADE return false; } + //Any controller input being held + //For analog, this means going being deadzone values + bool SHInputManager::AnyControllerInput(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerInputDown(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i]) && !controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerInputUp(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (!controllerConsideredHeld(i, controllers[cNum][i]) && controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerButton(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerButtonDown(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i]) && !controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerButtonUp(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i) + { + if (!controllerConsideredHeld(i, controllers[cNum][i]) && controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + //Only get of largest magnitude + double SHInputManager::GetBindingAxis(std::string bindingName, size_t cNum) noexcept + { + //Over keycodes, prioritise positive + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKey(k)) return 1.0; + } + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKey(k)) return -1.0; + } + + double largestMagnitude = 0.0; + + //Over controllerCodes + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + double newValue = 0.0; + if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum)) + if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = newValue; + } + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + double newValue = 0.0; + if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum)) + if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = -newValue; + } + + return largestMagnitude; + } + + bool SHInputManager::GetBindingPositiveButton(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKey(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingNegativeButton(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKey(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingPositiveButtonDown(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyDown(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputDown(c, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingNegativeButtonDown(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyDown(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputDown(c, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingPositiveButtonUp(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyUp(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputUp(c, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingNegativeButtonUp(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyUp(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputUp(c, nullptr, cNum)) return true; + } + + return false; + } + + //Fetches longest hold time + double SHInputManager::GetBindingPositiveHeldTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double maxHeldTime = 0.0; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c); + } + + return maxHeldTime; + } + + double SHInputManager::GetBindingNegativeHeldTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double maxHeldTime = 0.0; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c); + } + + return maxHeldTime; + } + + //Fetches shortest release time + double SHInputManager::GetBindingPositiveReleasedTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double minReleaseTime = _HUGE_ENUF; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c); + } + + return minReleaseTime; + } + + double SHInputManager::GetBindingNegativeReleasedTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double minReleaseTime = _HUGE_ENUF; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c); + } + + return minReleaseTime; + } + + //Only for mouse movement + //Get largest delta + double SHInputManager::GetBindingMouseVelocity(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + //Mouse velocity + double velX = 0.0; + double velY = 0.0; + GetMouseVelocity(&velX, &velY); + + return bindings[bindingName].mouseXPositiveMultiplier * velX + bindings[bindingName].mouseYPositiveMultiplier * velY; + } + } //namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Input/SHInputManager.h b/SHADE_Engine/src/Input/SHInputManager.h index d3e31004..04e5871d 100644 --- a/SHADE_Engine/src/Input/SHInputManager.h +++ b/SHADE_Engine/src/Input/SHInputManager.h @@ -10,9 +10,12 @@ *********************************************************************/ #pragma once -//#include -//#include "../../SHADE_Managed/src/SHpch.h" +#include +#include +#include +#include "../../SHADE_Managed/src/SHpch.h" #include "SH_API.h" +#pragma comment(lib, "xinput.lib") namespace SHADE { @@ -268,6 +271,58 @@ namespace SHADE OEM_CLEAR }; + enum class SH_CONTROLLERCODE + { + //Digital + DPAD_UP, + DPAD_DOWN, + DPAD_LEFT, + DPAD_RIGHT, + START, + BACK, + LEFT_THUMBSTICK, + RIGHT_THUMBSTICK, + LEFT_SHOULDER, + RIGHT_SHOULDER, + A, + B, + X, + Y, + + //1 Byte Unsigned Analog + LEFT_TRIGGER, + RIGHT_TRIGGER, + + //2 Byte Signed Analog + LEFT_THUMBSTICK_X, + LEFT_THUMBSTICK_Y, + RIGHT_THUMBSTICK_X, + RIGHT_THUMBSTICK_Y + }; + + private: + /*------------------------------------------------------------------------*/ + /* Struct for logical bindings */ + /*------------------------------------------------------------------------*/ + struct SH_API SHLogicalBindingData + { + //Key codes mapped to positive + std::set positiveKeyCodes; + + //Key codes mapped to negative + std::set negativeKeyCodes; + + //Controller Codes mapped to positive + std::set positiveControllerCodes; + + //Controller Codes mapped to negative + std::set negativeControllerCodes; + + //Mouse movement mapped to axes? + double mouseXPositiveMultiplier; + double mouseYPositiveMultiplier; + }; + public: //Updates current state of the input, with dt to be fetched from FRC //TODO should dt be fixed or variable? @@ -392,7 +447,7 @@ namespace SHADE keysToggleLast[static_cast(key)]); } - //Mouse///////////// + //Mouse///////////////////////////////////////////////////// //Get the mouse location with respect to the screen static inline void GetMouseScreenPosition (int* x = nullptr, @@ -428,7 +483,95 @@ namespace SHADE return mouseWheelVerticalDelta; } - //GET INPUT TIMINGS/////////////////////////////////////////////////////////// + /*------------------------------------------------------------------------*/ + /* Input state accessors (KB & M) */ + /*------------------------------------------------------------------------*/ + + //How many controller inputs of any kind are being used now + static inline unsigned GetControllerInputCount(size_t cNum = 0) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0; + return controllersInputCount[cNum]; + } + + //How many controller buttons are being pressed now + //Subtract from getControllerInputCount() for analog triggers / thumbsticks + static inline unsigned GetControllerButtonCount(size_t cNum = 0) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0; + return controllersButtonCount[cNum]; + } + + //Any controller input being held + //For analog, this means going being deadzone values + //controllerNum should be between 0 and 3 + static bool AnyControllerInput(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any controller input activated in THIS FRAME ONLY + //For analog, this means going being deadzone values + //controllerNum should be between 0 and 3 + static bool AnyControllerInputDown(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any controller input deactivated in THIS FRAME ONLY + //For analog, this means going below deadzone values + //controllerNum should be between 0 and 3 + static bool AnyControllerInputUp(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any DIGITAL controller buttons being held + //controllerNum should be between 0 and 3 + static bool AnyControllerButton(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any DIGITAL controller button activated in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static bool AnyControllerButtonDown(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any DIGITAL controller button deactivated in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static bool AnyControllerButtonUp(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //If controller input is being held in the current frame + //normalisedValue is the value of the input between 0 and 1, relevant for trigger and thumbstick data + //controllerNum should be between 0 and 3 + static inline bool GetControllerInput(SH_CONTROLLERCODE input, + double* normalisedValue = nullptr, + double* heldTime = nullptr, + double* releasedTime = nullptr, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return false; + if (normalisedValue) *normalisedValue = controllerNormalisedValue(static_cast(input), controllers[controllerNum][static_cast(input)]); + if (heldTime) *heldTime = controllersHeldTime[controllerNum][static_cast(input)]; + if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast(input)]; + return controllerConsideredHeld(static_cast(input), controllers[controllerNum][static_cast(input)]); + } + + //If controller input was considered to be held down in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static inline bool GetControllerInputDown(SH_CONTROLLERCODE input, + double* releasedTime = nullptr, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return false; + if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast(input)]; + return (controllerConsideredHeld(static_cast(input), controllers[controllerNum][static_cast(input)]) && + !controllerConsideredHeld(static_cast(input), controllersLast[controllerNum][static_cast(input)])); + } + + //If controller input was considered to be released in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static inline bool GetControllerInputUp(SH_CONTROLLERCODE input, + double* releasedTime = nullptr, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return false; + if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast(input)]; + return (!controllerConsideredHeld(static_cast(input), controllers[controllerNum][static_cast(input)]) && + controllerConsideredHeld(static_cast(input), controllersLast[controllerNum][static_cast(input)])); + } + + /*------------------------------------------------------------------------*/ + /* Timing accessors */ + /*------------------------------------------------------------------------*/ //Keyboard///////////// @@ -456,6 +599,191 @@ namespace SHADE return keysToggleOffTime[static_cast(key)]; } + //Controller////////////////////// + + //How long has this controller input been considered to be held down for + static inline double GetControllerInputHeldTime(SH_CONTROLLERCODE code, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return 0.0; + return controllersHeldTime[controllerNum][static_cast(code)]; + } + + //How long has this controller input been considered to be released for + static inline double GetControllerInputReleasedTime(SH_CONTROLLERCODE code, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return 0.0; + return controllersReleasedTime[controllerNum][static_cast(code)]; + } + + /*------------------------------------------------------------------------*/ + /* Binding Functions */ + /*------------------------------------------------------------------------*/ + + //Add a new binding to the map + static inline void BindingsAdd(std::string newBindingName) noexcept + { + bindings.insert({ newBindingName, SHLogicalBindingData() }); + } + + //Remove a binding from the map + //Returns 1 if found and removed, 0 if not found + static inline size_t BindingsRemove(std::string targetBindingName) noexcept + { + return bindings.erase(targetBindingName); + } + + //Clears all bindings from the list + static inline void BindingsClear() noexcept + { + bindings.clear(); + } + + //Get the number of bindings present + static inline size_t BindingsCount() noexcept + { + return bindings.size(); + } + + //Check positive keycodes to binding + static inline std::set const& BindingsGetPositiveKeyCodes(std::string bindingName) noexcept + { + return bindings[bindingName].positiveKeyCodes; + } + + //Add positive SH_KEYCODE to binding + static inline void BindingsAddPositiveKeyCode(std::string targetBindingName, + SH_KEYCODE toAdd) noexcept + { + bindings[targetBindingName].positiveKeyCodes.insert(toAdd); + } + + //Remove positive SH_KEYCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemovePositiveKeyCode(std::string targetBindingName, + SH_KEYCODE toRemove) noexcept + { + return bindings[targetBindingName].positiveKeyCodes.erase(toRemove); + } + + //Check negative keycodes to binding + static inline std::set const& BindingsGetNegativeKeyCodes(std::string bindingName) noexcept + { + return bindings[bindingName].negativeKeyCodes; + } + + //Add negative SH_KEYCODE to binding + static inline void BindingsAddNegativeKeyCode(std::string targetBindingName, + SH_KEYCODE toAdd) noexcept + { + bindings[targetBindingName].negativeKeyCodes.insert(toAdd); + } + + //Remove negative SH_KEYCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemoveNegativeKeyCode(std::string targetBindingName, + SH_KEYCODE toRemove) noexcept + { + return bindings[targetBindingName].negativeKeyCodes.erase(toRemove); + } + + //Check positive controllercodes to binding + static inline std::set const& BindingsGetPositiveControllerCodes(std::string bindingName) noexcept + { + return bindings[bindingName].positiveControllerCodes; + } + + //Add positive SH_CONTROLLERCODE to binding + static inline void BindingsAddPositiveControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toAdd) noexcept + { + bindings[targetBindingName].positiveControllerCodes.insert(toAdd); + } + + //Remove positive SH_CONTROLLERCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemovePositiveControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toRemove) noexcept + { + return bindings[targetBindingName].positiveControllerCodes.erase(toRemove); + } + + //Check negative controllercodes to binding + static inline std::set const& BindingsGetNegativeControllerCodes(std::string bindingName) noexcept + { + return bindings[bindingName].negativeControllerCodes; + } + + //Add negative SH_CONTROLLERCODE to binding + static inline void BindingsAddNegativeControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toAdd) noexcept + { + bindings[targetBindingName].negativeControllerCodes.insert(toAdd); + } + + //Remove negative SH_CONTROLLERCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemoveNegativeControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toRemove) noexcept + { + return bindings[targetBindingName].negativeControllerCodes.erase(toRemove); + } + + //Mouse movement bindings + + static inline double const BindingsGetMouseXPositiveMultiplier(std::string bindingName) noexcept + { + return bindings[bindingName].mouseXPositiveMultiplier; + } + + static inline void BindingsSetMouseXPositiveMultiplier(std::string bindingName, double newValue) noexcept + { + bindings[bindingName].mouseXPositiveMultiplier = newValue; + } + + static inline double const BindingsGetMouseYPositiveMultiplier(std::string bindingName) noexcept + { + return bindings[bindingName].mouseXPositiveMultiplier; + } + + static inline void BindingsSetMouseYPositiveMultiplier(std::string bindingName, double newValue) noexcept + { + bindings[bindingName].mouseXPositiveMultiplier = newValue; + } + + //Get the axis value of binding, between -1 and 1 + static double GetBindingAxis(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Whether binding is being held or not + //Does not work for mouse movement + static bool GetBindingPositiveButton(std::string bindingName, size_t controllerNumber = 0) noexcept; + static bool GetBindingNegativeButton(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Whether binding is pressed down IN THIS FRAME ONLY + //Does not work for mouse movement + static bool GetBindingPositiveButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept; + static bool GetBindingNegativeButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Whether binding is released IN THIS FRAME ONLY + //Does not work for mouse movement + static bool GetBindingPositiveButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept; + static bool GetBindingNegativeButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Binding times + + //Does not work for mouse movement + static double GetBindingPositiveHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + static double GetBindingNegativeHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Does not work for mouse movement + static double GetBindingPositiveReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + static double GetBindingNegativeReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Binding mouse velocity + //Only for mouse movement + static double GetBindingMouseVelocity(std::string bindingName, size_t controllerNumber = 0) noexcept; + /*------------------------------------------------------------------------*/ /* Other Functions */ /*------------------------------------------------------------------------*/ @@ -482,10 +810,36 @@ namespace SHADE /*------------------------------------------------------------------------*/ static constexpr size_t MAX_KEYS = UCHAR_MAX + 1; + //How many recognised controller inputs there are + //To see the list, see the enum class in this file + static constexpr size_t MAX_CONTROLLER_INPUT = 20; + + //On/off button count + static constexpr size_t NUM_CONTROLLER_BUTTON = 14; + + //8-bit trigger values + static constexpr size_t NUM_CONTROLLER_TRIGGER = 2; + + //16-bit thumbstick values + static constexpr size_t NUM_CONTROLLER_THUMBSTICK = 4; + /*------------------------------------------------------------------------*/ /* Data Members */ /*------------------------------------------------------------------------*/ + //If the last input is from controller(s) or KB/M + //True if from controller(s), False if from KB/M + //Useful for switching control hints between controllers and KB/M + static bool controllerInUse; + + //BINDINGS////////////////////////////////////////////////////////////////// + + //Key is for binding names, they must be unique + //Multiple physical inputs per virtual/logical input are to be added to + //sets inside the logicalBinding values + //TODO make this an array of 4 / 5 users for multiplayer support + static std::map bindings; + //KEYBOARD AND MOUSE BUTTONS//////////////////////////////////////////////// //How many keys are presently being pressed @@ -558,15 +912,48 @@ namespace SHADE //CONTROLLER VARIABLES////////////////////////////////////////////////////// - //OTHER VARIABLES/////////////////////////////////////////////////////////// + //Count how many controllers are presently connected + //Useful for if the game is to decide to take in controller or KB/M input + //Between 0 and 4 (inclusive) + static unsigned char controllersConnectedCount; - //Axis bindings - //X - - //Y + //How many inputs (of any kind) on the controller are being used now + //Includes analog triggers and thumbsticks + static unsigned controllersInputCount[XUSER_MAX_COUNT]; - //Other mappings + //How many DIGITAL buttons on the controller are being pressed now + static unsigned controllersButtonCount[XUSER_MAX_COUNT]; - //Buffer + //Current state of controllers + //First index is for controller number + //Second index is for input type + static short controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //State of controllers in the last frame + //First index is for controller number + //Second index is for input type + static short controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //Held and released times + + //Controller held durations + //Stops ticking up when released + //Will be reset when held again + static double controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //Key released durations + //Stops ticking up when held + //Will be reset when off again + static double controllersReleasedTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + /*------------------------------------------------------------------------*/ + /* Internal Helper Functions */ + /*------------------------------------------------------------------------*/ + + //Internal helper function for checking if input is considered held or not + static bool controllerConsideredHeld(size_t inputIdx, short value) noexcept; + + //Internal helper function for getting normalised controller value + static double controllerNormalisedValue(size_t inputIdx, short value) noexcept; }; } \ No newline at end of file From 6039ec534dc12548d8aa608b5078ad8741998707 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Sun, 23 Oct 2022 12:32:08 +0800 Subject: [PATCH 28/32] Added clamping of camera, and copying of position and rotation from transform --- SHADE_Engine/src/Camera/SHCameraComponent.cpp | 49 +++++++++++++++++++ SHADE_Engine/src/Camera/SHCameraSystem.cpp | 29 ++++++++++- SHADE_Engine/src/Camera/SHCameraSystem.h | 2 + 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.cpp b/SHADE_Engine/src/Camera/SHCameraComponent.cpp index 755311a0..5d49c887 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.cpp +++ b/SHADE_Engine/src/Camera/SHCameraComponent.cpp @@ -3,6 +3,7 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "SHCameraSystem.h" #include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/Transform/SHTransformComponent.h" namespace SHADE { @@ -23,33 +24,69 @@ namespace SHADE void SHCameraComponent::SetYaw(float yaw) noexcept { this->yaw = yaw; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + transform->SetWorldRotation(SHVec3{rotation.x,yaw, rotation.z}); + } dirtyView = true; } void SHCameraComponent::SetPitch(float pitch) noexcept { this->pitch = pitch; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + transform->SetWorldRotation(SHVec3{ pitch,rotation.y, rotation.z }); + } dirtyView = true; } void SHCameraComponent::SetRoll(float roll) noexcept { this->roll = roll; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + transform->SetWorldRotation(SHVec3{ rotation.x,rotation.y, roll}); + } dirtyView = true; } void SHCameraComponent::SetPositionX(float x) noexcept { position.x = x; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ x,position.y, position.z}); + } dirtyView = true; } void SHCameraComponent::SetPositionY(float y) noexcept { position.y = y; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ position.x,y, position.z }); + } dirtyView = true; } void SHCameraComponent::SetPositionZ(float z) noexcept { position.z = z; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ position.x,position.y, z }); + } dirtyView = true; } void SHCameraComponent::SetPosition(float x,float y, float z) noexcept @@ -57,11 +94,23 @@ namespace SHADE position.x = x; position.y = y; position.z = z; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ x,y, z }); + } dirtyView = true; } void SHCameraComponent::SetPosition(SHVec3& pos) noexcept { this->position = pos; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(pos); + } dirtyView = true; } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 04c017c0..576ceb6b 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -4,7 +4,7 @@ #include "Input/SHInputManager.h" #include "Math/Vector/SHVec2.h" #include "ECS_Base/Managers/SHComponentManager.h" - +#include "Math/Transform/SHTransformComponent.h" namespace SHADE @@ -84,10 +84,25 @@ namespace SHADE void SHCameraSystem::UpdateCameraComponent(SHCameraComponent& camera) noexcept { + if (SHComponentManager::HasComponent(camera.GetEID()) == true) + { + auto transform = SHComponentManager::GetComponent(camera.GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + camera.pitch = rotation.x; + camera.yaw = rotation.y; + camera.roll = rotation.z; + camera.position = transform->GetWorldPosition(); + } + + if (camera.dirtyView) { SHVec3 view, right, UP; + + + ClampCameraRotation(camera); + GetCameraAxis(camera, view, right, UP); camera.viewMatrix = SHMatrix::Identity; @@ -205,5 +220,17 @@ namespace SHADE return CreateDirector(); } } + void SHCameraSystem::ClampCameraRotation(SHCameraComponent& camera) noexcept + { + if (camera.pitch > 85) + camera.SetPitch(85); + if (camera.pitch < -85) + camera.SetPitch(-85); + if (camera.roll > 85) + camera.SetRoll(85); + if (camera.roll < -85) + camera.SetRoll(-85); + + } } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index 0d89b842..26d15a5d 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -49,6 +49,8 @@ namespace SHADE void GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& up) const noexcept; DirectorHandle CreateDirector() noexcept; DirectorHandle GetDirector(size_t index) noexcept; + void ClampCameraRotation(SHCameraComponent& camera) noexcept; + protected: void UpdateCameraComponent(SHCameraComponent& camera) noexcept; From c15994532bbf03d6b1c8bd3f25bc9161a8023240 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Sun, 23 Oct 2022 12:42:25 +0800 Subject: [PATCH 29/32] Added a function for editor camera update --- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 53 ++++++++++++++++++++++ SHADE_Engine/src/Camera/SHCameraSystem.h | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 576ceb6b..0f2003f1 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -9,7 +9,57 @@ namespace SHADE { + void SHCameraSystem::UpdateEditorCamera(double dt) noexcept + { + + auto& camera = editorCamera; + SHVec3 view, right, UP; + GetCameraAxis(camera, view, right, UP); + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) + { + camera.position -= right * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::D)) + { + camera.position += right * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::W)) + { + camera.position += view * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::S)) + { + camera.position -= view * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::Q)) + { + camera.position += UP * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::E)) + { + camera.position -= UP * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::RMB)) + { + double mouseX, mouseY; + SHInputManager::GetMouseVelocity(&mouseX, &mouseY); + + //std::cout << camera.yaw << std::endl; + + camera.pitch -= mouseY * dt * camera.turnSpeed.x; + camera.yaw -= mouseX * dt * camera.turnSpeed.y; + camera.dirtyView = true; + } + + UpdateCameraComponent(editorCamera); + } void SHCameraSystem::EditorCameraUpdate::Execute(double dt) noexcept { SHCameraSystem* system = static_cast(GetSystem()); @@ -222,6 +272,9 @@ namespace SHADE } void SHCameraSystem::ClampCameraRotation(SHCameraComponent& camera) noexcept { + + + if (camera.pitch > 85) camera.SetPitch(85); if (camera.pitch < -85) diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index 26d15a5d..dacda574 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -50,7 +50,7 @@ namespace SHADE DirectorHandle CreateDirector() noexcept; DirectorHandle GetDirector(size_t index) noexcept; void ClampCameraRotation(SHCameraComponent& camera) noexcept; - + void UpdateEditorCamera(double dt) noexcept; protected: void UpdateCameraComponent(SHCameraComponent& camera) noexcept; From 96a7896da92b7f3deabda7adedde73a7a0550560 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Sun, 23 Oct 2022 12:50:56 +0800 Subject: [PATCH 30/32] changed includes to SHHandle and SHResourceLibrary --- SHADE_Engine/src/Camera/SHCameraDirector.h | 2 +- SHADE_Engine/src/Camera/SHCameraSystem.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.h b/SHADE_Engine/src/Camera/SHCameraDirector.h index b1311147..5d09788b 100644 --- a/SHADE_Engine/src/Camera/SHCameraDirector.h +++ b/SHADE_Engine/src/Camera/SHCameraDirector.h @@ -3,7 +3,7 @@ #include "SH_API.h" #include "ECS_Base/Entity/SHEntity.h" #include "Math/SHMatrix.h" -#include "Resource/Handle.h" +#include "Resource/SHHandle.h" namespace SHADE diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index dacda574..68071160 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -3,7 +3,7 @@ #include "ECS_Base/System/SHSystem.h" #include "SHCameraComponent.h" #include "ECS_Base/System/SHSystemRoutine.h" -#include "Resource/ResourceLibrary.h" +#include "Resource/SHResourceLibrary.h" #include "SHCameraDirector.h" #include "SH_API.h" @@ -16,7 +16,7 @@ namespace SHADE //This is not tied to any entity. Hence this EID should not be used. SHCameraComponent editorCamera; - ResourceLibrary directorLibrary; + SHResourceLibrary directorLibrary; std::vector directorHandleList; public: From aa1b45c3de611167656ff96abad35ea6f8d60d1f Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Sun, 23 Oct 2022 15:03:24 +0800 Subject: [PATCH 31/32] Fixed editor camera --- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 3c5cae23..07995fc5 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -69,6 +69,7 @@ namespace SHADE if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) { + std::cout << "Camera movement: "<UpdateCameraComponent(system->editorCamera); } @@ -134,14 +136,14 @@ namespace SHADE void SHCameraSystem::UpdateCameraComponent(SHCameraComponent& camera) noexcept { - if (SHComponentManager::HasComponent(camera.GetEID()) == true) + if (SHComponentManager::HasComponent(camera.GetEID()) == true && &camera != &editorCamera) { auto transform = SHComponentManager::GetComponent(camera.GetEID()); SHVec3 rotation = transform->GetWorldRotation(); camera.pitch = rotation.x; camera.yaw = rotation.y; camera.roll = rotation.z; - camera.position = transform->GetWorldPosition(); + camera.position = transform->GetWorldPosition(); } @@ -151,7 +153,7 @@ namespace SHADE SHVec3 view, right, UP; - ClampCameraRotation(camera); + //ClampCameraRotation(camera); GetCameraAxis(camera, view, right, UP); From 64002c6f2bec9e2b2cc7044a879a42fb157248aa Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Sun, 23 Oct 2022 16:04:58 +0800 Subject: [PATCH 32/32] Camera Director integrated Still has bug --- .../src/Application/SBApplication.cpp | 1 + SHADE_Application/src/Scenes/SBTestScene.cpp | 5 + SHADE_Engine/src/Camera/SHCameraComponent.cpp | 62 ++++- SHADE_Engine/src/Camera/SHCameraComponent.h | 2 + SHADE_Engine/src/Camera/SHCameraDirector.cpp | 65 +++++ SHADE_Engine/src/Camera/SHCameraDirector.h | 43 +++ SHADE_Engine/src/Camera/SHCameraSystem.cpp | 252 +++++++++++++----- SHADE_Engine/src/Camera/SHCameraSystem.h | 23 +- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 32 ++- .../MiddleEnd/Interface/SHRenderer.cpp | 27 +- .../Graphics/MiddleEnd/Interface/SHRenderer.h | 7 +- 11 files changed, 421 insertions(+), 98 deletions(-) create mode 100644 SHADE_Engine/src/Camera/SHCameraDirector.cpp create mode 100644 SHADE_Engine/src/Camera/SHCameraDirector.h diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 8733e7b9..9bfd82de 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -93,6 +93,7 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); #ifdef SHEDITOR SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index f1d656ee..6b3fee1d 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -14,6 +14,7 @@ #include "Physics/Components/SHColliderComponent.h" #include "Assets/SHAssetManager.h" +#include "Camera/SHCameraComponent.h" using namespace SHADE; @@ -166,6 +167,10 @@ namespace Sandbox transformShowcase.SetWorldPosition({ 3.0f, -1.0f, -1.0f }); transformShowcase.SetLocalScale({ 5.0f, 5.0f, 5.0f }); scriptEngine->AddScript(raccoonShowcase, "RaccoonShowcase"); + + SHComponentManager::AddComponent(0); + SHComponentManager::RemoveComponent (0); + SHComponentManager::RemoveComponent (0); } void SBTestScene::Update(float dt) diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.cpp b/SHADE_Engine/src/Camera/SHCameraComponent.cpp index 650ed3c5..5d49c887 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.cpp +++ b/SHADE_Engine/src/Camera/SHCameraComponent.cpp @@ -1,13 +1,15 @@ #include "SHpch.h" #include "SHCameraComponent.h" #include "ECS_Base/Managers/SHComponentManager.h" - +#include "SHCameraSystem.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/Transform/SHTransformComponent.h" namespace SHADE { SHCameraComponent::SHCameraComponent() :yaw(0.0f), pitch(0.0f), roll(0.0f) - , width(1920.0f), height(1080.0f), zNear(0.01f), zFar(10000.0f), fov(90.0f), movementSpeed(1.0f), turnSpeed(1.0f) + , width(1920.0f), height(1080.0f), zNear(0.01f), zFar(10000.0f), fov(90.0f), movementSpeed(1.0f), turnSpeed(0.5f) , perspProj(true), dirtyView(true), dirtyProj(true) , viewMatrix(), projMatrix() , position() @@ -22,33 +24,69 @@ namespace SHADE void SHCameraComponent::SetYaw(float yaw) noexcept { this->yaw = yaw; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + transform->SetWorldRotation(SHVec3{rotation.x,yaw, rotation.z}); + } dirtyView = true; } void SHCameraComponent::SetPitch(float pitch) noexcept { this->pitch = pitch; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + transform->SetWorldRotation(SHVec3{ pitch,rotation.y, rotation.z }); + } dirtyView = true; } void SHCameraComponent::SetRoll(float roll) noexcept { this->roll = roll; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + transform->SetWorldRotation(SHVec3{ rotation.x,rotation.y, roll}); + } dirtyView = true; } void SHCameraComponent::SetPositionX(float x) noexcept { position.x = x; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ x,position.y, position.z}); + } dirtyView = true; } void SHCameraComponent::SetPositionY(float y) noexcept { position.y = y; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ position.x,y, position.z }); + } dirtyView = true; } void SHCameraComponent::SetPositionZ(float z) noexcept { position.z = z; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ position.x,position.y, z }); + } dirtyView = true; } void SHCameraComponent::SetPosition(float x,float y, float z) noexcept @@ -56,11 +94,23 @@ namespace SHADE position.x = x; position.y = y; position.z = z; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(SHVec3{ x,y, z }); + } dirtyView = true; } void SHCameraComponent::SetPosition(SHVec3& pos) noexcept { this->position = pos; + if (SHComponentManager::HasComponent(GetEID())) + { + auto transform = SHComponentManager::GetComponent(GetEID()); + SHVec3 position = transform->GetWorldPosition(); + transform->SetWorldRotation(pos); + } dirtyView = true; } @@ -128,4 +178,12 @@ namespace SHADE return projMatrix; } + void SHCameraComponent::SetMainCamera(size_t directorCameraIndex) noexcept + { + auto system = SHSystemManager::GetSystem(); + system->GetDirector(directorCameraIndex)->SetMainCamera(*this); + } + + + } diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.h b/SHADE_Engine/src/Camera/SHCameraComponent.h index c86fa160..1149b1e1 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.h +++ b/SHADE_Engine/src/Camera/SHCameraComponent.h @@ -70,6 +70,8 @@ namespace SHADE const SHMatrix& GetViewMatrix() const noexcept; const SHMatrix& GetProjMatrix() const noexcept; + void SetMainCamera(size_t cameraDirectorIndex = 0) noexcept; + float movementSpeed; SHVec3 turnSpeed; diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.cpp b/SHADE_Engine/src/Camera/SHCameraDirector.cpp new file mode 100644 index 00000000..559897c0 --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraDirector.cpp @@ -0,0 +1,65 @@ +#include "SHpch.h" +#include "SHCameraDirector.h" +#include "SHCameraComponent.h" +#include "ECS_Base/Managers/SHComponentManager.h" +#include "ECS_Base/SHECSMacros.h" +#include "ECS_Base/Managers/SHEntityManager.h" +#include "Tools/SHLog.h" + +namespace SHADE +{ + SHCameraDirector::SHCameraDirector() + :mainCameraEID(MAX_EID), transitionCameraEID(MAX_EID) + { + } + + + SHMatrix SHCameraDirector::GetViewMatrix() const noexcept + { + return viewMatrix; + } + SHMatrix SHCameraDirector::GetProjMatrix() const noexcept + { + return projMatrix; + } + SHMatrix SHCameraDirector::GetVPMatrix() const noexcept + { + return projMatrix * viewMatrix; + } + + void SHCameraDirector::UpdateMatrix() noexcept + { + if (mainCameraEID == MAX_EID) + { + auto& dense = SHComponentManager::GetDense(); + if (dense.size() == 0) + { + return; + } + mainCameraEID = dense[0].GetEID(); + } + SHCameraComponent* camComponent = SHComponentManager::GetComponent_s(mainCameraEID); + if (!camComponent) + { + SHLOG_WARNING("Camera Director warning: Entity does not have a camera"); + } + else + { + viewMatrix = camComponent->GetViewMatrix(); + projMatrix = camComponent->GetProjMatrix(); + } + } + + void SHCameraDirector::SetMainCamera(SHCameraComponent& camera) noexcept + { + if (SHEntityManager::IsValidEID(camera.GetEID()) == false) + { + SHLOG_WARNING("Camera Director Warning: Attempting to set an invalid entity as main camera.") + return; + } + mainCameraEID = camera.GetEID(); + } + + + +} diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.h b/SHADE_Engine/src/Camera/SHCameraDirector.h new file mode 100644 index 00000000..b1311147 --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraDirector.h @@ -0,0 +1,43 @@ +#pragma once + +#include "SH_API.h" +#include "ECS_Base/Entity/SHEntity.h" +#include "Math/SHMatrix.h" +#include "Resource/Handle.h" + + +namespace SHADE +{ + class SHCameraComponent; + + + + class SH_API SHCameraDirector + { + public: + SHCameraDirector(); + ~SHCameraDirector() = default; + + + EntityID mainCameraEID; + EntityID transitionCameraEID; + + SHMatrix GetViewMatrix() const noexcept; + SHMatrix GetProjMatrix() const noexcept; + SHMatrix GetVPMatrix() const noexcept; + void UpdateMatrix() noexcept; + void SetMainCamera(SHCameraComponent& cam) noexcept; + + + private: + + + protected: + SHMatrix viewMatrix; + SHMatrix projMatrix; + + }; + + typedef Handle DirectorHandle; + +} diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index c9822b82..40b63294 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -2,67 +2,115 @@ #include "SHCameraSystem.h" #include "Math/SHMathHelpers.h" #include "Input/SHInputManager.h" - +#include "Math/Vector/SHVec2.h" +#include "ECS_Base/Managers/SHComponentManager.h" +#include "Math/Transform/SHTransformComponent.h" namespace SHADE { + void SHCameraSystem::UpdateEditorCamera(double dt) noexcept + { + + auto& camera = editorCamera; + SHVec3 view, right, UP; + GetCameraAxis(camera, view, right, UP); + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) + { + camera.position -= right * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::D)) + { + camera.position += right * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::W)) + { + camera.position += view * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::S)) + { + camera.position -= view * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::Q)) + { + camera.position += UP * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::E)) + { + camera.position -= UP * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::RMB)) + { + double mouseX, mouseY; + SHInputManager::GetMouseVelocity(&mouseX, &mouseY); + + //std::cout << camera.yaw << std::endl; + + camera.pitch -= mouseY * dt * camera.turnSpeed.x; + camera.yaw -= mouseX * dt * camera.turnSpeed.y; + camera.dirtyView = true; + } + + UpdateCameraComponent(editorCamera); + } void SHCameraSystem::EditorCameraUpdate::Execute(double dt) noexcept { SHCameraSystem* system = static_cast(GetSystem()); auto& camera = system->editorCamera; - SHVec3 target{ 0.0f,0.0f,-1.0f }; - SHVec3 up = { 0.0f,1.0f,0.0f }; - - - SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); - SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); - target += camera.position; - ////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); - - //target = SHVec3::Normalise(target); - - SHVec3::RotateZ(up, camera.roll); - up = SHVec3::Normalise(up); - - - SHVec3 view = target - camera.position; view = SHVec3::Normalise(view); - SHVec3 right = SHVec3::Cross(view, up); right = SHVec3::Normalise(right); - const SHVec3 UP = SHVec3::Cross(view, right); - + SHVec3 view, right, UP; + system->GetCameraAxis(camera, view, right, UP); if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) { - system->editorCamera.position -= right * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + //std::cout << "Camera movement: "<editorCamera.position += right * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position += right * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::W)) { - system->editorCamera.position += view * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position += view * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::S)) { - system->editorCamera.position -= view * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position -= view * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::Q)) { - system->editorCamera.position += UP * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position += UP * dt * camera.movementSpeed; + camera.dirtyView = true; } if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::E)) { - system->editorCamera.position -= UP * dt * camera.movementSpeed; - system->editorCamera.dirtyView = true; + camera.position -= UP * dt * camera.movementSpeed; + camera.dirtyView = true; + } + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::RMB)) + { + double mouseX, mouseY; + SHInputManager::GetMouseVelocity(&mouseX,&mouseY); + + //std::cout << camera.yaw << std::endl; + + camera.pitch -= mouseY * dt * camera.turnSpeed.x; + camera.yaw -= mouseX * dt * camera.turnSpeed.y; + camera.dirtyView = true; } + //std::cout << "Camera position: " << camera.position.x << " " << camera.position.y << std::endl; system->UpdateCameraComponent(system->editorCamera); } @@ -88,26 +136,26 @@ namespace SHADE void SHCameraSystem::UpdateCameraComponent(SHCameraComponent& camera) noexcept { + if (SHComponentManager::HasComponent(camera.GetEID()) == true && &camera != &editorCamera) + { + auto transform = SHComponentManager::GetComponent(camera.GetEID()); + SHVec3 rotation = transform->GetWorldRotation(); + camera.pitch = SHMath::RadiansToDegrees(rotation.x); + camera.yaw = SHMath::RadiansToDegrees(rotation.y); + camera.roll = SHMath::RadiansToDegrees(rotation.z); + camera.position = transform->GetWorldPosition(); + } + + if (camera.dirtyView) { - SHVec3 target{ 0.0f,0.0f,-1.0f }; - SHVec3 up = { 0.0f,1.0f,0.0f }; + + SHVec3 view, right, UP; + + //ClampCameraRotation(camera); - SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); - SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); - target += camera.position; - ////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); - - //target = SHVec3::Normalise(target); - - SHVec3::RotateZ(up, camera.roll); - up = SHVec3::Normalise(up); - - - SHVec3 view = target - camera.position; view = SHVec3::Normalise(view); - SHVec3 right = SHVec3::Cross(view, up); right = SHVec3::Normalise(right); - const SHVec3 UP = SHVec3::Cross(view, right); + GetCameraAxis(camera, view, right, UP); camera.viewMatrix = SHMatrix::Identity; camera.viewMatrix(0, 0) = right[0]; @@ -143,38 +191,100 @@ namespace SHADE camera.projMatrix(3, 2) = 1.0f; camera.projMatrix(2, 3) = -(camera.zFar * camera.zNear) / (camera.zFar - camera.zNear); - //const float fov_rad = SHMath::DegreesToRadians(camera.fov); - //const float focal_length = 1.0f / tan(fov_rad * 0.5f); - - //camera.projMatrix(0,0) = focal_length / camera.GetAspectRatio(); - //camera.projMatrix(1,1) = -focal_length; - //camera.projMatrix(2,2) = camera.zNear / (camera.zFar - camera.zNear); - //camera.projMatrix(2,3) = camera.zFar * (camera.zNear / (camera.zFar - camera.zNear)); - //camera.projMatrix(3,2) = -1.0f; - //camera.projMatrix(3,3) = 0.0f; - - //camera.projMatrix = SHMatrix::Inverse(camera.projMatrix); - + camera.dirtyProj = false; } else { - const float R = camera.width * 0.5f; - const float L = -R; - const float T = camera.height * 0.5f; - const float B = -T; + //const float R = camera.width * 0.5f; + //const float L = -R; + //const float T = camera.height * 0.5f; + //const float B = -T; - camera.projMatrix = SHMatrix::Identity; - camera.projMatrix(0, 0) = 2.0f / (R - L); - camera.projMatrix(1, 1) = 2.0f / (B - T); - camera.projMatrix(2, 2) = 1.0f / (camera.zFar - camera.zNear); - camera.projMatrix(3, 0) = -(R + L) / (R - L); - camera.projMatrix(3, 1) = -(B + T) / (B - T); - camera.projMatrix(3, 2) = -camera.zNear / (camera.zFar - camera.zNear); + //camera.projMatrix = SHMatrix::Identity; + //camera.projMatrix(0, 0) = 2.0f / (R - L); + //camera.projMatrix(1, 1) = 2.0f / (B - T); + //camera.projMatrix(2, 2) = 1.0f / (camera.zFar - camera.zNear); + //camera.projMatrix(3, 0) = -(R + L) / (R - L); + //camera.projMatrix(3, 1) = -(B + T) / (B - T); + //camera.projMatrix(3, 2) = -camera.zNear / (camera.zFar - camera.zNear); camera.dirtyProj = false; } } } + void SHCameraSystem::GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& upVec) const noexcept + { + SHVec3 target{ 0.0f,0.0f,-1.0f }; + SHVec3 up = { 0.0f,1.0f,0.0f }; + + + target = SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); + target =SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); + target += camera.position; + ////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); + + //target = SHVec3::Normalise(target); + + SHVec3::RotateZ(up, camera.roll); + up = SHVec3::Normalise(up); + + + forward = target - camera.position; forward = SHVec3::Normalise(forward); + right = SHVec3::Cross(forward, up); right = SHVec3::Normalise(right); + upVec = SHVec3::Cross(forward, right); + } + + void SHCameraSystem::CameraSystemUpdate::Execute(double dt) noexcept + { + SHCameraSystem* system = static_cast(GetSystem()); + auto& dense = SHComponentManager::GetDense(); + for (auto& cam : dense) + { + system->UpdateCameraComponent(cam); + } + for (auto& handle : system->directorHandleList) + { + handle->UpdateMatrix(); + } + + + } + + + DirectorHandle SHCameraSystem::CreateDirector() noexcept + { + auto handle = directorLibrary.Create(); + directorHandleList.emplace_back(handle); + return handle; + } + + DirectorHandle SHCameraSystem::GetDirector(size_t index) noexcept + { + if (index < directorHandleList.size()) + { + return directorHandleList[index]; + } + else + { + return CreateDirector(); + } + } + void SHCameraSystem::ClampCameraRotation(SHCameraComponent& camera) noexcept + { + + + + if (camera.pitch > 85) + camera.SetPitch(85); + if (camera.pitch < -85) + camera.SetPitch(-85); + if (camera.roll > 85) + camera.SetRoll(85); + if (camera.roll < -85) + camera.SetRoll(-85); + + } + } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index fe7fd145..dacda574 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -3,9 +3,10 @@ #include "ECS_Base/System/SHSystem.h" #include "SHCameraComponent.h" #include "ECS_Base/System/SHSystemRoutine.h" +#include "Resource/ResourceLibrary.h" +#include "SHCameraDirector.h" #include "SH_API.h" - namespace SHADE { class SH_API SHCameraSystem final : public SHSystem @@ -14,8 +15,9 @@ namespace SHADE //A camera component that represents editor camera. //This is not tied to any entity. Hence this EID should not be used. SHCameraComponent editorCamera; - + ResourceLibrary directorLibrary; + std::vector directorHandleList; public: SHCameraSystem(void) = default; @@ -34,12 +36,27 @@ namespace SHADE }; friend class EditorCameraUpdate; - SHCameraComponent* GetEditorCamera (void) noexcept; + class SH_API CameraSystemUpdate final: public SHSystemRoutine + { + public: + CameraSystemUpdate() : SHSystemRoutine("Camera System Update", false) {}; + virtual void Execute(double dt)noexcept override final; + }; + friend class CameraSystemUpdate; + + SHCameraComponent* GetEditorCamera (void) noexcept; + void GetCameraAxis(SHCameraComponent const& camera, SHVec3& forward, SHVec3& right, SHVec3& up) const noexcept; + DirectorHandle CreateDirector() noexcept; + DirectorHandle GetDirector(size_t index) noexcept; + void ClampCameraRotation(SHCameraComponent& camera) noexcept; + void UpdateEditorCamera(double dt) noexcept; protected: void UpdateCameraComponent(SHCameraComponent& camera) noexcept; + + }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 992cbdf1..cbf3ad95 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -147,6 +147,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ auto windowDims = window->GetWindowSize(); + auto cameraSystem = SHSystemManager::GetSystem(); // Set Up Cameras screenCamera = resourceManager.Create(); @@ -198,6 +199,8 @@ namespace SHADE worldRenderer = worldViewport->AddRenderer(resourceManager, swapchain->GetNumImages(), renderContextCmdPools, descPool, SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::HIGH_FREQUENCY_GLOBALS], worldRenderGraph); worldRenderer->SetCamera(worldCamera); + worldRenderer->SetCameraDirector(cameraSystem->CreateDirector()); + auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); @@ -335,21 +338,7 @@ namespace SHADE auto cameraSystem = SHSystemManager::GetSystem(); -#ifdef SHEDITOR - auto editorSystem = SHSystemManager::GetSystem(); - if (editorSystem->editorState != SHEditor::State::PLAY) - { - worldRenderer->SetViewProjectionMatrix(SHMatrix::Transpose(cameraSystem->GetEditorCamera()->GetProjMatrix() * cameraSystem->GetEditorCamera()->GetViewMatrix())); - } - else - { - // main camera - } - -#else - // main camera -#endif // For every viewport for (int vpIndex = 0; vpIndex < static_cast(viewports.size()); ++vpIndex) @@ -399,7 +388,22 @@ namespace SHADE } // bind camera data + //renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex); + +#ifdef SHEDITOR + if (renderers[renIndex] == worldRenderer) + { + auto editorSystem = SHSystemManager::GetSystem(); + if (editorSystem->editorState != SHEditor::State::PLAY) + worldRenderer->UpdateDataAndBind(currentCmdBuffer, frameIndex, SHMatrix::Transpose(cameraSystem->GetEditorCamera()->GetProjMatrix() * cameraSystem->GetEditorCamera()->GetViewMatrix())); + else + renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex); + } + else + renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex); +#else renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex); +#endif // Draw first renderers[renIndex]->Draw(frameIndex, descPool); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp index a1806ccd..962130be 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp @@ -21,6 +21,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" #include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" #include "Graphics/Buffers/SHVkBuffer.h" +#include "Camera/SHCameraDirector.h" namespace SHADE { @@ -65,6 +66,11 @@ namespace SHADE camera = _camera; } + void SHRenderer::SetCameraDirector(Handle director) noexcept + { + cameraDirector = director; + } + /*-----------------------------------------------------------------------------------*/ /* Drawing Functions */ /*-----------------------------------------------------------------------------------*/ @@ -75,17 +81,24 @@ namespace SHADE void SHRenderer::UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex) noexcept { - if (camera) + if (camera && cameraDirector) { - //cpuCameraData.viewProjectionMatrix = camera->GetViewProjectionMatrix(); - cameraBuffer->WriteToMemory(&cpuCameraData, sizeof(SHShaderCameraData), 0, cameraDataAlignedSize * frameIndex); - - std::array dynamicOffsets{ frameIndex * cameraDataAlignedSize }; - - cmdBuffer->BindDescriptorSet(cameraDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, SHGraphicsConstants::DescriptorSetIndex::HIGH_FREQUENCY_GLOBALS, std::span{ dynamicOffsets.data(), 1 }); + UpdateDataAndBind(cmdBuffer, frameIndex, SHMatrix::Transpose(cameraDirector->GetVPMatrix())); } } + void SHRenderer::UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix exteriorMatrix) noexcept + { + SetViewProjectionMatrix(exteriorMatrix); + + //cpuCameraData.viewProjectionMatrix = camera->GetViewProjectionMatrix(); + cameraBuffer->WriteToMemory(&cpuCameraData, sizeof(SHShaderCameraData), 0, cameraDataAlignedSize * frameIndex); + + std::array dynamicOffsets{ frameIndex * cameraDataAlignedSize }; + + cmdBuffer->BindDescriptorSet(cameraDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, SHGraphicsConstants::DescriptorSetIndex::HIGH_FREQUENCY_GLOBALS, std::span{ dynamicOffsets.data(), 1 }); + } + void SHRenderer::UpdateCameraDataToBuffer(void) noexcept { } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h index 57c63e7f..a70e1996 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h @@ -40,6 +40,7 @@ namespace SHADE class SHGraphicsGlobalData; class SHVkDescriptorPool; class SHVkBuffer; + class SHCameraDirector; struct SHShaderCameraData { @@ -71,12 +72,14 @@ namespace SHADE /* Camera Registration */ /*-----------------------------------------------------------------------------*/ void SetCamera(Handle _camera); + void SetCameraDirector (Handle director) noexcept; /*-----------------------------------------------------------------------------*/ /* Drawing Functions */ /*-----------------------------------------------------------------------------*/ void Draw(uint32_t frameIndex, Handle descPool) noexcept; - void UpdateDataAndBind (Handle cmdBuffer, uint32_t frameIndex) noexcept; + void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex) noexcept; + void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix exteriorMatrix) noexcept; void UpdateCameraDataToBuffer (void) noexcept; void SetViewProjectionMatrix (SHMatrix const& vpMatrix) noexcept; @@ -99,6 +102,8 @@ namespace SHADE Handle cameraDescriptorSet; Handle cameraBuffer; + Handle cameraDirector; + // we really only need 1 copy even though we need %swapchainImages copies for // GPU. SHShaderCameraData cpuCameraData;