From 896d3c7f972cfe8b202fe6624b6573a704a288ed Mon Sep 17 00:00:00 2001 From: Atdunbg Date: Wed, 21 Jan 2026 23:25:41 +0800 Subject: [PATCH] add file watcher using widows api for windows platform, using handles to import and manager assets, some little tweaks --- Editor/CMakeLists.txt | 4 + Editor/Editor/EditorLayer.cpp | 17 +- Prism/src/Prism/Core/Application.cpp | 4 + Prism/src/Prism/Core/ImGui/ImGui.h | 24 +- Prism/src/Prism/Core/UUID.h | 2 +- Prism/src/Prism/Editor/AssetsManagerPanel.cpp | 471 ++++++++---------- Prism/src/Prism/Editor/AssetsManagerPanel.h | 38 +- .../Prism/Editor/PhysicsSettingsWindow.cpp | 8 +- Prism/src/Prism/Editor/SceneHierachyPanel.cpp | 6 +- .../Windows/WindowsFileSystemWatcher.cpp | 178 +++++++ Prism/src/Prism/Renderer/Renderer2D.cpp | 2 +- Prism/src/Prism/Scene/SceneSerializer.cpp | 3 + Prism/src/Prism/Utilities/AssetsManager.cpp | 407 ++++++++++----- Prism/src/Prism/Utilities/AssetsManager.h | 103 ++-- Prism/src/Prism/Utilities/FileSystemWatcher.h | 44 ++ 15 files changed, 837 insertions(+), 474 deletions(-) create mode 100644 Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp create mode 100644 Prism/src/Prism/Utilities/FileSystemWatcher.h diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index c012ebc..c5950db 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -5,6 +5,10 @@ set(CMAKE_BINARY_DIR ${CMAKE_BINARY_DIR}/bin) file(GLOB ASSETS assets) file(COPY ${ASSETS} DESTINATION ${CMAKE_BINARY_DIR}) +# imgui.ini file +file(GLOB IMGUI_INI imgui.ini) +file(COPY ${IMGUI_INI} DESTINATION ${CMAKE_BINARY_DIR}) + file(GLOB DOTNET_LIBRARY library) file(COPY ${DOTNET_LIBRARY} DESTINATION ${CMAKE_BINARY_DIR}) diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index ce61a6a..f4674ca 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -177,10 +177,13 @@ namespace Prism // OpenScene("assets/scenes/FPSDemo.scene"); NewScene(); + + FileSystemWatcher::StartWatching(); } void EditorLayer::OnDetach() { + FileSystemWatcher::StopWatching(); m_EditorScene->OnShutdown(); } @@ -863,18 +866,18 @@ namespace Prism auto payload = ImGui::AcceptDragDropPayload("scene_entity_assetsP"); if (payload) { - auto data = (DragDropData*)payload->Data; + UUID assetId = *(UUID*)payload->Data; + Asset& asset = AssetsManager::GetAssetFromId(assetId); - if (std::string_view(data->Type) == "PrismScene") + if (asset.Type == AssetType::Scene) { - auto sceneName = data->SourcePath; - OpenScene(sceneName); + OpenScene(asset.FilePath); } - if (std::string_view(data->Type) == "Mesh") + if (asset.Type == AssetType::Mesh) { - auto entity = m_EditorScene->CreateEntity(data->Name); - entity.AddComponent(Ref::Create(data->SourcePath)); + Entity entity = m_EditorScene->CreateEntity(asset.FileName); + entity.AddComponent(AssetsManager::InstantiateAsset(assetId)); } } ImGui::EndDragDropTarget(); diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index 9868535..ca9e11a 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -13,6 +13,7 @@ #include "tinyfiledialogs.h" #include "Prism/Physics/Physics3D.h" #include "Prism/Script/ScriptEngine.h" +#include "Prism/Utilities/AssetsManager.h" namespace Prism { @@ -42,6 +43,9 @@ namespace Prism Renderer::Init(); Renderer::WaitAndRender(); + + AssetTypes::Init(); + AssetsManager::Init(); } Application::~Application() diff --git a/Prism/src/Prism/Core/ImGui/ImGui.h b/Prism/src/Prism/Core/ImGui/ImGui.h index 579a11b..d482ab3 100644 --- a/Prism/src/Prism/Core/ImGui/ImGui.h +++ b/Prism/src/Prism/Core/ImGui/ImGui.h @@ -37,7 +37,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -66,7 +66,7 @@ namespace Prism::UI { static void Property(const char* label, const char* value) { - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -84,7 +84,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -105,7 +105,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -126,7 +126,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -147,7 +147,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -168,7 +168,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -189,7 +189,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -210,7 +210,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -231,7 +231,7 @@ namespace Prism::UI { { bool modified = false; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -272,7 +272,7 @@ namespace Prism::UI { static void BeginCheckboxGroup(const char* label) { - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); } @@ -284,7 +284,7 @@ namespace Prism::UI { if (++s_CheckboxCount > 1) ImGui::SameLine(); - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::SameLine(); s_IDBuffer[0] = '#'; diff --git a/Prism/src/Prism/Core/UUID.h b/Prism/src/Prism/Core/UUID.h index 8d75886..c4dcde0 100644 --- a/Prism/src/Prism/Core/UUID.h +++ b/Prism/src/Prism/Core/UUID.h @@ -10,7 +10,7 @@ namespace Prism { - class UUID + class PRISM_API UUID { public: UUID(); diff --git a/Prism/src/Prism/Editor/AssetsManagerPanel.cpp b/Prism/src/Prism/Editor/AssetsManagerPanel.cpp index 5e0b3e4..f56e94f 100644 --- a/Prism/src/Prism/Editor/AssetsManagerPanel.cpp +++ b/Prism/src/Prism/Editor/AssetsManagerPanel.cpp @@ -7,13 +7,16 @@ #include #include "Prism/Core/Application.h" -#include "Prism/Utilities/DragDropData.h" +#include "Prism/Core/Log.h" namespace Prism { AssetsManagerPanel::AssetsManagerPanel() { - AssetTypes::Init(); + AssetsManager::SetAssetChangeCallback([&]() + { + UpdateCurrentDirectory(m_CurrentDirIndex); + }); m_FolderTex = Texture2D::Create("assets/editor/folder.png"); m_FavoritesTex = Texture2D::Create("assets/editor/favourites.png"); @@ -23,6 +26,7 @@ namespace Prism m_SceneTex = Texture2D::Create("assets/editor/scene.png"); m_AssetIconMap[-1] = Texture2D::Create("assets/editor/file.png"); + m_AssetIconMap[AssetTypes::GetAssetTypeID("hdr")] = Texture2D::Create("assets/editor/file.png"); m_AssetIconMap[AssetTypes::GetAssetTypeID("fbx")] = Texture2D::Create("assets/editor/fbx.png"); m_AssetIconMap[AssetTypes::GetAssetTypeID("obj")] = Texture2D::Create("assets/editor/obj.png"); m_AssetIconMap[AssetTypes::GetAssetTypeID("wav")] = Texture2D::Create("assets/editor/wav.png"); @@ -41,15 +45,13 @@ namespace Prism m_GridView = Texture2D::Create("assets/editor/grid.png"); m_ListView = Texture2D::Create("assets/editor/list.png"); - m_BaseDirPath = "assets"; - m_CurrentDirPath = m_BaseDirPath; - m_PrevDirPath = m_CurrentDirPath; - m_BaseProjectDir = m_AssetManager.GetFileSystemContents(); - m_CurrentDir = m_BaseProjectDir; - m_BasePathLen = static_cast(strlen(m_BaseDirPath.c_str())); - m_DirDataLen = 0; + m_BaseDirIndex = 0; + m_CurrentDirIndex = 0; + m_PrevDirIndex = 0; + m_NextDirIndex = 0; - memset(m_InputBuffer, 0, sizeof(m_InputBuffer)); + m_BaseProjectDir = AssetsManager::GetDirectoryInfo(m_BaseDirIndex); + UpdateCurrentDirectory(m_BaseDirIndex); } void AssetsManagerPanel::OnImGuiRender() @@ -57,65 +59,31 @@ namespace Prism ImGui::Begin("Project", nullptr, ImGuiWindowFlags_MenuBar); { UI::BeginPropertyGrid(); - ImGui::SetColumnOffset(1, 200); + ImGui::SetColumnOffset(1, 250); - // There happens to be recursive tree unfolding issue which doesn't show nested directories/files ImGui::BeginChild("##folders_common"); { - if (ImGui::CollapsingHeader("res://", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) + if (ImGui::CollapsingHeader("Contents", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::TreeNode("Contents")) + for (const int ChildrenIndice : m_BaseProjectDir.ChildrenIndices) { - for (int i = 0; i < m_BaseProjectDir.size(); i++) - { - if (ImGui::TreeNode(m_BaseProjectDir[i].Filename.c_str())) - { - auto dirData = m_AssetManager.GetDirectoryContents(m_BaseProjectDir[i].AbsolutePath); - for (int j = 0; j < dirData.size(); j++) - { - if (!dirData[j].IsFile) - { - if (ImGui::TreeNode(dirData[j].Filename.c_str())) - ImGui::TreePop(); - } - else - { - std::string parentDir = m_AssetManager.GetParentPath(dirData[j].AbsolutePath); - ImGui::Indent(); - ImGui::Selectable(dirData[j].Filename.c_str(), false); - ImGui::Unindent(); - } - } - ImGui::TreePop(); - } - - if (m_IsDragging && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - { - m_MovePath = m_BaseProjectDir[i].AbsolutePath.c_str(); - } - } - ImGui::TreePop(); - } - - if (ImGui::IsMouseDown(1)) - { - //ImGui::OpenPopup("window"); + DrawDirectoryInfo(AssetsManager::GetDirectoryInfo(ChildrenIndice)); } } - - ImGui::EndChild(); } + ImGui::EndChild(); + if (ImGui::BeginDragDropTarget()) { - const auto data = ImGui::AcceptDragDropPayload("selectable", ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if (data) + const auto payload = ImGui::AcceptDragDropPayload("selectable", ImGuiDragDropFlags_AcceptNoDrawDefaultRect); + if (payload) { - const std::filesystem::path file = static_cast(data->Data); - if (m_AssetManager.MoveFile(file, m_MovePath)) + const std::string file = static_cast(payload->Data); + if (AssetsManager::MoveFile(file, m_MovePath)) { - PM_CORE_INFO("Moved File: " + file.string() + " to " + m_MovePath); - m_CurrentDir = m_AssetManager.GetDirectoryContents(m_CurrentDirPath); + PM_CORE_INFO("Moved File: " + file + " to " + m_MovePath); + UpdateCurrentDirectory(m_CurrentDirIndex); } m_IsDragging = false; } @@ -126,11 +94,8 @@ namespace Prism ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetWindowHeight() - 50)); { + ImGui::BeginChild("##directory_breadcrumbs", ImVec2(ImGui::GetColumnWidth() - 100, 30)); RenderBreadCrumbs(); - ImGui::SameLine(); - ImGui::Dummy(ImVec2(ImGui::GetColumnWidth() - 350, 0)); - ImGui::SameLine(); - RenderSearch(); ImGui::EndChild(); ImGui::BeginChild("Scrolling"); @@ -138,20 +103,27 @@ namespace Prism if (!m_DisplayListView) ImGui::Columns(10, nullptr, false); - for (int i = 0; i < m_CurrentDir.size(); i++) + for (const DirectoryInfo& dir : m_CurrentDirChildren) { - if (!m_CurrentDir.empty()) - { - if (!m_DisplayListView) - m_CurrentDir[i].IsFile ? RenderFileGridView(i) : RenderDirectoriesGridView(i); - else - m_CurrentDir[i].IsFile ? RenderFileListView(i) : RenderDirectoriesListView(i); + if (m_DisplayListView) + RenderDirectoriesListView(dir.DirectoryIndex); + else + RenderDirectoriesGridView(dir.DirectoryIndex); - ImGui::NextColumn(); - } + ImGui::NextColumn(); } - if (ImGui::BeginPopupContextWindow("Creating")) + for (Asset& asset : m_CurrentDirAssets) + { + if (m_DisplayListView) + RenderFileListView(asset); + else + RenderFileGridView(asset); + + ImGui::NextColumn(); + } + + if (ImGui::BeginPopupContextWindow()) { if (ImGui::BeginMenu("New")) { @@ -175,41 +147,26 @@ namespace Prism PM_CORE_INFO("Creating Prefab..."); } - if (ImGui::BeginMenu("Shaders")) - { - if (ImGui::MenuItem("Shader")) - { - PM_CORE_INFO("Creating Shader File..."); - } - - if (ImGui::MenuItem("Shader Graph")) - { - PM_CORE_INFO("Creating Shader Graph..."); - } - - ImGui::EndMenu(); - } - ImGui::EndMenu(); } ImGui::EndPopup(); } - - - ImGui::EndChild(); ImGui::EndChild(); } + ImGui::EndChild(); + if (ImGui::BeginDragDropTarget()) { - const auto data = ImGui::AcceptDragDropPayload("selectable", ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if (data) + const auto payload = ImGui::AcceptDragDropPayload("selectable", ImGuiDragDropFlags_AcceptNoDrawDefaultRect); + if (payload) { - const std::string a = static_cast(data->Data); - if (m_AssetManager.MoveFile(a, m_MovePath)) + const std::string data = static_cast(payload->Data); + if (AssetsManager::MoveFile(data, m_MovePath)) { - PM_CORE_INFO("Moved File: " + a + " to " + m_MovePath); + PM_CORE_INFO("Moved File: " + data + " to " + m_MovePath); + UpdateCurrentDirectory(m_CurrentDirIndex); } m_IsDragging = false; } @@ -223,72 +180,92 @@ namespace Prism if (ImGui::MenuItem("Import New Asset", "Ctrl + O")) { const std::string filename = Application::Get().OpenFile(""); - m_AssetManager.ProcessAsset(filename); + // TODO: } if (ImGui::MenuItem("Refresh", "Ctrl + R")) { - auto data = m_AssetManager.GetFileSystemContents(); - for (auto & i : data) - { - PM_CORE_INFO("{0}", i.Filename); - } + m_BaseProjectDir = AssetsManager::GetDirectoryInfo(m_BaseDirIndex); + UpdateCurrentDirectory(m_CurrentDirIndex); } - ImGui::EndMenu(); + } ImGui::EndMenuBar(); } - UI::EndPropertyGrid(); - ImGui::End(); + } + ImGui::End(); + } + + void AssetsManagerPanel::DrawDirectoryInfo(const DirectoryInfo& dir) + { + if (ImGui::TreeNode(dir.DirectoryName.c_str())) + { + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + UpdateCurrentDirectory(dir.DirectoryIndex); + + for (const int ChildrenIndice : dir.ChildrenIndices) + { + DirectoryInfo& child = AssetsManager::GetDirectoryInfo(ChildrenIndice); + DrawDirectoryInfo(child); + } + + ImGui::TreePop(); + } + + if (m_IsDragging && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + { + m_MovePath = dir.FilePath; } } - void AssetsManagerPanel::RenderFileListView(const int dirIndex) + void AssetsManagerPanel::RenderFileListView(Asset& asset) { - const size_t fileID = AssetTypes::GetAssetTypeID(m_CurrentDir[dirIndex].FileType); + const size_t fileID = AssetTypes::GetAssetTypeID(asset.Extension); const RendererID iconRef = m_AssetIconMap[fileID]->GetRendererID(); ImGui::Image((ImTextureID)iconRef, ImVec2(20, 20)); ImGui::SameLine(); - if (ImGui::Selectable((m_CurrentDir[dirIndex].Filename + std::to_string(dirIndex)).c_str(), false, ImGuiSelectableFlags_AllowDoubleClick)) + if (ImGui::Selectable(asset.FilePath.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick)) { - if (ImGui::IsMouseDoubleClicked(0)) - m_AssetManager.HandleAsset(m_CurrentDir[dirIndex].AbsolutePath); + // if (ImGui::IsMouseDoubleClicked(0)) + // m_AssetManager.HandleAsset(m_CurrentDir[dirIndex].AbsolutePath); + // TODO: } - HandleDragDrop(iconRef, dirIndex); + HandleDragDrop(iconRef, asset); } - void AssetsManagerPanel::RenderFileGridView(const int dirIndex) + void AssetsManagerPanel::RenderFileGridView(Asset& asset) { ImGui::BeginGroup(); - const size_t fileID = AssetTypes::GetAssetTypeID(m_CurrentDir[dirIndex].FileType); + const size_t fileID = AssetTypes::GetAssetTypeID(asset.Extension); const RendererID iconRef = m_AssetIconMap[fileID]->GetRendererID(); const float columnWidth = ImGui::GetColumnWidth(); - ImGui::ImageButton(std::string_view("##FileGridView" + std::to_string(dirIndex)).data(), (ImTextureID)iconRef, { columnWidth - 10.0F, columnWidth - 10.0F }); + ImGui::ImageButton(asset.FilePath.c_str(), (ImTextureID)iconRef, { columnWidth - 10.0f, columnWidth - 10.0f }); - HandleDragDrop(iconRef, dirIndex); + HandleDragDrop(iconRef, asset); - const std::string newFileName = m_AssetManager.StripExtras(m_CurrentDir[dirIndex].Filename); - ImGui::TextWrapped(newFileName.c_str()); + const std::string newFileName = AssetsManager::StripExtras(asset.FileName); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + columnWidth - 10.0f); + ImGui::TextWrapped("%s", newFileName.c_str()); + ImGui::PopTextWrapPos(); ImGui::EndGroup(); } - void AssetsManagerPanel::HandleDragDrop(RendererID icon, int dirIndex) + void AssetsManagerPanel::HandleDragDrop(const RendererID icon, const Asset& asset) { // Drag 'n' Drop Implementation For File Moving if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { ImGui::Image((ImTextureID)icon, ImVec2(20, 20)); ImGui::SameLine(); - ImGui::Text(m_CurrentDir[dirIndex].Filename.c_str()); - const int size = static_cast(sizeof(const char*) + strlen(m_CurrentDir[dirIndex].AbsolutePath.c_str())); - ImGui::SetDragDropPayload("selectable", m_CurrentDir[dirIndex].AbsolutePath.c_str(), size); + ImGui::Text("%s", asset.FilePath.c_str()); + ImGui::SetDragDropPayload("selectable", asset.FilePath.c_str(), asset.FilePath.length()); m_IsDragging = true; ImGui::EndDragDropSource(); } @@ -298,31 +275,10 @@ namespace Prism { ImGui::Image((ImTextureID)icon, ImVec2(20, 20)); ImGui::SameLine(); - ImGui::Text(m_CurrentDir[dirIndex].Filename.c_str()); - const AssetType assetType = AssetTypes::GetAssetTypeFromExtension(m_CurrentDir[dirIndex].FileType); - - if (assetType == AssetType::Mesh) - { - const char* sourceType = m_CurrentDir[dirIndex].AbsolutePath.c_str(); - const char* name = m_CurrentDir[dirIndex].Filename.c_str(); - const char* type = "Mesh"; - - const DragDropData d(type, sourceType, name); - ImGui::SetDragDropPayload("scene_entity_assetsP", &d, sizeof(d)); - m_IsDragging = true; - } - - if (assetType == AssetType::Scene) - { - const char* sourceType = m_CurrentDir[dirIndex].AbsolutePath.c_str(); - const char* name = m_CurrentDir[dirIndex].Filename.c_str(); - const char* type = "PrismScene"; - - const DragDropData d(type, sourceType, name); - ImGui::SetDragDropPayload("scene_entity_assetsP", &d, sizeof(d)); - m_IsDragging = true; - } + ImGui::Text("%s", asset.FilePath.c_str()); + ImGui::SetDragDropPayload("scene_entity_assetsP", &asset.ID, sizeof(UUID)); + m_IsDragging = true; ImGui::EndDragDropSource(); } @@ -333,13 +289,14 @@ namespace Prism ImGui::Image((ImTextureID)m_FolderTex->GetRendererID(), ImVec2(20, 20)); ImGui::SameLine(); - if (ImGui::Selectable(m_CurrentDir[dirIndex].Filename.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick)) + const auto& folderData = AssetsManager::GetDirectoryInfo(dirIndex); + + if (ImGui::Selectable(folderData.DirectoryName.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick)) { if (ImGui::IsMouseDoubleClicked(0)) { - m_PrevDirPath = m_CurrentDir[dirIndex].AbsolutePath; - m_CurrentDirPath = m_CurrentDir[dirIndex].AbsolutePath; - m_CurrentDir = m_AssetManager.GetDirectoryContents(m_CurrentDir[dirIndex].AbsolutePath); + m_PrevDirIndex = m_CurrentDirIndex; + UpdateCurrentDirectory(dirIndex); } } @@ -347,9 +304,10 @@ namespace Prism { ImGui::Image((ImTextureID)m_FolderTex->GetRendererID(), ImVec2(20, 20)); ImGui::SameLine(); - ImGui::Text(m_CurrentDir[dirIndex].Filename.c_str()); - const int size = static_cast(sizeof(const char*) + strlen(m_CurrentDir[dirIndex].AbsolutePath.c_str())); - ImGui::SetDragDropPayload("selectable", m_CurrentDir[dirIndex].AbsolutePath.c_str(), size); + + ImGui::Text("%s", folderData.DirectoryName.c_str()); + ImGui::SetDragDropPayload("selectable", &dirIndex, sizeof(int)); + m_IsDragging = true; ImGui::EndDragDropSource(); } @@ -360,148 +318,129 @@ namespace Prism ImGui::BeginGroup(); const float columnWidth = ImGui::GetColumnWidth(); - ImGui::ImageButton(std::string_view("##RenderDirectoriesGridView" + std::to_string(dirIndex)).data(), (ImTextureID)m_FolderTex->GetRendererID(), { columnWidth - 10.0F, columnWidth - 10.0F }); + ImGui::ImageButton(std::string_view("##RenderDirectoriesGridView" + std::to_string(dirIndex)).data(), (ImTextureID)m_FolderTex->GetRendererID(), { columnWidth - 10.0f, columnWidth - 10.0f }); + + const auto& folderData = AssetsManager::GetDirectoryInfo(dirIndex); if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) { - m_PrevDirPath = m_CurrentDir[dirIndex].AbsolutePath; - m_CurrentDirPath = m_CurrentDir[dirIndex].AbsolutePath; - m_CurrentDir = m_AssetManager.GetDirectoryContents(m_CurrentDir[dirIndex].AbsolutePath); - m_IsPathChanged = true; - m_DirDataLen = static_cast(m_CurrentDir.size()); + m_PrevDirIndex = m_CurrentDirIndex; + UpdateCurrentDirectory(dirIndex); } if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) { ImGui::Image((ImTextureID)m_FolderTex->GetRendererID(), ImVec2(20, 20)); ImGui::SameLine(); - ImGui::Text(m_CurrentDir[dirIndex].Filename.c_str()); - const int size = static_cast(sizeof(const char*) + strlen(m_CurrentDir[dirIndex].AbsolutePath.c_str())); - ImGui::SetDragDropPayload("selectable", m_CurrentDir[dirIndex].AbsolutePath.c_str(), size); + + ImGui::Text("%s", folderData.DirectoryName.c_str()); + ImGui::SetDragDropPayload("selectable_directory", &dirIndex, sizeof(int)); + m_IsDragging = true; ImGui::EndDragDropSource(); } - if (!m_IsPathChanged) - { - const auto fname = m_CurrentDir[dirIndex].Filename; - const auto newFname = m_AssetManager.StripExtras(fname); - ImGui::TextWrapped(newFname.c_str()); - } - else - { - for (int i = 0; i < m_DirDataLen; i++) - { - const auto fname = m_CurrentDir[i].Filename; - const auto newFname = m_AssetManager.StripExtras(fname); - ImGui::TextWrapped(newFname.c_str()); - m_IsPathChanged = false; - m_DirDataLen = 0; - } - } - + ImGui::TextWrapped("%s", folderData.DirectoryName.c_str()); ImGui::EndGroup(); - } void AssetsManagerPanel::RenderBreadCrumbs() { - ImGui::BeginChild("##directory_breadcrumbs", ImVec2(ImGui::GetColumnWidth() - 100, 30)); + const RendererID viewTexture = m_DisplayListView ? m_ListView->GetRendererID() : m_GridView->GetRendererID(); + if (ImGui::ImageButton((const char*)&viewTexture, (ImTextureID)viewTexture, ImVec2(20, 18))) { - const RendererID viewTexture = m_DisplayListView ? m_ListView->GetRendererID() : m_GridView->GetRendererID(); - if (ImGui::ImageButton((const char*)&viewTexture, (ImTextureID)viewTexture, ImVec2(20, 18))) + m_DisplayListView = !m_DisplayListView; + } + + ImGui::SameLine(); + const auto searchTexID = m_SearchTex->GetRendererID(); + if (ImGui::ImageButton((const char*)&searchTexID,(ImTextureID)searchTexID, ImVec2(20, 18))) + { + if (m_CurrentDirIndex == m_BaseDirIndex) return; + m_NextDirIndex = m_CurrentDirIndex; + m_PrevDirIndex = AssetsManager::GetDirectoryInfo(m_CurrentDirIndex).ParentIndex; + UpdateCurrentDirectory(m_PrevDirIndex); + } + + ImGui::SameLine(); + + const auto backbtnTexID = m_BackbtnTex->GetRendererID(); + if (ImGui::ImageButton((const char*)&backbtnTexID,(ImTextureID)backbtnTexID, ImVec2(20, 18))) + { + if (m_CurrentDirIndex == m_BaseDirIndex) return; + m_NextDirIndex = m_CurrentDirIndex; + m_PrevDirIndex = AssetsManager::GetDirectoryInfo(m_CurrentDirIndex).ParentIndex; + UpdateCurrentDirectory(m_PrevDirIndex); + } + + ImGui::SameLine(); + const auto fwrdbtnTexID = m_FwrdbtnTex->GetRendererID(); + if (ImGui::ImageButton((const char*)&fwrdbtnTexID,(ImTextureID)fwrdbtnTexID, ImVec2(20, 18))) + { + UpdateCurrentDirectory(m_NextDirIndex); + } + + ImGui::SameLine(); + { + ImGui::PushItemWidth(200); + + if (ImGui::InputTextWithHint("##Search", "Search...", m_InputBuffer, 100)) { - m_DisplayListView = !m_DisplayListView; - } + const SearchResults results = AssetsManager::SearchFiles(m_InputBuffer, m_CurrentDir.FilePath); - ImGui::SameLine(); - - const auto searchTexID = m_SearchTex->GetRendererID(); - - if (ImGui::ImageButton((const char*)&searchTexID,(ImTextureID)searchTexID, ImVec2(20, 18))) - m_ShowSearchBar = !m_ShowSearchBar; - - ImGui::SameLine(); - - if (m_ShowSearchBar) - { - ImGui::SameLine(); - ImGui::PushItemWidth(200); - - if (ImGui::InputTextWithHint("##Search", "Search...", m_InputBuffer, 100, ImGuiInputTextFlags_EnterReturnsTrue)) + if (strlen(m_InputBuffer) == 0) { - m_CurrentDir = m_AssetManager.SearchFiles(m_InputBuffer, m_CurrentDirPath); + UpdateCurrentDirectory(m_CurrentDirIndex); } - - ImGui::PopItemWidth(); - ImGui::SameLine(); - } - - const auto backBtnTexID = m_BackbtnTex->GetRendererID(); - if (ImGui::ImageButton((const char*)&backBtnTexID,(ImTextureID)backBtnTexID, ImVec2(20, 18))) - { - if (strlen(m_CurrentDirPath.c_str()) == m_BasePathLen) return; - m_ForwardPath = m_CurrentDirPath; - m_BackPath = m_AssetManager.GetParentPath(m_CurrentDirPath); - m_CurrentDir = m_AssetManager.GetDirectoryContents(m_BackPath); - m_CurrentDirPath = m_BackPath; - } - - ImGui::SameLine(); - - const auto FwrdBtnTexID = m_FwrdbtnTex->GetRendererID(); - if (ImGui::ImageButton((const char*)&FwrdBtnTexID,(ImTextureID)FwrdBtnTexID, ImVec2(20, 18))) - { - if (m_ForwardPath.empty()) return; - m_CurrentDir = m_AssetManager.GetDirectoryContents(m_ForwardPath); - m_CurrentDirPath = m_ForwardPath; - } - - ImGui::SameLine(); - - const auto data = m_AssetManager.GetDirectories(m_CurrentDirPath); - - for (int i = 0; i < data.size(); i++) - { - if (data[i] != m_BaseDirPath) + else { - ImGui::Image((ImTextureID)m_FolderRightTex->GetRendererID(), ImVec2(22, 23)); + m_CurrentDirChildren = results.Directories; + m_CurrentDirAssets = results.Assets; } - - ImGui::SameLine(); - - /* Multiply the size of the folder name with 7(magic number) to set the size of selectable widget properly */ - const auto size = (float)(strlen(data[i].c_str()) * 7); - - if (ImGui::Selectable(data[i].c_str(), false, 0, ImVec2(size, 22))) - { - /* Increament 1 to the existing index value to fully iterate the array */ - const int index = i + 1; - std::string path; - - /* Use the below loop to build a path from the selected folder from the breadcrumb to navigate to */ - for (int e = 0; e < index; e++) - { - path += data[e] + "/\\"; - } - - m_CurrentDir = m_AssetManager.GetDirectoryContents(path); - m_CurrentDirPath = path; - } - ImGui::SameLine(); } + ImGui::PopItemWidth(); + } + + ImGui::SameLine(); + if (m_UpdateBreadCrumbs) + { + m_BreadCrumbData.clear(); + + int currentDirIndex = m_CurrentDirIndex; + while (currentDirIndex != -1) + { + DirectoryInfo& dirInfo = AssetsManager::GetDirectoryInfo(currentDirIndex); + m_BreadCrumbData.push_back(dirInfo); + currentDirIndex = dirInfo.ParentIndex; + } + + std::reverse(m_BreadCrumbData.begin(), m_BreadCrumbData.end()); + + m_UpdateBreadCrumbs = false; + } + + for (auto & i : m_BreadCrumbData) + { + if (i.DirectoryName != "assets") + ImGui::Text("/"); + ImGui::SameLine(); - ImGui::Dummy(ImVec2(ImGui::GetColumnWidth() - 400, 0)); + const int size = (int)i.DirectoryName.length() * 7; + + if (ImGui::Selectable(i.DirectoryName.c_str(), false, 0, ImVec2((float)size, 22))) + { + UpdateCurrentDirectory(i.DirectoryIndex); + } ImGui::SameLine(); } - } - void AssetsManagerPanel::RenderSearch() - { - // TODO: not implemented + ImGui::SameLine(); + + ImGui::Dummy(ImVec2(ImGui::GetColumnWidth() - 400, 0)); + ImGui::SameLine(); } void AssetsManagerPanel::RenderBottom() @@ -513,4 +452,20 @@ namespace Prism } } + void AssetsManagerPanel::UpdateCurrentDirectory(int dirIndex) + { + if (m_CurrentDirIndex != dirIndex) + m_UpdateBreadCrumbs = true; + + m_CurrentDirChildren.clear(); + m_CurrentDirAssets.clear(); + + m_CurrentDirIndex = dirIndex; + m_CurrentDir = AssetsManager::GetDirectoryInfo(m_CurrentDirIndex); + + for (const int childIndex : m_CurrentDir.ChildrenIndices) + m_CurrentDirChildren.push_back(AssetsManager::GetDirectoryInfo(childIndex)); + + m_CurrentDirAssets = AssetsManager::GetAssetsInDirectory(m_CurrentDirIndex); + } } diff --git a/Prism/src/Prism/Editor/AssetsManagerPanel.h b/Prism/src/Prism/Editor/AssetsManagerPanel.h index c32d037..897322c 100644 --- a/Prism/src/Prism/Editor/AssetsManagerPanel.h +++ b/Prism/src/Prism/Editor/AssetsManagerPanel.h @@ -18,16 +18,20 @@ namespace Prism void OnImGuiRender(); private: - void RenderFileListView(int dirIndex); - void RenderFileGridView(int dirIndex); - void HandleDragDrop(RendererID icon, int dirIndex); + void DrawDirectoryInfo(const DirectoryInfo& dir); + + void RenderFileListView(Asset& asset); + void RenderFileGridView(Asset& asset); + void HandleDragDrop(RendererID icon, const Asset& asset); + void RenderDirectoriesListView(int dirIndex); void RenderDirectoriesGridView(int dirIndex); void RenderBreadCrumbs(); - void RenderSearch(); void RenderBottom(); - ImGuiInputTextCallback SearchCallback(ImGuiInputTextCallbackData* data); + void UpdateCurrentDirectory(int dirIndex); + + // ImGuiInputTextCallback SearchCallback(ImGuiInputTextCallbackData* data); private: Ref m_FolderTex; @@ -46,32 +50,30 @@ namespace Prism Ref m_GridView; Ref m_ListView; - std::string m_CurrentDirPath; - std::string m_BaseDirPath; - std::string m_PrevDirPath; std::string m_MovePath; - std::string m_ForwardPath; - std::string m_BackPath; - - int m_BasePathLen; - int m_DirDataLen; + int m_BaseDirIndex; + int m_CurrentDirIndex; + int m_PrevDirIndex; + int m_NextDirIndex; bool m_IsDragging = false; bool m_DisplayListView = false; bool m_UpdateBreadCrumbs = true; bool m_ShowSearchBar = false; - bool m_IsPathChanged = false; + bool m_DirectoryChanged = false; char m_InputBuffer[1024]{}; - std::vector m_CurrentDir; - std::vector m_BaseProjectDir; + DirectoryInfo m_CurrentDir; + DirectoryInfo m_BaseProjectDir; + std::vector m_CurrentDirChildren; + std::vector m_CurrentDirAssets; + + std::vector m_BreadCrumbData; ImGuiInputTextCallbackData m_Data; std::map> m_AssetIconMap; - //NotificationManager nManager; - AssetsManager m_AssetManager; }; } diff --git a/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp b/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp index aa5af5d..c7c0aa7 100644 --- a/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp +++ b/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp @@ -148,7 +148,7 @@ namespace Prism bool PhysicsSettingsWindow::Property(const char* label, const char** options, const int32_t optionCount, int32_t* selected) { const char* current = options[*selected]; - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -180,7 +180,7 @@ namespace Prism bool PhysicsSettingsWindow::Property(const char* label, float& value, const float min, const float max) { - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -195,7 +195,7 @@ namespace Prism bool PhysicsSettingsWindow::Property(const char* label, uint32_t& value, const uint32_t min, const uint32_t max) { - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); @@ -210,7 +210,7 @@ namespace Prism bool PhysicsSettingsWindow::Property(const char* label, glm::vec3& value, const float min, const float max) { - ImGui::Text(label); + ImGui::Text("%s", label); ImGui::NextColumn(); ImGui::PushItemWidth(-1); diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp index 02d8a39..272984f 100644 --- a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp @@ -54,7 +54,7 @@ namespace Prism ImGui::Columns(2); ImGui::SetColumnWidth(0, columnWidth); - ImGui::Text(label.c_str()); + ImGui::Text("%s", label.c_str()); ImGui::NextColumn(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); @@ -301,8 +301,8 @@ namespace Prism if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { - UUID entityId = entity.GetUUID(); - ImGui::Text(entity.GetComponent().Tag.c_str()); + const UUID entityId = entity.GetUUID(); + ImGui::Text("%s", entity.GetComponent().Tag.c_str()); ImGui::SetDragDropPayload("scene_entity_hierarchy", &entityId, sizeof(UUID)); ImGui::EndDragDropSource(); } diff --git a/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp new file mode 100644 index 0000000..f81a90b --- /dev/null +++ b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp @@ -0,0 +1,178 @@ +// +// Created by Atdunbg on 2026/1/21. +// + +#include + +#include "Prism/Utilities/FileSystemWatcher.h" + +#include +#include + +#include "Prism/Core/Log.h" + +namespace Prism +{ + FileSystemWatcher::FileSystemChangedCallbackFn FileSystemWatcher::s_Callback; + + static bool s_Watching = true; + static HANDLE s_WatcherThread; + + void FileSystemWatcher::SetChangeCallback(const FileSystemChangedCallbackFn& callback) + { + s_Callback = callback; + } + + static std::string wchar_to_string(const wchar_t* input) + { + if (!input) return {}; + + const int bufferSize = WideCharToMultiByte(CP_UTF8, 0, input, -1, nullptr, 0, nullptr, nullptr); + if (bufferSize == 0) return {}; + + std::string result(bufferSize, 0); + WideCharToMultiByte(CP_UTF8, 0, input, -1, &result[0], bufferSize, nullptr, nullptr); + + // 移除末尾的null终止符 + result.pop_back(); + return result; + } + + void FileSystemWatcher::StartWatching() + { + DWORD threadId; + s_WatcherThread = CreateThread(NULL, 0, Watch, NULL, 0, &threadId); + PM_CORE_ASSERT(s_WatcherThread != NULL); + PM_CORE_TRACE("Starting file watching services"); + } + + void FileSystemWatcher::StopWatching() + { + // TODO: this delay is too long + s_Watching = false; + const DWORD result = WaitForSingleObject(s_WatcherThread, 1000); + if (result == WAIT_TIMEOUT) + TerminateThread(s_WatcherThread, 0); + CloseHandle(s_WatcherThread); + PM_CORE_TRACE("closing file watching services"); + } + + unsigned long FileSystemWatcher::Watch(void* param) + { + const LPCSTR filepath = "assets"; + char* buffer = new char[1024]; + OVERLAPPED overlapped = { 0 }; + HANDLE handle = NULL; + DWORD bytesReturned = 0; + + ZeroMemory(buffer, 1024); + + handle = CreateFile( + filepath, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, + NULL + ); + + ZeroMemory(&overlapped, sizeof(overlapped)); + + if (handle == INVALID_HANDLE_VALUE) + PM_CORE_ERROR("Unable to accquire directory handle: {0}", GetLastError()); + + overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (overlapped.hEvent == NULL) + { + PM_CORE_ERROR("CreateEvent failed!"); + return 0; + } + + while (s_Watching) + { + DWORD status = ReadDirectoryChangesW( + handle, + buffer, + 1024, + TRUE, + FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, + &bytesReturned, + &overlapped, + NULL + ); + + if (!status) + PM_CORE_ERROR("{0}", GetLastError()); + + // NOTE(Peter): We can't use 'INFINITE' here since that will prevent the thread from closing when we close the editor + const DWORD waitOperation = WaitForSingleObject(overlapped.hEvent, 2000); + + // If nothing changed, just continue + if (waitOperation != WAIT_OBJECT_0) + continue; + + std::string oldName; + + for (;;) + { + auto& fni = (FILE_NOTIFY_INFORMATION&)*buffer; + std::filesystem::path filePath; + filePath = "assets/" + wchar_to_string(fni.FileName); + + FileSystemChangedEvent e; + e.Filepath = filePath.string(); + e.NewName = filePath.filename().string(); + e.OldName = filePath.filename().string(); + e.IsDirectory = std::filesystem::is_directory(filePath); + + switch (fni.Action) + { + case FILE_ACTION_ADDED: + { + e.Action = FileSystemAction::Added; + s_Callback(e); + break; + } + case FILE_ACTION_REMOVED: + { + e.Action = FileSystemAction::Delete; + s_Callback(e); + break; + } + case FILE_ACTION_MODIFIED: + { + e.Action = FileSystemAction::Modified; + s_Callback(e); + break; + } + case FILE_ACTION_RENAMED_OLD_NAME: + { + oldName = filePath.filename().string(); + break; + } + case FILE_ACTION_RENAMED_NEW_NAME: + { + e.OldName = oldName; + e.Action = FileSystemAction::Rename; + s_Callback(e); + break; + } + } + + if (!fni.NextEntryOffset) + { + ZeroMemory(buffer, 1024); + break; + } + + buffer += fni.NextEntryOffset; + } + } + + return 0; + } + + +} diff --git a/Prism/src/Prism/Renderer/Renderer2D.cpp b/Prism/src/Prism/Renderer/Renderer2D.cpp index 6dafaa6..d2b564f 100644 --- a/Prism/src/Prism/Renderer/Renderer2D.cpp +++ b/Prism/src/Prism/Renderer/Renderer2D.cpp @@ -242,7 +242,7 @@ namespace Prism s_Data.Stats.DrawCalls++; } - dataSize = (uint8_t*)s_Data.CircleVertexBufferPtr - (uint8_t*)s_Data.CircleVertexBufferBase; + dataSize = (uint32_t)((uint8_t*)s_Data.CircleVertexBufferPtr - (uint8_t*)s_Data.CircleVertexBufferBase); if (dataSize) { s_Data.CircleVertexBuffer->SetData(s_Data.CircleVertexBufferBase, dataSize); diff --git a/Prism/src/Prism/Scene/SceneSerializer.cpp b/Prism/src/Prism/Scene/SceneSerializer.cpp index ef1b802..c7f5e5e 100644 --- a/Prism/src/Prism/Scene/SceneSerializer.cpp +++ b/Prism/src/Prism/Scene/SceneSerializer.cpp @@ -557,7 +557,10 @@ namespace Prism YAML::Node data = YAML::Load(strStream.str()); if (!data["Scene"]) + { + PM_CORE_ERROR("cannot open file: {0}", filepath); return false; + } std::string sceneName = data["Scene"].as(); PM_CORE_INFO("Deserializing scene '{0}'", sceneName); diff --git a/Prism/src/Prism/Utilities/AssetsManager.cpp b/Prism/src/Prism/Utilities/AssetsManager.cpp index db3d6cf..2ab706f 100644 --- a/Prism/src/Prism/Utilities/AssetsManager.cpp +++ b/Prism/src/Prism/Utilities/AssetsManager.cpp @@ -5,6 +5,10 @@ #include "AssetsManager.h" #include +#include + +#include "Prism/Core/Log.h" +#include "Prism/Renderer/Mesh.h" namespace Prism { @@ -14,6 +18,7 @@ namespace Prism s_Types["fbx"] = AssetType::Mesh; s_Types["obj"] = AssetType::Mesh; s_Types["png"] = AssetType::Image; + s_Types["hdr"] = AssetType::EnvMap; s_Types["blend"] = AssetType::Mesh; s_Types["wav"] = AssetType::Audio; s_Types["ogg"] = AssetType::Audio; @@ -37,24 +42,167 @@ namespace Prism } std::map AssetTypes::s_Types; + AssetsManager::AssetsChangeEventFn AssetsManager::s_AssetsChangeCallback; + std::unordered_map AssetsManager::s_LoadedAssets; + std::vector AssetsManager::s_Directories; - AssetsManager::AssetsManager() + void AssetsManager::Init() { + FileSystemWatcher::SetChangeCallback(OnFileSystemChanged); + ReloadAssets(); } - std::string AssetsManager::ParseFilename(const std::string& filepath, const char& delim) + void AssetsManager::SetAssetChangeCallback(const AssetsChangeEventFn& callback) + { + s_AssetsChangeCallback = callback; + } + + + DirectoryInfo& AssetsManager::GetDirectoryInfo(const int index) + { + PM_CORE_ASSERT(index >= 0 && index < s_Directories.size()); + return s_Directories[index]; + } + + + std::vector AssetsManager::GetAssetsInDirectory(const int dirIndex) + { + std::vector results; + + for (const auto& asset : s_LoadedAssets) + { + if (asset.second.ParentDirectory == dirIndex) + results.push_back(asset.second); + } + + return results; + } + + std::vector AssetsManager::GetDirectoryNames(const std::string& filepath) + { + std::vector result; + size_t start; + size_t end = 0; + + while ((start = filepath.find_first_not_of("/\\", end)) != std::string::npos) + { + end = filepath.find("/\\", start); + result.push_back(filepath.substr(start, end - start)); + } + + return result; + } + + SearchResults AssetsManager::SearchFiles(const std::string& query, const std::string& searchPath) + { + SearchResults results; + + if (!searchPath.empty()) + { + for (const auto& dir : s_Directories) + { + if (dir.DirectoryName.find(query) != std::string::npos && dir.FilePath.find(searchPath) != std::string::npos) + { + results.Directories.push_back(dir); + } + } + + for (const auto&[key, asset] : s_LoadedAssets) + { + if (asset.FileName.find(query) != std::string::npos && asset.FilePath.find(searchPath) != std::string::npos) + { + results.Assets.push_back(asset); + } + } + } + + return results; + } + + + std::string AssetsManager::GetParentPath(const std::string& path) + { + return std::filesystem::path(path).parent_path().string(); + } + + template + Ref AssetsManager::InstantiateAsset(UUID assetId) + { + PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end()); + return Ref(static_cast(s_LoadedAssets[assetId].Data)); + } + + // Scene, Mesh, Texture, EnvMap, Image, Audio, Script, Other + template<> + Ref AssetsManager::InstantiateAsset(UUID assetId) + { + PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end()); + return Ref(static_cast(s_LoadedAssets[assetId].Data)); + } + + template PRISM_API Ref AssetsManager::InstantiateAsset(UUID assetId); + + + Asset& AssetsManager::GetAssetFromId(UUID assetId) + { + PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end()); + return s_LoadedAssets[assetId]; + } + + bool AssetsManager::MoveFile(const std::string& originalPath, const std::string& dest) + { + // TODO: fixeme: MoveFile cannot work + try + { + std::filesystem::rename(originalPath, dest); + const std::string newPath = dest + "/" + ParseFilename(originalPath, "/\\"); + return std::filesystem::exists(newPath); + }catch (const std::exception& e) + { + PM_CORE_ERROR("Move File error: {0}", e.what()); + return false; + } + } + + std::string AssetsManager::StripExtras(const std::string& filename) { std::vector out; size_t start; size_t end = 0; - while ((start = filepath.find_first_not_of(delim, end)) != std::string::npos) + while ((start = filename.find_first_not_of('.', end)) != std::string::npos) { - end = filepath.find(delim, start); - out.push_back(filepath.substr(start, end - start)); + end = filename.find('.', start); + out.push_back(filename.substr(start, end - start)); } - return out[out.size() - 1]; + if (out[0].length() >= 10) + { + auto cutFilename = out[0].substr(0, 9) + "..."; + return cutFilename; + } + + const auto filenameLength = out[0].length(); + const auto paddingToAdd = 9 - filenameLength; + + std::string newFileName; + + for (int i = 0; i <= paddingToAdd; i++) + { + newFileName += " "; + } + + newFileName += out[0]; + + return newFileName; + } + + std::string AssetsManager::ParseFilename(const std::string& filepath, const std::string_view& delim) + { + const size_t pos = filepath.find_last_of(delim); + if (pos == std::string::npos) + return filepath; + return filepath.substr(pos + 1); } std::string AssetsManager::ParseFileType(const std::string& filename) @@ -72,20 +220,90 @@ namespace Prism return out[out.size() - 1]; } - void AssetsManager::HandleAsset(const std::string& filepath) + std::string AssetsManager::RemoveExtension(const std::string& filename) { + const size_t end = filename.find_last_of('.'); + std::string newName = filename.substr(0, end); + + return newName; } - void AssetsManager::ProcessAsset(const std::string& assetType) - { - std::string filename = ParseFilename(assetType, '/\\'); - const std::string filetype = ParseFileType(assetType); - if (filetype == "blend") + void AssetsManager::ImportAsset(const std::string& filepath, bool reimport, int parentIndex) + { + Asset asset{}; + asset.ID = UUID(); + asset.FilePath = filepath; + asset.FileName = ParseFilename(filepath, "/\\"); + asset.Extension = ParseFileType(asset.FileName); + asset.Type = AssetTypes::GetAssetTypeFromExtension(asset.Extension); + asset.ParentDirectory = parentIndex; + + switch (asset.Type) { - // TODO: this actually unuse now - ConvertAsset(assetType, "fbx"); + case AssetType::Scene: + { + asset.Data = nullptr; + break; + } + case AssetType::Mesh: + { + if (asset.Extension == "blend") + break; + + Ref mesh = Ref::Create(filepath); + // NOTE(Peter): Required to make sure that the asset doesn't get destroyed when Ref goes out of scope + mesh->IncRefCount(); + asset.Data = mesh.Raw(); + break; + } + case AssetType::Texture: + { + Ref texture = Texture2D::Create(filepath); + texture->IncRefCount(); + asset.Data = texture.Raw(); + break; + } + case AssetType::EnvMap: + { + // TODO + /*Ref texture = Ref::Create(filepath); + texture->IncRefCount(); + asset.Data = texture.Raw();*/ + break; + } + case AssetType::Audio: + { + break; + } + case AssetType::Script: + { + asset.Data = &asset.FilePath; + break; + } + case AssetType::Other: + { + asset.Data = &asset.FilePath; + break; + } } + + if (reimport) + { + for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); ++it) + { + if (it->second.FilePath == filepath) + { + if (it->second.Data != nullptr) + delete it->second.Data; + it->second.Data = asset.Data; + asset.Data = nullptr; + return; + } + } + } + + s_LoadedAssets[asset.ID] = asset; } void AssetsManager::ConvertAsset(const std::string& assetPath, const std::string& conversionType) @@ -128,137 +346,78 @@ namespace Prism system(convCommand.c_str()); } - std::vector AssetsManager::GetFileSystemContents() + int AssetsManager::ProcessDirectory(const std::string& directoryPath,const int parentIndex) { - const std::string path = "assets"; - std::vector directories; + DirectoryInfo dirInfo; + dirInfo.DirectoryName = std::filesystem::path(directoryPath).filename().string(); + dirInfo.ParentIndex = parentIndex; + dirInfo.FilePath = directoryPath; + s_Directories.push_back(dirInfo); + int currentIndex = (int)s_Directories.size() - 1; + s_Directories[currentIndex].DirectoryIndex = currentIndex; - for (const auto& entry : std::filesystem::directory_iterator(path)) + for (auto entry : std::filesystem::directory_iterator(directoryPath)) { - const bool isDir = std::filesystem::is_directory(entry); - - std::string dir_data = ParseFilename(entry.path().string(), '/\\'); - std::string fileExt = ParseFileType(dir_data); - directories.emplace_back(dir_data, fileExt, entry.path().string(), !isDir); - } - - return directories; - } - - std::vector AssetsManager::GetDirectoryContents(const std::string& filepath, bool recursive) - { - std::vector directories; - - if (recursive) - { - for (const auto& entry : std::filesystem::recursive_directory_iterator(filepath)) + if (entry.is_directory()) { - const bool isDir = std::filesystem::is_directory(entry); - std::string dir_data = ParseFilename(entry.path().string(), '/\\'); - directories.emplace_back(dir_data, ".scene", entry.path().string(), !isDir); + int childIndex = ProcessDirectory(entry.path().string(), currentIndex); + s_Directories[currentIndex].ChildrenIndices.push_back(childIndex); } - } - else - { - for (const auto& entry : std::filesystem::directory_iterator(filepath)) + else { - const bool isDir = std::filesystem::is_directory(entry); - std::string dir_data = ParseFilename(entry.path().string(), '/\\'); - std::string fileExt = ParseFileType(dir_data); - directories.emplace_back(dir_data, fileExt, entry.path().string(), !isDir); + ImportAsset(entry.path().string(), false, currentIndex); } } - return directories; + return currentIndex; } - std::vector AssetsManager::SearchFiles(const std::string& query, const std::string& searchPath) + void AssetsManager::ReloadAssets() { - std::vector result; + ProcessDirectory("assets"); + } - if (!searchPath.empty()) + void AssetsManager::OnFileSystemChanged(FileSystemChangedEvent e) + { + e.NewName = RemoveExtension(e.NewName); + e.OldName = RemoveExtension(e.OldName); + + if (e.Action == FileSystemAction::Added) { - std::vector contents = GetDirectoryContents(searchPath, true); + if (!e.IsDirectory) + ImportAsset(e.Filepath); + } - for (auto& entry : contents) + if (e.Action == FileSystemAction::Modified) + { + if (!e.IsDirectory) + ImportAsset(e.Filepath, true); + } + + if (e.Action == FileSystemAction::Rename) + { + for (auto& kv : s_LoadedAssets) { - if (entry.Filename.find(query) != std::string::npos) - result.emplace_back(std::move(entry)); + if (kv.second.FileName == e.OldName) + kv.second.FileName = e.NewName; } } - return result; - } - - std::string AssetsManager::GetParentPath(const std::string& path) - { - return std::filesystem::path(path).parent_path().string(); - } - - std::vector AssetsManager::GetDirectories(const std::string& filepath) - { - std::vector result; - size_t start; - size_t end = 0; - - while ((start = filepath.find_first_not_of("/\\", end)) != std::string::npos) + if (e.Action == FileSystemAction::Delete) { - end = filepath.find("/\\", start); - result.push_back(filepath.substr(start, end - start)); + /* + for (auto it = m_LoadedAssets.begin(); it != m_LoadedAssets.end(); it++) + { + if (it->DirectoryName != e.NewName) + continue; + + m_LoadedAssets.erase(it); + break; + } + */ } - return result; - } - - bool AssetsManager::MoveFile(const std::filesystem::path& originalPath, const std::filesystem::path& dest) - { - // TODO: fixeme: MoveFile cannot work - try - { - std::filesystem::rename(originalPath, dest); - const std::string newPath = dest.string() + "/" + ParseFilename(originalPath.string(), '/\\'); - return std::filesystem::exists(newPath); - }catch (const std::exception& e) - { - PM_CORE_ERROR("Move File error: {0}", e.what()); - return false; - } - } - - std::string AssetsManager::StripExtras(const std::string& filename) - { - std::vector out; - size_t start; - size_t end = 0; - - while ((start = filename.find_first_not_of('.', end)) != std::string::npos) - { - end = filename.find('.', start); - out.push_back(filename.substr(start, end - start)); - } - - if (out[0].length() >= 10) - { - auto cutFilename = out[0].substr(0, 9) + "..."; - return cutFilename; - } - - const auto filenameLength = out[0].length(); - const auto paddingToAdd = 9 - filenameLength; - - std::string newFileName; - - for (int i = 0; i <= paddingToAdd; i++) - { - newFileName += " "; - } - - newFileName += out[0]; - - return newFileName; - } - - void AssetsManager::ImportAsset(const std::string& assetPath, const std::string& assetName) - { + if (s_AssetsChangeCallback) + s_AssetsChangeCallback(); } } diff --git a/Prism/src/Prism/Utilities/AssetsManager.h b/Prism/src/Prism/Utilities/AssetsManager.h index 7450031..5065570 100644 --- a/Prism/src/Prism/Utilities/AssetsManager.h +++ b/Prism/src/Prism/Utilities/AssetsManager.h @@ -6,13 +6,16 @@ #define PRISM_ASSETSMANAGER_H #include -#include + +#include "FileSystemWatcher.h" +#include "Prism/Core/Ref.h" +#include "Prism/Core/UUID.h" namespace Prism { enum class AssetType { - Scene, Mesh, Image, Audio, Script, Other + Scene, Mesh, Texture, EnvMap, Image, Audio, Script, Other }; class AssetTypes @@ -28,65 +31,73 @@ namespace Prism struct DirectoryInfo { - std::string Filename; - std::string FileType; - std::string AbsolutePath; - bool IsFile; + std::string DirectoryName; + std::string FilePath; + int DirectoryIndex; + int ParentIndex; + std::vector ChildrenIndices; + }; - DirectoryInfo(std::string filename, std::string fileType, std::string absolutePath, const bool isFile) - : Filename(std::move(filename)), FileType(std::move(fileType)), AbsolutePath(std::move(absolutePath)), IsFile(isFile) - { - } + struct Asset + { + UUID ID; + std::string FilePath; + std::string FileName; + std::string Extension; + AssetType Type; + int ParentDirectory; + void* Data; + }; - DirectoryInfo(const DirectoryInfo& other) - : Filename(other.Filename), FileType(other.FileType), AbsolutePath(other.AbsolutePath), IsFile(other.IsFile) - { - } - - DirectoryInfo(DirectoryInfo&& other) - : Filename(std::move(other.Filename)), FileType(std::move(other.FileType)), AbsolutePath(std::move(other.AbsolutePath)), IsFile(other.IsFile) - { - } - - DirectoryInfo& operator=(const DirectoryInfo& other) - { - if (this != &other) - { - Filename = other.Filename; - FileType = other.FileType; - AbsolutePath = other.AbsolutePath; - IsFile = other.IsFile; - } - return *this; - } + struct SearchResults + { + std::vector Directories; + std::vector Assets; }; class PRISM_API AssetsManager { public: - AssetsManager(); + using AssetsChangeEventFn = std::function; - std::string ParseFilename(const std::string& filepath, const char& delim); - std::string ParseFileType(const std::string& filename); + public: + static void Init(); + static void SetAssetChangeCallback(const AssetsChangeEventFn& callback); + static DirectoryInfo& GetDirectoryInfo(int index); + static std::vector GetAssetsInDirectory(int dirIndex); + static std::vector GetDirectoryNames(const std::string& filepath); - void HandleAsset(const std::string& filepath); - void ProcessAsset(const std::string& assetType); - void ConvertAsset(const std::string& assetPath, const std::string& conversionType); + static SearchResults SearchFiles(const std::string& query, const std::string& searchPath); + static std::string GetParentPath(const std::string& path); - std::vector GetFileSystemContents(); - std::vector GetDirectoryContents(const std::string& filepath, bool recursive = false); - std::vector SearchFiles(const std::string& query, const std::string& searchPath); + template + static Ref InstantiateAsset(UUID assetId); - std::string GetParentPath(const std::string& path); + static Asset& GetAssetFromId(UUID assetId); - std::vector GetDirectories(const std::string& filepath); + // TODO: This will NOT live here + static bool MoveFile(const std::string& originalPath, const std::string& dest); - bool MoveFile(const std::filesystem::path& originalPath, const std::filesystem::path& dest); - - std::string StripExtras(const std::string& filename); + static std::string StripExtras(const std::string& filename); private: - void ImportAsset(const std::string& assetPath, const std::string& assetName); + static std::string ParseFilename(const std::string& filepath, const std::string_view& delim); + static std::string ParseFileType(const std::string& filename); + static std::string RemoveExtension(const std::string& filename); + + static void ImportAsset(const std::string& filepath, bool reimport = false, int parentIndex = -1); + static void ConvertAsset(const std::string& assetPath, const std::string& conversionType); + static int ProcessDirectory(const std::string& directoryPath, int parentIndex = -1); + static void ReloadAssets(); + // static void WriteAssetsToDisk(); + + static void OnFileSystemChanged(FileSystemChangedEvent e); + + private: + static std::unordered_map s_LoadedAssets; + static std::vector s_Directories; + static AssetsChangeEventFn s_AssetsChangeCallback; + }; } diff --git a/Prism/src/Prism/Utilities/FileSystemWatcher.h b/Prism/src/Prism/Utilities/FileSystemWatcher.h new file mode 100644 index 0000000..9541668 --- /dev/null +++ b/Prism/src/Prism/Utilities/FileSystemWatcher.h @@ -0,0 +1,44 @@ +// +// Created by Atdunbg on 2026/1/21. +// + +#ifndef PRISM_FILESYSTEMWATCHER_H +#define PRISM_FILESYSTEMWATCHER_H + +#include + +namespace Prism +{ + enum class FileSystemAction + { + Added, Rename, Modified, Delete + }; + + struct FileSystemChangedEvent + { + FileSystemAction Action; + std::string Filepath; + std::string OldName; + std::string NewName; + bool IsDirectory; + }; + + class PRISM_API FileSystemWatcher + { + public: + using FileSystemChangedCallbackFn = std::function; + + static void SetChangeCallback(const FileSystemChangedCallbackFn& callback); + static void StartWatching(); + static void StopWatching(); + + private: + static unsigned long Watch(void* param); + + private: + static FileSystemChangedCallbackFn s_Callback; + }; + +} + +#endif //PRISM_FILESYSTEMWATCHER_H \ No newline at end of file