lots of asset manager changes. using meta to manage asset, add twoside material flag for grid

This commit is contained in:
2026-02-15 16:17:23 +08:00
parent 896d3c7f97
commit 2bbe332532
45 changed files with 2114 additions and 1333 deletions

View File

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

View File

@ -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<MeshComponent>(Ref<Mesh>::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> asset = AssetsManager::GetAsset<Asset>(assetHandle);
if (asset.Type == AssetType::Mesh)
{
Entity entity = m_EditorScene->CreateEntity(asset.FileName);
entity.AddComponent<MeshComponent>(AssetsManager::InstantiateAsset<Mesh>(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<MeshComponent>(Ref<Mesh>(asset));
SelectEntity(entity);
}
}
}
ImGui::EndDragDropTarget();

View File

@ -118,7 +118,7 @@ namespace Prism
struct RoughnessInput
{
float Value = 0.5f;
float Value = 1.0f;
Ref<Texture2D> TextureMap;
bool UseTexture = false;
};

View File

@ -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<AssetHandle> ChildDirectories;
Directory() = default;
};
}
#endif //PRISM_ASSET_H

View File

@ -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>& asset, AssetType type)
{
YAML::Emitter out;
out << YAML::BeginMap;
switch (type)
{
case Prism::AssetType::PhysicsMat:
{
Ref<PhysicsMaterial> material = Ref<PhysicsMaterial>(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<Asset> AssetSerializer::DeserializeYAML(const Ref<Asset>& 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>();
float dynamicFriction = data["DynamicFriction"].as<float>();
float bounciness = data["Bounciness"].as<float>();
return Ref<PhysicsMaterial>::Create(staticFriction, dynamicFriction, bounciness);
}
return nullptr;
}
Ref<Asset> AssetSerializer::LoadAssetInfo(const std::string& filepath, AssetHandle parentHandle, AssetType type)
{
Ref<Asset> asset;
if (type == AssetType::Directory)
asset = Ref<Directory>::Create();
else
asset = Ref<Asset>::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<Asset> AssetSerializer::LoadAssetData(Ref<Asset>& asset)
{
if (asset->Type == AssetType::Directory)
return asset;
Ref<Asset> temp = asset;
bool loadYAMLData = true;
switch (asset->Type)
{
case AssetType::Mesh:
{
if (asset->Extension != "blend")
asset = Ref<Mesh>::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<Environment>::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>& 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<uint64_t>();
asset->FileName = data["FileName"].as<std::string>();
asset->FilePath = data["FilePath"].as<std::string>();
asset->Extension = data["Extension"].as<std::string>();
asset->Type = (AssetType)data["Type"].as<int>();
}
void AssetSerializer::CreateMetaFile(const Ref<Asset>& 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();
}
}

View File

@ -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<typename T>
static void SerializeAsset(const Ref<T>& asset)
{
static_assert(std::is_base_of<Asset, T>::value, "SerializeAsset only accepts types that inherit from Asset");
SerializeAsset(asset, asset->Type);
}
static Ref<Asset> LoadAssetInfo(const std::string& filepath, AssetHandle parentHandle, AssetType type);
static Ref<Asset> LoadAssetData(Ref<Asset>& asset);
private:
static void SerializeAsset(const Ref<Asset>& asset, AssetType type);
static Ref<Asset> DeserializeYAML(const Ref<Asset>& asset);
static void LoadMetaData(Ref<Asset>& asset);
static void CreateMetaFile(const Ref<Asset>& asset);
private:
friend class AssetsManager;
};
}
#endif //PRISM_ASSETSERIALIZER_H

View File

@ -0,0 +1,450 @@
//
// Created by Atdunbg on 2026/1/20.
//
#include "AssetsManager.h"
#include <filesystem>
#include <utility>
#include "Prism/Core/Log.h"
#include "Prism/Renderer/Mesh.h"
#include <yaml-cpp/yaml.h>
#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<std::string>()(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<std::string, AssetType> AssetTypes::s_Types;
AssetsManager::AssetsChangeEventFn AssetsManager::s_AssetsChangeCallback;
std::unordered_map<AssetHandle, Ref<Asset>> 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<Ref<Asset>> AssetsManager::GetAssetsInDirectory(AssetHandle directoryHandle)
{
std::vector<Ref<Asset>> 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<Ref<Asset>> AssetsManager::SearchFiles(const std::string& query, const std::string& searchPath)
{
std::vector<Ref<Asset>> 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>& 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 <typename T>
Ref<T> AssetsManager::GetAsset(const AssetHandle assetHandle)
{
PM_CORE_ASSERT(s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end());
Ref<Asset> asset = s_LoadedAssets[assetHandle];
if (!asset->IsDataLoaded)
asset = AssetSerializer::LoadAssetData(asset);
return asset.As<T>();
}
template PRISM_API Ref<Asset> AssetsManager::GetAsset(AssetHandle);
template PRISM_API Ref<Mesh> AssetsManager::GetAsset(AssetHandle);
template PRISM_API Ref<PhysicsMaterial> AssetsManager::GetAsset(AssetHandle);
template PRISM_API Ref<Environment> AssetsManager::GetAsset(AssetHandle);
template PRISM_API Ref<Directory> AssetsManager::GetAsset(AssetHandle);
// temp
Ref<PhysicsMaterial> AssetsManager::CreateAssetPhysicsMaterial(const std::string& filename, const AssetType type, const AssetHandle& directoryHandle, float v1, float v2, float v3)
{
const auto& directory = GetAsset<Directory>(directoryHandle);
Ref<PhysicsMaterial> asset = Ref<PhysicsMaterial>::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<std::string>()(asset->FilePath);
s_LoadedAssets[asset->Handle] = asset;
AssetSerializer::SerializeAsset(asset);
return asset;
}
void AssetsManager::RemoveAsset(AssetHandle assetHandle)
{
Ref<Asset> asset = s_LoadedAssets[assetHandle];
if (asset->Type == AssetType::Directory)
{
if (IsAssetHandleValid(asset->ParentDirectory))
{
auto& childList = s_LoadedAssets[asset->ParentDirectory].As<Directory>()->ChildDirectories;
childList.erase(std::remove(childList.begin(), childList.end(), assetHandle), childList.end());
}
for (auto child : asset.As<Directory>()->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<typename T, typename... Args>
Ref<T> AssetsManager::CreateAsset(const std::string& filename, AssetType type, AssetHandle directoryHandle, Args&&... args)
{
static_assert(std::is_base_of_v<Asset, T>, "CreateAsset only works for types derived from Asset");
const auto& directory = GetAsset<Directory>(directoryHandle);
Ref<T> asset = Ref<T>::Create(std::forward<Args>(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<std::string>()(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<std::string> out;
size_t start;
size_t end = 0;
while ((start = filename.find_first_not_of('.', end)) != std::string::npos)
{
end = filename.find('.', start);
out.push_back(filename.substr(start, end - start));
}
if (out[0].length() >= 10)
{
auto cutFilename = out[0].substr(0, 9) + "...";
return cutFilename;
}
const auto filenameLength = out[0].length();
const auto paddingToAdd = 9 - filenameLength;
std::string newFileName;
for (int i = 0; i <= paddingToAdd; i++)
{
newFileName += " ";
}
newFileName += out[0];
return newFileName;
}
void AssetsManager::ImportAsset(const std::string& filepath, AssetHandle parentHandle)
{
const std::string extension = Utils::GetExtension(filepath);
if (extension == "meta")
return;
const AssetType type = AssetTypes::GetAssetTypeFromExtension(extension);
Ref<Asset> 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<Directory> dirInfo = AssetSerializer::LoadAssetInfo(directoryPath, parentHandle, AssetType::Directory).As<Directory>();
s_LoadedAssets[dirInfo->Handle] = dirInfo;
if (parentHandle != dirInfo->Handle && IsAssetHandleValid(parentHandle))
s_LoadedAssets[parentHandle].As<Directory>()->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<Directory>& dir, const std::string& dirName)
{
if (dir->FileName == dirName)
return dir->Handle;
for (const AssetHandle& childHandle : dir->ChildDirectories)
{
Ref<Directory> child = GetAsset<Directory>(childHandle);
AssetHandle dirHandle = FindParentHandleInChildren(child, dirName);
if (IsAssetHandleValid(dirHandle))
return dirHandle;
}
return 0;
}
AssetHandle AssetsManager::FindParentHandle(const std::string& filepath)
{
const std::vector<std::string> parts = Utils::SplitString(filepath, "/\\");
const std::string& parentFolder = parts[parts.size() - 2];
Ref<Directory> assetsDirectory = GetAsset<Directory>(0);
return FindParentHandleInChildren(assetsDirectory, parentFolder);
}
}

View File

@ -0,0 +1,86 @@
//
// Created by Atdunbg on 2026/1/20.
//
#ifndef PRISM_ASSETSMANAGER_H
#define PRISM_ASSETSMANAGER_H
#include <map>
#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<std::string, AssetType> s_Types;
};
class PRISM_API AssetsManager
{
public:
using AssetsChangeEventFn = std::function<void()>;
public:
static void Init();
static void SetAssetChangeCallback(const AssetsChangeEventFn& callback);
static void Shutdown();
static std::vector<Ref<Asset>> GetAssetsInDirectory(AssetHandle directoryHandle);
static std::vector<Ref<Asset>> 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>& asset, const std::string& newName);
static Ref<PhysicsMaterial> CreateAssetPhysicsMaterial(const std::string& filename, AssetType type, const AssetHandle& directoryHandle, float v1, float v2, float v3);
static void RemoveAsset(AssetHandle assetHandle);
template<typename T, typename... Args>
static Ref<T> CreateAsset(const std::string& filename, AssetType type, AssetHandle directoryHandle, Args&&... args);
template<typename T>
static Ref<T> 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<Directory>& dir, const std::string& dirName);
static AssetHandle FindParentHandle(const std::string& filepath);
private:
static std::unordered_map<AssetHandle, Ref<Asset>> s_LoadedAssets;
static AssetsChangeEventFn s_AssetsChangeCallback;
};
}
#endif //PRISM_ASSETSMANAGER_H

View File

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

View File

@ -9,6 +9,9 @@
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#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<typename T>
static bool PropertyAssetReference(const char* label, Ref<T>& object, AssetType supportedType)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
if (object)
{
char* assetName = ((Ref<Asset>&)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<T>(assetHandle);
modified = true;
}
}
}
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static void EndPropertyGrid()
{
ImGui::Columns(1);

View File

@ -123,6 +123,12 @@ namespace Prism
m_Instance = instance;
}
template<typename T2>
Ref<T2> As()
{
return Ref<T2>(*this);
}
template<typename... Args>
static Ref<T> Create(Args&&... args)
{

View File

@ -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<TextureEditor>(AssetType::Texture);
RegisterEditor<PhysicsMaterialEditor>(AssetType::PhysicsMat);
}
void AssetEditorPanel::OnImGuiRender()
{
for (const auto& kv : s_Editors)
kv.second->OnImGuiRender();
}
void AssetEditorPanel::OpenEditor(const Ref<Asset>& 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 <typename T>
void AssetEditorPanel::RegisterEditor(const AssetType type)
{
static_assert(std::is_base_of_v<AssetEditor, T>, "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<T>();
}
template PRISM_API void AssetEditorPanel::RegisterEditor<TextureEditor>(AssetType);
template PRISM_API void AssetEditorPanel::RegisterEditor<PhysicsMaterialEditor>(AssetType);
std::unordered_map<AssetType, Scope<AssetEditor>> AssetEditorPanel::s_Editors;
}

View File

@ -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>& 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>& asset);
template<typename T>
static void RegisterEditor(AssetType type);
private:
static std::unordered_map<AssetType, Scope<AssetEditor>> s_Editors;
};
}
#endif //PRISM_ASSETEDITORPANEL_H

View File

@ -6,7 +6,9 @@
#include <filesystem>
#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<Directory>(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<char*>(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<Directory>(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>& 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<char*>(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<Directory>& dir = AssetsManager::GetAsset<Directory>(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>& 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>& 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<Directory>& dirInfo = AssetsManager::GetAsset<Directory>(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>& 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<Directory>(m_CurrentDirHandle);
m_CurrentDirAssets = AssetsManager::GetAssetsInDirectory(m_CurrentDirHandle);
}
}

View File

@ -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<typename T>
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<T> 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>& asset);
void HandleDragDrop(RendererID icon, const Ref<Asset>& asset);
void RenderBreadCrumbs();
void RenderBottom();
void UpdateCurrentDirectory(int dirIndex);
void HandleRenaming(Ref<Asset>& asset);
// ImGuiInputTextCallback SearchCallback(ImGuiInputTextCallbackData* data);
void UpdateCurrentDirectory(AssetHandle directoryHandle);
private:
Ref<Texture2D> m_FolderTex;
Ref<Texture2D> m_FavoritesTex;
Ref<Texture2D> m_FileTex;
Ref<Texture2D> m_GoBackTex;
Ref<Texture2D> m_ScriptTex;
Ref<Texture2D> m_ResourceTex;
Ref<Texture2D> m_SceneTex;
Ref<Texture2D> m_BackbtnTex;
Ref<Texture2D> m_FwrdbtnTex;
Ref<Texture2D> m_FolderRightTex;
Ref<Texture2D> m_TagsTex;
Ref<Texture2D> m_SearchTex;
Ref<Texture2D> m_GridView;
Ref<Texture2D> 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<DirectoryInfo> m_CurrentDirChildren;
std::vector<Asset> m_CurrentDirAssets;
AssetHandle m_CurrentDirHandle;
AssetHandle m_BaseDirectoryHandle;
AssetHandle m_PrevDirHandle;
AssetHandle m_NextDirHandle;
Ref<Directory> m_CurrentDirectory;
Ref<Directory> m_BaseDirectory;
std::vector<Ref<Asset>> m_CurrentDirAssets;
std::vector<DirectoryInfo> m_BreadCrumbData;
std::vector<Ref<Directory>> m_BreadCrumbData;
AssetHandle m_DraggedAssetId = 0;
SelectionStack<AssetHandle> m_SelectedAssets;
bool m_RenamingSelected = false;
ImGuiInputTextCallbackData m_Data;
std::map<size_t, Ref<Texture2D>> m_AssetIconMap;
};
}

