diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b4ba57..e479115 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # set MSVC output directory if(MSVC) # config - + string(REPLACE "/showIncludes" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # temp config set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4251") diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index f4674ca..04a4992 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -11,11 +11,11 @@ #include "Prism/Core/Input.h" #include "Prism/Core/Math/Math.h" +#include "Prism/Editor/AssetEditorPanel.h" #include "Prism/Editor/PhysicsSettingsWindow.h" #include "Prism/Physics/Physics3D.h" #include "Prism/Renderer/Renderer2D.h" #include "Prism/Script/ScriptEngine.h" -#include "Prism/Utilities/DragDropData.h" namespace Prism { @@ -178,12 +178,13 @@ namespace Prism // OpenScene("assets/scenes/FPSDemo.scene"); NewScene(); - FileSystemWatcher::StartWatching(); + AssetEditorPanel::RegisterDefaultEditors(); + FileSystem::StartWatching(); } void EditorLayer::OnDetach() { - FileSystemWatcher::StopWatching(); + FileSystem::StopWatching(); m_EditorScene->OnShutdown(); } @@ -404,10 +405,6 @@ namespace Prism ImGui::EndMenuBar(); } - m_SceneHierarchyPanel->OnImGuiRender(); - PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings); - SceneRenderer::OnImGuiRender(); - // m_EditorCamera.OnImGuiRender(); ImGui::Begin("Materials"); @@ -616,7 +613,6 @@ namespace Prism - ScriptEngine::OnImGuiRender(); ImGui::End(); @@ -661,6 +657,12 @@ namespace Prism if (m_UIShowBoundingBoxes && Property("On Top", m_UIShowBoundingBoxesOnTop)) ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop); + m_SceneHierarchyPanel->OnImGuiRender(); + PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings); + SceneRenderer::OnImGuiRender(); + AssetEditorPanel::OnImGuiRender(); + ScriptEngine::OnImGuiRender(); + m_AssetManagerPanel->OnImGuiRender(); m_ObjectsPanel->OnImGuiRender(); @@ -772,7 +774,7 @@ namespace Prism m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); if (m_RuntimeScene) m_RuntimeScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 1000.0f)); + m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); @@ -847,37 +849,28 @@ namespace Prism if (ImGui::BeginDragDropTarget()) { - auto payload = ImGui::AcceptDragDropPayload("scene_entity_objectP"); + auto payload = ImGui::AcceptDragDropPayload("asset_payload"); if (payload) { - const auto data = (DragDropData*)payload->Data; - if (std::string_view(data->Type) == "Mesh") - { - auto entity = m_EditorScene->CreateEntity(data->Name); - entity.AddComponent(Ref::Create(data->SourcePath)); - } - } - ImGui::EndDragDropTarget(); - } + int count = payload->DataSize / sizeof(AssetHandle); - /* Payload Implementation For Getting Assets In The Viewport From Asset Manager */ - if (ImGui::BeginDragDropTarget()) - { - auto payload = ImGui::AcceptDragDropPayload("scene_entity_assetsP"); - if (payload) - { - UUID assetId = *(UUID*)payload->Data; - Asset& asset = AssetsManager::GetAssetFromId(assetId); - - if (asset.Type == AssetType::Scene) + for (int i = 0; i < count; i++) { - OpenScene(asset.FilePath); - } + AssetHandle assetHandle = *(((AssetHandle*)payload->Data) + i); + Ref asset = AssetsManager::GetAsset(assetHandle); - if (asset.Type == AssetType::Mesh) - { - Entity entity = m_EditorScene->CreateEntity(asset.FileName); - entity.AddComponent(AssetsManager::InstantiateAsset(assetId)); + // We can't really support dragging and dropping scenes when we're dropping multiple assets + if (count == 1 && asset->Type == AssetType::Scene) + { + OpenScene(asset->FilePath); + } + + if (asset->Type == AssetType::Mesh) + { + Entity entity = m_EditorScene->CreateEntity(asset->FileName); + entity.AddComponent(Ref(asset)); + SelectEntity(entity); + } } } ImGui::EndDragDropTarget(); diff --git a/Editor/Editor/EditorLayer.h b/Editor/Editor/EditorLayer.h index 02bb0ef..c63ba11 100644 --- a/Editor/Editor/EditorLayer.h +++ b/Editor/Editor/EditorLayer.h @@ -118,7 +118,7 @@ namespace Prism struct RoughnessInput { - float Value = 0.5f; + float Value = 1.0f; Ref TextureMap; bool UseTexture = false; }; diff --git a/Prism/src/Prism/Asset/Asset.h b/Prism/src/Prism/Asset/Asset.h new file mode 100644 index 0000000..509b65e --- /dev/null +++ b/Prism/src/Prism/Asset/Asset.h @@ -0,0 +1,64 @@ +// +// Created by Atdunbg on 2026/2/3. +// + +#ifndef PRISM_ASSET_H +#define PRISM_ASSET_H + +#include "Prism/Core/UUID.h" +#include "Prism/Core/Ref.h" + +namespace Prism +{ + enum class AssetType + { + Scene, Mesh, Texture, EnvMap, Audio, Script, PhysicsMat, Directory, Other + }; + + using AssetHandle = UUID; + + class PRISM_API Asset : public RefCounted + { + public: + AssetHandle Handle; + AssetType Type; + + std::string FilePath; + std::string FileName; + std::string Extension; + AssetHandle ParentDirectory; + + bool IsDataLoaded = false; + + virtual ~Asset() + { + } + }; + + class PRISM_API PhysicsMaterial : public Asset + { + public: + float StaticFriction; + float DynamicFriction; + float Bounciness; + + PhysicsMaterial() = default; + + PhysicsMaterial(float staticFriction, float dynamicFriction, float bounciness) + : StaticFriction(staticFriction), DynamicFriction(dynamicFriction), Bounciness(bounciness) + { + } + }; + + // Treating directories as assets simplifies the asset manager window rendering by a lot + class Directory : public Asset + { + public: + std::vector ChildDirectories; + + Directory() = default; + }; + +} + +#endif //PRISM_ASSET_H diff --git a/Prism/src/Prism/Asset/AssetSerializer.cpp b/Prism/src/Prism/Asset/AssetSerializer.cpp new file mode 100644 index 0000000..903302e --- /dev/null +++ b/Prism/src/Prism/Asset/AssetSerializer.cpp @@ -0,0 +1,198 @@ +// +// Created by Atdunbg on 2026/2/13. +// + +#include "AssetSerializer.h" + +#include "Prism/Utilities/StringUtils.h" +#include "Prism/Utilities/FileSystem.h" +#include "Prism/Renderer/Mesh.h" +#include "Prism/Renderer/SceneEnvironment.h" +#include "Prism/Renderer/SceneRenderer.h" + +#include "yaml-cpp/yaml.h" + +namespace Prism +{ + + + void AssetSerializer::SerializeAsset(const Ref& asset, AssetType type) + { + YAML::Emitter out; + + out << YAML::BeginMap; + + switch (type) + { + case Prism::AssetType::PhysicsMat: + { + Ref material = Ref(asset); + out << YAML::Key << "StaticFriction" << material->StaticFriction; + out << YAML::Key << "DynamicFriction" << material->DynamicFriction; + out << YAML::Key << "Bounciness" << material->Bounciness; + break; + } + } + out << YAML::EndMap; + + std::ofstream fout(asset->FilePath); + fout << out.c_str(); + } + + Ref AssetSerializer::DeserializeYAML(const Ref& asset) + { + const std::ifstream stream(asset->FilePath); + std::stringstream strStream; + strStream << stream.rdbuf(); + + YAML::Node data = YAML::Load(strStream.str()); + + if (asset->Type == AssetType::PhysicsMat) + { + float staticFriction = data["StaticFriction"].as(); + float dynamicFriction = data["DynamicFriction"].as(); + float bounciness = data["Bounciness"].as(); + + return Ref::Create(staticFriction, dynamicFriction, bounciness); + } + + return nullptr; + } + + + Ref AssetSerializer::LoadAssetInfo(const std::string& filepath, AssetHandle parentHandle, AssetType type) + { + Ref asset; + + if (type == AssetType::Directory) + asset = Ref::Create(); + else + asset = Ref::Create(); + + + const std::string extension = Utils::GetExtension(filepath); + + asset->FilePath = filepath; + std::replace(asset->FilePath.begin(), asset->FilePath.end(), '\\', '/'); + + const bool hasMeta = FileSystem::Exists(asset->FilePath + ".meta"); + if (hasMeta) + { + LoadMetaData(asset); + } + else + { + if (filepath == "assets") + asset->Handle = 0; + else + asset->Handle = AssetHandle(); + + asset->FileName = Utils::RemoveExtension(Utils::GetFilename(filepath)); + asset->Extension = Utils::GetExtension(filepath); + asset->Type = type; + } + + asset->ParentDirectory = parentHandle; + asset->IsDataLoaded = false; + + if (!hasMeta) + CreateMetaFile(asset); + + return asset; + } + + Ref AssetSerializer::LoadAssetData(Ref& asset) + { + if (asset->Type == AssetType::Directory) + return asset; + + Ref temp = asset; + bool loadYAMLData = true; + + switch (asset->Type) + { + case AssetType::Mesh: + { + if (asset->Extension != "blend") + asset = Ref::Create(asset->FilePath); + loadYAMLData = false; + break; + } + case AssetType::Texture: + { + asset = Texture2D::Create(asset->FilePath); + loadYAMLData = false; + break; + } + case AssetType::EnvMap: + { + auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(asset->FilePath); + asset = Ref::Create(radiance, irradiance); + loadYAMLData = false; + break; + } + case AssetType::Scene: + case AssetType::Audio: + case AssetType::Script: + case AssetType::Other: + { + loadYAMLData = false; + break; + } + case AssetType::PhysicsMat: + break; + } + + if (loadYAMLData) + { + asset = DeserializeYAML(asset); + PM_CORE_ASSERT(asset, "Failed to load asset"); + } + + asset->Handle = temp->Handle; + asset->FilePath = temp->FilePath; + asset->FileName = temp->FileName; + asset->Extension = temp->Extension; + asset->ParentDirectory = temp->ParentDirectory; + asset->Type = temp->Type; + asset->IsDataLoaded = true; + + return asset; + } + + + void AssetSerializer::LoadMetaData(Ref& asset) + { + std::ifstream stream(asset->FilePath + ".meta"); + std::stringstream strStream; + strStream << stream.rdbuf(); + + YAML::Node data = YAML::Load(strStream.str()); + if (!data["Asset"]) + { + PM_CORE_ASSERT("Invalid File Format"); + } + + asset->Handle = data["Asset"].as(); + asset->FileName = data["FileName"].as(); + asset->FilePath = data["FilePath"].as(); + asset->Extension = data["Extension"].as(); + asset->Type = (AssetType)data["Type"].as(); + } + + void AssetSerializer::CreateMetaFile(const Ref& asset) + { + YAML::Emitter out; + out << YAML::BeginMap; + out << YAML::Key << "Asset" << YAML::Value << asset->Handle; + out << YAML::Key << "FileName" << YAML::Value << asset->FileName; + out << YAML::Key << "FilePath" << YAML::Value << asset->FilePath; + out << YAML::Key << "Extension" << YAML::Value << asset->Extension; + out << YAML::Key << "Directory" << YAML::Value << asset->ParentDirectory; + out << YAML::Key << "Type" << YAML::Value << (int)asset->Type; + out << YAML::EndMap; + + std::ofstream fout(asset->FilePath + ".meta"); + fout << out.c_str(); + } +} \ No newline at end of file diff --git a/Prism/src/Prism/Asset/AssetSerializer.h b/Prism/src/Prism/Asset/AssetSerializer.h new file mode 100644 index 0000000..040b9c0 --- /dev/null +++ b/Prism/src/Prism/Asset/AssetSerializer.h @@ -0,0 +1,38 @@ +// +// Created by Atdunbg on 2026/2/13. +// + +#ifndef PRISM_ASSETSERIALIZER_H +#define PRISM_ASSETSERIALIZER_H + +#include "Asset.h" +#include "Prism/Core/Ref.h" + +namespace Prism +{ + class PRISM_API AssetSerializer + { + public: + template + static void SerializeAsset(const Ref& asset) + { + static_assert(std::is_base_of::value, "SerializeAsset only accepts types that inherit from Asset"); + SerializeAsset(asset, asset->Type); + } + + static Ref LoadAssetInfo(const std::string& filepath, AssetHandle parentHandle, AssetType type); + static Ref LoadAssetData(Ref& asset); + + private: + static void SerializeAsset(const Ref& asset, AssetType type); + static Ref DeserializeYAML(const Ref& asset); + static void LoadMetaData(Ref& asset); + static void CreateMetaFile(const Ref& asset); + + private: + friend class AssetsManager; + }; +} + + +#endif //PRISM_ASSETSERIALIZER_H \ No newline at end of file diff --git a/Prism/src/Prism/Asset/AssetsManager.cpp b/Prism/src/Prism/Asset/AssetsManager.cpp new file mode 100644 index 0000000..bb63144 --- /dev/null +++ b/Prism/src/Prism/Asset/AssetsManager.cpp @@ -0,0 +1,450 @@ +// +// Created by Atdunbg on 2026/1/20. +// + +#include "AssetsManager.h" + +#include +#include + +#include "Prism/Core/Log.h" +#include "Prism/Renderer/Mesh.h" + +#include + +#include "AssetSerializer.h" +#include "Prism/Renderer/SceneEnvironment.h" +#include "Prism/Utilities/StringUtils.h" + +namespace Prism +{ + void AssetTypes::Init() + { + s_Types["scene"] = AssetType::Scene; + s_Types["pmx"] = AssetType::Mesh; + s_Types["fbx"] = AssetType::Mesh; + s_Types["obj"] = AssetType::Mesh; + s_Types["png"] = AssetType::Texture; + s_Types["hdr"] = AssetType::EnvMap; + s_Types["blend"] = AssetType::Mesh; + s_Types["hpm"] = AssetType::PhysicsMat; + s_Types["wav"] = AssetType::Audio; + s_Types["ogg"] = AssetType::Audio; + s_Types["cs"] = AssetType::Script; + } + + size_t AssetTypes::GetAssetTypeID(const std::string& extension) + { + if (extension.empty()) + return 0; + + for (const auto& kv : s_Types) + { + if (kv.first == extension) + return std::hash()(extension); + } + + return -1; + } + + AssetType AssetTypes::GetAssetTypeFromExtension(const std::string& extension) + { + return s_Types.find(extension) != s_Types.end() ? s_Types[extension] : AssetType::Other; + } + + std::map AssetTypes::s_Types; + AssetsManager::AssetsChangeEventFn AssetsManager::s_AssetsChangeCallback; + std::unordered_map> AssetsManager::s_LoadedAssets; + + void AssetsManager::Init() + { + FileSystem::SetChangeCallback(OnFileSystemChanged); + ReloadAssets(); + } + + void AssetsManager::SetAssetChangeCallback(const AssetsChangeEventFn& callback) + { + s_AssetsChangeCallback = callback; + } + + void AssetsManager::Shutdown() + { + s_LoadedAssets.clear(); + } + + std::vector> AssetsManager::GetAssetsInDirectory(AssetHandle directoryHandle) + { + std::vector> results; + + for (const auto& asset : s_LoadedAssets) + { + if (asset.second && asset.second->ParentDirectory == directoryHandle && asset.second->Handle != directoryHandle) + results.push_back(asset.second); + } + + return results; + } + + + std::vector> AssetsManager::SearchFiles(const std::string& query, const std::string& searchPath) + { + std::vector> results; + + if (!searchPath.empty()) + { + for (const auto&[key, asset] : s_LoadedAssets) + { + if (asset->FileName.find(query) != std::string::npos && asset->FilePath.find(searchPath) != std::string::npos) + { + results.push_back(asset); + } + } + } + + return results; + } + + + std::string AssetsManager::GetParentPath(const std::string& path) + { + return std::filesystem::path(path).parent_path().string(); + } + + bool AssetsManager::IsDirectory(const std::string& filepath) + { + for (auto&[handle, asset] : s_LoadedAssets) + { + if (asset->Type == AssetType::Directory && asset->FilePath == filepath) + return true; + } + + return false; + } + + AssetHandle AssetsManager::GetAssetIDForFile(const std::string& filepath) + { + for (auto&[id, asset] : s_LoadedAssets) + { + if (asset->FilePath == filepath) + return id; + } + + return 0; + } + + bool AssetsManager::IsAssetHandleValid(const AssetHandle& assetHandle) + { + return s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end(); + } + + void AssetsManager::Rename(Ref& asset, const std::string& newName) + { + const std::string newFilePath = FileSystem::Rename(asset->FilePath, newName); + const std::string oldFilePath = asset->FilePath; + asset->FilePath = newFilePath; + asset->FileName = newName; + + if (FileSystem::Exists(oldFilePath + ".meta")) + { + std::string metaFileName = oldFilePath; + + if (!asset->Extension.empty()) + metaFileName += "." + asset->Extension; + + FileSystem::Rename(oldFilePath + ".meta", metaFileName); + AssetSerializer::CreateMetaFile(asset); + } + } + + template + Ref AssetsManager::GetAsset(const AssetHandle assetHandle) + { + PM_CORE_ASSERT(s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end()); + Ref asset = s_LoadedAssets[assetHandle]; + + if (!asset->IsDataLoaded) + asset = AssetSerializer::LoadAssetData(asset); + + return asset.As(); + } + + template PRISM_API Ref AssetsManager::GetAsset(AssetHandle); + template PRISM_API Ref AssetsManager::GetAsset(AssetHandle); + template PRISM_API Ref AssetsManager::GetAsset(AssetHandle); + template PRISM_API Ref AssetsManager::GetAsset(AssetHandle); + template PRISM_API Ref AssetsManager::GetAsset(AssetHandle); + + // temp + Ref AssetsManager::CreateAssetPhysicsMaterial(const std::string& filename, const AssetType type, const AssetHandle& directoryHandle, float v1, float v2, float v3) + { + + const auto& directory = GetAsset(directoryHandle); + + Ref asset = Ref::Create(v1, v2, v3); + asset->Type = type; + asset->FilePath = directory->FilePath + "/" + filename; + asset->FileName = Utils::RemoveExtension(Utils::GetFilename(asset->FilePath)); + asset->Extension = Utils::GetFilename(filename); + asset->ParentDirectory = directoryHandle; + asset->Handle = std::hash()(asset->FilePath); + s_LoadedAssets[asset->Handle] = asset; + + AssetSerializer::SerializeAsset(asset); + + return asset; + } + + void AssetsManager::RemoveAsset(AssetHandle assetHandle) + { + Ref asset = s_LoadedAssets[assetHandle]; + if (asset->Type == AssetType::Directory) + { + if (IsAssetHandleValid(asset->ParentDirectory)) + { + auto& childList = s_LoadedAssets[asset->ParentDirectory].As()->ChildDirectories; + childList.erase(std::remove(childList.begin(), childList.end(), assetHandle), childList.end()); + } + + for (auto child : asset.As()->ChildDirectories) + RemoveAsset(child); + + for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); ) + { + if (it->second->ParentDirectory != assetHandle) + { + ++it; + continue; + } + + it = s_LoadedAssets.erase(it); + } + } + + s_LoadedAssets.erase(assetHandle); + } + + template + Ref AssetsManager::CreateAsset(const std::string& filename, AssetType type, AssetHandle directoryHandle, Args&&... args) + { + static_assert(std::is_base_of_v, "CreateAsset only works for types derived from Asset"); + + const auto& directory = GetAsset(directoryHandle); + + Ref asset = Ref::Create(std::forward(args)...); + asset->Type = type; + asset->FilePath = directory->FilePath + "/" + filename; + asset->FileName = Utils::RemoveExtension(Utils::GetFilename(asset->FilePath)); + asset->Extension = Utils::GetFilename(filename); + asset->ParentDirectory = directoryHandle; + asset->Handle = std::hash()(asset->FilePath); + asset->IsDataLoaded = true; + s_LoadedAssets[asset->Handle] = asset; + + AssetSerializer::SerializeAsset(asset); + + return asset; + } + + + + bool AssetsManager::IsAssetType(const AssetHandle assetHandle, const AssetType type) + { + return s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end() && s_LoadedAssets[assetHandle]->Type == type; + } + + + 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& filepath, AssetHandle parentHandle) + { + const std::string extension = Utils::GetExtension(filepath); + if (extension == "meta") + return; + + const AssetType type = AssetTypes::GetAssetTypeFromExtension(extension); + Ref asset = AssetSerializer::LoadAssetInfo(filepath, parentHandle, type); + + if (s_LoadedAssets.find(asset->Handle) != s_LoadedAssets.end()) + { + if (s_LoadedAssets[asset->Handle]->IsDataLoaded) + { + asset = AssetSerializer::LoadAssetData(asset); + } + } + + s_LoadedAssets[asset->Handle] = asset; + } + + void AssetsManager::ConvertAsset(const std::string& assetPath, const std::string& conversionType) + { + // Create a filestream to write a blender python script for conversion of the asset + // The 'bpy.ops.export_scene.(asset-type-to-convert) function runs blender in background and exports the file' + std::string path = std::filesystem::temp_directory_path().string(); + std::ofstream fileStream(path + "export.py"); + + // Importing the python modules required for the export to work out + fileStream << "import bpy\n"; + fileStream << "import sys\n"; + + if (conversionType == "fbx") + fileStream << "bpy.ops.export_scene.fbx(filepath=r'" + path + "asset.fbx" + "', axis_forward='-Z', axis_up='Y')\n"; + + if (conversionType == "obj") + fileStream << "bpy.ops.export_scene.obj(filepath=r'" + path + "asset.obj" + "', axis_forward='-Z', axis_up='Y')\n"; + + fileStream.close(); + + // This section involves creating the command to export the .blend file to the required asset type + // The command goes something like this + // blender.exe path\to\files\cube.blend --background --python path\to\file\export.py + + std::string blender_base_path = R"(D:\Application\Blender5.0.1\blender.exe)"; + std::string p_asset_path = '"' + assetPath + '"'; + std::string p_blender_path = '"' + blender_base_path + '"'; + std::string p_script_path = '"' + path + "export.py" + '"'; + + // Creating the actual command that will execute + std::string convCommand = '"' + p_blender_path + " " + p_asset_path + " --background --python " + p_script_path + "" + '"'; + + // Just for debugging(it took me 1hr for this string literals n stuff! It better work!) + PM_CORE_INFO("{0}", convCommand); + + // Fire the above created command + + system(convCommand.c_str()); + } + + AssetHandle AssetsManager::ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle) + { + Ref dirInfo = AssetSerializer::LoadAssetInfo(directoryPath, parentHandle, AssetType::Directory).As(); + s_LoadedAssets[dirInfo->Handle] = dirInfo; + + if (parentHandle != dirInfo->Handle && IsAssetHandleValid(parentHandle)) + s_LoadedAssets[parentHandle].As()->ChildDirectories.push_back(dirInfo->Handle); + + for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) + { + if (entry.is_directory()) + ProcessDirectory(entry.path().string(), dirInfo->Handle); + else + ImportAsset(entry.path().string(), dirInfo->Handle); + } + + return dirInfo->Handle; + } + + void AssetsManager::ReloadAssets() + { + ProcessDirectory("assets", 0); + } + + void AssetsManager::OnFileSystemChanged(FileSystemChangedEvent e) + { + e.NewName = Utils::RemoveExtension(e.NewName); + e.OldName = Utils::RemoveExtension(e.OldName); + + AssetHandle parentHandle = FindParentHandle(e.FilePath); + + switch (e.Action) + { + case FileSystemAction::Added: + { + if (e.IsDirectory) + ProcessDirectory(e.FilePath, parentHandle); + else + ImportAsset(e.FilePath, parentHandle); + } + break; + case FileSystemAction::Modified: + { + if (!e.IsDirectory) + ImportAsset(e.FilePath, parentHandle); + } + break; + case FileSystemAction::Rename: + { + for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); it++) + { + if (it->second->FileName == e.OldName) + { + it->second->FilePath = e.FilePath; + it->second->FileName = e.NewName; + } + } + } + break; + case FileSystemAction::Delete: + { + for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); it++) + { + if (it->second->FilePath != e.FilePath) + continue; + + RemoveAsset(it->first); + break; + } + } + break; + } + + + if (s_AssetsChangeCallback) + s_AssetsChangeCallback(); + } + + AssetHandle AssetsManager::FindParentHandleInChildren(Ref& dir, const std::string& dirName) + { + if (dir->FileName == dirName) + return dir->Handle; + + for (const AssetHandle& childHandle : dir->ChildDirectories) + { + Ref child = GetAsset(childHandle); + AssetHandle dirHandle = FindParentHandleInChildren(child, dirName); + + if (IsAssetHandleValid(dirHandle)) + return dirHandle; + } + + return 0; + } + + AssetHandle AssetsManager::FindParentHandle(const std::string& filepath) + { + const std::vector parts = Utils::SplitString(filepath, "/\\"); + const std::string& parentFolder = parts[parts.size() - 2]; + Ref assetsDirectory = GetAsset(0); + return FindParentHandleInChildren(assetsDirectory, parentFolder); + } +} diff --git a/Prism/src/Prism/Asset/AssetsManager.h b/Prism/src/Prism/Asset/AssetsManager.h new file mode 100644 index 0000000..55af083 --- /dev/null +++ b/Prism/src/Prism/Asset/AssetsManager.h @@ -0,0 +1,86 @@ +// +// Created by Atdunbg on 2026/1/20. +// + +#ifndef PRISM_ASSETSMANAGER_H +#define PRISM_ASSETSMANAGER_H + +#include + +#include "Asset.h" +#include "Prism/Utilities/FileSystem.h" +#include "Prism/Core/Ref.h" + +namespace Prism +{ + + class AssetTypes + { + public: + static void Init(); + static size_t GetAssetTypeID(const std::string& extension); + static AssetType GetAssetTypeFromExtension(const std::string& extension); + + private: + static std::map s_Types; + }; + + class PRISM_API AssetsManager + { + public: + using AssetsChangeEventFn = std::function; + + public: + static void Init(); + static void SetAssetChangeCallback(const AssetsChangeEventFn& callback); + static void Shutdown(); + + static std::vector> GetAssetsInDirectory(AssetHandle directoryHandle); + + static std::vector> SearchFiles(const std::string& query, const std::string& searchPath); + + static std::string GetParentPath(const std::string& path); + + static bool IsDirectory(const std::string& filepath); + + static AssetHandle GetAssetIDForFile(const std::string& filepath); + static bool IsAssetHandleValid(const AssetHandle& assetHandle); + + static void Rename(Ref& asset, const std::string& newName); + + static Ref CreateAssetPhysicsMaterial(const std::string& filename, AssetType type, const AssetHandle& directoryHandle, float v1, float v2, float v3); + + static void RemoveAsset(AssetHandle assetHandle); + + template + static Ref CreateAsset(const std::string& filename, AssetType type, AssetHandle directoryHandle, Args&&... args); + + template + static Ref GetAsset(AssetHandle assetHandle); + + static bool IsAssetType(AssetHandle assetHandle, AssetType type); + + static std::string StripExtras(const std::string& filename); + + private: + + static void ImportAsset(const std::string& filepath, AssetHandle parentHandle); + static void ConvertAsset(const std::string& assetPath, const std::string& conversionType); + static AssetHandle ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle); + static void ReloadAssets(); + + static void OnFileSystemChanged(FileSystemChangedEvent e); + + static AssetHandle FindParentHandleInChildren(Ref& dir, const std::string& dirName); + static AssetHandle FindParentHandle(const std::string& filepath); + + private: + static std::unordered_map> s_LoadedAssets; + static AssetsChangeEventFn s_AssetsChangeCallback; + + }; + +} + + +#endif //PRISM_ASSETSMANAGER_H \ No newline at end of file diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index ca9e11a..f7b4f65 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -13,7 +13,7 @@ #include "tinyfiledialogs.h" #include "Prism/Physics/Physics3D.h" #include "Prism/Script/ScriptEngine.h" -#include "Prism/Utilities/AssetsManager.h" +#include "Prism/Asset/AssetsManager.h" namespace Prism { @@ -55,6 +55,7 @@ namespace Prism Physics3D::Shutdown(); ScriptEngine::Shutdown(); + AssetsManager::Shutdown(); } void Application::Run() diff --git a/Prism/src/Prism/Core/ImGui/ImGui.h b/Prism/src/Prism/Core/ImGui/ImGui.h index d482ab3..01c078f 100644 --- a/Prism/src/Prism/Core/ImGui/ImGui.h +++ b/Prism/src/Prism/Core/ImGui/ImGui.h @@ -9,6 +9,9 @@ #include #include +#include "../../Asset/Asset.h" +#include "../../Asset/AssetsManager.h" + namespace Prism::UI { static int s_UIContextID = 0; @@ -143,7 +146,7 @@ namespace Prism::UI { return modified; } - static bool Property(const char* label, float& value, const float delta = 0.1f, const float min = 0.0f, const float max = 0.0f) + static bool Property(const char* label, float& value, const float delta = 0.1f, const float min = 0.0f, const float max = 0.0f, const bool readOnly = false) { bool modified = false; @@ -155,8 +158,16 @@ namespace Prism::UI { s_IDBuffer[1] = '#'; memset(s_IDBuffer + 2, 0, 14); snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - if (ImGui::DragFloat(s_IDBuffer, &value, delta, min, max)) - modified = true; + + if (!readOnly) + { + if (ImGui::DragFloat(s_IDBuffer, &value, delta, min, max)) + modified = true; + } + else + { + ImGui::InputFloat(s_IDBuffer, &value, 0.0f, 0.0f, "%.3f", ImGuiInputTextFlags_ReadOnly); + } ImGui::PopItemWidth(); ImGui::NextColumn(); @@ -248,6 +259,46 @@ namespace Prism::UI { return modified; } + template + static bool PropertyAssetReference(const char* label, Ref& object, AssetType supportedType) + { + bool modified = false; + + ImGui::Text("%s", label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + if (object) + { + char* assetName = ((Ref&)object)->FileName.data(); + ImGui::InputText("##assetRef", assetName, 256, ImGuiInputTextFlags_ReadOnly); + } + else + { + ImGui::InputText("##assetRef", (char*)"Null", 256, ImGuiInputTextFlags_ReadOnly); + } + + + if (ImGui::BeginDragDropTarget()) + { + auto data = ImGui::AcceptDragDropPayload("asset_payload"); + + if (data) + { + AssetHandle assetHandle = *(AssetHandle*)data->Data; + if (AssetsManager::IsAssetType(assetHandle, supportedType)) + { + object = AssetsManager::GetAsset(assetHandle); + modified = true; + } + } + } + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + return modified; + } + static void EndPropertyGrid() { ImGui::Columns(1); diff --git a/Prism/src/Prism/Core/Ref.h b/Prism/src/Prism/Core/Ref.h index 8c1d8fe..3759d88 100644 --- a/Prism/src/Prism/Core/Ref.h +++ b/Prism/src/Prism/Core/Ref.h @@ -123,6 +123,12 @@ namespace Prism m_Instance = instance; } + template + Ref As() + { + return Ref(*this); + } + template static Ref Create(Args&&... args) { diff --git a/Prism/src/Prism/Editor/AssetEditorPanel.cpp b/Prism/src/Prism/Editor/AssetEditorPanel.cpp new file mode 100644 index 0000000..16b6606 --- /dev/null +++ b/Prism/src/Prism/Editor/AssetEditorPanel.cpp @@ -0,0 +1,85 @@ +// +// Created by Atdunbg on 2026/2/14. +// + +#include "AssetEditorPanel.h" + +#include "DefaultAssetEditors.h" +#include "Prism/Core/Log.h" + +namespace Prism +{ + AssetEditor::AssetEditor(const char* title) + : m_Title(title), m_MinSize(200, 400), m_MaxSize(2000, 2000) + { + } + + void AssetEditor::OnImGuiRender() + { + if (!m_IsOpen) + return; + + // NOTE(Peter): SetNextWindowSizeConstraints requires a max constraint that's above 0. For now we're just setting it to a large value + ImGui::SetNextWindowSizeConstraints(m_MinSize, m_MaxSize); + ImGui::Begin(m_Title, &m_IsOpen, m_Flags); + Render(); + ImGui::End(); + } + + void AssetEditor::SetMinSize(uint32_t width, uint32_t height) + { + if (width <= 0) width = 200; + if (height <= 0) height = 400; + + m_MinSize = ImVec2((float)width, (float)height); + } + + void AssetEditor::SetMaxSize(uint32_t width, uint32_t height) + { + if (width <= 0) width = 2000; + if (height <= 0) height = 2000; + if (width <= (uint32_t)m_MinSize.x) width = (uint32_t)m_MinSize.x * 2; + if (height <= (uint32_t)m_MinSize.y) height = (uint32_t)m_MinSize.y * 2; + + m_MaxSize = ImVec2((float)width, (float)height); + } + + void AssetEditorPanel::RegisterDefaultEditors() + { + RegisterEditor(AssetType::Texture); + RegisterEditor(AssetType::PhysicsMat); + } + + void AssetEditorPanel::OnImGuiRender() + { + for (const auto& kv : s_Editors) + kv.second->OnImGuiRender(); + } + + void AssetEditorPanel::OpenEditor(const Ref& asset) + { + if (s_Editors.find(asset->Type) == s_Editors.end()) + { + PM_CORE_WARN("No editor registered for {0} assets", asset->Extension); + return; + } + + s_Editors[asset->Type]->SetOpen(true); + s_Editors[asset->Type]->SetAsset(asset); + } + + template + void AssetEditorPanel::RegisterEditor(const AssetType type) + { + static_assert(std::is_base_of_v, "AssetEditorPanel::RegisterEditor requires template type to inherit from AssetEditor"); + PM_CORE_ASSERT(s_Editors.find(type) == s_Editors.end(), "There's already an editor for that asset!"); + s_Editors[type] = CreateScope(); + } + + template PRISM_API void AssetEditorPanel::RegisterEditor(AssetType); + template PRISM_API void AssetEditorPanel::RegisterEditor(AssetType); + + std::unordered_map> AssetEditorPanel::s_Editors; + + +} diff --git a/Prism/src/Prism/Editor/AssetEditorPanel.h b/Prism/src/Prism/Editor/AssetEditorPanel.h new file mode 100644 index 0000000..01f15a4 --- /dev/null +++ b/Prism/src/Prism/Editor/AssetEditorPanel.h @@ -0,0 +1,56 @@ +// +// Created by Atdunbg on 2026/2/14. +// + +#ifndef PRISM_ASSETEDITORPANEL_H +#define PRISM_ASSETEDITORPANEL_H + +#include "Prism/Core/ImGui/ImGui.h" + +namespace Prism +{ + class AssetEditor + { + + public: + virtual ~AssetEditor(){} + + void OnImGuiRender(); + void SetOpen(const bool isOpen) { m_IsOpen = isOpen; } + virtual void SetAsset(const Ref& asset) = 0; + + protected: + AssetEditor(const char* title); + + protected: + void SetMinSize(uint32_t width, uint32_t height); + void SetMaxSize(uint32_t width, uint32_t height); + + private: + virtual void Render() = 0; + + private: + const char* m_Title; + bool m_IsOpen = false; + + ImGuiWindowFlags m_Flags = 0; + ImVec2 m_MinSize, m_MaxSize; + }; + class PRISM_API AssetEditorPanel + { + public: + static void RegisterDefaultEditors(); + static void OnImGuiRender(); + static void OpenEditor(const Ref& asset); + + template + static void RegisterEditor(AssetType type); + + private: + static std::unordered_map> s_Editors; + }; + +} + + +#endif //PRISM_ASSETEDITORPANEL_H \ No newline at end of file diff --git a/Prism/src/Prism/Editor/AssetsManagerPanel.cpp b/Prism/src/Prism/Editor/AssetsManagerPanel.cpp index f56e94f..1e8201f 100644 --- a/Prism/src/Prism/Editor/AssetsManagerPanel.cpp +++ b/Prism/src/Prism/Editor/AssetsManagerPanel.cpp @@ -6,7 +6,9 @@ #include +#include "AssetEditorPanel.h" #include "Prism/Core/Application.h" +#include "Prism/Core/Input.h" #include "Prism/Core/Log.h" namespace Prism @@ -15,17 +17,13 @@ namespace Prism { AssetsManager::SetAssetChangeCallback([&]() { - UpdateCurrentDirectory(m_CurrentDirIndex); + UpdateCurrentDirectory(m_CurrentDirHandle); }); m_FolderTex = Texture2D::Create("assets/editor/folder.png"); - m_FavoritesTex = Texture2D::Create("assets/editor/favourites.png"); - m_GoBackTex = Texture2D::Create("assets/editor/back.png"); - m_ScriptTex = Texture2D::Create("assets/editor/script.png"); - m_ResourceTex = Texture2D::Create("assets/editor/resource.png"); - m_SceneTex = Texture2D::Create("assets/editor/scene.png"); m_AssetIconMap[-1] = Texture2D::Create("assets/editor/file.png"); + m_AssetIconMap[0] = m_FolderTex; 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"); @@ -41,361 +39,371 @@ namespace Prism m_FwrdbtnTex = Texture2D::Create("assets/editor/btn_fwrd.png"); m_FolderRightTex = Texture2D::Create("assets/editor/folder_hierarchy.png"); m_SearchTex = Texture2D::Create("assets/editor/search.png"); - m_TagsTex = Texture2D::Create("assets/editor/tags.png"); - m_GridView = Texture2D::Create("assets/editor/grid.png"); - m_ListView = Texture2D::Create("assets/editor/list.png"); - m_BaseDirIndex = 0; - m_CurrentDirIndex = 0; - m_PrevDirIndex = 0; - m_NextDirIndex = 0; + m_BaseDirectoryHandle = 0; + m_BaseDirectory = AssetsManager::GetAsset(m_BaseDirectoryHandle); + UpdateCurrentDirectory(m_BaseDirectoryHandle); - m_BaseProjectDir = AssetsManager::GetDirectoryInfo(m_BaseDirIndex); - UpdateCurrentDirectory(m_BaseDirIndex); + memset(m_InputBuffer, 0, MAX_INPUT_BUFFER_LENGTH); } + static int s_ColumnCount = 10; + void AssetsManagerPanel::OnImGuiRender() { - ImGui::Begin("Project", nullptr, ImGuiWindowFlags_MenuBar); + ImGui::Begin("Project", nullptr, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar); { + // list folder (Contents) UI::BeginPropertyGrid(); - ImGui::SetColumnOffset(1, 250); - - ImGui::BeginChild("##folders_common"); { - if (ImGui::CollapsingHeader("Contents", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) + ImGui::SetColumnOffset(1, 250); + + ImGui::BeginChild("##folders_common"); { - for (const int ChildrenIndice : m_BaseProjectDir.ChildrenIndices) + if (ImGui::CollapsingHeader("Contents", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) { - DrawDirectoryInfo(AssetsManager::GetDirectoryInfo(ChildrenIndice)); + for (const AssetHandle& child : m_BaseDirectory->ChildDirectories) + { + DrawDirectoryInfo(child); + } } } - } - ImGui::EndChild(); + ImGui::EndChild(); - if (ImGui::BeginDragDropTarget()) - { - const auto payload = ImGui::AcceptDragDropPayload("selectable", ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if (payload) + ImGui::NextColumn(); + + // Grid and list files + ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetWindowHeight() - 60)); + // ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetContentRegionAvail().y)); { - const std::string file = static_cast(payload->Data); - if (AssetsManager::MoveFile(file, m_MovePath)) + ImGui::BeginChild("##directory_breadcrumbs", ImVec2(ImGui::GetColumnWidth() - 100, 30)); + RenderBreadCrumbs(); + ImGui::EndChild(); + + ImGui::BeginChild("Scrolling"); { - PM_CORE_INFO("Moved File: " + file + " to " + m_MovePath); - UpdateCurrentDirectory(m_CurrentDirIndex); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + + if (Input::IsKeyPressed(KeyCode::ESCAPE) || (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !m_IsAnyItemHovered)) + { + m_SelectedAssets.Clear(); + m_RenamingSelected = false; + } + + m_IsAnyItemHovered = false; + + + + if (ImGui::BeginPopupContextWindow()) + { + if (ImGui::BeginMenu("Create")) + { + if (ImGui::MenuItem("Folder")) + { + PM_CORE_INFO("Creating Folder..."); + const bool created = FileSystem::CreateFolder(m_CurrentDirectory->FilePath + "/New Folder"); + if (created) + { + UpdateCurrentDirectory(m_CurrentDirHandle); + const auto& createdDirectory = AssetsManager::GetAsset(AssetsManager::GetAssetIDForFile(m_CurrentDirectory->FilePath + "/New Folder")); + m_SelectedAssets.Select(createdDirectory->Handle); + memset(m_InputBuffer, 0, MAX_INPUT_BUFFER_LENGTH); + memcpy(m_InputBuffer, createdDirectory->FileName.c_str(), createdDirectory->FileName.size()); + m_RenamingSelected = true; + } + } + + if (ImGui::MenuItem("Scene")) + { + PM_CORE_INFO("Creating Scene..."); + } + + if (ImGui::MenuItem("Script")) + { + PM_CORE_INFO("Creating Script..."); + } + + if (ImGui::MenuItem("Prefab")) + { + PM_CORE_INFO("Creating Prefab..."); + } + + ImGui::EndMenu(); + } + + if (ImGui::MenuItem("Import")) + { + //std::string filename = Application::Get().OpenFile(""); + } + + if (ImGui::MenuItem("Refresh")) + { + UpdateCurrentDirectory(m_CurrentDirHandle); + } + + if (ImGui::MenuItem("Physics Material")) + { + // TODO: use template + AssetsManager::CreateAssetPhysicsMaterial("New Physics Material.hpm", AssetType::PhysicsMat, m_CurrentDirHandle, 0.6f, 0.6f, 0.0f); + UpdateCurrentDirectory(m_CurrentDirHandle); + } + + ImGui::EndPopup(); + } + + ImGui::Columns(s_ColumnCount, nullptr, false); + + if (!m_CurrentDirAssets.empty()) + { + for (Ref& asset : m_CurrentDirAssets) + { + if (m_SkipRenderingThisFrame) + { + m_SkipRenderingThisFrame = false; + break; + } + RenderAsset(asset); + ImGui::NextColumn(); + } + } + + if (m_IsDragging && !ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0.1f)) + { + m_IsDragging = false; + m_DraggedAssetId = 0; + } + + ImGui::PopStyleColor(); } - m_IsDragging = false; + ImGui::EndChild(); } - ImGui::EndDragDropTarget(); + ImGui::EndChild(); } + ImGui::BeginChild("##panel_controls", ImVec2(ImGui::GetColumnWidth() - 12, 20), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + ImGui::Columns(4, 0, false); ImGui::NextColumn(); - - ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetWindowHeight() - 50)); - { - ImGui::BeginChild("##directory_breadcrumbs", ImVec2(ImGui::GetColumnWidth() - 100, 30)); - RenderBreadCrumbs(); - ImGui::EndChild(); - - ImGui::BeginChild("Scrolling"); - - if (!m_DisplayListView) - ImGui::Columns(10, nullptr, false); - - for (const DirectoryInfo& dir : m_CurrentDirChildren) - { - if (m_DisplayListView) - RenderDirectoriesListView(dir.DirectoryIndex); - else - RenderDirectoriesGridView(dir.DirectoryIndex); - - ImGui::NextColumn(); - } - - for (Asset& asset : m_CurrentDirAssets) - { - if (m_DisplayListView) - RenderFileListView(asset); - else - RenderFileGridView(asset); - - ImGui::NextColumn(); - } - - if (ImGui::BeginPopupContextWindow()) - { - if (ImGui::BeginMenu("New")) - { - if (ImGui::MenuItem("Folder")) - { - PM_CORE_INFO("Creating Folder..."); - } - - if (ImGui::MenuItem("Scene")) - { - PM_CORE_INFO("Creating Scene..."); - } - - if (ImGui::MenuItem("Script")) - { - PM_CORE_INFO("Creating Script..."); - } - - if (ImGui::MenuItem("Prefab")) - { - PM_CORE_INFO("Creating Prefab..."); - } - - ImGui::EndMenu(); - } - - ImGui::EndPopup(); - } - ImGui::EndChild(); - } + ImGui::NextColumn(); + ImGui::NextColumn(); + ImGui::SetNextItemWidth(ImGui::GetColumnWidth()); + ImGui::SliderInt("##column_count", &s_ColumnCount, 2, 15); ImGui::EndChild(); - - if (ImGui::BeginDragDropTarget()) - { - const auto payload = ImGui::AcceptDragDropPayload("selectable", ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if (payload) - { - const std::string data = static_cast(payload->Data); - if (AssetsManager::MoveFile(data, m_MovePath)) - { - PM_CORE_INFO("Moved File: " + data + " to " + m_MovePath); - UpdateCurrentDirectory(m_CurrentDirIndex); - } - m_IsDragging = false; - } - ImGui::EndDragDropTarget(); - } - - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Create")) - { - if (ImGui::MenuItem("Import New Asset", "Ctrl + O")) - { - const std::string filename = Application::Get().OpenFile(""); - // TODO: - } - - if (ImGui::MenuItem("Refresh", "Ctrl + R")) - { - m_BaseProjectDir = AssetsManager::GetDirectoryInfo(m_BaseDirIndex); - UpdateCurrentDirectory(m_CurrentDirIndex); - } - ImGui::EndMenu(); - - } - ImGui::EndMenuBar(); - } UI::EndPropertyGrid(); } ImGui::End(); } - void AssetsManagerPanel::DrawDirectoryInfo(const DirectoryInfo& dir) + void AssetsManagerPanel::DrawDirectoryInfo(const AssetHandle& directory) { - if (ImGui::TreeNode(dir.DirectoryName.c_str())) + const Ref& dir = AssetsManager::GetAsset(directory); + + if (ImGui::TreeNode(dir->FileName.c_str())) { if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - UpdateCurrentDirectory(dir.DirectoryIndex); + UpdateCurrentDirectory(directory); - for (const int ChildrenIndice : dir.ChildrenIndices) + for (AssetHandle child : dir->ChildDirectories) { - DirectoryInfo& child = AssetsManager::GetDirectoryInfo(ChildrenIndice); DrawDirectoryInfo(child); } ImGui::TreePop(); } - - if (m_IsDragging && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - { - m_MovePath = dir.FilePath; - } } - void AssetsManagerPanel::RenderFileListView(Asset& asset) + + void AssetsManagerPanel::RenderAsset(Ref& asset) { - const size_t fileID = AssetTypes::GetAssetTypeID(asset.Extension); - const RendererID iconRef = m_AssetIconMap[fileID]->GetRendererID(); - ImGui::Image((ImTextureID)iconRef, ImVec2(20, 20)); + // These caches are currently required for when we change directories + AssetHandle assetHandle = asset->Handle; + std::string filename = asset->FileName; - ImGui::SameLine(); - - if (ImGui::Selectable(asset.FilePath.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick)) - { - // if (ImGui::IsMouseDoubleClicked(0)) - // m_AssetManager.HandleAsset(m_CurrentDir[dirIndex].AbsolutePath); - // TODO: - } - - HandleDragDrop(iconRef, asset); - } - - void AssetsManagerPanel::RenderFileGridView(Asset& asset) - { + ImGui::PushID(&asset->Handle); ImGui::BeginGroup(); - const size_t fileID = AssetTypes::GetAssetTypeID(asset.Extension); + size_t fileID = AssetTypes::GetAssetTypeID(asset->Extension); + fileID = m_AssetIconMap.find(fileID) != m_AssetIconMap.end() ? fileID : -1; const RendererID iconRef = m_AssetIconMap[fileID]->GetRendererID(); - const float columnWidth = ImGui::GetColumnWidth(); - ImGui::ImageButton(asset.FilePath.c_str(), (ImTextureID)iconRef, { columnWidth - 10.0f, columnWidth - 10.0f }); + if (m_SelectedAssets.IsSelected(assetHandle)) + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.25f, 0.25f, 0.25f, 0.75f)); + + float buttonWidth = ImGui::GetColumnWidth() - 15.0F; + ImGui::ImageButton("##AssetButton", (ImTextureID)iconRef, { buttonWidth, buttonWidth }); + + if (m_SelectedAssets.IsSelected(assetHandle)) + ImGui::PopStyleColor(); HandleDragDrop(iconRef, asset); - 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(const RendererID icon, const Asset& asset) - { - // Drag 'n' Drop Implementation For File Moving - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) + if (ImGui::IsItemHovered()) { - ImGui::Image((ImTextureID)icon, ImVec2(20, 20)); - ImGui::SameLine(); - ImGui::Text("%s", asset.FilePath.c_str()); - ImGui::SetDragDropPayload("selectable", asset.FilePath.c_str(), asset.FilePath.length()); - m_IsDragging = true; - ImGui::EndDragDropSource(); - } + m_IsAnyItemHovered = true; - // Drag 'n' Drop Implementation For Asset Handling In Scene Viewport - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image((ImTextureID)icon, ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("%s", asset.FilePath.c_str()); - ImGui::SetDragDropPayload("scene_entity_assetsP", &asset.ID, sizeof(UUID)); - m_IsDragging = true; - - ImGui::EndDragDropSource(); - } - } - - void AssetsManagerPanel::RenderDirectoriesListView(int dirIndex) - { - ImGui::Image((ImTextureID)m_FolderTex->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - const auto& folderData = AssetsManager::GetDirectoryInfo(dirIndex); - - if (ImGui::Selectable(folderData.DirectoryName.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick)) - { - if (ImGui::IsMouseDoubleClicked(0)) + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - m_PrevDirIndex = m_CurrentDirIndex; - UpdateCurrentDirectory(dirIndex); - } - } - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) - { - ImGui::Image((ImTextureID)m_FolderTex->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("%s", folderData.DirectoryName.c_str()); - ImGui::SetDragDropPayload("selectable", &dirIndex, sizeof(int)); - - m_IsDragging = true; - ImGui::EndDragDropSource(); - } - } - - void AssetsManagerPanel::RenderDirectoriesGridView(int dirIndex) - { - 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 }); - - const auto& folderData = AssetsManager::GetDirectoryInfo(dirIndex); - - if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) - { - m_PrevDirIndex = m_CurrentDirIndex; - UpdateCurrentDirectory(dirIndex); - } - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) - { - ImGui::Image((ImTextureID)m_FolderTex->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("%s", folderData.DirectoryName.c_str()); - ImGui::SetDragDropPayload("selectable_directory", &dirIndex, sizeof(int)); - - m_IsDragging = true; - ImGui::EndDragDropSource(); - } - - ImGui::TextWrapped("%s", folderData.DirectoryName.c_str()); - ImGui::EndGroup(); - } - - void AssetsManagerPanel::RenderBreadCrumbs() - { - 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)) - { - const SearchResults results = AssetsManager::SearchFiles(m_InputBuffer, m_CurrentDir.FilePath); - - if (strlen(m_InputBuffer) == 0) + if (asset->Type == AssetType::Directory) { - UpdateCurrentDirectory(m_CurrentDirIndex); + m_PrevDirHandle = m_CurrentDirHandle; + UpdateCurrentDirectory(assetHandle); + m_SkipRenderingThisFrame = true; } else { - m_CurrentDirChildren = results.Directories; - m_CurrentDirAssets = results.Assets; + AssetEditorPanel::OpenEditor(asset); + } + } + + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !m_IsDragging) + { + if (!Input::IsKeyPressed(KeyCode::LEFT_CONTROL)) + m_SelectedAssets.Clear(); + + if (m_SelectedAssets.IsSelected(assetHandle)) + m_SelectedAssets.Deselect(assetHandle); + else + m_SelectedAssets.Select(assetHandle); + } + } + + bool shouldOpenDeleteModal = false; + + if (ImGui::BeginPopupContextItem()) + { + if (ImGui::MenuItem("Rename")) + { + m_SelectedAssets.Select(assetHandle); + memset(m_InputBuffer, 0, MAX_INPUT_BUFFER_LENGTH); + memcpy(m_InputBuffer, filename.c_str(), filename.size()); + m_RenamingSelected = true; + } + + if (ImGui::MenuItem("Delete")) + shouldOpenDeleteModal = true; + + ImGui::EndPopup(); + } + + if (shouldOpenDeleteModal) + { + ImGui::OpenPopup("Delete Asset"); + shouldOpenDeleteModal = false; + } + + bool deleted = false; + if (ImGui::BeginPopupModal("Delete Asset", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + if (asset->Type == AssetType::Directory) + ImGui::Text("Are you sure you want to delete %s and everything within it?", filename.c_str()); + else + ImGui::Text("Are you sure you want to delete %s?", filename.c_str()); + + const float columnWidth = ImGui::GetContentRegionAvail().x / 4.0f; + + ImGui::Columns(4, 0, false); + ImGui::SetColumnWidth(0, columnWidth); + ImGui::SetColumnWidth(1, columnWidth); + ImGui::SetColumnWidth(2, columnWidth); + ImGui::SetColumnWidth(3, columnWidth); + ImGui::NextColumn(); + if (ImGui::Button("Yes", ImVec2(columnWidth, 0))) + { + // Cache this so that we can delete the meta file if the asset was deleted successfully + std::string filepath = asset->FilePath; + deleted = FileSystem::PrismDeleteFile(filepath); + if (deleted) + { + FileSystem::PrismDeleteFile(filepath + ".meta"); + AssetsManager::RemoveAsset(assetHandle); + m_SkipRenderingThisFrame = true; + UpdateCurrentDirectory(m_CurrentDirHandle); + } + + ImGui::CloseCurrentPopup(); + } + + ImGui::NextColumn(); + ImGui::SetItemDefaultFocus(); + if (ImGui::Button("No", ImVec2(columnWidth, 0))) + ImGui::CloseCurrentPopup(); + + ImGui::NextColumn(); + ImGui::EndPopup(); + } + + if (!deleted) + { + ImGui::SetNextItemWidth(buttonWidth); + + if (!m_SelectedAssets.IsSelected(assetHandle) || !m_RenamingSelected) + ImGui::TextWrapped("%s", filename.c_str()); + + if (m_SelectedAssets.IsSelected(assetHandle)) + HandleRenaming(asset); + } + + ImGui::EndGroup(); + ImGui::PopID(); + } + + void AssetsManagerPanel::HandleDragDrop(const RendererID icon, const Ref& asset) + { + if (!m_SelectedAssets.IsSelected(asset->Handle) || m_IsDragging) + return; + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped) && ImGui::IsItemClicked(ImGuiMouseButton_Left)) + m_IsDragging = true; + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) + { + ImGui::Image((ImTextureID)icon, ImVec2(20, 20)); + ImGui::SameLine(); + + ImGui::Text("%s", asset->FileName.c_str()); + ImGui::SetDragDropPayload("asset_payload", m_SelectedAssets.GetSelectionData(), m_SelectedAssets.SelectionCount() * sizeof(AssetHandle)); + + m_IsDragging = true; + ImGui::EndDragDropSource(); + } + } + + + void AssetsManagerPanel::RenderBreadCrumbs() + { + if (ImGui::ImageButton("##backbtn", (ImTextureID)m_BackbtnTex->GetRendererID(), ImVec2(20, 18))) + { + if (m_CurrentDirHandle == m_BaseDirectoryHandle) return; + m_NextDirHandle = m_CurrentDirHandle; + m_PrevDirHandle = m_CurrentDirectory->ParentDirectory; + UpdateCurrentDirectory(m_PrevDirHandle); + } + + ImGui::SameLine(); + + if (ImGui::ImageButton("##fwrdbtn", (ImTextureID)m_FwrdbtnTex->GetRendererID(), ImVec2(20, 18))) + { + UpdateCurrentDirectory(m_NextDirHandle); + } + + ImGui::SameLine(); + + { + ImGui::PushItemWidth(200); + char* buf = m_InputBuffer; + if (m_RenamingSelected) + buf = '\0'; + + if (ImGui::InputTextWithHint("##asset_panel_search", "Search...", buf, MAX_INPUT_BUFFER_LENGTH)) + { + if (strlen(m_InputBuffer) == 0) + { + UpdateCurrentDirectory(m_CurrentDirHandle); + } + else + { + m_CurrentDirAssets = AssetsManager::SearchFiles(m_InputBuffer, m_CurrentDirectory->FilePath); } } @@ -403,18 +411,20 @@ namespace Prism } ImGui::SameLine(); + if (m_UpdateBreadCrumbs) { m_BreadCrumbData.clear(); - int currentDirIndex = m_CurrentDirIndex; - while (currentDirIndex != -1) + AssetHandle currentHandle = m_CurrentDirHandle; + while (currentHandle != 0) { - DirectoryInfo& dirInfo = AssetsManager::GetDirectoryInfo(currentDirIndex); + const Ref& dirInfo = AssetsManager::GetAsset(currentHandle); m_BreadCrumbData.push_back(dirInfo); - currentDirIndex = dirInfo.ParentIndex; + currentHandle = dirInfo->ParentDirectory; } + m_BreadCrumbData.push_back(m_BaseDirectory); std::reverse(m_BreadCrumbData.begin(), m_BreadCrumbData.end()); m_UpdateBreadCrumbs = false; @@ -422,16 +432,16 @@ namespace Prism for (auto & i : m_BreadCrumbData) { - if (i.DirectoryName != "assets") + if (i->FileName != "assets") ImGui::Text("/"); ImGui::SameLine(); - const int size = (int)i.DirectoryName.length() * 7; + const float size = (float)strlen(i->FileName.c_str()) * 7; - if (ImGui::Selectable(i.DirectoryName.c_str(), false, 0, ImVec2((float)size, 22))) + if (ImGui::Selectable(i->FileName.c_str(), false, 0, ImVec2(size, 22))) { - UpdateCurrentDirectory(i.DirectoryIndex); + UpdateCurrentDirectory(i->Handle); } ImGui::SameLine(); @@ -441,31 +451,45 @@ namespace Prism ImGui::Dummy(ImVec2(ImGui::GetColumnWidth() - 400, 0)); ImGui::SameLine(); + } - void AssetsManagerPanel::RenderBottom() + void AssetsManagerPanel::HandleRenaming(Ref& asset) { - /* Will be used for object select indication. Ex. 3 folders 1 file selected */ - ImGui::BeginChild("##nav", ImVec2(ImGui::GetColumnWidth() - 12, 23)); + if (m_SelectedAssets.SelectionCount() > 1) + return; + + if (!m_RenamingSelected && Input::IsKeyPressed(KeyCode::F2)) { - ImGui::EndChild(); + memset(m_InputBuffer, 0, MAX_INPUT_BUFFER_LENGTH); + memcpy(m_InputBuffer, asset->FileName.c_str(), asset->FileName.size()); + m_RenamingSelected = true; + } + + if (m_RenamingSelected) + { + ImGui::SetKeyboardFocusHere(); + if (ImGui::InputText("##rename_dummy", m_InputBuffer, MAX_INPUT_BUFFER_LENGTH, ImGuiInputTextFlags_EnterReturnsTrue)) + { + PM_CORE_INFO("Renaming to {0}", m_InputBuffer); + AssetsManager::Rename(asset, m_InputBuffer); + m_RenamingSelected = false; + m_SelectedAssets.Clear(); + m_SkipRenderingThisFrame = true; + UpdateCurrentDirectory(m_CurrentDirHandle); + } } } - void AssetsManagerPanel::UpdateCurrentDirectory(int dirIndex) + + void AssetsManagerPanel::UpdateCurrentDirectory(AssetHandle directoryHandle) { - if (m_CurrentDirIndex != dirIndex) + if (m_CurrentDirHandle != directoryHandle) 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); + m_CurrentDirHandle = directoryHandle; + m_CurrentDirectory = AssetsManager::GetAsset(m_CurrentDirHandle); + m_CurrentDirAssets = AssetsManager::GetAssetsInDirectory(m_CurrentDirHandle); } } diff --git a/Prism/src/Prism/Editor/AssetsManagerPanel.h b/Prism/src/Prism/Editor/AssetsManagerPanel.h index 897322c..81438d8 100644 --- a/Prism/src/Prism/Editor/AssetsManagerPanel.h +++ b/Prism/src/Prism/Editor/AssetsManagerPanel.h @@ -5,12 +5,65 @@ #ifndef PRISM_ASSETSMANAGERPANEL_H #define PRISM_ASSETSMANAGERPANEL_H -#include "Prism/Utilities/AssetsManager.h" +#include "Prism/Asset/AssetsManager.h" #include "Prism/Renderer/Texture.h" #include "Prism/Core/ImGui/ImGui.h" +#define MAX_INPUT_BUFFER_LENGTH 128 + namespace Prism { + template + struct SelectionStack + { + public: + void Select(T item) + { + m_Selections.push_back(item); + } + + void Deselect(T item) + { + for (auto it = m_Selections.begin(); it != m_Selections.end(); it++) + { + if (*it == item) + { + m_Selections.erase(it); + break; + } + } + } + + bool IsSelected(T item) const + { + for (auto selection : m_Selections) + { + if (selection == item) + return true; + } + + return false; + } + + void Clear() + { + m_Selections.clear(); + } + + size_t SelectionCount() const + { + return m_Selections.size(); + } + + T* GetSelectionData() + { + return m_Selections.data(); + } + + private: + std::vector m_Selections; + }; + class PRISM_API AssetsManagerPanel { public: @@ -18,61 +71,47 @@ namespace Prism void OnImGuiRender(); private: - void DrawDirectoryInfo(const DirectoryInfo& dir); + void DrawDirectoryInfo(const AssetHandle& directory); - void RenderFileListView(Asset& asset); - void RenderFileGridView(Asset& asset); - void HandleDragDrop(RendererID icon, const Asset& asset); - - void RenderDirectoriesListView(int dirIndex); - void RenderDirectoriesGridView(int dirIndex); + void RenderAsset(Ref& asset); + void HandleDragDrop(RendererID icon, const Ref& asset); void RenderBreadCrumbs(); - void RenderBottom(); - void UpdateCurrentDirectory(int dirIndex); + void HandleRenaming(Ref& asset); - // ImGuiInputTextCallback SearchCallback(ImGuiInputTextCallbackData* data); + void UpdateCurrentDirectory(AssetHandle directoryHandle); private: Ref m_FolderTex; - Ref m_FavoritesTex; - Ref m_FileTex; - Ref m_GoBackTex; - Ref m_ScriptTex; - Ref m_ResourceTex; - Ref m_SceneTex; - Ref m_BackbtnTex; Ref m_FwrdbtnTex; Ref m_FolderRightTex; - Ref m_TagsTex; Ref m_SearchTex; - Ref m_GridView; - Ref m_ListView; std::string m_MovePath; - 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_DirectoryChanged = false; + bool m_IsAnyItemHovered = false; + bool m_SkipRenderingThisFrame = false; - char m_InputBuffer[1024]{}; + char m_InputBuffer[MAX_INPUT_BUFFER_LENGTH]; - DirectoryInfo m_CurrentDir; - DirectoryInfo m_BaseProjectDir; - std::vector m_CurrentDirChildren; - std::vector m_CurrentDirAssets; + AssetHandle m_CurrentDirHandle; + AssetHandle m_BaseDirectoryHandle; + AssetHandle m_PrevDirHandle; + AssetHandle m_NextDirHandle; + Ref m_CurrentDirectory; + Ref m_BaseDirectory; + std::vector> m_CurrentDirAssets; - std::vector m_BreadCrumbData; + std::vector> m_BreadCrumbData; + + AssetHandle m_DraggedAssetId = 0; + SelectionStack m_SelectedAssets; + + bool m_RenamingSelected = false; - ImGuiInputTextCallbackData m_Data; std::map> m_AssetIconMap; }; } diff --git a/Prism/src/Prism/Editor/DefaultAssetEditors.cpp b/Prism/src/Prism/Editor/DefaultAssetEditors.cpp new file mode 100644 index 0000000..309d4b6 --- /dev/null +++ b/Prism/src/Prism/Editor/DefaultAssetEditors.cpp @@ -0,0 +1,56 @@ +// +// Created by Atdunbg on 2026/2/14. +// + +#include "DefaultAssetEditors.h" + +namespace Prism +{ + + // MaterialEditor + PhysicsMaterialEditor::PhysicsMaterialEditor() + : AssetEditor("Edit Physics Material") {} + + void PhysicsMaterialEditor::Render() + { + if (!m_Asset) + SetOpen(false); + + UI::BeginPropertyGrid(); + UI::Property("Static Friction", m_Asset->StaticFriction); + UI::Property("Dynamic Friction", m_Asset->DynamicFriction); + UI::Property("Bounciness", m_Asset->Bounciness); + UI::EndPropertyGrid(); + } + + + // MaterialEditor + TextureEditor::TextureEditor() + : AssetEditor("Edit Texture") + { + SetMinSize(200, 600); + SetMaxSize(500, 1000); + } + + void TextureEditor::Render() + { + if (!m_Asset) + SetOpen(false); + + float textureWidth = (float)m_Asset->GetWidth(); + float textureHeight = (float)m_Asset->GetHeight(); + float bitsPerPixel = (float)Texture::GetBPP(m_Asset->GetFormat()); + float imageSize = ImGui::GetWindowWidth() - 40; + imageSize = glm::min(imageSize, 500.0f); + + ImGui::SetCursorPosX(20); + ImGui::Image((ImTextureID)m_Asset->GetRendererID(), { imageSize, imageSize }); + + UI::BeginPropertyGrid(); + UI::Property("Width", textureWidth, 0.1f, 0.0f, 0.0f, true); + UI::Property("Height", textureHeight, 0.1f, 0.0f, 0.0f, true); + UI::Property("Bits", bitsPerPixel, 0.1f, 0.0f, 0.0f, true); + UI::EndPropertyGrid(); + } + +} \ No newline at end of file diff --git a/Prism/src/Prism/Editor/DefaultAssetEditors.h b/Prism/src/Prism/Editor/DefaultAssetEditors.h new file mode 100644 index 0000000..a8cd02b --- /dev/null +++ b/Prism/src/Prism/Editor/DefaultAssetEditors.h @@ -0,0 +1,43 @@ +// +// Created by Atdunbg on 2026/2/14. +// + +#ifndef PRISM_DEFAULTASSETEDITORS_H +#define PRISM_DEFAULTASSETEDITORS_H +#include "AssetEditorPanel.h" +#include "Prism/Renderer/Texture.h" + + +namespace Prism +{ + class PhysicsMaterialEditor : public AssetEditor + { + public: + PhysicsMaterialEditor(); + + virtual void SetAsset(const Ref& asset) override { m_Asset = (Ref)asset; } + + private: + virtual void Render() override; + + private: + Ref m_Asset; + }; + + class TextureEditor : public AssetEditor + { + public: + TextureEditor(); + + virtual void SetAsset(const Ref& asset) override { m_Asset = static_cast>(asset); } + + private: + virtual void Render() override; + + private: + Ref m_Asset; + }; + +} + +#endif //PRISM_DEFAULTASSETEDITORS_H \ No newline at end of file diff --git a/Prism/src/Prism/Editor/ObjectsPanel.cpp b/Prism/src/Prism/Editor/ObjectsPanel.cpp index 0969137..39c9bcb 100644 --- a/Prism/src/Prism/Editor/ObjectsPanel.cpp +++ b/Prism/src/Prism/Editor/ObjectsPanel.cpp @@ -6,7 +6,7 @@ #include "imgui.h" -#include "Prism/Utilities/DragDropData.h" +#include "../Asset/AssetsManager.h" namespace Prism { @@ -17,135 +17,46 @@ namespace Prism void ObjectsPanel::OnImGuiRender() { - ImGui::Begin("Objects", nullptr, ImGuiWindowFlags_None); - { - char buff[100] = {}; - const auto inputText = "InputText"; - const auto inputHint = "Start Typing To Search"; - ImGui::PushItemWidth(ImGui::GetWindowWidth() - 20); - ImGui::InputTextWithHint(inputText, inputHint, buff, 100); + static const AssetHandle CubeHandle = AssetsManager::GetAssetIDForFile("assets/meshes/Default/Cube.fbx"); + static const AssetHandle CapsuleHandle = AssetsManager::GetAssetIDForFile("assets/meshes/Default/Capsule.fbx"); + static const AssetHandle SphereHandle = AssetsManager::GetAssetIDForFile("assets/meshes/Default/Sphere.fbx"); + static const AssetHandle CylinderHandle = AssetsManager::GetAssetIDForFile("assets/meshes/Default/Cylinder.fbx"); + static const AssetHandle TorusHandle = AssetsManager::GetAssetIDForFile("assets/meshes/Default/Torus.fbx"); + static const AssetHandle PlaneHandle = AssetsManager::GetAssetIDForFile("assets/meshes/Default/Plane.fbx"); + static const AssetHandle ConeHandle = AssetsManager::GetAssetIDForFile("assets/meshes/Default/Cone.fbx"); - ImGui::BeginChild("##objects_window"); - { - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(30, 30)); - ImGui::SameLine(); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); - ImGui::Selectable("Cube"); - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("Cube"); - - const DragDropData data("Mesh", "assets/meshes/Default/Cube.fbx", "Cube"); - ImGui::SetDragDropPayload("scene_entity_objectP", &data, sizeof(data)); - ImGui::EndDragDropSource(); - } - - ImGui::Image((ImTextureID)m_CubeImage->GetRendererID(), ImVec2(30, 30)); - ImGui::SameLine(); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); - ImGui::Selectable("Capsule"); - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image((ImTextureID)m_CubeImage->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("Capsule"); - - const DragDropData data("Mesh", "assets/meshes/Default/Capsule.fbx", "Capsule"); - ImGui::SetDragDropPayload("scene_entity_objectP", &data, sizeof(data)); - ImGui::EndDragDropSource(); - } - - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(30, 30)); - ImGui::SameLine(); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); - ImGui::Selectable("Sphere"); - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("Sphere"); - const DragDropData data("Mesh", "assets/meshes/Default/Sphere.fbx", "Sphere"); - ImGui::SetDragDropPayload("scene_entity_objectP", &data, sizeof(data)); - ImGui::EndDragDropSource(); - } - - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(30, 30)); - ImGui::SameLine(); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); - ImGui::Selectable("Cylinder"); - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("Cylinder"); - const DragDropData data("Mesh", "assets/meshes/Default/Cylinder.fbx", "Cylinder"); - ImGui::SetDragDropPayload("scene_entity_objectP", &data, sizeof(data)); - ImGui::EndDragDropSource(); - } - - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(30, 30)); - ImGui::SameLine(); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); - ImGui::Selectable("Torus"); - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("Torus"); - const DragDropData data("Mesh", "assets/meshes/Default/Torus.fbx", "Torus"); - ImGui::SetDragDropPayload("scene_entity_objectP", &data, sizeof(data)); - ImGui::EndDragDropSource(); - } - - - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(30, 30)); - ImGui::SameLine(); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); - ImGui::Selectable("Plane"); - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("Plane"); - const DragDropData data("Mesh", "assets/meshes/Default/Plane.fbx", "Plane"); - ImGui::SetDragDropPayload("scene_entity_objectP", &data, sizeof(data)); - ImGui::EndDragDropSource(); - } - - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(30, 30)); - ImGui::SameLine(); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); - ImGui::Selectable("Cone"); - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) - { - ImGui::Image(m_CubeImage->GetRendererID(), ImVec2(20, 20)); - ImGui::SameLine(); - - ImGui::Text("Cone"); - const DragDropData data("Mesh", "assets/meshes/Default/Cone.fbx", "Cone"); - ImGui::SetDragDropPayload("scene_entity_objectP", &data, sizeof(data)); - ImGui::EndDragDropSource(); - } - - ImGui::EndChild(); - } - } + ImGui::Begin("Objects"); + { + ImGui::BeginChild("##objects_window"); + DrawObject("Cube", CubeHandle); + DrawObject("Capsule", CapsuleHandle); + DrawObject("Sphere", SphereHandle); + DrawObject("Cylinder", CylinderHandle); + DrawObject("Torus", TorusHandle); + DrawObject("Plane", PlaneHandle); + DrawObject("Cone", ConeHandle); + ImGui::EndChild(); + } ImGui::End(); } + + void ObjectsPanel::DrawObject(const char* label, const AssetHandle handle) + { + ImGui::Image((ImTextureID)m_CubeImage->GetRendererID(), ImVec2(30, 30)); + ImGui::SameLine(); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); + ImGui::Selectable(label); + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) + { + ImGui::Image((ImTextureID)m_CubeImage->GetRendererID(), ImVec2(20, 20)); + ImGui::SameLine(); + + ImGui::Text("%s", label); + + ImGui::SetDragDropPayload("asset_payload", &handle, sizeof(AssetHandle)); + ImGui::EndDragDropSource(); + } + } } diff --git a/Prism/src/Prism/Editor/ObjectsPanel.h b/Prism/src/Prism/Editor/ObjectsPanel.h index 09facad..92f59bb 100644 --- a/Prism/src/Prism/Editor/ObjectsPanel.h +++ b/Prism/src/Prism/Editor/ObjectsPanel.h @@ -5,6 +5,7 @@ #ifndef PRISM_OBJECTSPANEL_H #define PRISM_OBJECTSPANEL_H #include "Prism/Renderer/Texture.h" +#include "Prism/Asset/AssetsManager.h" namespace Prism { @@ -15,6 +16,9 @@ namespace Prism void OnImGuiRender(); + private: + void DrawObject(const char* label, AssetHandle handle); + private: Ref m_CubeImage; }; diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp index 272984f..a7b5a64 100644 --- a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp @@ -282,6 +282,9 @@ namespace Prism ImGuiTreeNodeFlags node_flags = (entity == m_SelectionContext ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_OpenOnArrow; node_flags |= ImGuiTreeNodeFlags_SpanAvailWidth; + if (entity.Children().empty()) + node_flags |= ImGuiTreeNodeFlags_Leaf; + const bool opened = ImGui::TreeNodeEx((const void*)(uintptr_t)(uint32_t)entity, node_flags, name); if (ImGui::IsItemClicked()) { @@ -316,18 +319,21 @@ namespace Prism UUID droppedHandle = *static_cast(payload->Data); Entity e = m_Context->FindEntityByUUID(droppedHandle); - // Remove from previous parent - Entity previousParent = m_Context->FindEntityByUUID(e.GetParentUUID()); - if (previousParent) + if (!entity.IsDescendantOf(e)) { - auto& parentChildren = previousParent.Children(); - parentChildren.erase(std::remove(parentChildren.begin(), parentChildren.end(), droppedHandle), parentChildren.end()); + // Remove from previous parent + Entity previousParent = m_Context->FindEntityByUUID(e.GetParentUUID()); + if (previousParent) + { + auto& parentChildren = previousParent.Children(); + parentChildren.erase(std::remove(parentChildren.begin(), parentChildren.end(), droppedHandle), parentChildren.end()); + } + + e.SetParentUUID(entity.GetUUID()); + entity.Children().push_back(droppedHandle); + + PM_CORE_INFO("Dropping Entity {0} on {1}", (uint64_t)droppedHandle, (uint64_t)entity.GetUUID()); } - - e.SetParentUUID(entity.GetUUID()); - auto& children = entity.Children(); - children.push_back(droppedHandle); - PM_CORE_INFO("Dropping Entity {0} on {1}", (uint64_t)droppedHandle, (uint64_t)entity.GetUUID()); } @@ -514,7 +520,6 @@ namespace Prism AddComponentPopup("BoxCollider2D"); AddComponentPopup("CircleCollider2D"); AddComponentPopup("RigidBody"); - AddComponentPopup("PhysicsMaterial"); AddComponentPopup("BoxCollider"); AddComponentPopup("SphereCollider"); AddComponentPopup("CapsuleCollider"); @@ -559,6 +564,12 @@ namespace Prism } DrawComponent("Mesh", entity, [](MeshComponent& meshComponent) { + + UI::BeginPropertyGrid(); + UI::PropertyAssetReference("Mesh", meshComponent.Mesh, AssetType::Mesh); + UI::EndPropertyGrid(); + + /* ImGui::Columns(3); ImGui::SetColumnWidth(0, 100); ImGui::SetColumnWidth(1, 300); @@ -581,6 +592,7 @@ namespace Prism meshComponent.Mesh = Ref::Create(file); } ImGui::Columns(1); + */ }); DrawComponent("Camera", entity, [](CameraComponent& cameraComponent) { @@ -651,28 +663,9 @@ namespace Prism DrawComponent("Sky Light", entity, [](SkyLightComponent& slc) { - ImGui::Columns(3); - ImGui::SetColumnWidth(0, 100); - ImGui::SetColumnWidth(1, 300); - ImGui::SetColumnWidth(2, 40); - ImGui::Text("File Path"); - ImGui::NextColumn(); - ImGui::PushItemWidth(-1); - if (!slc.SceneEnvironment.FilePath.empty()) - ImGui::InputText("##envfilepath", (char*)slc.SceneEnvironment.FilePath.c_str(), 256, ImGuiInputTextFlags_ReadOnly); - else - ImGui::InputText("##envfilepath", (char*)"Empty", 256, ImGuiInputTextFlags_ReadOnly); - ImGui::PopItemWidth(); - ImGui::NextColumn(); - if (ImGui::Button("...##openenv")) - { - std::string file = Application::Get().OpenFile("*.hdr"); - if (!file.empty()) - slc.SceneEnvironment = Environment::Load(file); - } - ImGui::Columns(1); UI::BeginPropertyGrid(); + UI::PropertyAssetReference("Environment Map", slc.SceneEnvironment, AssetType::EnvMap); UI::Property("Intensity", slc.Intensity, 0.01f, 0.0f, 5.0f); UI::EndPropertyGrid(); }); @@ -916,17 +909,6 @@ namespace Prism } }); - DrawComponent("Physics Material", entity, [](PhysicsMaterialComponent& pmc) - { - UI::BeginPropertyGrid(); - - UI::Property("Static Friction", pmc.StaticFriction, 0.01f, 0.0f, 1.0f); - UI::Property("Dynamic Friction", pmc.DynamicFriction, 0.01f, 0.0f, 1.0f); - UI::Property("Bounciness", pmc.Bounciness, 0.01f, 0.0f, 1.0f); - - UI::EndPropertyGrid(); - }); - DrawComponent("Box Collider", entity, [](BoxColliderComponent& bcc) { UI::BeginPropertyGrid(); @@ -937,6 +919,7 @@ namespace Prism } UI::Property("IsTrigger", bcc.IsTrigger); + UI::PropertyAssetReference("Material", bcc.Material, AssetType::PhysicsMat); UI::EndPropertyGrid(); }); @@ -951,6 +934,7 @@ namespace Prism } UI::Property("IsTrigger", scc.IsTrigger); + UI::PropertyAssetReference("Material", scc.Material, AssetType::PhysicsMat); UI::EndPropertyGrid(); }); @@ -965,6 +949,7 @@ namespace Prism if (UI::Property("Height", ccc.Height)) changed = true; UI::Property("Is Trigger", ccc.IsTrigger); + UI::PropertyAssetReference("Material", ccc.Material, AssetType::PhysicsMat); if (changed) { @@ -976,8 +961,18 @@ namespace Prism DrawComponent("Mesh Collider", entity, [&](MeshColliderComponent& mcc) { + UI::BeginPropertyGrid(); + if (mcc.OverrideMesh) { + if (UI::PropertyAssetReference("Mesh", mcc.CollisionMesh, AssetType::Mesh)) + { + if (mcc.IsConvex) + PhysicsWrappers::CreateConvexMesh(mcc, glm::vec3(1.0f)); + else + PhysicsWrappers::CreateTriangleMesh(mcc, glm::vec3(1.0f)); + } + /* ImGui::Columns(3); ImGui::SetColumnWidth(0, 100); ImGui::SetColumnWidth(1, 250); @@ -1004,9 +999,9 @@ namespace Prism } } ImGui::EndColumns(); + */ } - UI::BeginPropertyGrid(); if (UI::Property("Is Convex", mcc.IsConvex)) { if (mcc.CollisionMesh) @@ -1019,6 +1014,8 @@ namespace Prism } UI::Property("Is Trigger", mcc.IsTrigger); + UI::PropertyAssetReference("Material", mcc.Material, AssetType::PhysicsMat); + if (UI::Property("Override Mesh", mcc.OverrideMesh)) { if (!mcc.OverrideMesh && entity.HasComponent()) diff --git a/Prism/src/Prism/Physics/PhysicsActor.cpp b/Prism/src/Prism/Physics/PhysicsActor.cpp index b2e7355..cc5d253 100644 --- a/Prism/src/Prism/Physics/PhysicsActor.cpp +++ b/Prism/src/Prism/Physics/PhysicsActor.cpp @@ -16,17 +16,6 @@ namespace Prism PhysicsActor::PhysicsActor(Entity entity) : m_Entity(entity), m_RigidBody(entity.GetComponent()) { - if (!m_Entity.HasComponent()) - { - m_Material.StaticFriction = 1.0f; - m_Material.DynamicFriction = 1.0f; - m_Material.Bounciness = 0.0f; - } - else - { - m_Material = entity.GetComponent(); - } - Initialize(); } @@ -213,7 +202,6 @@ namespace Prism m_ActorInternal = actor; } - m_MaterialInternal = physics.createMaterial(m_Material.StaticFriction, m_Material.DynamicFriction, m_Material.Bounciness); if (m_Entity.HasComponent()) PhysicsWrappers::AddBoxCollider(*this); if (m_Entity.HasComponent()) PhysicsWrappers::AddSphereCollider(*this); if (m_Entity.HasComponent()) PhysicsWrappers::AddCapsuleCollider(*this); diff --git a/Prism/src/Prism/Physics/PhysicsActor.h b/Prism/src/Prism/Physics/PhysicsActor.h index e7d83dc..9d73233 100644 --- a/Prism/src/Prism/Physics/PhysicsActor.h +++ b/Prism/src/Prism/Physics/PhysicsActor.h @@ -61,10 +61,8 @@ namespace Prism private: Entity m_Entity; RigidBodyComponent& m_RigidBody; - PhysicsMaterialComponent m_Material; physx::PxRigidActor* m_ActorInternal; - physx::PxMaterial* m_MaterialInternal; friend class Physics3D; friend class PhysicsWrappers; diff --git a/Prism/src/Prism/Physics/PhysicsWrappers.cpp b/Prism/src/Prism/Physics/PhysicsWrappers.cpp index 7d0d2ce..336183a 100644 --- a/Prism/src/Prism/Physics/PhysicsWrappers.cpp +++ b/Prism/src/Prism/Physics/PhysicsWrappers.cpp @@ -181,7 +181,11 @@ namespace Prism void PhysicsWrappers::AddBoxCollider(PhysicsActor& actor) { - const auto& collider = actor.m_Entity.GetComponent(); + auto& collider = actor.m_Entity.GetComponent(); + + if (!collider.Material) + collider.Material = Ref::Create(0.6F, 0.6F, 0.0f); + const glm::vec3 scale = actor.m_Entity.Transform().Scale; glm::vec3 colliderSize = collider.Size; @@ -190,8 +194,9 @@ namespace Prism if (scale.z != 0.0f) colliderSize.z *= scale.z; const auto boxGeometry = physx::PxBoxGeometry(colliderSize.x / 2.0f, colliderSize.y / 2.0f, colliderSize.z / 2.0f); + const physx::PxMaterial* material = s_Physics->createMaterial(collider.Material->StaticFriction, collider.Material->DynamicFriction, collider.Material->Bounciness); + physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(*actor.m_ActorInternal, boxGeometry, *material); - physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(*actor.m_ActorInternal, boxGeometry, *actor.m_MaterialInternal); shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); @@ -200,7 +205,10 @@ namespace Prism void PhysicsWrappers::AddSphereCollider(PhysicsActor& actor) { - const auto& collider = actor.m_Entity.GetComponent(); + auto& collider = actor.m_Entity.GetComponent(); + if (!collider.Material) + collider.Material = Ref::Create(0.6F, 0.6F, 0.0f); + const glm::vec3 scale = actor.m_Entity.Transform().Scale; float colliderRadius = collider.Radius; @@ -208,15 +216,19 @@ namespace Prism const auto sphereGeometry = physx::PxSphereGeometry(colliderRadius); - physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(*actor.m_ActorInternal, sphereGeometry, *actor.m_MaterialInternal); - // physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(actor, sphereGeometry, material); + const physx::PxMaterial* material = s_Physics->createMaterial(collider.Material->StaticFriction, collider.Material->DynamicFriction, collider.Material->Bounciness); + physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(*actor.m_ActorInternal, sphereGeometry, *material); + shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); } void PhysicsWrappers::AddCapsuleCollider(PhysicsActor& actor) { - const auto& collider = actor.m_Entity.GetComponent(); + auto& collider = actor.m_Entity.GetComponent(); + if (!collider.Material) + collider.Material = Ref::Create(0.6F, 0.6F, 0.0f); + const glm::vec3 scale = actor.m_Entity.Transform().Scale; float colliderRadius = collider.Radius; float colliderHeight = collider.Height; @@ -227,7 +239,14 @@ namespace Prism const auto capsuleGeometry = physx::PxCapsuleGeometry(colliderRadius, colliderHeight * 0.5f); - physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(*actor.m_ActorInternal, capsuleGeometry, *actor.m_MaterialInternal); + const physx::PxQuat rotation(physx::PxHalfPi, physx::PxVec3(0.0f, 0.0f, 1.0f)); + const physx::PxTransform localPose(physx::PxVec3(0.0f), rotation); + + const physx::PxMaterial* material = s_Physics->createMaterial(collider.Material->StaticFriction, collider.Material->DynamicFriction, collider.Material->Bounciness); + physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(*actor.m_ActorInternal, capsuleGeometry, *material); + + shape->setLocalPose(localPose); + shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); } @@ -235,7 +254,12 @@ namespace Prism void PhysicsWrappers::AddMeshCollider(PhysicsActor& actor) { auto& collider = actor.m_Entity.GetComponent(); + if (!collider.Material) + collider.Material = Ref::Create(0.6F, 0.6F, 0.0f); + glm::vec3 scale = actor.m_Entity.Transform().Scale; + physx::PxMaterial* material = s_Physics->createMaterial(collider.Material->StaticFriction, collider.Material->DynamicFriction, collider.Material->Bounciness); + physx::PxMaterial* materials[] = { material }; if (collider.IsConvex) { @@ -243,7 +267,6 @@ namespace Prism for (const auto shape : shapes) { - physx::PxMaterial* materials[] = { actor.m_MaterialInternal }; shape->setMaterials(materials, 1); shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); @@ -257,7 +280,6 @@ namespace Prism for (const auto shape : shapes) { - physx::PxMaterial* materials[] = { actor.m_MaterialInternal }; shape->setMaterials(materials, 1); shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp index fe733d2..14f5058 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp @@ -6,6 +6,7 @@ #include "Prism/Core/Log.h" #include "Prism/Renderer/Renderer.h" +#include "Prism/Utilities/StringUtils.h" namespace Prism { @@ -284,41 +285,19 @@ namespace Prism return FindToken(string.c_str(), token); } - std::vector SplitString(const std::string& string, const std::string& delimiters) - { - size_t start = 0; - size_t end = string.find_first_of(delimiters); - - std::vector result; - - while (end <= std::string::npos) - { - std::string token = string.substr(start, end - start); - if (!token.empty()) - result.push_back(token); - - if (end == std::string::npos) - break; - - start = end + 1; - end = string.find_first_of(delimiters, start); - } - - return result; - } std::vector SplitString(const std::string& string, const char delimiter) { - return SplitString(string, std::string(1, delimiter)); + return Utils::SplitString(string, std::string(1, delimiter)); } std::vector Tokenize(const std::string& string) { - return SplitString(string, " \t\n\r"); + return Utils::SplitString(string, " \t\n\r"); } std::vector GetLines(const std::string& string) { - return SplitString(string, "\n"); + return Utils::SplitString(string, "\n"); } std::string GetBlock(const char* str, const char** outPosition) @@ -345,11 +324,6 @@ namespace Prism return std::string(str, length); } - bool StartsWith(const std::string& string, const std::string& start) - { - return string.find(start) == 0; - } - void OpenGLShader::Parse() { const char* token; @@ -461,7 +435,7 @@ namespace Prism declaration = new OpenGLShaderUniformDeclaration(domain, t, name, count); } - if (StartsWith(name, "r_")) + if (Utils::StartsWith(name, "r_")) { if (domain == ShaderDomain::Vertex) ((OpenGLShaderUniformBufferDeclaration*)m_VSRendererUniformBuffers.front())->PushUniform(declaration); diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp index b0674fa..b9051ac 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp @@ -204,7 +204,7 @@ namespace Prism const uint32_t faceHeight = m_Height / 3; PM_CORE_ASSERT(faceWidth == faceHeight, "Non-square faces!"); - std::array faces; + std::array faces{}; for (size_t i = 0; i < faces.size(); i++) faces[i] = new uint8_t[faceWidth * faceHeight * 3]; // 3 BPP @@ -214,10 +214,10 @@ namespace Prism { for (size_t y = 0; y < faceHeight; y++) { - size_t yOffset = y + faceHeight; + const size_t yOffset = y + faceHeight; for (size_t x = 0; x < faceWidth; x++) { - size_t xOffset = x + i * faceWidth; + const size_t xOffset = x + i * faceWidth; faces[faceIndex][(x + y * faceWidth) * 3 + 0] = m_ImageData[(xOffset + yOffset * m_Width) * 3 + 0]; faces[faceIndex][(x + y * faceWidth) * 3 + 1] = m_ImageData[(xOffset + yOffset * m_Width) * 3 + 1]; faces[faceIndex][(x + y * faceWidth) * 3 + 2] = m_ImageData[(xOffset + yOffset * m_Width) * 3 + 2]; @@ -234,10 +234,10 @@ namespace Prism for (size_t y = 0; y < faceHeight; y++) { - size_t yOffset = y + i * faceHeight; + const size_t yOffset = y + i * faceHeight; for (size_t x = 0; x < faceWidth; x++) { - size_t xOffset = x + faceWidth; + const size_t xOffset = x + faceWidth; faces[faceIndex][(x + y * faceWidth) * 3 + 0] = m_ImageData[(xOffset + yOffset * m_Width) * 3 + 0]; faces[faceIndex][(x + y * faceWidth) * 3 + 1] = m_ImageData[(xOffset + yOffset * m_Width) * 3 + 1]; faces[faceIndex][(x + y * faceWidth) * 3 + 2] = m_ImageData[(xOffset + yOffset * m_Width) * 3 + 2]; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h index 779a701..5187ec6 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h @@ -87,7 +87,7 @@ namespace Prism TextureFormat m_Format; uint32_t m_Width, m_Height; - uint8_t* m_ImageData; + uint8_t* m_ImageData = nullptr; std::string m_FilePath; }; diff --git a/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp index f81a90b..40c1784 100644 --- a/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp +++ b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp @@ -4,25 +4,65 @@ #include -#include "Prism/Utilities/FileSystemWatcher.h" +#include "Prism/Utilities/FileSystem.h" #include #include #include "Prism/Core/Log.h" +#include "Prism/Asset/AssetsManager.h" namespace Prism { - FileSystemWatcher::FileSystemChangedCallbackFn FileSystemWatcher::s_Callback; + FileSystem::FileSystemChangedCallbackFn FileSystem::s_Callback; static bool s_Watching = true; + static bool s_IgnoreNextChange = false; static HANDLE s_WatcherThread; - void FileSystemWatcher::SetChangeCallback(const FileSystemChangedCallbackFn& callback) + bool FileSystem::CreateFolder(const std::filesystem::path& filepath) + { + const BOOL created = CreateDirectoryA(filepath.string().c_str(), NULL); + if (!created) + { + const DWORD error = GetLastError(); + + if (error == ERROR_ALREADY_EXISTS) + PM_CORE_ERROR("{0} already exists!", filepath.string()); + + if (error == ERROR_PATH_NOT_FOUND) + PM_CORE_ERROR("{0}: One or more directories don't exist.", filepath.string()); + + return false; + } + + return true; + } + + bool FileSystem::Exists(const std::string& filepath) + { + const DWORD attribs = GetFileAttributesA(filepath.c_str()); + + if (attribs == INVALID_FILE_ATTRIBUTES) + return false; + + return true; + } + + void FileSystem::SetChangeCallback(const FileSystemChangedCallbackFn& callback) { s_Callback = callback; } + /* + static std::string wchar_to_string(wchar_t* input) + { + std::wstring string_input(input); + std::string converted(string_input.begin(), string_input.end()); + return converted; + } + */ + static std::string wchar_to_string(const wchar_t* input) { if (!input) return {}; @@ -38,7 +78,37 @@ namespace Prism return result; } - void FileSystemWatcher::StartWatching() + std::string FileSystem::Rename(const std::string& filepath, const std::string& newName) + { + s_IgnoreNextChange = true; + std::filesystem::path p = filepath; + std::string newFilePath = p.parent_path().string() + "/" + newName + p.extension().string(); + MoveFileA(filepath.c_str(), newFilePath.c_str()); + s_IgnoreNextChange = false; + return newFilePath; + } + + bool FileSystem::PrismDeleteFile(const std::string& filepath) + { + s_IgnoreNextChange = true; + std::string fp = filepath; + fp.append(1, '\0'); + SHFILEOPSTRUCTA file_op; + file_op.hwnd = NULL; + file_op.wFunc = FO_DELETE; + file_op.pFrom = fp.c_str(); + file_op.pTo = ""; + file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; + file_op.fAnyOperationsAborted = false; + file_op.hNameMappings = 0; + file_op.lpszProgressTitle = ""; + const int result = SHFileOperationA(&file_op); + s_IgnoreNextChange = false; + return result == 0; + } + + + void FileSystem::StartWatching() { DWORD threadId; s_WatcherThread = CreateThread(NULL, 0, Watch, NULL, 0, &threadId); @@ -46,7 +116,7 @@ namespace Prism PM_CORE_TRACE("Starting file watching services"); } - void FileSystemWatcher::StopWatching() + void FileSystem::StopWatching() { // TODO: this delay is too long s_Watching = false; @@ -57,16 +127,14 @@ namespace Prism PM_CORE_TRACE("closing file watching services"); } - unsigned long FileSystemWatcher::Watch(void* param) + unsigned long FileSystem::Watch(void* param) { const LPCSTR filepath = "assets"; - char* buffer = new char[1024]; + BYTE* buffer = new BYTE[10 * 1024]; // 1 MB OVERLAPPED overlapped = { 0 }; HANDLE handle = NULL; DWORD bytesReturned = 0; - ZeroMemory(buffer, 1024); - handle = CreateFile( filepath, FILE_LIST_DIRECTORY, @@ -95,7 +163,7 @@ namespace Prism DWORD status = ReadDirectoryChangesW( handle, buffer, - 1024, + 10 * 1024 * sizeof(BYTE), TRUE, FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, &bytesReturned, @@ -106,28 +174,32 @@ namespace Prism 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; + if (s_IgnoreNextChange) + continue; + std::string oldName; + char fileName[MAX_PATH * 10] = ""; + + FILE_NOTIFY_INFORMATION* current = (FILE_NOTIFY_INFORMATION*)buffer; for (;;) { - auto& fni = (FILE_NOTIFY_INFORMATION&)*buffer; - std::filesystem::path filePath; - filePath = "assets/" + wchar_to_string(fni.FileName); + ZeroMemory(fileName, sizeof(fileName)); + + WideCharToMultiByte(CP_ACP, 0, current->FileName, current->FileNameLength / sizeof(WCHAR), fileName, sizeof(fileName), NULL, NULL); + std::filesystem::path filePath = std::filesystem::path("assets") / std::string(fileName); FileSystemChangedEvent e; - e.Filepath = filePath.string(); + e.FilePath = filePath.string(); e.NewName = filePath.filename().string(); e.OldName = filePath.filename().string(); e.IsDirectory = std::filesystem::is_directory(filePath); - switch (fni.Action) + switch (current->Action) { case FILE_ACTION_ADDED: { @@ -137,6 +209,7 @@ namespace Prism } case FILE_ACTION_REMOVED: { + e.IsDirectory = AssetsManager::IsDirectory(e.FilePath); e.Action = FileSystemAction::Delete; s_Callback(e); break; @@ -161,13 +234,10 @@ namespace Prism } } - if (!fni.NextEntryOffset) - { - ZeroMemory(buffer, 1024); + if (!current->NextEntryOffset) break; - } - buffer += fni.NextEntryOffset; + current += current->NextEntryOffset; } } diff --git a/Prism/src/Prism/Renderer/Mesh.cpp b/Prism/src/Prism/Renderer/Mesh.cpp index 9251da9..8f727e5 100644 --- a/Prism/src/Prism/Renderer/Mesh.cpp +++ b/Prism/src/Prism/Renderer/Mesh.cpp @@ -276,7 +276,7 @@ namespace Prism float shininess, metalness; if (aiMaterial->Get(AI_MATKEY_SHININESS, shininess) != aiReturn_SUCCESS) - shininess = 80.0f; // Default value + shininess = 0.0f; // Default value aiMaterial->Get(AI_MATKEY_REFLECTIVITY, metalness); if (aiMaterial->Get(AI_MATKEY_REFLECTIVITY, metalness) != aiReturn_SUCCESS) metalness = 0.0f; diff --git a/Prism/src/Prism/Renderer/Mesh.h b/Prism/src/Prism/Renderer/Mesh.h index 649b1ab..f726d59 100644 --- a/Prism/src/Prism/Renderer/Mesh.h +++ b/Prism/src/Prism/Renderer/Mesh.h @@ -13,6 +13,7 @@ #include "VertexArray.h" #include "Prism/Core/TimeStep.h" #include "Prism/Core/Math/AABB.h" +#include "../Asset/Asset.h" struct aiNode; struct aiAnimation; @@ -103,7 +104,7 @@ namespace Prism std::string NodeName, MeshName; }; - class PRISM_API Mesh : public RefCounted + class PRISM_API Mesh : public Asset { public: @@ -181,7 +182,7 @@ namespace Prism float m_AnimationTime = 0.0f; float m_WorldTime = 0.0f; float m_TimeMultiplier = 1.0f; - bool m_AnimationPlaying = true; + bool m_AnimationPlaying = false; std::string m_FilePath; private: diff --git a/Prism/src/Prism/Renderer/SceneEnvironment.cpp b/Prism/src/Prism/Renderer/SceneEnvironment.cpp index b3e5a4a..3add635 100644 --- a/Prism/src/Prism/Renderer/SceneEnvironment.cpp +++ b/Prism/src/Prism/Renderer/SceneEnvironment.cpp @@ -7,9 +7,4 @@ namespace Prism { - Environment Environment::Load(const std::string& filepath) - { - auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath); - return { filepath, radiance, irradiance }; - } } diff --git a/Prism/src/Prism/Renderer/SceneEnvironment.h b/Prism/src/Prism/Renderer/SceneEnvironment.h index 640fe1d..6db71b5 100644 --- a/Prism/src/Prism/Renderer/SceneEnvironment.h +++ b/Prism/src/Prism/Renderer/SceneEnvironment.h @@ -10,13 +10,16 @@ namespace Prism { - struct Environment + class PRISM_API Environment : public Asset { - std::string FilePath; + public: + Environment() = default; + Environment(const Ref& radianceMap, const Ref& irradianceMap) + : RadianceMap(radianceMap), IrradianceMap(irradianceMap) {} + Ref RadianceMap; Ref IrradianceMap; - static PRISM_API Environment Load(const std::string& filepath); }; } diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp index 30f5a73..204bf3f 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.cpp +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -27,7 +27,7 @@ namespace Prism // Resources Ref SkyboxMaterial; - Environment SceneEnvironment; + Ref SceneEnvironment; Light ActiveLight; } SceneData; @@ -148,6 +148,7 @@ namespace Prism constexpr float gridSize = 0.025f; s_Data.GridMaterial->Set("u_Scale", gridScale); s_Data.GridMaterial->Set("u_Res", gridSize); + s_Data.GridMaterial->SetFlag(MaterialFlag::TwoSided, true); // outline const auto outlineShader = Shader::Create("assets/shaders/Outline.glsl"); @@ -465,8 +466,8 @@ namespace Prism baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity); // Environment (TODO: don't do this per mesh) - baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap); - baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment.IrradianceMap); + baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment->RadianceMap); + baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment->IrradianceMap); baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT); // Set lights (TODO: move to light environment and don't do per mesh) @@ -530,8 +531,8 @@ namespace Prism baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity); // Environment (TODO: don't do this per mesh) - baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap); - baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment.IrradianceMap); + baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment->RadianceMap); + baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment->IrradianceMap); baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT); baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]); diff --git a/Prism/src/Prism/Renderer/Texture.h b/Prism/src/Prism/Renderer/Texture.h index 1d04294..53a4293 100644 --- a/Prism/src/Prism/Renderer/Texture.h +++ b/Prism/src/Prism/Renderer/Texture.h @@ -7,6 +7,7 @@ #include "RendererAPI.h" #include "Prism/Core/Buffer.h" #include "Prism/Core/Ref.h" +#include "../Asset/Asset.h" namespace Prism @@ -27,7 +28,7 @@ namespace Prism Repeat = 2 }; - class PRISM_API Texture : public RefCounted + class PRISM_API Texture : public Asset { public: virtual ~Texture() {} diff --git a/Prism/src/Prism/Scene/Components.h b/Prism/src/Prism/Scene/Components.h index 769dbfb..9bfd306 100644 --- a/Prism/src/Prism/Scene/Components.h +++ b/Prism/src/Prism/Scene/Components.h @@ -28,13 +28,14 @@ namespace Prism UUID ID = 0; }; - struct ParentComponent + struct RelationshipComponent { - UUID ParentHandle = 0; + UUID ParentHandle = 0; + std::vector Children; - ParentComponent() = default; - ParentComponent(const ParentComponent& other) = default; - ParentComponent(const UUID& parent) + RelationshipComponent() = default; + RelationshipComponent(const RelationshipComponent& other) = default; + RelationshipComponent(UUID parent) : ParentHandle(parent) {} }; @@ -178,6 +179,7 @@ namespace Prism { float Radius = 0.5f; bool IsTrigger = false; + Ref Material; // The mesh that will be drawn in the editor to show the collision bounds Ref DebugMesh; @@ -211,22 +213,12 @@ namespace Prism RigidBodyComponent(const RigidBodyComponent& other) = default; }; - // TODO: This will eventually be a resource, but that requires object referencing through the editor - struct PhysicsMaterialComponent - { - float StaticFriction = 1.0f; - float DynamicFriction = 1.0f; - float Bounciness = 1.0f; - - PhysicsMaterialComponent() = default; - PhysicsMaterialComponent(const PhysicsMaterialComponent& other) = default; - }; - struct BoxColliderComponent { glm::vec3 Size = { 1.0f, 1.0f, 1.0f }; glm::vec3 Offset = { 0.0f, 0.0f, 0.0f }; bool IsTrigger = false; + Ref Material; // The mesh that will be drawn in the editor to show the collision bounds Ref DebugMesh; @@ -240,6 +232,7 @@ namespace Prism float Radius = 0.5f; float Height = 1.0f; bool IsTrigger = false; + Ref Material; Ref DebugMesh; @@ -254,6 +247,7 @@ namespace Prism bool IsConvex = false; bool IsTrigger = false; bool OverrideMesh = false; + Ref Material; MeshColliderComponent() = default; MeshColliderComponent(const MeshColliderComponent& other) = default; @@ -284,9 +278,14 @@ namespace Prism struct SkyLightComponent { - Environment SceneEnvironment; + Ref SceneEnvironment; float Intensity = 1.0f; float Angle = 0.0f; + + SkyLightComponent() + : SceneEnvironment(Ref::Create()) + { + } }; } diff --git a/Prism/src/Prism/Scene/Entity.cpp b/Prism/src/Prism/Scene/Entity.cpp index c6fbb05..dc006bb 100644 --- a/Prism/src/Prism/Scene/Entity.cpp +++ b/Prism/src/Prism/Scene/Entity.cpp @@ -6,4 +6,30 @@ namespace Prism { + bool Entity::IsAncesterOf(Entity entity) + { + const auto& children = Children(); + + if (children.size() == 0) + return false; + + for (UUID child : children) + { + if (child == entity.GetUUID()) + return true; + } + + for (UUID child : children) + { + if (m_Scene->FindEntityByUUID(child).IsAncesterOf(entity)) + return true; + } + + return false; + } + + bool Entity::IsDescendantOf(Entity entity) const + { + return entity.IsAncesterOf(*this); + } } diff --git a/Prism/src/Prism/Scene/Entity.h b/Prism/src/Prism/Scene/Entity.h index e16ad7e..377e884 100644 --- a/Prism/src/Prism/Scene/Entity.h +++ b/Prism/src/Prism/Scene/Entity.h @@ -67,9 +67,13 @@ namespace Prism return !(*this == other); } - void SetParentUUID(const UUID& parent) { GetComponent().ParentHandle = parent; } - UUID GetParentUUID() { return GetComponent().ParentHandle; } - std::vector& Children() { return GetComponent().Children; } + void SetParentUUID(UUID parent) { GetComponent().ParentHandle = parent; } + UUID GetParentUUID() { return GetComponent().ParentHandle; } + std::vector& Children() { return GetComponent().Children; } + + bool IsAncesterOf(Entity entity); + bool IsDescendantOf(Entity entity) const; + UUID GetUUID() { return GetComponent().ID; } UUID GetSceneUUID() const { return m_Scene->GetUUID(); } diff --git a/Prism/src/Prism/Scene/Scene.cpp b/Prism/src/Prism/Scene/Scene.cpp index 7d03ad9..f8a1492 100644 --- a/Prism/src/Prism/Scene/Scene.cpp +++ b/Prism/src/Prism/Scene/Scene.cpp @@ -260,14 +260,14 @@ namespace Prism // TODO: only one sky light at the moment! { - m_Environment = Environment(); + m_Environment = Ref::Create(); const auto lights = m_Registry.group(entt::get); for (const auto entity : lights) { auto [transformComponent, skyLightComponent] = lights.get(entity); m_Environment = skyLightComponent.SceneEnvironment; m_EnvironmentIntensity = skyLightComponent.Intensity; - SetSkybox(m_Environment.RadianceMap); + SetSkybox(m_Environment->RadianceMap); } } @@ -339,14 +339,14 @@ namespace Prism // TODO: only one sky light at the moment! { - m_Environment = Environment(); + m_Environment = Ref::Create(); auto lights = m_Registry.group(entt::get); for (auto entity : lights) { auto [transformComponent, skyLightComponent] = lights.get(entity); m_Environment = skyLightComponent.SceneEnvironment; m_EnvironmentIntensity = skyLightComponent.Intensity; - SetSkybox(m_Environment.RadianceMap); + SetSkybox(m_Environment->RadianceMap); } } @@ -618,7 +618,7 @@ namespace Prism idComponent.ID = {}; entity.AddComponent(); - entity.AddComponent(); + entity.AddComponent(); entity.AddComponent(); if (!name.empty()) @@ -635,7 +635,7 @@ namespace Prism idComponent.ID = uuid; entity.AddComponent(); - entity.AddComponent(); + entity.AddComponent(); entity.AddComponent(); if (!name.empty()) @@ -681,9 +681,7 @@ namespace Prism CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); - // TODO: should copy parent - CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); - CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); @@ -695,7 +693,6 @@ namespace Prism CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); - CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); @@ -762,8 +759,7 @@ namespace Prism CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); - CopyComponent(target->m_Registry, m_Registry, enttMap); - CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); @@ -775,7 +771,6 @@ namespace Prism CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); - CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); diff --git a/Prism/src/Prism/Scene/Scene.h b/Prism/src/Prism/Scene/Scene.h index 8494ddc..bd462d4 100644 --- a/Prism/src/Prism/Scene/Scene.h +++ b/Prism/src/Prism/Scene/Scene.h @@ -65,7 +65,7 @@ namespace Prism void SetViewportSize(uint32_t width, uint32_t height); - const Environment& GetEnvironment() const { return m_Environment; } + const Ref& GetEnvironment() const { return m_Environment; } void SetSkybox(const Ref& skybox); float& GetSkyboxLod() { return m_SkyboxLod; } @@ -130,7 +130,7 @@ namespace Prism bool m_IsPlaying = false; - Environment m_Environment; + Ref m_Environment; float m_EnvironmentIntensity = 1.0f; Ref m_SkyboxTexture; Ref m_SkyboxMaterial; diff --git a/Prism/src/Prism/Scene/SceneSerializer.cpp b/Prism/src/Prism/Scene/SceneSerializer.cpp index c7f5e5e..ec144c1 100644 --- a/Prism/src/Prism/Scene/SceneSerializer.cpp +++ b/Prism/src/Prism/Scene/SceneSerializer.cpp @@ -13,6 +13,7 @@ #include "Prism/Physics/PhysicsLayer.h" #include "Prism/Physics/PhysicsWrappers.h" #include "Prism/Renderer/Meshfactory.h" +#include "../Asset/AssetsManager.h" namespace YAML { @@ -160,19 +161,15 @@ namespace Prism out << YAML::Key << "Entity"; out << YAML::Value << uuid; - if (entity.HasComponent()) + if (entity.HasComponent()) { - auto& parent = entity.GetComponent(); - out << YAML::Key << "Parent" << YAML::Value << parent.ParentHandle; - } + auto& relationshipComponent = entity.GetComponent(); + out << YAML::Key << "Parent" << YAML::Value << relationshipComponent.ParentHandle; - if (entity.HasComponent()) - { - const auto& childrenComponent = entity.GetComponent(); out << YAML::Key << "Children"; out << YAML::Value << YAML::BeginSeq; - for (auto child : childrenComponent.Children) + for (auto child : relationshipComponent.Children) { out << YAML::BeginMap; out << YAML::Key << "Handle" << YAML::Value << child; @@ -262,7 +259,10 @@ namespace Prism out << YAML::BeginMap; // MeshComponent const auto mesh = entity.GetComponent().Mesh; - out << YAML::Key << "AssetPath" << YAML::Value << mesh->GetFilePath(); + if (mesh) + out << YAML::Key << "AssetID" << YAML::Value << mesh->Handle; + else + out << YAML::Key << "AssetID" << YAML::Value << 0; out << YAML::EndMap; // MeshComponent } @@ -287,7 +287,7 @@ namespace Prism out << YAML::BeginMap; // SkyLightComponent const auto& skyLightComponent = entity.GetComponent(); - out << YAML::Key << "EnvironmentAssetPath" << YAML::Value << skyLightComponent.SceneEnvironment.FilePath; + out << YAML::Key << "EnvironmentMap" << YAML::Value << skyLightComponent.SceneEnvironment->Handle; out << YAML::Key << "Intensity" << YAML::Value << skyLightComponent.Intensity; out << YAML::Key << "Angle" << YAML::Value << skyLightComponent.Angle; @@ -401,19 +401,6 @@ namespace Prism out << YAML::EndMap; // RigidBodyComponent } - if (entity.HasComponent()) - { - out << YAML::Key << "PhysicsMaterialComponent"; - out << YAML::BeginMap; // PhysicsMaterialComponent - - const auto& physicsMaterial = entity.GetComponent(); - out << YAML::Key << "StaticFriction" << YAML::Value << physicsMaterial.StaticFriction; - out << YAML::Key << "DynamicFriction" << YAML::Value << physicsMaterial.DynamicFriction; - out << YAML::Key << "Bounciness" << YAML::Value << physicsMaterial.Bounciness; - - out << YAML::EndMap; - } - if (entity.HasComponent()) { out << YAML::Key << "BoxColliderComponent"; @@ -424,6 +411,11 @@ namespace Prism out << YAML::Key << "Size" << YAML::Value << boxColliderComponent.Size; out << YAML::Key << "IsTrigger" << YAML::Value << boxColliderComponent.IsTrigger; + if (boxColliderComponent.Material) + out << YAML::Key << "Material" << YAML::Value << boxColliderComponent.Material->Handle; + else + out << YAML::Key << "Material" << YAML::Value << 0; + out << YAML::EndMap; // BoxColliderComponent } @@ -436,6 +428,11 @@ namespace Prism out << YAML::Key << "Radius" << YAML::Value << sphereColliderComponent.Radius; out << YAML::Key << "IsTrigger" << YAML::Value << sphereColliderComponent.IsTrigger; + if (sphereColliderComponent.Material) + out << YAML::Key << "Material" << YAML::Value << sphereColliderComponent.Material->Handle; + else + out << YAML::Key << "Material" << YAML::Value << 0; + out << YAML::EndMap; // SphereColliderComponent } @@ -449,6 +446,11 @@ namespace Prism out << YAML::Key << "Height" << YAML::Value << capsuleColliderComponent.Height; out << YAML::Key << "IsTrigger" << YAML::Value << capsuleColliderComponent.IsTrigger; + if (capsuleColliderComponent.Material) + out << YAML::Key << "Material" << YAML::Value << capsuleColliderComponent.Material->Handle; + else + out << YAML::Key << "Material" << YAML::Value << 0; + out << YAML::EndMap; // CapsuleColliderComponent } @@ -460,12 +462,17 @@ namespace Prism auto meshColliderComponent = entity.GetComponent(); if (meshColliderComponent.OverrideMesh) - out << YAML::Key << "AssetPath" << YAML::Value << meshColliderComponent.CollisionMesh->GetFilePath(); + out << YAML::Key << "AssetID" << YAML::Value << meshColliderComponent.CollisionMesh->Handle; out << YAML::Key << "IsConvex" << YAML::Value << meshColliderComponent.IsConvex; out << YAML::Key << "IsTrigger" << YAML::Value << meshColliderComponent.IsTrigger; out << YAML::Key << "OverrideMesh" << YAML::Value << meshColliderComponent.OverrideMesh; + if (meshColliderComponent.Material) + out << YAML::Key << "Material" << YAML::Value << meshColliderComponent.Material->Handle; + else + out << YAML::Key << "Material" << YAML::Value << 0; + out << YAML::EndMap; // MeshColliderComponent } @@ -477,7 +484,7 @@ namespace Prism out << YAML::Key << "Environment"; out << YAML::Value; out << YAML::BeginMap; // Environment - out << YAML::Key << "AssetPath" << YAML::Value << scene->GetEnvironment().FilePath; + out << YAML::Key << "AssetHandle" << YAML::Value << scene->GetEnvironment()->Handle; const auto& light = scene->GetLight(); out << YAML::Key << "Light" << YAML::Value; out << YAML::BeginMap; // Light @@ -598,8 +605,9 @@ namespace Prism Entity deserializedEntity = m_Scene->CreateEntityWithID(uuid, name); + auto& relationshipComponent = deserializedEntity.GetComponent(); uint64_t parentHandle = entity["Parent"] ? entity["Parent"].as() : 0; - deserializedEntity.GetComponent().ParentHandle = parentHandle; + relationshipComponent.ParentHandle = parentHandle; const auto children = entity["Children"]; if (children) @@ -607,7 +615,7 @@ namespace Prism for (auto child : children) { auto childHandle = child["Handle"].as(); - deserializedEntity.GetComponent().Children.emplace_back(childHandle); + relationshipComponent.Children.push_back(childHandle); } } @@ -711,22 +719,24 @@ namespace Prism } } - if (auto meshComponent = entity["MeshComponent"]) + auto meshComponent = entity["MeshComponent"]; + if (meshComponent) { - const std::string meshPath = meshComponent["AssetPath"].as(); - // TEMP (because script creates mesh component...) - if (!deserializedEntity.HasComponent()) + UUID assetID; + if (meshComponent["AssetPath"]) { - Ref mesh; - if (!CheckPath(meshPath)) - missingPaths.emplace_back(meshPath); - else - mesh = Ref::Create(meshPath); - - deserializedEntity.AddComponent(mesh); + const std::string assetFilePath = meshComponent["AssetPath"].as(); + assetID = AssetsManager::GetAssetIDForFile(assetFilePath); + } + else + { + assetID = meshComponent["AssetID"].as(); } - PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath); + if (AssetsManager::IsAssetHandleValid(assetID) && !deserializedEntity.HasComponent()) + { + deserializedEntity.AddComponent(AssetsManager::GetAsset(assetID)); + } } if (auto directionalLightComponent = entity["DirectionalLightComponent"]) @@ -741,18 +751,23 @@ namespace Prism if (auto skyLightComponent = entity["SkyLightComponent"]) { auto& component = deserializedEntity.AddComponent(); - const std::string env = skyLightComponent["EnvironmentAssetPath"].as(); - if (!env.empty()) + + AssetHandle assetHandle; + if (skyLightComponent["EnvironmentAssetPath"]) { - if (!CheckPath(env)) - { - missingPaths.emplace_back(env); - } - else - { - component.SceneEnvironment = Environment::Load(env); - } + const std::string filePath = skyLightComponent["EnvironmentAssetPath"].as(); + assetHandle = AssetsManager::GetAssetIDForFile(filePath); } + else + { + assetHandle = skyLightComponent["EnvironmentMap"].as(); + } + + if (AssetsManager::IsAssetHandleValid(assetHandle)) + { + component.SceneEnvironment = AssetsManager::GetAsset(assetHandle); + } + component.Intensity = skyLightComponent["Intensity"].as(); component.Angle = skyLightComponent["Angle"].as(); } @@ -837,20 +852,19 @@ namespace Prism } - if (auto physicsMaterialComponent = entity["PhysicsMaterialComponent"]) - { - auto& component = deserializedEntity.AddComponent(); - component.StaticFriction = physicsMaterialComponent["StaticFriction"].as(); - component.DynamicFriction = physicsMaterialComponent["DynamicFriction"].as(); - component.Bounciness = physicsMaterialComponent["Bounciness"].as(); - } - if (auto boxColliderComponent = entity["BoxColliderComponent"]) { auto& component = deserializedEntity.AddComponent(); component.Offset = boxColliderComponent["Offset"].as(); component.Size = boxColliderComponent["Size"].as(); component.IsTrigger = boxColliderComponent["IsTrigger"] ? boxColliderComponent["IsTrigger"].as() : false; + + auto material = boxColliderComponent["Material"]; + if (material && AssetsManager::IsAssetHandleValid(material.as())) + { + component.Material = AssetsManager::GetAsset(material.as()); + } + component.DebugMesh = MeshFactory::CreateBox(component.Size); } @@ -859,6 +873,11 @@ namespace Prism auto& component = deserializedEntity.AddComponent(); component.Radius = sphereColliderComponent["Radius"].as(); component.IsTrigger = sphereColliderComponent["IsTrigger"] ? sphereColliderComponent["IsTrigger"].as() : false; + + auto material = sphereColliderComponent["Material"]; + if (material && AssetsManager::IsAssetHandleValid(material.as())) + component.Material = AssetsManager::GetAsset(material.as()); + component.DebugMesh = MeshFactory::CreateSphere(component.Radius); } @@ -868,6 +887,11 @@ namespace Prism component.Radius = capsuleColliderComponent["Radius"].as(); component.Height = capsuleColliderComponent["Height"].as(); component.IsTrigger = capsuleColliderComponent["IsTrigger"] ? capsuleColliderComponent["IsTrigger"].as() : false; + + auto material = capsuleColliderComponent["Material"]; + if (material && AssetsManager::IsAssetHandleValid(material.as())) + component.Material = AssetsManager::GetAsset(material.as()); + component.DebugMesh = MeshFactory::CreateCapsule(component.Radius, component.Height); } @@ -878,16 +902,19 @@ namespace Prism if (overrideMesh) { - auto meshPath = meshColliderComponent["AssetPath"].as(); - if (!CheckPath(meshPath)) + UUID assetID; + if (meshComponent["AssetPath"]) { - missingPaths.emplace_back(meshPath); + const auto assetFilePath = meshComponent["AssetPath"].as(); + assetID = AssetsManager::GetAssetIDForFile(assetFilePath); } else { - collisionMesh = Ref::Create(meshPath); + assetID = meshComponent["AssetID"].as(); } - collisionMesh = Ref::Create(meshPath); + + if (AssetsManager::IsAssetHandleValid(assetID)) + collisionMesh = AssetsManager::GetAsset(assetID); } if (collisionMesh) @@ -897,16 +924,46 @@ namespace Prism component.IsTrigger = meshColliderComponent["IsTrigger"] ? meshColliderComponent["IsTrigger"].as() : false; component.OverrideMesh = overrideMesh; - if (component.IsConvex) - PhysicsWrappers::CreateConvexMesh(component, deserializedEntity.Transform().Scale); - else - PhysicsWrappers::CreateTriangleMesh(component, deserializedEntity.Transform().Scale); + auto material = meshColliderComponent["Material"]; + if (material && AssetsManager::IsAssetHandleValid(material.as())) + { + component.Material = AssetsManager::GetAsset(material.as()); + + if (component.IsConvex) + PhysicsWrappers::CreateConvexMesh(component, deserializedEntity.Transform().Scale); + else + PhysicsWrappers::CreateTriangleMesh(component, deserializedEntity.Transform().Scale); + } } else { PM_CORE_WARN("MeshColliderComponent in use without valid mesh!"); } } + + + // NOTE: Compatibility fix for older scenes + auto physicsMaterialComponent = entity["PhysicsMaterialComponent"]; + if (physicsMaterialComponent) + { + //auto& component = deserializedEntity.AddComponent(); + Ref material = Ref::Create(); + material->StaticFriction = physicsMaterialComponent["StaticFriction"].as(); + material->DynamicFriction = physicsMaterialComponent["DynamicFriction"].as(); + material->Bounciness = physicsMaterialComponent["Bounciness"].as(); + + if (deserializedEntity.HasComponent()) + deserializedEntity.GetComponent().Material = material; + + if (deserializedEntity.HasComponent()) + deserializedEntity.GetComponent().Material = material; + + if (deserializedEntity.HasComponent()) + deserializedEntity.GetComponent().Material = material; + + if (deserializedEntity.HasComponent()) + deserializedEntity.GetComponent().Material = material; + } } } diff --git a/Prism/src/Prism/Utilities/AssetsManager.cpp b/Prism/src/Prism/Utilities/AssetsManager.cpp deleted file mode 100644 index 2ab706f..0000000 --- a/Prism/src/Prism/Utilities/AssetsManager.cpp +++ /dev/null @@ -1,423 +0,0 @@ -// -// Created by Atdunbg on 2026/1/20. -// - -#include "AssetsManager.h" - -#include -#include - -#include "Prism/Core/Log.h" -#include "Prism/Renderer/Mesh.h" - -namespace Prism -{ - void AssetTypes::Init() - { - s_Types["scene"] = AssetType::Scene; - 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; - s_Types["cs"] = AssetType::Script; - } - - size_t AssetTypes::GetAssetTypeID(const std::string& extension) - { - for (const auto& kv : s_Types) - { - if (kv.first == extension) - return std::hash()(extension); - } - - return -1; - } - - AssetType AssetTypes::GetAssetTypeFromExtension(const std::string& extension) - { - return s_Types.find(extension) != s_Types.end() ? s_Types[extension] : AssetType::Other; - } - - std::map AssetTypes::s_Types; - AssetsManager::AssetsChangeEventFn AssetsManager::s_AssetsChangeCallback; - std::unordered_map AssetsManager::s_LoadedAssets; - std::vector AssetsManager::s_Directories; - - void AssetsManager::Init() - { - FileSystemWatcher::SetChangeCallback(OnFileSystemChanged); - ReloadAssets(); - } - - 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 = 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; - } - - 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) - { - size_t start; - size_t end = 0; - std::vector out; - - while ((start = filename.find_first_not_of('.', end)) != std::string::npos) - { - end = filename.find('.', start); - out.push_back(filename.substr(start, end - start)); - } - - return out[out.size() - 1]; - } - - 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::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) - { - 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) - { - // Create a filestream to write a blender python script for conversion of the asset - // The 'bpy.ops.export_scene.(asset-type-to-convert) function runs blender in background and exports the file' - std::string path = std::filesystem::temp_directory_path().string(); - std::ofstream fileStream(path + "export.py"); - - // Importing the python modules required for the export to work out - fileStream << "import bpy\n"; - fileStream << "import sys\n"; - - if (conversionType == "fbx") - fileStream << "bpy.ops.export_scene.fbx(filepath=r'" + path + "asset.fbx" + "', axis_forward='-Z', axis_up='Y')\n"; - - if (conversionType == "obj") - fileStream << "bpy.ops.export_scene.obj(filepath=r'" + path + "asset.obj" + "', axis_forward='-Z', axis_up='Y')\n"; - - fileStream.close(); - - // This section involves creating the command to export the .blend file to the required asset type - // The command goes something like this - // blender.exe path\to\files\cube.blend --background --python path\to\file\export.py - - std::string blender_base_path = R"(D:\Application\Blender5.0.1\blender.exe)"; - std::string p_asset_path = '"' + assetPath + '"'; - std::string p_blender_path = '"' + blender_base_path + '"'; - std::string p_script_path = '"' + path + "export.py" + '"'; - - // Creating the actual command that will execute - std::string convCommand = '"' + p_blender_path + " " + p_asset_path + " --background --python " + p_script_path + "" + '"'; - - // Just for debugging(it took me 1hr for this string literals n stuff! It better work!) - PM_CORE_INFO("{0}", convCommand); - - // Fire the above created command - - // TODO: Platform Abstraction! - system(convCommand.c_str()); - } - - int AssetsManager::ProcessDirectory(const std::string& directoryPath,const int parentIndex) - { - 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 (auto entry : std::filesystem::directory_iterator(directoryPath)) - { - if (entry.is_directory()) - { - int childIndex = ProcessDirectory(entry.path().string(), currentIndex); - s_Directories[currentIndex].ChildrenIndices.push_back(childIndex); - } - else - { - ImportAsset(entry.path().string(), false, currentIndex); - } - } - - return currentIndex; - } - - void AssetsManager::ReloadAssets() - { - ProcessDirectory("assets"); - } - - void AssetsManager::OnFileSystemChanged(FileSystemChangedEvent e) - { - e.NewName = RemoveExtension(e.NewName); - e.OldName = RemoveExtension(e.OldName); - - if (e.Action == FileSystemAction::Added) - { - 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; - } - } - - 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; - } - */ - } - - if (s_AssetsChangeCallback) - s_AssetsChangeCallback(); - } -} diff --git a/Prism/src/Prism/Utilities/AssetsManager.h b/Prism/src/Prism/Utilities/AssetsManager.h deleted file mode 100644 index 5065570..0000000 --- a/Prism/src/Prism/Utilities/AssetsManager.h +++ /dev/null @@ -1,105 +0,0 @@ -// -// Created by Atdunbg on 2026/1/20. -// - -#ifndef PRISM_ASSETSMANAGER_H -#define PRISM_ASSETSMANAGER_H - -#include - -#include "FileSystemWatcher.h" -#include "Prism/Core/Ref.h" -#include "Prism/Core/UUID.h" - -namespace Prism -{ - enum class AssetType - { - Scene, Mesh, Texture, EnvMap, Image, Audio, Script, Other - }; - - class AssetTypes - { - public: - static void Init(); - static size_t GetAssetTypeID(const std::string& extension); - static AssetType GetAssetTypeFromExtension(const std::string& extension); - - private: - static std::map s_Types; - }; - - struct DirectoryInfo - { - std::string DirectoryName; - std::string FilePath; - int DirectoryIndex; - int ParentIndex; - std::vector ChildrenIndices; - }; - - struct Asset - { - UUID ID; - std::string FilePath; - std::string FileName; - std::string Extension; - AssetType Type; - int ParentDirectory; - void* Data; - }; - - struct SearchResults - { - std::vector Directories; - std::vector Assets; - }; - - class PRISM_API AssetsManager - { - public: - using AssetsChangeEventFn = std::function; - - 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); - - static SearchResults SearchFiles(const std::string& query, const std::string& searchPath); - static std::string GetParentPath(const std::string& path); - - template - static Ref InstantiateAsset(UUID assetId); - - static Asset& GetAssetFromId(UUID assetId); - - // TODO: This will NOT live here - static bool MoveFile(const std::string& originalPath, const std::string& dest); - - static std::string StripExtras(const std::string& filename); - - private: - 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; - - }; -} - - -#endif //PRISM_ASSETSMANAGER_H \ No newline at end of file diff --git a/Prism/src/Prism/Utilities/DragDropData.h b/Prism/src/Prism/Utilities/DragDropData.h deleted file mode 100644 index 4467f53..0000000 --- a/Prism/src/Prism/Utilities/DragDropData.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Atdunbg on 2026/1/20. -// - -#ifndef PRISM_DRAGDROPDATA_H -#define PRISM_DRAGDROPDATA_H - -namespace Prism -{ - struct DragDropData - { - public: - const char* Type; - const char* SourcePath; - const char* Name; - - DragDropData(const char* type, const char* sourcePath, const char* name) - : Type(type), SourcePath(sourcePath), Name(name) - { - } - }; -} - -#endif //PRISM_DRAGDROPDATA_H \ No newline at end of file diff --git a/Prism/src/Prism/Utilities/FileSystemWatcher.h b/Prism/src/Prism/Utilities/FileSystem.h similarity index 62% rename from Prism/src/Prism/Utilities/FileSystemWatcher.h rename to Prism/src/Prism/Utilities/FileSystem.h index 9541668..62a807c 100644 --- a/Prism/src/Prism/Utilities/FileSystemWatcher.h +++ b/Prism/src/Prism/Utilities/FileSystem.h @@ -2,8 +2,8 @@ // Created by Atdunbg on 2026/1/21. // -#ifndef PRISM_FILESYSTEMWATCHER_H -#define PRISM_FILESYSTEMWATCHER_H +#ifndef PRISM_FILESYSTEM_H +#define PRISM_FILESYSTEM_H #include @@ -17,17 +17,23 @@ namespace Prism struct FileSystemChangedEvent { FileSystemAction Action; - std::string Filepath; + std::string FilePath; std::string OldName; std::string NewName; bool IsDirectory; }; - class PRISM_API FileSystemWatcher + class PRISM_API FileSystem { public: using FileSystemChangedCallbackFn = std::function; + public: + static bool CreateFolder(const std::filesystem::path& filepath); + static bool Exists(const std::string& filepath); + static std::string Rename(const std::string& filepath, const std::string& newName); + static bool PrismDeleteFile(const std::string& filepath); + static void SetChangeCallback(const FileSystemChangedCallbackFn& callback); static void StartWatching(); static void StopWatching(); @@ -41,4 +47,4 @@ namespace Prism } -#endif //PRISM_FILESYSTEMWATCHER_H \ No newline at end of file +#endif //PRISM_FILESYSTEM_H \ No newline at end of file diff --git a/Prism/src/Prism/Utilities/StringUtils.cpp b/Prism/src/Prism/Utilities/StringUtils.cpp new file mode 100644 index 0000000..a660245 --- /dev/null +++ b/Prism/src/Prism/Utilities/StringUtils.cpp @@ -0,0 +1,69 @@ +// +// Created by Atdunbg on 2026/2/13. +// + +#include "StringUtils.h" + +namespace Prism::Utils +{ + + std::string GetFilename(const std::string& filepath) + { + std::vector parts = SplitString(filepath, "/\\"); + + if (!parts.empty()) + return parts[parts.size() - 1]; + + return ""; + } + + std::string GetExtension(const std::string& filename) + { + std::vector parts = SplitString(filename, '.'); + + if (parts.size() > 1) + return parts[parts.size() - 1]; + + return ""; + } + + std::string RemoveExtension(const std::string& filename) + { + return filename.substr(0, filename.find_last_of('.')); + } + + bool StartsWith(const std::string& string, const std::string& start) + { + return string.find(start) == 0; + } + + std::vector SplitString(const std::string& string, const std::string& delimiters) + { + size_t start = 0; + size_t end = string.find_first_of(delimiters); + + std::vector result; + + while (end <= std::string::npos) + { + const std::string token = string.substr(start, end - start); + if (!token.empty()) + result.push_back(token); + + if (end == std::string::npos) + break; + + start = end + 1; + end = string.find_first_of(delimiters, start); + } + + return result; + } + + std::vector SplitString(const std::string& string, const char delimiter) + { + return SplitString(string, std::string(1, delimiter)); + } + + +} diff --git a/Prism/src/Prism/Utilities/StringUtils.h b/Prism/src/Prism/Utilities/StringUtils.h new file mode 100644 index 0000000..4f99ec5 --- /dev/null +++ b/Prism/src/Prism/Utilities/StringUtils.h @@ -0,0 +1,22 @@ +// +// Created by Atdunbg on 2026/2/13. +// + +#ifndef PRISM_STRINGUTILS_H +#define PRISM_STRINGUTILS_H + +#include + +namespace Prism::Utils +{ + std::string GetFilename(const std::string& filepath); + std::string GetExtension(const std::string& filename); + std::string RemoveExtension(const std::string& filename); + bool StartsWith(const std::string& string, const std::string& start); + std::vector SplitString(const std::string& string, const std::string& delimiters); + std::vector SplitString(const std::string& string, const char delimiter); + +} + + +#endif //PRISM_STRINGUTILS_H \ No newline at end of file