add file watcher using widows api for windows platform, using handles to import and manager assets, some little tweaks

This commit is contained in:
2026-01-21 23:25:41 +08:00
parent cf3a46bae1
commit 896d3c7f97
15 changed files with 837 additions and 474 deletions

View File

@ -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})

View File

@ -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<MeshComponent>(Ref<Mesh>::Create(data->SourcePath));
Entity entity = m_EditorScene->CreateEntity(asset.FileName);
entity.AddComponent<MeshComponent>(AssetsManager::InstantiateAsset<Mesh>(assetId));
}
}
ImGui::EndDragDropTarget();

View File

@ -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()

View File

@ -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] = '#';

View File

@ -10,7 +10,7 @@
namespace Prism
{
class UUID
class PRISM_API UUID
{
public:
UUID();

View File

@ -7,13 +7,16 @@
#include <filesystem>
#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<int>(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();
DrawDirectoryInfo(AssetsManager::GetDirectoryInfo(ChildrenIndice));
}
}
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");
}
}
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<char*>(data->Data);
if (m_AssetManager.MoveFile(file, m_MovePath))
const std::string file = static_cast<char*>(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);
if (m_DisplayListView)
RenderDirectoriesListView(dir.DirectoryIndex);
else
m_CurrentDir[i].IsFile ? RenderFileListView(i) : RenderDirectoriesListView(i);
RenderDirectoriesGridView(dir.DirectoryIndex);
ImGui::NextColumn();
}
for (Asset& asset : m_CurrentDirAssets)
{
if (m_DisplayListView)
RenderFileListView(asset);
else
RenderFileGridView(asset);
ImGui::NextColumn();
}
if (ImGui::BeginPopupContextWindow("Creating"))
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<char*>(data->Data);
if (m_AssetManager.MoveFile(a, m_MovePath))
const std::string data = static_cast<char*>(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();
}
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);
}
void AssetsManagerPanel::RenderFileListView(const int dirIndex)
ImGui::TreePop();
}
if (m_IsDragging && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
{
const size_t fileID = AssetTypes::GetAssetTypeID(m_CurrentDir[dirIndex].FileType);
m_MovePath = dir.FilePath;
}
}
void AssetsManagerPanel::RenderFileListView(Asset& asset)
{
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<int>(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));
ImGui::Text("%s", asset.FilePath.c_str());
ImGui::SetDragDropPayload("scene_entity_assetsP", &asset.ID, sizeof(UUID));
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::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<int>(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,53 +318,33 @@ 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<int>(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<int>(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)))
@ -415,94 +353,95 @@ namespace Prism
}
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)
{
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, ImGuiInputTextFlags_EnterReturnsTrue))
if (ImGui::InputTextWithHint("##Search", "Search...", m_InputBuffer, 100))
{
m_CurrentDir = m_AssetManager.SearchFiles(m_InputBuffer, m_CurrentDirPath);
const SearchResults results = AssetsManager::SearchFiles(m_InputBuffer, m_CurrentDir.FilePath);
if (strlen(m_InputBuffer) == 0)
{
UpdateCurrentDirectory(m_CurrentDirIndex);
}
else
{
m_CurrentDirChildren = results.Directories;
m_CurrentDirAssets = results.Assets;
}
}
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_UpdateBreadCrumbs)
{
if (m_ForwardPath.empty()) return;
m_CurrentDir = m_AssetManager.GetDirectoryContents(m_ForwardPath);
m_CurrentDirPath = m_ForwardPath;
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();
const auto data = m_AssetManager.GetDirectories(m_CurrentDirPath);
const int size = (int)i.DirectoryName.length() * 7;
for (int i = 0; i < data.size(); i++)
if (ImGui::Selectable(i.DirectoryName.c_str(), false, 0, ImVec2((float)size, 22)))
{
if (data[i] != m_BaseDirPath)
{
ImGui::Image((ImTextureID)m_FolderRightTex->GetRendererID(), ImVec2(22, 23));
UpdateCurrentDirectory(i.DirectoryIndex);
}
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::SameLine();
ImGui::Dummy(ImVec2(ImGui::GetColumnWidth() - 400, 0));
ImGui::SameLine();
}
}
void AssetsManagerPanel::RenderSearch()
{
// TODO: not implemented
}
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);
}
}

View File

@ -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<Texture2D> m_FolderTex;
@ -46,32 +50,30 @@ namespace Prism
Ref<Texture2D> m_GridView;
Ref<Texture2D> 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<DirectoryInfo> m_CurrentDir;
std::vector<DirectoryInfo> m_BaseProjectDir;
DirectoryInfo m_CurrentDir;
DirectoryInfo m_BaseProjectDir;
std::vector<DirectoryInfo> m_CurrentDirChildren;
std::vector<Asset> m_CurrentDirAssets;
std::vector<DirectoryInfo> m_BreadCrumbData;
ImGuiInputTextCallbackData m_Data;
std::map<size_t, Ref<Texture2D>> m_AssetIconMap;
//NotificationManager nManager;
AssetsManager m_AssetManager;
};
}

View File

@ -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);

View File

@ -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<TagComponent>().Tag.c_str());
const UUID entityId = entity.GetUUID();
ImGui::Text("%s", entity.GetComponent<TagComponent>().Tag.c_str());
ImGui::SetDragDropPayload("scene_entity_hierarchy", &entityId, sizeof(UUID));
ImGui::EndDragDropSource();
}

View File

@ -0,0 +1,178 @@
//
// Created by Atdunbg on 2026/1/21.
//
#include <codecvt>
#include "Prism/Utilities/FileSystemWatcher.h"
#include <Windows.h>
#include <filesystem>
#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;
}
}

View File

@ -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);

View File