View File

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

View File

@ -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>& asset) override { m_Asset = (Ref<PhysicsMaterial>)asset; }
private:
virtual void Render() override;
private:
Ref<PhysicsMaterial> m_Asset;
};
class TextureEditor : public AssetEditor
{
public:
TextureEditor();
virtual void SetAsset(const Ref<Asset>& asset) override { m_Asset = static_cast<Ref<Texture>>(asset); }
private:
virtual void Render() override;
private:
Ref<Texture> m_Asset;
};
}
#endif //PRISM_DEFAULTASSETEDITORS_H

View File

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

View File

@ -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<Texture2D> m_CubeImage;
};

View File

@ -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<UUID*>(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<BoxCollider2DComponent>("BoxCollider2D");
AddComponentPopup<CircleCollider2DComponent>("CircleCollider2D");
AddComponentPopup<RigidBodyComponent>("RigidBody");
AddComponentPopup<PhysicsMaterialComponent>("PhysicsMaterial");
AddComponentPopup<BoxColliderComponent>("BoxCollider");
AddComponentPopup<SphereColliderComponent>("SphereCollider");
AddComponentPopup<CapsuleColliderComponent>("CapsuleCollider");
@ -559,6 +564,12 @@ namespace Prism
}
DrawComponent<MeshComponent>("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<Mesh>::Create(file);
}
ImGui::Columns(1);
*/
});
DrawComponent<CameraComponent>("Camera", entity, [](CameraComponent& cameraComponent) {
@ -651,28 +663,9 @@ namespace Prism
DrawComponent<SkyLightComponent>("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<PhysicsMaterialComponent>("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<BoxColliderComponent>("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<MeshColliderComponent>("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<MeshComponent>())

View File

@ -16,17 +16,6 @@ namespace Prism
PhysicsActor::PhysicsActor(Entity entity)
: m_Entity(entity), m_RigidBody(entity.GetComponent<RigidBodyComponent>())
{
if (!m_Entity.HasComponent<PhysicsMaterialComponent>())
{
m_Material.StaticFriction = 1.0f;
m_Material.DynamicFriction = 1.0f;
m_Material.Bounciness = 0.0f;
}
else
{
m_Material = entity.GetComponent<PhysicsMaterialComponent>();
}
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<BoxColliderComponent>()) PhysicsWrappers::AddBoxCollider(*this);
if (m_Entity.HasComponent<SphereColliderComponent>()) PhysicsWrappers::AddSphereCollider(*this);
if (m_Entity.HasComponent<CapsuleColliderComponent>()) PhysicsWrappers::AddCapsuleCollider(*this);

View File

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

View File

@ -181,7 +181,11 @@ namespace Prism
void PhysicsWrappers::AddBoxCollider(PhysicsActor& actor)
{
const auto& collider = actor.m_Entity.GetComponent<BoxColliderComponent>();
auto& collider = actor.m_Entity.GetComponent<BoxColliderComponent>();
if (!collider.Material)
collider.Material = Ref<PhysicsMaterial>::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<SphereColliderComponent>();
auto& collider = actor.m_Entity.GetComponent<SphereColliderComponent>();
if (!collider.Material)
collider.Material = Ref<PhysicsMaterial>::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<CapsuleColliderComponent>();
auto& collider = actor.m_Entity.GetComponent<CapsuleColliderComponent>();
if (!collider.Material)
collider.Material = Ref<PhysicsMaterial>::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<MeshColliderComponent>();
if (!collider.Material)
collider.Material = Ref<PhysicsMaterial>::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);

View File

@ -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<std::string> SplitString(const std::string& string, const std::string& delimiters)
{
size_t start = 0;
size_t end = string.find_first_of(delimiters);
std::vector<std::string> 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<std::string> 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<std::string> Tokenize(const std::string& string)
{
return SplitString(string, " \t\n\r");
return Utils::SplitString(string, " \t\n\r");
}
std::vector<std::string> 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);

View File

@ -204,7 +204,7 @@ namespace Prism
const uint32_t faceHeight = m_Height / 3;
PM_CORE_ASSERT(faceWidth == faceHeight, "Non-square faces!");
std::array<uint8_t*, 6> faces;
std::array<uint8_t*, 6> 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];

View File

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

View File

@ -4,25 +4,65 @@
#include <codecvt>
#include "Prism/Utilities/FileSystemWatcher.h"
#include "Prism/Utilities/FileSystem.h"
#include <Windows.h>
#include <filesystem>
#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;
}
}

