diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index de548060..24f0a214 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -4,7 +4,7 @@ //#define SHEDITOR #ifdef SHEDITOR -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" //#include "Scenes/SBEditorScene.h" #endif // SHEDITOR @@ -94,7 +94,7 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); - SHSystemManager::RegisterRoutine(); + //SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); #ifdef SHEDITOR diff --git a/SHADE_Engine/premake5.lua b/SHADE_Engine/premake5.lua index 18920194..bc1f6a03 100644 --- a/SHADE_Engine/premake5.lua +++ b/SHADE_Engine/premake5.lua @@ -8,7 +8,7 @@ project "SHADE_Engine" pchheader "SHpch.h" pchsource "%{prj.location}/src/SHpch.cpp" staticruntime "off" - + buildoptions{"/bigobj"} files { "%{prj.location}/src/**.h", diff --git a/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp b/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp index f9849d78..ce0615e1 100644 --- a/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp +++ b/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp @@ -6,12 +6,12 @@ namespace SHADE { //TODO: Convert to RTTR? - constexpr auto DRAG_EID = "DragEID"; - constexpr auto DRAG_RESOURCE = "DragResource"; - struct SHDragDrop { + using DragDropTag = std::string_view; + static constexpr DragDropTag DRAG_EID = "DragEID"; + static constexpr DragDropTag DRAG_RESOURCE = "DragResource"; static bool BeginSource(ImGuiDragDropFlags const flags = 0); /** * \brief Ends the DragDrop Source. ONLY CALL IF BeginSource returns true diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp index caad9b10..8c71eb8f 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp @@ -4,14 +4,16 @@ #include "Editor/IconsMaterialDesign.h" #include "Editor/SHImGuiHelpers.hpp" #include +#include #include "Assets/SHAssetManager.h" +#include "Editor/IconsFontAwesome6.h" #include "Editor/DragDrop/SHDragDrop.hpp" namespace SHADE { SHAssetBrowser::SHAssetBrowser() - :SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar) + :SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar), rootFolder(SHAssetManager::GetRootFolder()), prevFolder(rootFolder), currentFolder(rootFolder) { } @@ -23,62 +25,140 @@ namespace SHADE void SHAssetBrowser::Update() { SHEditorWindow::Update(); - if(Begin()) + if (Begin()) { + RecursivelyDrawTree(rootFolder); DrawMenuBar(); - auto const& assets = SHAssetManager::GetAllAssets(); - if(ImGui::BeginTable("AssetBrowserTable", 3)) - { - ImGui::TableNextColumn(); - ImGui::TableHeader("Asset ID"); - ImGui::TableNextColumn(); - ImGui::TableHeader("Name"); - ImGui::TableNextColumn(); - ImGui::TableHeader("Type"); - for(SHAsset const& asset : assets) - { - DrawAsset(asset); - } - ImGui::EndTable(); - } + DrawCurrentFolder(); } ImGui::End(); } void SHAssetBrowser::DrawMenuBar() { - if(ImGui::BeginMenuBar()) + if (ImGui::BeginMenuBar()) { ImGui::EndMenuBar(); } } - void SHAssetBrowser::DrawAsset(SHAsset const& asset) + ImRect SHAssetBrowser::RecursivelyDrawTree(FolderPointer folder) { - ImGui::PushID(asset.id); - ImGui::BeginGroup(); - - ImGui::TableNextColumn(); - ImGui::Selectable(std::format("{}", asset.id).data(), false, ImGuiSelectableFlags_SpanAllColumns); - if(SHDragDrop::BeginSource()) + auto const& subFolders = folder->subFolders; + auto const& files = folder->files; + const bool isSelected = std::ranges::find(selectedFolders, folder) != selectedFolders.end(); + ImGuiTreeNodeFlags flags = (subFolders.empty() && files.empty()) ? ImGuiTreeNodeFlags_Leaf : ImGuiTreeNodeFlags_OpenOnArrow; + if (isSelected) + flags |= ImGuiTreeNodeFlags_Selected; + if (folder == rootFolder) + flags |= ImGuiTreeNodeFlags_DefaultOpen; + + bool isOpen = ImGui::TreeNodeEx(folder, flags, "%s %s", ICON_MD_FOLDER, folder->name.data()); + const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + if(ImGui::IsItemClicked()) { - auto id = asset.id; - ImGui::Text("Moving Asset: %zu", id); - SHDragDrop::SetPayload(DRAG_RESOURCE, &id); - SHDragDrop::EndSource(); + selectedFolders.clear(); + selectedFolders.push_back(folder); + } + if (isOpen) + { + const ImColor treeLineColor = ImGui::GetColorU32(ImGuiCol_CheckMark); + const float horizontalOffset = 0.0f; + ImDrawList* drawList = ImGui::GetWindowDrawList(); + ImVec2 vertLineStart = ImGui::GetCursorScreenPos(); + vertLineStart.x += horizontalOffset; + ImVec2 vertLineEnd = vertLineStart; + for (auto const& subFolder : subFolders) + { + const float horizontalLineSize = 8.0f; + const ImRect childRect = RecursivelyDrawTree(subFolder); + const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f; + drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1); + vertLineEnd.y = midPoint; + } + for (auto const& file : files) + { + const float horizontalLineSize = 25.0f; + const ImRect childRect = DrawFile(file); + const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f; + drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1); + vertLineEnd.y = midPoint; + } + drawList->AddLine(vertLineStart, vertLineEnd, treeLineColor, 1); + ImGui::TreePop(); + } + return nodeRect; + } + + void SHAssetBrowser::DrawCurrentFolder() + { + //auto const& subFolders = currentFolder->subFolders; + //ImVec2 initialCursorPos = ImGui::GetCursorPos(); + //ImVec2 initialRegionAvail = ImGui::GetContentRegionAvail(); + //int maxTiles = initialRegionAvail.x / tileWidth; + //float maxX = (maxTiles - 1)*tileWidth; + //ImVec2 tilePos = initialCursorPos; + //for (auto const& subFolder : subFolders) + //{ + // ImGui::SetCursorPos(tilePos); + // ImGui::BeginGroup(); + // ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, {0.0f, 0.0f}); + // ImGui::Button(ICON_MD_FOLDER, {tileWidth}); + // ImGui::Text(subFolder->name.data()); + // ImGui::PopStyleVar(); + // ImGui::EndGroup(); + // if(tilePos.x >= maxX) + // { + // tilePos.x = initialCursorPos.x; + // } + // else + // { + // ImGui::SameLine(); + // tilePos.x += tileWidth; + // } + //} + } + + ImRect SHAssetBrowser::DrawFile(SHFile const& file) noexcept + { + if (file.assetMeta == nullptr) + return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + const bool isSelected = std::ranges::find(selectedAssets, file.assetMeta->id) != selectedAssets.end(); + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf; + if (isSelected) + flags |= ImGuiTreeNodeFlags_Selected; + std::string icon{}; + + switch(file.assetMeta->type) + { + case AssetType::INVALID: break; + case AssetType::SHADER: icon = ICON_FA_FILE_CODE; break; + case AssetType::SHADER_BUILT_IN: icon = ICON_FA_FILE_CODE; break; + case AssetType::TEXTURE: icon = ICON_FA_IMAGES; break; + case AssetType::MESH: icon = ICON_FA_CUBES; break; + case AssetType::SCENE: icon = ICON_MD_IMAGE; break; + case AssetType::PREFAB: icon = ICON_FA_BOX_OPEN; break; + case AssetType::MATERIAL: break; + case AssetType::MAX_COUNT: break; + default: ; } - ImGui::TableNextColumn(); - ImGui::Text("%s", asset.name.c_str()); - - ImGui::TableNextColumn(); - ImGui::Text("%s", "Type"); - - ImGui::EndGroup(); - ImGui::PopID(); - - - + ImGui::TreeNodeEx(file.assetMeta, flags, "%s %s", icon.data(), file.assetMeta->name.data()); + const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + if(SHDragDrop::BeginSource()) + { + auto id = file.assetMeta->id; + ImGui::Text("Moving Asset: %s [%zu]", file.name.data(), file.assetMeta->id); + SHDragDrop::SetPayload(SHDragDrop::DRAG_RESOURCE, &id); + SHDragDrop::EndSource(); + } + if(ImGui::IsItemClicked()) + { + selectedAssets.clear(); + selectedAssets.push_back(file.assetMeta->id); + } + ImGui::TreePop(); + return nodeRect; } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h index 0e3053bc..d56fc029 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h @@ -1,7 +1,9 @@ #pragma once +#include "imgui_internal.h" #include "Assets/SHAsset.h" #include "Editor/EditorWindow/SHEditorWindow.h" +#include "Filesystem/SHFolder.h" namespace SHADE { @@ -16,9 +18,14 @@ namespace SHADE void Refresh(); private: void DrawMenuBar(); - void DrawAsset(SHAsset const& asset); + ImRect RecursivelyDrawTree(FolderPointer folder); + void DrawCurrentFolder(); + ImRect DrawFile(SHFile const& file) noexcept; - float idColumnWidth, nameColumnWidth, typeColumnWidth; + FolderPointer rootFolder, prevFolder, currentFolder; + std::vector selectedFolders; + std::vector selectedAssets; + static constexpr float tileWidth = 50.0f; }; -} \ No newline at end of file +} diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 7c774067..55d78421 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -6,7 +6,7 @@ //#==============================================================# //|| SHADE Includes || //#==============================================================# -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "Editor/SHImGuiHelpers.hpp" #include "Editor/SHEditorWidgets.hpp" #include "SHHierarchyPanel.h" @@ -48,7 +48,7 @@ namespace SHADE if (Begin()) { - if(skipFrame) + if (skipFrame) { ImGui::End(); skipFrame = false; @@ -63,9 +63,9 @@ namespace SHADE for (const auto child : children) { - if(child) + if (child) RecursivelyDrawEntityNode(child); - if(skipFrame) + if (skipFrame) { ImGui::End(); return; @@ -77,12 +77,36 @@ namespace SHADE SHLOG_WARNING("Scene Graph root is null! Unable to render hierarchy.") } - if(ImGui::IsWindowHovered() && !SHDragDrop::hasDragDrop && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) + if (ImGui::IsWindowHovered() && !SHDragDrop::hasDragDrop && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { - if(auto editor = SHSystemManager::GetSystem()) + if (auto editor = SHSystemManager::GetSystem()) editor->selectedEntities.clear(); } ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal); + if (ImGui::IsWindowFocused()) + { + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_A)) + { + SelectAllEntities(); + } + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_C)) + { + CopySelectedEntities(); + } + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && !ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyReleased(ImGuiKey_V)) + { + PasteEntities(); + } + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyReleased(ImGuiKey_V)) + { + const auto editor = SHSystemManager::GetSystem(); + if (editor->selectedEntities.size() == 1) + { + PasteEntities(editor->selectedEntities.back()); + } + } + } + } ImGui::End(); } @@ -94,7 +118,7 @@ namespace SHADE void SHHierarchyPanel::SetScrollTo(EntityID eid) { - if(eid == MAX_EID) + if (eid == MAX_EID) return; scrollTo = eid; } @@ -106,8 +130,10 @@ namespace SHADE { if (ImGui::BeginMenuBar()) { - ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x * 0.75f); - if(ImGui::SmallButton(ICON_MD_DESELECT)) + auto size = ImGui::GetWindowSize(); + auto g = ImGui::GetCurrentContext(); + ImGui::SetCursorPosX(size.x - g->Style.FramePadding.x * 15.0f); + if (ImGui::SmallButton(ICON_MD_CLEAR_ALL)) { auto editor = SHSystemManager::GetSystem(); editor->selectedEntities.clear(); @@ -134,7 +160,7 @@ namespace SHADE ImRect SHHierarchyPanel::RecursivelyDrawEntityNode(SHSceneNode* const currentNode) { - if(currentNode == nullptr) + if (currentNode == nullptr) return {}; auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); @@ -142,7 +168,7 @@ namespace SHADE auto& children = currentNode->GetChildren(); EntityID eid = currentNode->GetEntityID(); - if(scrollTo != MAX_EID && eid == scrollTo) + if (scrollTo != MAX_EID && eid == scrollTo) { ImGui::SetScrollHereY(); scrollTo = MAX_EID; @@ -169,23 +195,23 @@ namespace SHADE if (SHDragDrop::BeginSource()) { std::string moveLabel = "Moving EID: "; - if(!isSelected) + if (!isSelected) editor->selectedEntities.push_back(eid); - for(int i = 0; i < static_cast(editor->selectedEntities.size()); ++i) + 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())) + if (i + 1 < static_cast(editor->selectedEntities.size())) { moveLabel.append(", "); } } ImGui::Text(moveLabel.c_str()); - SHDragDrop::SetPayload>(DRAG_EID, &editor->selectedEntities); + SHDragDrop::SetPayload>(SHDragDrop::DRAG_EID, &editor->selectedEntities); SHDragDrop::EndSource(); } else if (SHDragDrop::BeginTarget()) //If Received DragDrop { - if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(DRAG_EID)) //If payload is valid + if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(SHDragDrop::DRAG_EID)) //If payload is valid { ParentSelectedEntities(eid); SHDragDrop::EndTarget(); @@ -193,43 +219,43 @@ namespace SHADE } //Context menu - if(ImGui::BeginPopupContextItem(std::to_string(eid).c_str())) + if (ImGui::BeginPopupContextItem(std::to_string(eid).c_str())) { - if(!isSelected) + if (!isSelected) { editor->selectedEntities.clear(); editor->selectedEntities.push_back(eid); } - if(ImGui::Selectable("Copy")) + if (ImGui::Selectable("Copy")) { - SHClipboardUtilities::WriteToClipboard(SHSerialization::SerializeEntitiesToString(editor->selectedEntities)); + CopySelectedEntities(); } - if(ImGui::Selectable("Paste")) + if (ImGui::Selectable("Paste")) { - SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard())); + PasteEntities(); skipFrame = true; ImGui::EndPopup(); - if(isNodeOpen) + if (isNodeOpen) ImGui::TreePop(); return nodeRect; } - if(ImGui::Selectable("Paste as Child")) + if (ImGui::Selectable("Paste as Child")) { - SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard(), eid)); + PasteEntities(eid); skipFrame = true; } - if(ImGui::Selectable(std::format("{} Delete", ICON_MD_DELETE).data())) + if (ImGui::Selectable(std::format("{} Delete", ICON_MD_DELETE).data())) { SHEntityManager::DestroyEntity(eid); } - - if((currentNode->GetParent() != sceneGraph.GetRoot()) && ImGui::Selectable(std::format("{} Unparent Selected", ICON_MD_NORTH_WEST).data())) + + if ((currentNode->GetParent() != sceneGraph.GetRoot()) && ImGui::Selectable(std::format("{} Unparent Selected", ICON_MD_NORTH_WEST).data())) { ParentSelectedEntities(MAX_EID); } ImGui::EndPopup(); } - + //Handle node selection if (ImGui::IsItemHovered()) { @@ -237,11 +263,11 @@ namespace SHADE { if (!isSelected) { - if(ImGui::IsKeyDown(ImGuiKey_LeftShift)) + if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) { - if(editor->selectedEntities.size() >= 1) + if (editor->selectedEntities.size() >= 1) { - SelectRangeOfEntities(editor->selectedEntities[0], eid); + SelectRangeOfEntities(editor->selectedEntities[0], eid); } else editor->selectedEntities.clear(); } @@ -299,12 +325,12 @@ namespace SHADE auto const editor = SHSystemManager::GetSystem(); SHEntityParentCommand::EntityParentData entityParentData; std::vector parentedEIDS; - for(auto const& eid : editor->selectedEntities) + for (auto const& eid : editor->selectedEntities) { - if(sceneGraph.GetChild(eid, parentEID) == nullptr) + if (sceneGraph.GetChild(eid, parentEID) == nullptr) { parentedEIDS.push_back(eid); - if(auto parent = sceneGraph.GetParent(eid)) + if (auto parent = sceneGraph.GetParent(eid)) entityParentData[eid].oldParentEID = parent->GetEntityID(); entityParentData[eid].newParentEID = parentEID; } @@ -319,34 +345,57 @@ namespace SHADE editor->selectedEntities.clear(); auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); sceneGraph.Traverse([&](SHSceneNode* nodePtr) - { - auto eid = nodePtr->GetEntityID(); - if(!startSelecting) { - if(eid == beginEID || eid == endEID) + auto eid = nodePtr->GetEntityID(); + if (!startSelecting) { - startSelecting = true; - editor->selectedEntities.push_back(eid); - } - } - else - { - if(!endSelecting) - { - editor->selectedEntities.push_back(eid); - if(eid == endEID || eid == beginEID) + if (eid == beginEID || eid == endEID) { - endSelecting = true; + startSelecting = true; + editor->selectedEntities.push_back(eid); } } - } - }); + else + { + if (!endSelecting) + { + editor->selectedEntities.push_back(eid); + if (eid == endEID || eid == beginEID) + { + endSelecting = true; + } + } + } + }); + } + + void SHHierarchyPanel::SelectAllEntities() + { + const auto editor = SHSystemManager::GetSystem(); + editor->selectedEntities.clear(); + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + sceneGraph.Traverse([&](SHSceneNode* nodePtr) + { + auto eid = nodePtr->GetEntityID(); + editor->selectedEntities.push_back(eid); + }); + } + + void SHHierarchyPanel::CopySelectedEntities() + { + const auto editor = SHSystemManager::GetSystem(); + SHClipboardUtilities::WriteToClipboard(SHSerialization::SerializeEntitiesToString(editor->selectedEntities)); + } + + void SHHierarchyPanel::PasteEntities(EntityID parentEID) + { + SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard(), parentEID)); } void SHCreateEntityCommand::Execute() { EntityID newEID = SHEntityManager::CreateEntity(eid); - if(eid == MAX_EID) + if (eid == MAX_EID) eid = newEID; } @@ -358,9 +407,9 @@ namespace SHADE void SHEntityParentCommand::Execute() { auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - for(auto const& eid : entities) + for (auto const& eid : entities) { - if(entityParentData[eid].newParentEID == MAX_EID) + if (entityParentData[eid].newParentEID == MAX_EID) sceneGraph.SetParent(eid, nullptr); else sceneGraph.SetParent(eid, entityParentData[eid].newParentEID); @@ -370,9 +419,9 @@ namespace SHADE void SHEntityParentCommand::Undo() { auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - for(auto const& eid : entities) + for (auto const& eid : entities) { - if(entityParentData[eid].oldParentEID == MAX_EID) + if (entityParentData[eid].oldParentEID == MAX_EID) sceneGraph.SetParent(eid, nullptr); else sceneGraph.SetParent(eid, entityParentData[eid].oldParentEID); diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index 8fae5d6d..9b26e9d6 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -30,7 +30,9 @@ namespace SHADE void CreateChildEntity(EntityID parentEID) const noexcept; void ParentSelectedEntities(EntityID parentEID) const noexcept; void SelectRangeOfEntities(EntityID beginEID, EntityID EndEID); - + void SelectAllEntities(); + void CopySelectedEntities(); + void PasteEntities(EntityID parentEID = MAX_EID); bool skipFrame = false; std::string filter; bool isAnyNodeSelected = false; diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.h b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.h new file mode 100644 index 00000000..69f4c145 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.h @@ -0,0 +1,12 @@ +#pragma once +#include "ECS_Base/Components/SHComponent.h" + +namespace SHADE +{ + template::value, bool> = true> + static void DrawContextMenu(T* component); + template, bool> = true> + static void DrawComponent(T* component); +} + +#include "SHEditorComponentView.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 9899d4a4..521e1213 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -13,25 +13,28 @@ #include "Editor/IconsFontAwesome6.h" #include "ECS_Base/Components/SHComponent.h" #include "Editor/SHEditorWidgets.hpp" +#include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" #include "Physics/Components/SHColliderComponent.h" #include "Reflection/SHReflectionMetadata.h" +#include "Resource/SHResourceManager.h" + namespace SHADE { template std::vector GetRTTREnumNames() { auto const rttrType = rttr::type::get(); - if(!rttrType.is_enumeration()) + if (!rttrType.is_enumeration()) return {}; auto const enumAlign = rttrType.get_enumeration(); auto const names = enumAlign.get_names(); std::vector result; - std::transform(names.begin(), names.end(), std::back_inserter(result), [](rttr::string_view const& name){return name.data();}); + std::transform(names.begin(), names.end(), std::back_inserter(result), [](rttr::string_view const& name) {return name.data(); }); return result; } - template::value, bool> = true> + template::value, bool>> static void DrawContextMenu(T* component) { if (!component) @@ -60,13 +63,15 @@ namespace SHADE ImGui::EndPopup(); } } - template, bool> = true> + template, bool>> static void DrawComponent(T* component) { if (!component) return; - const auto componentType = rttr::type::get(*component); + const auto componentType = rttr::type::get(); + ImGui::PushID(SHFamilyID::GetID()); SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); + ImGui::PopID(); ImGui::SameLine(); if (ImGui::CollapsingHeader(componentType.get_name().data())) { @@ -75,7 +80,8 @@ namespace SHADE for (auto const& property : properties) { auto const& type = property.get_type(); - + auto tooltip = property.get_metadata(META::tooltip); + bool const& isAngleInRad = property.get_metadata(META::angleInRad).is_valid() ? property.get_metadata(META::angleInRad).template get_value() : false; if (type.is_enumeration()) { auto enumAlign = type.get_enumeration(); @@ -89,29 +95,25 @@ namespace SHADE auto values = enumAlign.get_values(); auto it = std::next(values.begin(), idx); property.set_value(component, *it); - }); + }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else if (type.is_arithmetic()) { if (type == rttr::type::get()) { - SHEditorWidgets::CheckBox(property.get_name().data(), [component, property] {return property.get_value(component).to_bool(); }, [component, property](bool const& result) {property.set_value(component, result); }); + SHEditorWidgets::CheckBox(property.get_name().data(), [component, property] {return property.get_value(component).to_bool(); }, [component, property](bool const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } - //else if (type == rttr::type::get()) - //{ - // - //} else if (type == rttr::type::get() || type == rttr::type::get() || type == rttr::type::get() || type == rttr::type::get()) { auto metaMin = property.get_metadata(META::min); auto metaMax = property.get_metadata(META::max); if (metaMin && metaMax) { - SHEditorWidgets::SliderInt(property.get_name().data(), metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }); + SHEditorWidgets::SliderInt(property.get_name().data(), metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else { - SHEditorWidgets::DragInt(property.get_name().data(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }); + SHEditorWidgets::DragInt(property.get_name().data(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -120,11 +122,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U8, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U8, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U8, [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U8, [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -133,11 +135,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U16, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U16, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U16, [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U16, [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -146,11 +148,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U32, metaMin.template get_value(), metaMax.template get_value(), [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U32, metaMin.template get_value(), metaMax.template get_value(), [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U32, [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U32, [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -159,11 +161,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U64, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U64, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U64, [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U64, [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -171,17 +173,17 @@ namespace SHADE auto metaMin = property.get_metadata(META::min); auto metaMax = property.get_metadata(META::max); float min{}, max{}; - if(metaMin.is_valid()) + if (metaMin.is_valid()) min = std::max(metaMin.template get_value(), -FLT_MAX * 0.5f); - if(metaMax.is_valid()) + if (metaMax.is_valid()) max = std::min(metaMax.template get_value(), FLT_MAX * 0.5f); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderFloat(property.get_name().data(), min, max, [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }); + SHEditorWidgets::SliderFloat(property.get_name().data(), min, max, [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else { - SHEditorWidgets::DragFloat(property.get_name().data(), [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }, "Test"); + SHEditorWidgets::DragFloat(property.get_name().data(), [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -190,25 +192,25 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_Double, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_Double, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_Double, [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }, 0.1f); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_Double, [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }, 0.1f, {}, {}, "%.3f", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } } else if (type == rttr::type::get()) { - SHEditorWidgets::DragVec4(property.get_name().data(), { "X", "Y", "Z", "W" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec4 vec) {return property.set_value(component, vec); }); + SHEditorWidgets::DragVec4(property.get_name().data(), { "X", "Y", "Z", "W" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec4 vec) {return property.set_value(component, vec); }, isAngleInRad, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else if (type == rttr::type::get()) { - SHEditorWidgets::DragVec3(property.get_name().data(), { "X", "Y", "Z" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec3 vec) {return property.set_value(component, vec); }); + SHEditorWidgets::DragVec3(property.get_name().data(), { "X", "Y", "Z" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec3 vec) {return property.set_value(component, vec); }, isAngleInRad, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else if (type == rttr::type::get()) { - SHEditorWidgets::DragVec2(property.get_name().data(), { "X", "Y" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec2 vec) {return property.set_value(component, vec); }); + SHEditorWidgets::DragVec2(property.get_name().data(), { "X", "Y" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec2 vec) {return property.set_value(component, vec); }, isAngleInRad, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } @@ -223,10 +225,10 @@ namespace SHADE return; // Get transform component for extrapolating relative sizes - auto* transformComponent = SHComponentManager::GetComponent(component->GetEID()); + auto* transformComponent = SHComponentManager::GetComponent_s(component->GetEID()); const auto componentType = rttr::type::get(*component); - SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }); + SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); ImGui::SameLine(); if (ImGui::CollapsingHeader(componentType.get_name().data())) { @@ -234,8 +236,8 @@ 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}; + 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); @@ -244,12 +246,12 @@ namespace SHADE if (collider->GetType() == SHCollider::Type::BOX) { - SHEditorWidgets::BeginPanel( std::format("{} Box Collider #{}", ICON_FA_CUBE, 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, transformComponent] { return (transformComponent->GetWorldScale() * 2.0f) * box->GetHalfExtents(); }, + "Half Extents", { "X", "Y", "Z" }, + [box, transformComponent] { return (transformComponent->GetWorldScale() * 2.0f) * box->GetHalfExtents(); }, [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); } else if (collider->GetType() == SHCollider::Type::SPHERE) @@ -258,14 +260,14 @@ namespace SHADE auto sphere = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragFloat ( - "Radius", + "Radius", [sphere, transformComponent] { const SHVec3& TF_WORLD_SCALE = transformComponent->GetWorldScale(); const float MAX_SCALE = SHMath::Max({ TF_WORLD_SCALE.x, TF_WORLD_SCALE.y, TF_WORLD_SCALE.z }); return sphere->GetRadius() / MAX_SCALE; - }, - [collider](float const& value) { collider->SetBoundingSphere(value);}); + }, + [collider](float const& value) { collider->SetBoundingSphere(value); }); } else if (collider->GetType() == SHCollider::Type::CAPSULE) { @@ -276,14 +278,14 @@ namespace SHADE SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [&collider] {return collider->GetPositionOffset(); }, [&collider](SHVec3 const& vec) {collider->SetPositionOffset(vec); }); SHEditorWidgets::EndPanel(); } - if(ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data())) + if (ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data())) { colliderToDelete = i; } SHEditorWidgets::EndPanel(); ImGui::PopID(); } - if(colliderToDelete.has_value()) + if (colliderToDelete.has_value()) { component->RemoveCollider(colliderToDelete.value()); } @@ -291,11 +293,11 @@ namespace SHADE if (ImGui::BeginMenu("Add Collider")) { - if(ImGui::Selectable("Box Collider")) + if (ImGui::Selectable("Box Collider")) { component->AddBoundingBox(); } - if(ImGui::Selectable("Sphere Collider")) + if (ImGui::Selectable("Sphere Collider")) { component->AddBoundingSphere(); } @@ -308,10 +310,10 @@ namespace SHADE template<> static void DrawComponent(SHLightComponent* component) { - if (!component) + if (!component) return; const auto componentType = rttr::type::get(*component); - SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }); + SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); ImGui::SameLine(); if (ImGui::CollapsingHeader(componentType.get_name().data())) { @@ -319,19 +321,48 @@ namespace SHADE static auto const enumAlign = rttr::type::get().get_enumeration(); static std::vector list(GetRTTREnumNames()); - + SHEditorWidgets::ComboBox("Type", list, [component] {return static_cast(component->GetType()); }, [component](int const& idx) { component->SetType(static_cast(idx)); }); - SHEditorWidgets::DragVec3("Position", {"X", "Y", "Z"}, [component](){return component->GetPosition();}, [component](SHVec3 const& vec){component->SetPosition(vec);}); - SHEditorWidgets::DragVec3("Direction", {"X", "Y", "Z"}, [component](){return component->GetDirection();}, [component](SHVec3 const& vec){component->SetDirection(vec);}); - SHEditorWidgets::ColorPicker("Color", [component](){return component->GetColor();}, [component](SHVec4 const& rgba){component->SetColor(rgba);}); - SHEditorWidgets::DragFloat("Strength", [component](){return component->GetStrength();}, [component](float const& value){component->SetStrength(value);}); + SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component]() {return component->GetPosition(); }, [component](SHVec3 const& vec) {component->SetPosition(vec); }); + SHEditorWidgets::DragVec3("Direction", { "X", "Y", "Z" }, [component]() {return component->GetDirection(); }, [component](SHVec3 const& vec) {component->SetDirection(vec); }); + SHEditorWidgets::ColorPicker("Color", [component]() {return component->GetColor(); }, [component](SHVec4 const& rgba) {component->SetColor(rgba); }); + SHEditorWidgets::DragFloat("Strength", [component]() {return component->GetStrength(); }, [component](float const& value) {component->SetStrength(value); }); } else { DrawContextMenu(component); } } -} \ No newline at end of file + + template<> + static void DrawComponent(SHRenderable* component) + { + if (!component) + return; + const auto componentType = rttr::type::get(*component); + SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); + ImGui::SameLine(); + if (ImGui::CollapsingHeader(componentType.get_name().data())) + { + DrawContextMenu(component); + Handle const& mesh = component->GetMesh(); + + SHEditorWidgets::DragDropReadOnlyField("Mesh", std::to_string(SHResourceManager::GetAssetID(mesh).value_or(0)).data(), [component]() + { + Handle const& mesh = component->GetMesh(); + return SHResourceManager::GetAssetID(mesh).value_or(0); + }, + [component](AssetID const& id) + { + component->SetMesh(SHResourceManager::LoadOrGet(id)); + }, SHDragDrop::DRAG_RESOURCE); + } + else + { + DrawContextMenu(component); + } + } +} diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index ebb241ef..c4a86785 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -1,6 +1,6 @@ #include "SHpch.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "SHEditorInspector.h" #include "ECS_Base/SHECSMacros.h" @@ -10,17 +10,14 @@ #include "Editor/SHImGuiHelpers.hpp" #include "Editor/SHEditorWidgets.hpp" -#include "SHEditorComponentView.hpp" -#include "ECS_Base/UnitTesting/SHTestComponents.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Scripting/SHScriptEngine.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "ECS_Base/Managers/SHSystemManager.h" -#include "AudioSystem/SHAudioSystem.h" #include "Physics/Components/SHRigidBodyComponent.h" #include "Physics/Components/SHColliderComponent.h" #include "Camera/SHCameraComponent.h" +#include "SHEditorComponentView.h" namespace SHADE { @@ -30,8 +27,17 @@ namespace SHADE bool selected = false; if(!SHComponentManager::HasComponent(eid)) { - if(selected = ImGui::Selectable(std::format("Add {}", rttr::type::get().get_name().data()).data()); selected) + const char* componentName = rttr::type::get().get_name().data(); + if(selected = ImGui::Selectable(std::format("Add {}", componentName).data()); selected) SHComponentManager::AddComponent(eid); + if(ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Adds", componentName); ImGui::SameLine(); + ImGui::TextColored(ImGuiColors::green, "%s", componentName); ImGui::SameLine(); + ImGui::Text("to this entity", componentName); + ImGui::EndTooltip(); + } } return selected; } @@ -42,13 +48,26 @@ namespace SHADE bool selected = false; if (!SHComponentManager::HasComponent(eid)) { - if(selected = ImGui::Selectable(std::format("Add {}", rttr::type::get().get_name().data()).data()); selected) + const char* componentName = rttr::type::get().get_name().data(); + + if(selected = ImGui::Selectable(std::format("Add {}", componentName).data()); selected) { if(SHComponentManager::GetComponent_s(eid) == nullptr) SHComponentManager::AddComponent(eid); SHComponentManager::AddComponent(eid); } + if(ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Adds", componentName); ImGui::SameLine(); + ImGui::TextColored(ImGuiColors::green, "%s", componentName); ImGui::SameLine(); + ImGui::Text("to this entity", componentName); + ImGui::Text("Adds"); ImGui::SameLine(); + ImGui::TextColored(ImGuiColors::red, "%s", rttr::type::get().get_name().data()); ImGui::SameLine(); + ImGui::Text("if the entity does not already have it"); + ImGui::EndTooltip(); + } } return selected; } @@ -117,6 +136,7 @@ namespace SHADE { DrawAddComponentButton(eid); DrawAddComponentButton(eid); + DrawAddComponentButton(eid); // Components that require Transforms diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index c33f4fb6..50b878a8 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -3,7 +3,7 @@ //#==============================================================# //|| SHADE Includes || //#==============================================================# -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "SHEditorMenuBar.h" #include "Editor/IconsMaterialDesign.h" #include "Editor/Command/SHCommandManager.h" diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp index f5170999..d6ef8d19 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp @@ -5,13 +5,16 @@ #include "ImGuizmo.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/IconsMaterialDesign.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" #include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" #include +#include "Camera/SHCameraSystem.h" +#include "FRC/SHFramerateController.h" + constexpr std::string_view windowName = "\xef\x80\x95 Viewport"; namespace SHADE @@ -30,8 +33,15 @@ namespace SHADE void SHEditorViewport::Update() { SHEditorWindow::Update(); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f,0.0f)); - if(Begin()) + if (shouldUpdateCamera) + { + auto camSystem = SHSystemManager::GetSystem(); + camSystem->UpdateEditorCamera(SHFrameRateController::GetRawDeltaTime()); + shouldUpdateCamera = false; + } + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + + if (Begin()) { ImGuizmo::SetDrawlist(); DrawMenuBar(); @@ -39,21 +49,38 @@ namespace SHADE auto const& descriptorSet = gfxSystem->GetPostOffscreenRenderSystem()->GetDescriptorSetGroup()->GetVkHandle()[0]; auto mousePos = ImGui::GetMousePos(); beginCursorPos = ImGui::GetCursorScreenPos(); - viewportMousePos = {mousePos.x - beginCursorPos.x, mousePos.y - beginCursorPos.y}; - gfxSystem->GetMousePickSystem ()->SetViewportMousePos (viewportMousePos); + viewportMousePos = { mousePos.x - beginCursorPos.x, mousePos.y - beginCursorPos.y }; + gfxSystem->GetMousePickSystem()->SetViewportMousePos(viewportMousePos); - ImGui::Image((ImTextureID)descriptorSet, {beginContentRegionAvailable.x, beginContentRegionAvailable.y}); + ImGui::Image((ImTextureID)descriptorSet, { beginContentRegionAvailable.x, beginContentRegionAvailable.y }); - if(ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right)) + 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(); + + shouldUpdateCamera = true; + } + if (ImGui::IsWindowFocused() && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) + { + if (ImGui::IsKeyReleased(ImGuiKey_Q)) + { + transformGizmo.operation = SHTransformGizmo::Operation::TRANSLATE; + } + if (ImGui::IsKeyReleased(ImGuiKey_W)) + { + transformGizmo.operation = SHTransformGizmo::Operation::ROTATE; + } + if (ImGui::IsKeyReleased(ImGuiKey_E)) + { + transformGizmo.operation = SHTransformGizmo::Operation::SCALE; + } } } - ImGuizmo::SetRect(beginCursorPos.x , beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); + ImGuizmo::SetRect(beginCursorPos.x, beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); transformGizmo.Draw(); ImGui::End(); ImGui::PopStyleVar(); @@ -72,11 +99,12 @@ namespace SHADE //auto pos = ImGui::GetCursorPos(); //windowCursorPos = {} - if(beginContentRegionAvailable.x == 0 || beginContentRegionAvailable.y == 0) + if (beginContentRegionAvailable.x == 0 || beginContentRegionAvailable.y == 0) { - beginContentRegionAvailable = windowSize; + beginContentRegionAvailable = windowSize; } gfxSystem->PrepareResize(static_cast(beginContentRegionAvailable.x), static_cast(beginContentRegionAvailable.y)); + shouldUpdateCamera = true; } void SHEditorViewport::OnPosChange() @@ -86,44 +114,63 @@ namespace SHADE void SHEditorViewport::DrawMenuBar() noexcept { - if(ImGui::BeginMenuBar()) + if (ImGui::BeginMenuBar()) { + ImGui::BeginDisabled(ImGui::IsWindowFocused() && ImGui::IsMouseDown(ImGuiMouseButton_Right)); bool const isTranslate = transformGizmo.operation == SHTransformGizmo::Operation::TRANSLATE; ImGui::BeginDisabled(isTranslate); - if(isTranslate) + if (isTranslate) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::Button(ICON_MD_OPEN_WITH)) + if (ImGui::Button(ICON_MD_OPEN_WITH)) { transformGizmo.operation = SHTransformGizmo::Operation::TRANSLATE; } ImGui::EndDisabled(); - if(isTranslate) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::BeginTooltip(); + ImGui::Text("Translate [Q]"); + ImGui::EndTooltip(); + } + if (isTranslate) ImGui::PopStyleColor(); bool const isRotate = transformGizmo.operation == SHTransformGizmo::Operation::ROTATE; ImGui::BeginDisabled(isRotate); - if(isRotate) + if (isRotate) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::Button(ICON_MD_AUTORENEW)) + if (ImGui::Button(ICON_MD_AUTORENEW)) { transformGizmo.operation = SHTransformGizmo::Operation::ROTATE; } ImGui::EndDisabled(); - if(isRotate) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::BeginTooltip(); + ImGui::Text("Rotate [W]"); + ImGui::EndTooltip(); + } + if (isRotate) ImGui::PopStyleColor(); bool const isScale = transformGizmo.operation == SHTransformGizmo::Operation::SCALE; ImGui::BeginDisabled(isScale); - if(isScale) + if (isScale) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::Button(ICON_MD_EXPAND)) + if (ImGui::Button(ICON_MD_EXPAND)) { transformGizmo.operation = SHTransformGizmo::Operation::SCALE; } ImGui::EndDisabled(); - if(isScale) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::BeginTooltip(); + ImGui::Text("Scale [E]"); + ImGui::EndTooltip(); + } + if (isScale) ImGui::PopStyleColor(); - + ImGui::EndDisabled(); ImGui::EndMenuBar(); } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h index 80b13285..0fae4317 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h @@ -14,7 +14,7 @@ namespace SHADE { - class SHEditorViewport final : public SHEditorWindow + class SHEditorViewport final : public SHEditorWindow { public: SHEditorViewport(); @@ -28,5 +28,6 @@ namespace SHADE private: void DrawMenuBar() noexcept; SHVec2 beginCursorPos; + bool shouldUpdateCamera = false; };//class SHEditorViewport }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp index 3c984051..e3bbc809 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp @@ -3,7 +3,7 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "Editor/SHImGuiHelpers.hpp" #include #include @@ -63,6 +63,9 @@ namespace SHADE if (selectedEntityTransformComponent == nullptr) return; + if(!selectedEntityTransformComponent->isActive) + return; + SHMatrix mat = selectedEntityTransformComponent->GetTRS(); useSnap = ImGui::IsKeyDown(ImGuiKey_LeftCtrl); if(useSnap) diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 06fadfee..cf5056a5 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -21,7 +21,7 @@ #include "Graphics/MiddleEnd/Interface/SHViewport.h" #include "Graphics/MiddleEnd/Interface/SHRenderer.h" -#include "SHEditor.hpp" +#include "SHEditor.h" #include "SHEditorWidgets.hpp" #include "Math/Transform/SHTransformSystem.h" @@ -175,7 +175,7 @@ namespace SHADE ImFontConfig icons_config{}; icons_config.MergeMode = true; icons_config.GlyphOffset.y = 5.f; 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 }; + constexpr ImWchar icon_ranges_md[] = { ICON_MIN_MD, ICON_MAX_16_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(); } @@ -293,6 +293,7 @@ namespace SHADE //#==============================================================# void SHEditor::InitBackend() { +#ifdef SHEDITOR if(ImGui_ImplSDL2_InitForVulkan(sdlWindow) == false) { SHLOG_CRITICAL("Editor backend initialisation; Failed to perform SDL initialisation for Vulkan") @@ -339,6 +340,7 @@ namespace SHADE renderGraph->GetNode("ImGui Node")->GetSubpass("ImGui Draw")->AddExteriorDrawCalls([](Handle& cmd) { ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->GetVkCommandBuffer()); }); +#endif } void SHEditor::PollPicking() diff --git a/SHADE_Engine/src/Editor/SHEditor.hpp b/SHADE_Engine/src/Editor/SHEditor.h similarity index 100% rename from SHADE_Engine/src/Editor/SHEditor.hpp rename to SHADE_Engine/src/Editor/SHEditor.h diff --git a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp index 46538827..053348d7 100644 --- a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp +++ b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp @@ -22,6 +22,8 @@ #include #include +#include "DragDrop/SHDragDrop.hpp" + namespace SHADE { class SH_API SHEditorWidgets @@ -41,7 +43,7 @@ namespace SHADE ImGui::BeginGroup(); auto itemSpacing = ImGui::GetStyle().ItemSpacing; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.2f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); auto frameHeight = ImGui::GetFrameHeight(); @@ -86,7 +88,7 @@ namespace SHADE auto itemSpacing = ImGui::GetStyle().ItemSpacing; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.2f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); auto frameHeight = ImGui::GetFrameHeight(); @@ -127,7 +129,7 @@ namespace SHADE ImGui::GetWindowDrawList()->AddRect( frameRect.Min, frameRect.Max, - ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Button)), + ImColor(ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)), halfFrame.x); ImGui::PopClipRect(); @@ -174,15 +176,19 @@ namespace SHADE ImGui::SetColumnWidth(-1, 80.0f); ImGui::Text(label.c_str()); if (isHovered) - *isHovered = ImGui::IsItemHovered(); + *isHovered |= ImGui::IsItemHovered(); ImGui::NextColumn(); for (std::size_t i = 0; i < N; ++i) { ImGui::PushID(static_cast(i)); - ImGui::TextUnformatted(componentLabels[i].c_str(), ImGui::FindRenderedTextEnd(componentLabels[i].c_str())); ImGui::SameLine(); + ImGui::TextUnformatted(componentLabels[i].c_str(), ImGui::FindRenderedTextEnd(componentLabels[i].c_str())); + if (isHovered) + *isHovered |= ImGui::IsItemHovered(); + ImGui::SameLine(); ImGui::SetNextItemWidth(80.0f); valueChanged |= ImGui::DragFloat("##v", values[i], speed, valueMin, valueMax, displayFormat, flags); - + if (isHovered) + *isHovered |= ImGui::IsItemHovered(); const ImVec2 min = ImGui::GetItemRectMin(); const ImVec2 max = ImGui::GetItemRectMax(); const float spacing = g.Style.FrameRounding; @@ -203,23 +209,31 @@ namespace SHADE } static bool DragVec2(const std::string& label, std::vectorconst& componentLabels, std::function get, - std::function set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f, + std::function set, bool const& isAnAngleInRad = false, std::string_view const& tooltip = {}, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, ImGuiSliderFlags flags = 0) { SHVec2 values = get(); + if(isAnAngleInRad) + { + values = {SHMath::RadiansToDegrees(values.x), SHMath::RadiansToDegrees(values.y)}; + } bool const changed = DragN(label, componentLabels, { &values.x, &values.y }, speed, displayFormat, valueMin, valueMax, flags); static bool startRecording = false; if (changed) { + if(isAnAngleInRad) + { + values = {SHMath::DegreesToRadians(values.x), SHMath::DegreesToRadians(values.y)}; + } SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), values, set)), startRecording); if (!startRecording) startRecording = true; } if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) startRecording = false; - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); @@ -230,17 +244,24 @@ namespace SHADE } static bool DragVec3(const std::string& label, std::vectorconst& componentLabels, std::function get, - std::function set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f, + std::function set, bool const& isAnAngleInRad = false, std::string_view const& tooltip = {}, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, ImGuiSliderFlags flags = 0) { SHVec3 values = get(); - bool const changed = DragN(label, componentLabels, { &values.x, &values.y, &values.z }, speed, displayFormat, valueMin, valueMax, flags); - - + if(isAnAngleInRad) + { + values = {SHMath::RadiansToDegrees(values.x), SHMath::RadiansToDegrees(values.y), SHMath::RadiansToDegrees(values.z)}; + } + bool isHovered = false; + bool const changed = DragN(label, componentLabels, { &values.x, &values.y, &values.z }, speed, displayFormat, valueMin, valueMax, flags, &isHovered); static bool startRecording = false; if (changed) { SHVec3 old = get(); + if(isAnAngleInRad) + { + values = {SHMath::DegreesToRadians(values.x), SHMath::DegreesToRadians(values.y), SHMath::DegreesToRadians(values.z)}; + } SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(old, values, set)), startRecording); if (!startRecording) startRecording = true; @@ -249,9 +270,9 @@ namespace SHADE { startRecording = false; } - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (isHovered) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); @@ -262,14 +283,22 @@ namespace SHADE } static bool DragVec4(const std::string& label, std::vectorconst& componentLabels, std::function get, - std::function set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f, + std::function set, bool const& isAnAngleInRad = false, std::string_view const& tooltip = {}, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, ImGuiSliderFlags flags = 0) { SHVec4 values = get(); + if(isAnAngleInRad) + { + values = {SHMath::RadiansToDegrees(values.x), SHMath::RadiansToDegrees(values.y), SHMath::RadiansToDegrees(values.z), SHMath::RadiansToDegrees(values.w)}; + } bool const changed = DragN(label, componentLabels, { &values.x, &values.y, &values.z, &values.w }, speed, displayFormat, valueMin, valueMax, flags); static bool startRecording = false; if (changed) { + if(isAnAngleInRad) + { + values = {SHMath::DegreesToRadians(values.x), SHMath::DegreesToRadians(values.y), SHMath::DegreesToRadians(values.z), SHMath::DegreesToRadians(values.w)}; + } SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), values, set)), startRecording); if (!startRecording) startRecording = true; @@ -278,9 +307,9 @@ namespace SHADE { startRecording = false; } - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); @@ -297,7 +326,7 @@ namespace SHADE static void TextLabel(std::string_view const& text, bool sameLine = true) { const ImVec2 textSize = ImGui::CalcTextSize(text.data(), NULL, true); - if(textSize.x > 0.0f) + if (textSize.x > 0.0f) { ImGui::Text(text.data()); ImGui::SameLine(); @@ -310,32 +339,32 @@ namespace SHADE ImGui::BeginGroup(); ImGui::PushID(label.data()); TextLabel(label); - if (ImGui::Checkbox("##", &value)) + bool const changed = ImGui::Checkbox("##", &value); + if (changed) { SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), value, set)), false); - return true; } ImGui::PopID(); ImGui::EndGroup(); - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); ImGui::EndTooltip(); } } - return false; + return changed; } template - static bool RadioButton(std::vector const& label, std::vector const& listTypes, std::function get, std::function set ,std::string_view const& tooltip = {}) + static bool RadioButton(std::vector const& label, std::vector const& listTypes, std::function get, std::function set, std::string_view const& tooltip = {}) { T type = get(); ImGui::BeginGroup(); ImGui::PushID(label.data()); - TextLabel(label); + //TextLabel(label); for (size_t i = 0; i < listTypes.size(); i++) { if (ImGui::RadioButton(label[i].c_str(), type == listTypes[i])) @@ -366,12 +395,11 @@ namespace SHADE ImGui::BeginGroup(); ImGui::PushID(label.data()); TextLabel(label); - if (ImGui::InputText("##", &text, flag, callback, userData)) + bool const changed = ImGui::InputText("##", &text, flag, callback, userData); + if (changed) { if (ImGui::IsItemDeactivatedAfterEdit()) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), text, set)), false); - - return true; } ImGui::PopID(); ImGui::EndGroup(); @@ -384,7 +412,37 @@ namespace SHADE ImGui::EndTooltip(); } } - return false; + return changed; + } + + template + static bool DragDropReadOnlyField(std::string const& label, std::string_view const& fieldVTextValue, std::function const& get, std::function const& set, SHDragDrop::DragDropTag const& dragDropTag, std::string_view const& tooltip = {}) + { + std::string text = fieldVTextValue.data(); + ImGui::BeginGroup(); + ImGui::PushID(label.data()); + TextLabel(label); + bool const changed = ImGui::InputText("##", &text, ImGuiInputTextFlags_ReadOnly, nullptr, nullptr); + if(SHDragDrop::BeginTarget()) + { + if(T* payload = SHDragDrop::AcceptPayload(dragDropTag)) + { + SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), *payload, set)), false); + SHDragDrop::EndTarget(); + } + } + ImGui::PopID(); + ImGui::EndGroup(); + if (!tooltip.empty()) + { + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text(tooltip.data()); + ImGui::EndTooltip(); + } + } + return changed; } template @@ -442,9 +500,9 @@ namespace SHADE } ImGui::PopID(); ImGui::EndGroup(); - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 4983bd48..7391da2c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -17,7 +17,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Windowing/Surface/SHVkSurface.h" #include "Graphics/Swapchain/SHVkSwapchain.h" #include "Camera/SHCameraSystem.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "ECS_Base/Managers/SHSystemManager.h" //#include "SHRenderer.h" #include "Graphics/Windowing/SHWindow.h" diff --git a/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp b/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp index 44a1cb0e..7995e394 100644 --- a/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp +++ b/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp @@ -54,13 +54,14 @@ namespace SHADE if (wndData.isFullscreen) { - dwExStyle = WS_EX_APPWINDOW | WS_EX_ACCEPTFILES; + dwExStyle = WS_EX_APPWINDOW; dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + dwExStyle |= WS_EX_ACCEPTFILES; } else { - dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES; - + dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwExStyle |= WS_EX_ACCEPTFILES; if (wndData.frameEnabled) { dwStyle = WNDSTYLE::SHWS_WINDOWED; @@ -87,7 +88,7 @@ namespace SHADE } } - //DPI_AWARENESS_CONTEXT prevDPIContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + DPI_AWARENESS_CONTEXT prevDPIContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); RECT windowRect; windowRect.left = wndData.x; //or CW_USEDEFAULT ? @@ -97,13 +98,16 @@ namespace SHADE AdjustWindowRectEx(&windowRect, dwStyle, false, dwExStyle); //Create window - wndHWND = CreateWindowEx(0, (LPWSTR) wndData.name.c_str(), (LPWSTR)wndData.title.c_str(), dwStyle, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, parent, NULL, hInstance, NULL); - + wndHWND = CreateWindowEx(dwExStyle, (LPWSTR) wndData.name.c_str(), (LPWSTR)wndData.title.c_str(), dwStyle, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, parent, NULL, hInstance, NULL); + if (!wndHWND) { //DWORD err = GetLastError(); return false; } + BOOL help = ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD); + help &= ChangeWindowMessageFilter (WM_COPYDATA, MSGFLT_ADD); + help &= ChangeWindowMessageFilter (0x0049, MSGFLT_ADD); if (wndData.isVisible) { @@ -318,13 +322,13 @@ namespace SHADE case WM_CREATE: OnCreate(hwnd, reinterpret_cast(wparam)); break; + case WM_QUIT: case WM_CLOSE: - case WM_DESTROY: OnDestroy(); return 0; case WM_DROPFILES: - //OnFileDrop(reinterpret_cast(wparam)); + OnFileDrop(reinterpret_cast(wparam)); break; case WM_ENTERSIZEMOVE: case WM_EXITSIZEMOVE: @@ -386,12 +390,25 @@ namespace SHADE void SHWindow::OnDestroy() { OnClose(); + DragAcceptFiles(wndHWND, false); this->Destroy(); } - //void SHWindow::OnFileDrop(HDROP drop) - //{ - //} + void SHWindow::OnFileDrop(HDROP drop) + { + + int const numFiles = static_cast(DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0)); + for(int i = 0; i < numFiles; ++i) + { + //char fileNameBuffer[MAX_PATH]; + std::wstring fileNameBuffer; + fileNameBuffer.reserve(MAX_PATH); + DragQueryFile(drop, static_cast(i), fileNameBuffer.data(), MAX_PATH); + std::string name(fileNameBuffer.begin(), fileNameBuffer.end()); + SHLOG_INFO("Dropped: {}", name) + } + DragFinish(drop); + } void SHWindow::OnSize([[maybe_unused]] UINT msg,[[maybe_unused]] UINT type, SIZE size) { diff --git a/SHADE_Engine/src/Graphics/Windowing/SHWindow.h b/SHADE_Engine/src/Graphics/Windowing/SHWindow.h index 0a180285..8595ce4b 100644 --- a/SHADE_Engine/src/Graphics/Windowing/SHWindow.h +++ b/SHADE_Engine/src/Graphics/Windowing/SHWindow.h @@ -2,6 +2,7 @@ #define SH_WINDOW_H #include +#include #include #include #include "SHWindowMap.h" @@ -10,7 +11,7 @@ namespace SHADE { constexpr uint16_t MAX_BUFFER = 1024; - + enum WNDSTYLE : DWORD { SHWS_WINDOWED = WS_OVERLAPPEDWINDOW, @@ -47,7 +48,7 @@ namespace SHADE //bool canFullscreen = true; - unsigned bgColor = WHITE_BRUSH; + unsigned bgColor = DKGRAY_BRUSH; //bool transparent = false; @@ -168,7 +169,7 @@ namespace SHADE void OnCreate(HWND hwnd, LPCREATESTRUCT create_struct); void OnClose(); void OnDestroy(); - //void OnFileDrop(HDROP drop); + void OnFileDrop(HDROP drop); void OnSize(UINT msg, UINT type, SIZE size); void OnPosChange(LPWINDOWPOS pos); void OnPaint(HDC hdc, LPPAINTSTRUCT paint); diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp index e56cbc8d..43742855 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp @@ -14,6 +14,7 @@ #include "SHTransformComponent.h" // Project Headers #include "Math/SHMathHelpers.h" +#include "Reflection/SHReflectionMetadata.h" namespace SHADE { @@ -184,7 +185,7 @@ RTTR_REGISTRATION using namespace rttr; registration::class_("Transform Component") - .property("Translate" , &SHTransformComponent::GetLocalPosition , &SHTransformComponent::SetLocalPosition ) - .property("Rotate" , &SHTransformComponent::GetLocalRotation , select_overload(&SHTransformComponent::SetLocalRotation) ) - .property("Scale" , &SHTransformComponent::GetLocalScale , &SHTransformComponent::SetLocalScale ); + .property("Translate" ,&SHTransformComponent::GetLocalPosition ,&SHTransformComponent::SetLocalPosition ) (metadata(META::tooltip, "Translate")) + .property("Rotate" ,&SHTransformComponent::GetLocalRotation ,select_overload(&SHTransformComponent::SetLocalRotation) ) (metadata(META::tooltip, "Rotate"), metadata(META::angleInRad, true)) + .property("Scale" ,&SHTransformComponent::GetLocalScale ,&SHTransformComponent::SetLocalScale ) (metadata(META::tooltip, "Scale")); } \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index a2ab6880..156a47cc 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -263,6 +263,9 @@ namespace SHADE auto* node = EVENT_DATA->data->node; auto* tf = SHComponentManager::GetComponent_s(node->GetEntityID()); + if(tf == nullptr) + return EVENT_DATA->handle; + // Recompute local transform and store localToWorld Matrix SHMatrix localToWorld = SHMatrix::Identity; SHMatrix worldToLocal = SHMatrix::Identity; diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp index 44142aaf..03241dd4 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp @@ -17,7 +17,7 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "Math/SHMathHelpers.h" #include "Scene/SHSceneManager.h" #include "Math/Transform/SHTransformComponent.h" diff --git a/SHADE_Engine/src/Reflection/SHReflectionMetadata.h b/SHADE_Engine/src/Reflection/SHReflectionMetadata.h index 0cc6d8a5..b4dc009c 100644 --- a/SHADE_Engine/src/Reflection/SHReflectionMetadata.h +++ b/SHADE_Engine/src/Reflection/SHReflectionMetadata.h @@ -7,5 +7,6 @@ namespace SHADE constexpr const char* min = "MIN"; constexpr const char* max = "MAX"; constexpr const char* tooltip = "tooltip"; + constexpr const char* angleInRad = "angleInRad"; } } diff --git a/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.cpp b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.cpp new file mode 100644 index 00000000..8ab098b8 --- /dev/null +++ b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.cpp @@ -0,0 +1,56 @@ +#include "SHpch.h" +#include "SHPrefabManager.h" + +namespace SHADE +{ + SHPrefabManager::PrefabMap SHPrefabManager::prefabMap{}; + + void SHPrefabManager::AddPrefab(AssetID const& prefabAssetID) noexcept + { + prefabMap.insert({ prefabAssetID, {} }); + } + + void SHPrefabManager::RemovePrefab(AssetID const& prefabAssetID) noexcept + { + prefabMap.erase(prefabAssetID); + } + + void SHPrefabManager::ClearPrefab(AssetID const& prefabAssetID) noexcept + { + if (prefabMap.contains(prefabAssetID)) + { + prefabMap[prefabAssetID].clear(); + } + } + + void SHPrefabManager::UpdateAllPrefabEntities(AssetID const& prefabAssetID) noexcept + { + //Loop through all entities and deserialize new data + } + + void SHPrefabManager::AddEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept + { + if (prefabMap.contains(prefabAssetID)) + { + prefabMap[prefabAssetID].insert(eid); + } + } + + void SHPrefabManager::RemoveEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept + { + if (prefabMap.contains(prefabAssetID)) + { + prefabMap[prefabAssetID].erase(eid); + } + } + + void SHPrefabManager::Clear() noexcept + { + prefabMap.clear(); + } + + bool SHPrefabManager::Empty() noexcept + { + return prefabMap.empty(); + } +} diff --git a/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.h b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.h new file mode 100644 index 00000000..37c317ed --- /dev/null +++ b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Assets/SHAssetMacros.h" +#include "ECS_Base/SHECSMacros.h" +#include +#include + + +namespace SHADE +{ + class SHPrefabManager + { + public: + using PrefabMap = std::unordered_map>; + + static void AddPrefab(AssetID const& prefabAssetID) noexcept; + static void RemovePrefab(AssetID const& prefabAssetID) noexcept; + static void ClearPrefab(AssetID const& prefabAssetID) noexcept; + static void UpdateAllPrefabEntities(AssetID const& prefabAssetID) noexcept; + static void AddEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept; + static void RemoveEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept; + static void Clear() noexcept; + static bool Empty() noexcept; + + private: + static PrefabMap prefabMap; + }; +} diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index bcd378c5..a6f02249 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -239,6 +239,7 @@ namespace YAML YAML::convert::decode(colliderNode, rhs.GetCollider(numColliders++)); } } + return true; } }; diff --git a/SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp new file mode 100644 index 00000000..bd13801a --- /dev/null +++ b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp @@ -0,0 +1,71 @@ +#include "SHpch.h" +#include "SHWinDialog.h" +#include +#include + +namespace SHADE +{ + void SHWinDialog::DisplayMessageBox(MessageBoxType const& messageBoxType, std::string const& title, std::string const& text) + { + if(messageBoxType == MessageBoxType::MB_MAX) + return; + + UINT flags = MB_APPLMODAL | MB_SETFOREGROUND | MB_OK; + flags |= static_cast(messageBoxType); + + const std::wstring wTitle(title.begin(), title.end()); + const std::wstring wText(text.begin(), text.end()); + + MessageBox(GetDesktopWindow(), wText.data(), wTitle.data(), flags); + } + + std::vector SHWinDialog::DisplayOpenDialog(OpenSaveConfig const& openSaveConfig) + { + const HWND hwnd = GetDesktopWindow(); + HRESULT hResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if(SUCCEEDED(hResult)) + { + IFileOpenDialog* pFileOpen; + + //Create Dialog object + hResult = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_IFileOpenDialog, reinterpret_cast(pFileOpen)); + + if(SUCCEEDED(hResult)) + { + //Show the open dialog box + hResult = pFileOpen->Show(hwnd); + + //Get file name from the dialoh box + if(SUCCEEDED(hResult)) + { + if(openSaveConfig.openMultiple) + { + IShellItemArray* pItemArray; + hResult = pFileOpen->GetResults(&pItemArray); + if(SUCCEEDED(hResult)) + { + + } + } + else + { + IShellItem* pItem; + hResult = pFileOpen->GetResult(&pItem); + if(SUCCEEDED(hResult)) + { + PWSTR pszFilePath; + hResult = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); + } + } + + } + } + } + return {}; + } + + std::vector SHWinDialog::DisplaySaveAsDialog(OpenSaveConfig const& openSaveConfig) + { + return{}; + } +} diff --git a/SHADE_Engine/src/Tools/Dialog/SHWinDialog.h b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.h new file mode 100644 index 00000000..02fe07b9 --- /dev/null +++ b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +namespace SHADE +{ + //https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox + enum class MessageBoxType + { + MB_ERROR = 0x00000010L, + MB_QUESTION = 0x00000020L, + MB_WARNING = 0x00000030L, + MB_INFO = 0x00000040L, + MB_MAX = 0 + }; + + struct OpenSaveConfig + { + using Extension = std::string; + using Extensions = std::vector; + using FileTypeDesc = std::pair; + using FilterList = std::vector; + + std::string title = "Open"; + bool openFolders = false; + bool openMultiple = false; + FilterList filterList{}; + }; + + struct SHWinDialog + { + static void DisplayMessageBox(MessageBoxType const& messageBoxType, std::string const& title, std::string const& text); + static std::vector DisplayOpenDialog(OpenSaveConfig const& openSaveConfig); + static std::vector DisplaySaveAsDialog(OpenSaveConfig const& openSaveConfig); + }; +}