@ -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<std::string>();
PM_CORE_INFO("Deserializing scene '{0}'", sceneName);

View File

@ -5,6 +5,10 @@
#include "AssetsManager.h"
#include <filesystem>
#include <utility>
#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<std::string, AssetType> AssetTypes::s_Types;
AssetsManager::AssetsChangeEventFn AssetsManager::s_AssetsChangeCallback;
std::unordered_map<size_t, Asset> AssetsManager::s_LoadedAssets;
std::vector<DirectoryInfo> 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<Asset> AssetsManager::GetAssetsInDirectory(const int dirIndex)
{
std::vector<Asset> results;
for (const auto& asset : s_LoadedAssets)
{
if (asset.second.ParentDirectory == dirIndex)
results.push_back(asset.second);
}
return results;
}
std::vector<std::string> AssetsManager::GetDirectoryNames(const std::string& filepath)
{
std::vector<std::string> 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 <typename T>
Ref<T> AssetsManager::InstantiateAsset(UUID assetId)
{
PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end());
return Ref<T>(static_cast<T*>(s_LoadedAssets[assetId].Data));
}
// Scene, Mesh, Texture, EnvMap, Image, Audio, Script, Other
template<>
Ref<Mesh> AssetsManager::InstantiateAsset(UUID assetId)
{
PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end());
return Ref<Mesh>(static_cast<Mesh*>(s_LoadedAssets[assetId].Data));
}
template PRISM_API Ref<Mesh> 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<std::string> 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)
{
// TODO: this actually unuse now
ConvertAsset(assetType, "fbx");
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)
{
case AssetType::Scene:
{
asset.Data = nullptr;
break;
}
case AssetType::Mesh:
{
if (asset.Extension == "blend")
break;
Ref<Mesh> mesh = Ref<Mesh>::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<Texture2D> texture = Texture2D::Create(filepath);
texture->IncRefCount();
asset.Data = texture.Raw();
break;
}
case AssetType::EnvMap:
{
// TODO
/*Ref<TextureCube> texture = Ref<TextureCube>::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<DirectoryInfo> AssetsManager::GetFileSystemContents()
int AssetsManager::ProcessDirectory(const std::string& directoryPath,const int parentIndex)
{
const std::string path = "assets";
std::vector<DirectoryInfo> 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<DirectoryInfo> AssetsManager::GetDirectoryContents(const std::string& filepath, bool recursive)
if (entry.is_directory())
{
std::vector<DirectoryInfo> directories;
if (recursive)
{
for (const auto& entry : std::filesystem::recursive_directory_iterator(filepath))
{
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))
{
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<DirectoryInfo> AssetsManager::SearchFiles(const std::string& query, const std::string& searchPath)
void AssetsManager::ReloadAssets()
{
std::vector<DirectoryInfo> result;
ProcessDirectory("assets");
}
if (!searchPath.empty())
void AssetsManager::OnFileSystemChanged(FileSystemChangedEvent e)
{
std::vector<DirectoryInfo> contents = GetDirectoryContents(searchPath, true);
e.NewName = RemoveExtension(e.NewName);
e.OldName = RemoveExtension(e.OldName);
for (auto& entry : contents)
if (e.Action == FileSystemAction::Added)
{
if (entry.Filename.find(query) != std::string::npos)
result.emplace_back(std::move(entry));
if (!e.IsDirectory)
ImportAsset(e.Filepath);
}
if (e.Action == FileSystemAction::Modified)
{
if (!e.IsDirectory)
ImportAsset(e.Filepath, true);
}
if (e.Action == FileSystemAction::Rename)
{
for (auto& kv : s_LoadedAssets)
{
if (kv.second.FileName == e.OldName)
kv.second.FileName = e.NewName;
}
}
return result;
if (e.Action == FileSystemAction::Delete)
{
/*
for (auto it = m_LoadedAssets.begin(); it != m_LoadedAssets.end(); it++)
{
if (it->DirectoryName != e.NewName)
continue;
m_LoadedAssets.erase(it);
break;
}
*/
}
std::string AssetsManager::GetParentPath(const std::string& path)
{
return std::filesystem::path(path).parent_path().string();
}
std::vector<std::string> AssetsManager::GetDirectories(const std::string& filepath)
{
std::vector<std::string> 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;
}
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<std::string> 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();
}
}

View File

@ -6,13 +6,16 @@
#define PRISM_ASSETSMANAGER_H
#include <map>
#include <utility>
#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<int> 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)
struct SearchResults
{
}
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;
}
std::vector<DirectoryInfo> Directories;
std::vector<Asset> Assets;
};
class PRISM_API AssetsManager
{
public:
AssetsManager();
using AssetsChangeEventFn = std::function<void()>;
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<Asset> GetAssetsInDirectory(int dirIndex);
static std::vector<std::string> 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<DirectoryInfo> GetFileSystemContents();
std::vector<DirectoryInfo> GetDirectoryContents(const std::string& filepath, bool recursive = false);
std::vector<DirectoryInfo> SearchFiles(const std::string& query, const std::string& searchPath);
template<typename T>
static Ref<T> InstantiateAsset(UUID assetId);
std::string GetParentPath(const std::string& path);
static Asset& GetAssetFromId(UUID assetId);
std::vector<std::string> 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<size_t, Asset> s_LoadedAssets;
static std::vector<DirectoryInfo> s_Directories;
static AssetsChangeEventFn s_AssetsChangeCallback;
};
}

View File

@ -0,0 +1,44 @@
//
// Created by Atdunbg on 2026/1/21.
//
#ifndef PRISM_FILESYSTEMWATCHER_H
#define PRISM_FILESYSTEMWATCHER_H
#include <functional>
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<void(FileSystemChangedEvent)>;
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