View File

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

View File

@ -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:

View File

@ -7,9 +7,4 @@
namespace Prism
{
Environment Environment::Load(const std::string& filepath)
{
auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath);
return { filepath, radiance, irradiance };
}
}

View File

@ -10,13 +10,16 @@
namespace Prism
{
struct Environment
class PRISM_API Environment : public Asset
{
std::string FilePath;
public:
Environment() = default;
Environment(const Ref<TextureCube>& radianceMap, const Ref<TextureCube>& irradianceMap)
: RadianceMap(radianceMap), IrradianceMap(irradianceMap) {}
Ref<TextureCube> RadianceMap;
Ref<TextureCube> IrradianceMap;
static PRISM_API Environment Load(const std::string& filepath);
};
}

View File

@ -27,7 +27,7 @@ namespace Prism
// Resources
Ref<MaterialInstance> SkyboxMaterial;
Environment SceneEnvironment;
Ref<Environment> 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]);

View File

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

View File

@ -28,13 +28,14 @@ namespace Prism
UUID ID = 0;
};
struct ParentComponent
struct RelationshipComponent
{
UUID ParentHandle = 0;
UUID ParentHandle = 0;
std::vector<UUID> 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<PhysicsMaterial> Material;
// The mesh that will be drawn in the editor to show the collision bounds
Ref<Mesh> 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<PhysicsMaterial> Material;
// The mesh that will be drawn in the editor to show the collision bounds
Ref<Mesh> DebugMesh;
@ -240,6 +232,7 @@ namespace Prism
float Radius = 0.5f;
float Height = 1.0f;
bool IsTrigger = false;
Ref<PhysicsMaterial> Material;
Ref<Mesh> DebugMesh;
@ -254,6 +247,7 @@ namespace Prism
bool IsConvex = false;
bool IsTrigger = false;
bool OverrideMesh = false;
Ref<PhysicsMaterial> Material;
MeshColliderComponent() = default;
MeshColliderComponent(const MeshColliderComponent& other) = default;
@ -284,9 +278,14 @@ namespace Prism
struct SkyLightComponent
{
Environment SceneEnvironment;
Ref<Environment> SceneEnvironment;
float Intensity = 1.0f;
float Angle = 0.0f;
SkyLightComponent()
: SceneEnvironment(Ref<Environment>::Create())
{
}
};
}

View File

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

View File

@ -67,9 +67,13 @@ namespace Prism
return !(*this == other);
}
void SetParentUUID(const UUID& parent) { GetComponent<ParentComponent>().ParentHandle = parent; }
UUID GetParentUUID() { return GetComponent<ParentComponent>().ParentHandle; }
std::vector<UUID>& Children() { return GetComponent<ChildrenComponent>().Children; }
void SetParentUUID(UUID parent) { GetComponent<RelationshipComponent>().ParentHandle = parent; }
UUID GetParentUUID() { return GetComponent<RelationshipComponent>().ParentHandle; }
std::vector<UUID>& Children() { return GetComponent<RelationshipComponent>().Children; }
bool IsAncesterOf(Entity entity);
bool IsDescendantOf(Entity entity) const;
UUID GetUUID() { return GetComponent<IDComponent>().ID; }
UUID GetSceneUUID() const { return m_Scene->GetUUID(); }

View File

@ -260,14 +260,14 @@ namespace Prism
// TODO: only one sky light at the moment!
{
m_Environment = Environment();
m_Environment = Ref<Environment>::Create();
const auto lights = m_Registry.group<SkyLightComponent>(entt::get<TransformComponent>);
for (const auto entity : lights)
{
auto [transformComponent, skyLightComponent] = lights.get<TransformComponent, SkyLightComponent>(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<Environment>::Create();
auto lights = m_Registry.group<SkyLightComponent>(entt::get<TransformComponent>);
for (auto entity : lights)
{
auto [transformComponent, skyLightComponent] = lights.get<TransformComponent, SkyLightComponent>(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<TransformComponent>();
entity.AddComponent<ParentComponent>();
entity.AddComponent<RelationshipComponent>();
entity.AddComponent<ChildrenComponent>();
if (!name.empty())
@ -635,7 +635,7 @@ namespace Prism
idComponent.ID = uuid;
entity.AddComponent<TransformComponent>();
entity.AddComponent<ParentComponent>();
entity.AddComponent<RelationshipComponent>();
entity.AddComponent<ChildrenComponent>();
if (!name.empty())
@ -681,9 +681,7 @@ namespace Prism
CopyComponentIfExists<TransformComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
// TODO: should copy parent
CopyComponentIfExists<ParentComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<ChildrenComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<RelationshipComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<MeshComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<DirectionalLightComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
@ -695,7 +693,6 @@ namespace Prism
CopyComponentIfExists<BoxCollider2DComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<CircleCollider2DComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<RigidBodyComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<PhysicsMaterialComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<BoxColliderComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<SphereColliderComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<CapsuleColliderComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
@ -762,8 +759,7 @@ namespace Prism
CopyComponent<TagComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<TransformComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<ParentComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<ChildrenComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<RelationshipComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<MeshComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<DirectionalLightComponent>(target->m_Registry, m_Registry, enttMap);
@ -775,7 +771,6 @@ namespace Prism
CopyComponent<BoxCollider2DComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<CircleCollider2DComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<RigidBodyComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<PhysicsMaterialComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<BoxColliderComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<SphereColliderComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<CapsuleColliderComponent>(target->m_Registry, m_Registry, enttMap);

View File

@ -65,7 +65,7 @@ namespace Prism
void SetViewportSize(uint32_t width, uint32_t height);
const Environment& GetEnvironment() const { return m_Environment; }
const Ref<Environment>& GetEnvironment() const { return m_Environment; }
void SetSkybox(const Ref<TextureCube>& skybox);
float& GetSkyboxLod() { return m_SkyboxLod; }
@ -130,7 +130,7 @@ namespace Prism
bool m_IsPlaying = false;
Environment m_Environment;
Ref<Environment> m_Environment;
float m_EnvironmentIntensity = 1.0f;
Ref<TextureCube> m_SkyboxTexture;
Ref<MaterialInstance> m_SkyboxMaterial;

View File

@ -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<ParentComponent>())
if (entity.HasComponent<RelationshipComponent>())
{
auto& parent = entity.GetComponent<ParentComponent>();
out << YAML::Key << "Parent" << YAML::Value << parent.ParentHandle;
}
auto& relationshipComponent = entity.GetComponent<RelationshipComponent>();
out << YAML::Key << "Parent" << YAML::Value << relationshipComponent.ParentHandle;
if (entity.HasComponent<ChildrenComponent>())
{
const auto& childrenComponent = entity.GetComponent<ChildrenComponent>();
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<MeshComponent>().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<SkyLightComponent>();
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<PhysicsMaterialComponent>())
{
out << YAML::Key << "PhysicsMaterialComponent";
out << YAML::BeginMap; // PhysicsMaterialComponent
const auto& physicsMaterial = entity.GetComponent<PhysicsMaterialComponent>();
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<BoxColliderComponent>())
{
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<MeshColliderComponent>();
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<RelationshipComponent>();
uint64_t parentHandle = entity["Parent"] ? entity["Parent"].as<uint64_t>() : 0;
deserializedEntity.GetComponent<ParentComponent>().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<uint64_t>();
deserializedEntity.GetComponent<ChildrenComponent>().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<std::string>();
// TEMP (because script creates mesh component...)
if (!deserializedEntity.HasComponent<MeshComponent>())
UUID assetID;
if (meshComponent["AssetPath"])
{
Ref<Mesh> mesh;
if (!CheckPath(meshPath))
missingPaths.emplace_back(meshPath);
else
mesh = Ref<Mesh>::Create(meshPath);
deserializedEntity.AddComponent<MeshComponent>(mesh);
const std::string assetFilePath = meshComponent["AssetPath"].as<std::string>();
assetID = AssetsManager::GetAssetIDForFile(assetFilePath);
}
else
{
assetID = meshComponent["AssetID"].as<uint64_t>();
}
PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath);
if (AssetsManager::IsAssetHandleValid(assetID) && !deserializedEntity.HasComponent<MeshComponent>())
{
deserializedEntity.AddComponent<MeshComponent>(AssetsManager::GetAsset<Mesh>(assetID));
}
}
if (auto directionalLightComponent = entity["DirectionalLightComponent"])
@ -741,18 +751,23 @@ namespace Prism
if (auto skyLightComponent = entity["SkyLightComponent"])
{
auto& component = deserializedEntity.AddComponent<SkyLightComponent>();
const std::string env = skyLightComponent["EnvironmentAssetPath"].as<std::string>();
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<std::string>();
assetHandle = AssetsManager::GetAssetIDForFile(filePath);
}
else
{
assetHandle = skyLightComponent["EnvironmentMap"].as<uint64_t>();
}
if (AssetsManager::IsAssetHandleValid(assetHandle))
{
component.SceneEnvironment = AssetsManager::GetAsset<Environment>(assetHandle);
}
component.Intensity = skyLightComponent["Intensity"].as<float>();
component.Angle = skyLightComponent["Angle"].as<float>();
}
@ -837,20 +852,19 @@ namespace Prism
}
if (auto physicsMaterialComponent = entity["PhysicsMaterialComponent"])
{
auto& component = deserializedEntity.AddComponent<PhysicsMaterialComponent>();
component.StaticFriction = physicsMaterialComponent["StaticFriction"].as<float>();
component.DynamicFriction = physicsMaterialComponent["DynamicFriction"].as<float>();
component.Bounciness = physicsMaterialComponent["Bounciness"].as<float>();
}
if (auto boxColliderComponent = entity["BoxColliderComponent"])
{
auto& component = deserializedEntity.AddComponent<BoxColliderComponent>();
component.Offset = boxColliderComponent["Offset"].as<glm::vec3>();
component.Size = boxColliderComponent["Size"].as<glm::vec3>();
component.IsTrigger = boxColliderComponent["IsTrigger"] ? boxColliderComponent["IsTrigger"].as<bool>() : false;
auto material = boxColliderComponent["Material"];
if (material && AssetsManager::IsAssetHandleValid(material.as<uint64_t>()))
{
component.Material = AssetsManager::GetAsset<PhysicsMaterial>(material.as<uint64_t>());
}
component.DebugMesh = MeshFactory::CreateBox(component.Size);
}
@ -859,6 +873,11 @@ namespace Prism
auto& component = deserializedEntity.AddComponent<SphereColliderComponent>();
component.Radius = sphereColliderComponent["Radius"].as<float>();
component.IsTrigger = sphereColliderComponent["IsTrigger"] ? sphereColliderComponent["IsTrigger"].as<bool>() : false;
auto material = sphereColliderComponent["Material"];
if (material && AssetsManager::IsAssetHandleValid(material.as<uint64_t>()))
component.Material = AssetsManager::GetAsset<PhysicsMaterial>(material.as<uint64_t>());
component.DebugMesh = MeshFactory::CreateSphere(component.Radius);
}
@ -868,6 +887,11 @@ namespace Prism
component.Radius = capsuleColliderComponent["Radius"].as<float>();
component.Height = capsuleColliderComponent["Height"].as<float>();
component.IsTrigger = capsuleColliderComponent["IsTrigger"] ? capsuleColliderComponent["IsTrigger"].as<bool>() : false;
auto material = capsuleColliderComponent["Material"];
if (material && AssetsManager::IsAssetHandleValid(material.as<uint64_t>()))
component.Material = AssetsManager::GetAsset<PhysicsMaterial>(material.as<uint64_t>());
component.DebugMesh = MeshFactory::CreateCapsule(component.Radius, component.Height);
}
@ -878,16 +902,19 @@ namespace Prism
if (overrideMesh)
{
auto meshPath = meshColliderComponent["AssetPath"].as<std::string>();
if (!CheckPath(meshPath))
UUID assetID;
if (meshComponent["AssetPath"])
{
missingPaths.emplace_back(meshPath);
const auto assetFilePath = meshComponent["AssetPath"].as<std::string>();
assetID = AssetsManager::GetAssetIDForFile(assetFilePath);
}
else
{
collisionMesh = Ref<Mesh>::Create(meshPath);
assetID = meshComponent["AssetID"].as<uint64_t>();
}
collisionMesh = Ref<Mesh>::Create(meshPath);
if (AssetsManager::IsAssetHandleValid(assetID))
collisionMesh = AssetsManager::GetAsset<Mesh>(assetID);
}
if (collisionMesh)
@ -897,16 +924,46 @@ namespace Prism
component.IsTrigger = meshColliderComponent["IsTrigger"] ? meshColliderComponent["IsTrigger"].as<bool>() : 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<uint64_t>()))
{
component.Material = AssetsManager::GetAsset<PhysicsMaterial>(material.as<uint64_t>());
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<PhysicsMaterialComponent>();
Ref<PhysicsMaterial> material = Ref<PhysicsMaterial>::Create();
material->StaticFriction = physicsMaterialComponent["StaticFriction"].as<float>();
material->DynamicFriction = physicsMaterialComponent["DynamicFriction"].as<float>();
material->Bounciness = physicsMaterialComponent["Bounciness"].as<float>();
if (deserializedEntity.HasComponent<BoxColliderComponent>())
deserializedEntity.GetComponent<BoxColliderComponent>().Material = material;
if (deserializedEntity.HasComponent<SphereColliderComponent>())
deserializedEntity.GetComponent<SphereColliderComponent>().Material = material;
if (deserializedEntity.HasComponent<CapsuleColliderComponent>())
deserializedEntity.GetComponent<CapsuleColliderComponent>().Material = material;
if (deserializedEntity.HasComponent<MeshColliderComponent>())
deserializedEntity.GetComponent<MeshColliderComponent>().Material = material;
}
}
}

View File

@ -1,423 +0,0 @@
//
// Created by Atdunbg on 2026/1/20.
//
#include "AssetsManager.h"
#include <filesystem>
#include <utility>
#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<std::string>()(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<std::string, AssetType> AssetTypes::s_Types;
AssetsManager::AssetsChangeEventFn AssetsManager::s_AssetsChangeCallback;
std::unordered_map<size_t, Asset> AssetsManager::s_LoadedAssets;
std::vector<DirectoryInfo> AssetsManager::s_Directories;
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<Asset> AssetsManager::GetAssetsInDirectory(const int dirIndex)
{
std::vector<Asset> results;
for (const auto& asset : s_LoadedAssets)
{
if (asset.second.ParentDirectory == dirIndex)
results.push_back(asset.second);
}
return results;
}
std::vector<std::string> AssetsManager::GetDirectoryNames(const std::string& filepath)
{
std::vector<std::string> result;
size_t start;
size_t end = 0;
while ((start = filepath.find_first_not_of("/\\", end)) != std::string::npos)
{
end = filepath.find("/\\", start);
result.push_back(filepath.substr(start, end - start));
}
return result;
}
SearchResults AssetsManager::SearchFiles(const std::string& query, const std::string& searchPath)
{
SearchResults results;
if (!searchPath.empty())
{
for (const auto& dir : s_Directories)
{
if (dir.DirectoryName.find(query) != std::string::npos && dir.FilePath.find(searchPath) != std::string::npos)
{
results.Directories.push_back(dir);
}
}
for (const auto&[key, asset] : s_LoadedAssets)
{
if (asset.FileName.find(query) != std::string::npos && asset.FilePath.find(searchPath) != std::string::npos)
{
results.Assets.push_back(asset);
}
}
}
return results;
}
std::string AssetsManager::GetParentPath(const std::string& path)
{
return std::filesystem::path(path).parent_path().string();
}
template <typename T>
Ref<T> AssetsManager::InstantiateAsset(UUID assetId)
{
PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end());
return Ref<T>(static_cast<T*>(s_LoadedAssets[assetId].Data));
}
// Scene, Mesh, Texture, EnvMap, Image, Audio, Script, Other
template<>
Ref<Mesh> AssetsManager::InstantiateAsset(UUID assetId)
{
PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end());
return Ref<Mesh>(static_cast<Mesh*>(s_LoadedAssets[assetId].Data));
}
template PRISM_API Ref<Mesh> AssetsManager::InstantiateAsset(UUID assetId);
Asset& AssetsManager::GetAssetFromId(UUID assetId)
{
PM_CORE_ASSERT(s_LoadedAssets.find(assetId) != s_LoadedAssets.end());
return s_LoadedAssets[assetId];
}
bool AssetsManager::MoveFile(const std::string& originalPath, const std::string& dest)
{
// TODO: fixeme: MoveFile cannot work
try
{
std::filesystem::rename(originalPath, dest);
const std::string newPath = dest + "/" + ParseFilename(originalPath, "/\\");
return std::filesystem::exists(newPath);
}catch (const std::exception& e)
{
PM_CORE_ERROR("Move File error: {0}", e.what());
return false;
}
}
std::string AssetsManager::StripExtras(const std::string& filename)
{
std::vector<std::string> out;
size_t start;
size_t end = 0;
while ((start = 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<std::string> 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> mesh = Ref<Mesh>::Create(filepath);
// NOTE(Peter): Required to make sure that the asset doesn't get destroyed when Ref goes out of scope
mesh->IncRefCount();
asset.Data = mesh.Raw();
break;
}
case AssetType::Texture:
{
Ref<Texture2D> texture = Texture2D::Create(filepath);
texture->IncRefCount();
asset.Data = texture.Raw();
break;
}
case AssetType::EnvMap:
{
// TODO
/*Ref<TextureCube> texture = Ref<TextureCube>::Create(filepath);
texture->IncRefCount();
asset.Data = texture.Raw();*/
break;
}
case AssetType::Audio:
{
break;
}
case AssetType::Script:
{
asset.Data = &asset.FilePath;
break;
}
case AssetType::Other:
{
asset.Data = &asset.FilePath;
break;
}
}
if (reimport)
{
for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); ++it)
{
if (it->second.FilePath == filepath)
{
if (it->second.Data != nullptr)
delete it->second.Data;
it->second.Data = asset.Data;
asset.Data = nullptr;
return;
}
}
}
s_LoadedAssets[asset.ID] = asset;
}
void AssetsManager::ConvertAsset(const std::string& assetPath, const std::string& conversionType)
{
// 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();
}
}

View File

@ -1,105 +0,0 @@
//
// Created by Atdunbg on 2026/1/20.
//
#ifndef PRISM_ASSETSMANAGER_H
#define PRISM_ASSETSMANAGER_H
#include <map>
#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<std::string, AssetType> s_Types;
};
struct DirectoryInfo
{
std::string DirectoryName;
std::string FilePath;
int DirectoryIndex;
int ParentIndex;
std::vector<int> ChildrenIndices;
};
struct Asset
{
UUID ID;
std::string FilePath;
std::string FileName;
std::string Extension;
AssetType Type;
int ParentDirectory;
void* Data;
};
struct SearchResults
{
std::vector<DirectoryInfo> Directories;
std::vector<Asset> Assets;
};
class PRISM_API AssetsManager
{
public:
using AssetsChangeEventFn = std::function<void()>;
public:
static void Init();
static void SetAssetChangeCallback(const AssetsChangeEventFn& callback);
static DirectoryInfo& GetDirectoryInfo(int index);
static std::vector<Asset> GetAssetsInDirectory(int dirIndex);
static std::vector<std::string> GetDirectoryNames(const std::string& filepath);
static SearchResults SearchFiles(const std::string& query, const std::string& searchPath);
static std::string GetParentPath(const std::string& path);
template<typename T>
static Ref<T> 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<size_t, Asset> s_LoadedAssets;
static std::vector<DirectoryInfo> s_Directories;
static AssetsChangeEventFn s_AssetsChangeCallback;
};
}
#endif //PRISM_ASSETSMANAGER_H

View File

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

View File

@ -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 <functional>
@ -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<void(FileSystemChangedEvent)>;
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
#endif //PRISM_FILESYSTEM_H

View File

@ -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<std::string> parts = SplitString(filepath, "/\\");
if (!parts.empty())
return parts[parts.size() - 1];
return "";
}
std::string GetExtension(const std::string& filename)
{
std::vector<std::string> 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<std::string> SplitString(const std::string& string, const std::string& delimiters)
{
size_t start = 0;
size_t end = string.find_first_of(delimiters);
std::vector<std::string> 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<std::string> SplitString(const std::string& string, const char delimiter)
{
return SplitString(string, std::string(1, delimiter));
}
}

View File

@ -0,0 +1,22 @@
//
// Created by Atdunbg on 2026/2/13.
//
#ifndef PRISM_STRINGUTILS_H
#define PRISM_STRINGUTILS_H
#include <string>
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<std::string> SplitString(const std::string& string, const std::string& delimiters);
std::vector<std::string> SplitString(const std::string& string, const char delimiter);
}
#endif //PRISM_STRINGUTILS_H