Added Project Asset Pack file

This commit is contained in:
OusmBlueNinja 2025-05-11 22:54:48 -05:00
parent 6a29c5bdee
commit 86abf8e5b7
12 changed files with 394 additions and 218 deletions

View File

@ -27,12 +27,12 @@ DockId=0x0000000F,0
[Window][Viewport] [Window][Viewport]
Pos=342,19 Pos=342,19
Size=1185,488 Size=1185,526
Collapsed=0 Collapsed=0
DockId=0x00000011,0 DockId=0x00000011,0
[Window][##MainMenuBar] [Window][##MainMenuBar]
Size=1280,19 Size=1920,19
Collapsed=0 Collapsed=0
[Window][Performance Info] [Window][Performance Info]
@ -42,8 +42,8 @@ Collapsed=0
DockId=0x00000015,0 DockId=0x00000015,0
[Window][Console] [Window][Console]
Pos=342,509 Pos=342,547
Size=1185,273 Size=1185,235
Collapsed=0 Collapsed=0
DockId=0x00000012,0 DockId=0x00000012,0
@ -125,8 +125,8 @@ Collapsed=0
DockId=0x0000000D,0 DockId=0x0000000D,0
[Window][Resources] [Window][Resources]
Pos=0,465 Pos=0,19
Size=341,712 Size=341,701
Collapsed=0 Collapsed=0
DockId=0x00000003,0 DockId=0x00000003,0
@ -147,7 +147,7 @@ Size=536,391
Collapsed=0 Collapsed=0
[Window][Create New Project] [Window][Create New Project]
Pos=298,22 Pos=505,191
Size=600,209 Size=600,209
Collapsed=0 Collapsed=0
@ -208,8 +208,8 @@ DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,115
DockNode ID=0x0000001D Parent=0x00000017 SizeRef=1208,763 Split=X Selected=0xC450F867 DockNode ID=0x0000001D Parent=0x00000017 SizeRef=1208,763 Split=X Selected=0xC450F867
DockNode ID=0x0000000F Parent=0x0000001D SizeRef=340,399 HiddenTabBar=1 Selected=0x12EF0F59 DockNode ID=0x0000000F Parent=0x0000001D SizeRef=340,399 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000010 Parent=0x0000001D SizeRef=1185,399 Split=Y Selected=0xC450F867 DockNode ID=0x00000010 Parent=0x0000001D SizeRef=1185,399 Split=Y Selected=0xC450F867
DockNode ID=0x00000011 Parent=0x00000010 SizeRef=1185,488 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867 DockNode ID=0x00000011 Parent=0x00000010 SizeRef=1185,526 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x00000012 Parent=0x00000010 SizeRef=1185,273 HiddenTabBar=1 Selected=0xEA83D666 DockNode ID=0x00000012 Parent=0x00000010 SizeRef=1185,235 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x0000001E Parent=0x00000017 SizeRef=1208,393 HiddenTabBar=1 Selected=0x9C2B5678 DockNode ID=0x0000001E Parent=0x00000017 SizeRef=1208,393 HiddenTabBar=1 Selected=0x9C2B5678
DockNode ID=0x00000018 Parent=0x00000007 SizeRef=391,860 Split=Y Selected=0x36DC96AB DockNode ID=0x00000018 Parent=0x00000007 SizeRef=391,860 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000001B Parent=0x00000018 SizeRef=367,636 HiddenTabBar=1 Selected=0x36DC96AB DockNode ID=0x0000001B Parent=0x00000018 SizeRef=367,636 HiddenTabBar=1 Selected=0x36DC96AB

View File

@ -760,6 +760,11 @@ void Engine::Run()
ImGui::Separator(); ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
if (ImGui::MenuItem("Save Project"))
{
Logger::LogInfo("Saving Project.");
ProjectManager::SaveCurrentProject();
}
if (ImGui::MenuItem("Load Project")) if (ImGui::MenuItem("Load Project"))
{ {
std::string file = OpenFileDialog(FileDialogType::Project); std::string file = OpenFileDialog(FileDialogType::Project);

View File

@ -16,7 +16,7 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
#define ASSET_MANEFEST_NAME "assets.capk"
@ -184,7 +184,6 @@ void FileExplorer::Show(bool* p_open)
int cols = std::max(1, int(avail / (iconSize + padding))); int cols = std::max(1, int(avail / (iconSize + padding)));
ImGui::Columns(cols, nullptr, false); ImGui::Columns(cols, nullptr, false);
// Draw each entry
for (auto &e : entries) { for (auto &e : entries) {
fs::path p = e.path(); fs::path p = e.path();
std::string pathStr = p.string(); std::string pathStr = p.string();
@ -192,28 +191,30 @@ void FileExplorer::Show(bool* p_open)
ImGui::PushID(pathStr.c_str()); ImGui::PushID(pathStr.c_str());
// --- IMAGE THUMBNAIL w/ InvisibleButton for clicks --- // 1) pick thumbnail
if (type == AssetType::Image) { ImTextureID thumbID = 0;
if (e.is_directory()) {
thumbID = (ImTextureID)(intptr_t)s_folderIcon;
} else if (type == AssetType::Image) {
if (auto* asset = AssetManager::GetAssetByPath(pathStr)) if (auto* asset = AssetManager::GetAssetByPath(pathStr))
if (asset->type == AssetType::Image) { if (asset->type == AssetType::Image)
auto* img = dynamic_cast<const ImageAssetInfo*>(asset); if (auto* img = dynamic_cast<const ImageAssetInfo*>(asset))
if (img) { thumbID = (ImTextureID)(intptr_t)img->textureID;
if (ImGui::ImageButton("##Image", (ImTextureID)(intptr_t)img->textureID, ImVec2(iconSize, iconSize)))
{
s_currentDir = p;
} }
if (!thumbID) {
GLuint iconTex = EngineLoadTextureIfNeeded(
std::string(ICONS_PATH) + IconFileForPath(p));
thumbID = (ImTextureID)(intptr_t)iconTex;
} }
}
} // 2) DRAW the single widget with a consistent ID: "##entry"
else if (e.is_directory()) { bool clicked = false;
if (ImGui::ImageButton("##item", if (e.is_directory() || type == AssetType::Image) {
(ImTextureID)(intptr_t)s_folderIcon, clicked = ImGui::ImageButton(
ImVec2(iconSize, iconSize))) "##entry",
{ thumbID,
s_currentDir = p; ImVec2(iconSize, iconSize));
} } else {
}
else {
std::string label; std::string label;
switch (type) { switch (type) {
case AssetType::Prefab: label = "Prefab"; break; case AssetType::Prefab: label = "Prefab"; break;
@ -229,29 +230,21 @@ void FileExplorer::Show(bool* p_open)
label.erase(0,1); label.erase(0,1);
break; break;
} }
if (ImGui::Button(label.c_str(), clicked = ImGui::Button(
ImVec2(iconSize, iconSize))) (std::string("##entry") + label).c_str(),
{ ImVec2(iconSize, iconSize));
}
// 3) handle click/navigation
if (clicked) {
if (e.is_directory())
s_currentDir = p;
else
SelectedPath = pathStr; SelectedPath = pathStr;
} }
}
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { // 4) context menu tied to the same "##entry" ID
if (auto* asset = AssetManager::GetAssetByPath(pathStr)) { if (ImGui::BeginPopupContextItem("##entry")) {
if (type == AssetType::Image)
ImGui::SetDragDropPayload(ASSET_TEXTURE,
&asset->uaid,
sizeof(asset->uaid));
else if (type == AssetType::Prefab)
ImGui::SetDragDropPayload(ASSET_PREFAB,
&asset->uaid,
sizeof(asset->uaid));
ImGui::TextUnformatted(p.filename().string().c_str());
}
ImGui::EndDragDropSource();
}
if (ImGui::BeginPopupContextItem()) {
if (e.is_directory() && ImGui::MenuItem("Open")) if (e.is_directory() && ImGui::MenuItem("Open"))
s_currentDir = p; s_currentDir = p;
if (!e.is_directory() && ImGui::MenuItem("Open File")) if (!e.is_directory() && ImGui::MenuItem("Open File"))
@ -261,11 +254,32 @@ void FileExplorer::Show(bool* p_open)
ImGui::EndPopup(); ImGui::EndPopup();
} }
// 5) drag-drop source also tied to "##entry"
if (!e.is_directory() &&
ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
{
if (auto* asset = AssetManager::GetAssetByPath(pathStr)) {
if (type == AssetType::Image)
ImGui::SetDragDropPayload(ASSET_TEXTURE,
&asset->uaid,
sizeof(asset->uaid));
else if (type == AssetType::Prefab)
ImGui::SetDragDropPayload(ASSET_PREFAB,
&asset->uaid,
sizeof(asset->uaid));
}
ImGui::TextUnformatted(p.filename().string().c_str());
ImGui::EndDragDropSource();
}
// 6) label and next column
ImGui::TextWrapped("%s", p.filename().string().c_str()); ImGui::TextWrapped("%s", p.filename().string().c_str());
ImGui::NextColumn(); ImGui::NextColumn();
ImGui::PopID(); ImGui::PopID();
} }
ImGui::Columns(1); ImGui::Columns(1);
ImGui::EndChild(); ImGui::EndChild();
ImGui::End(); ImGui::End();
@ -549,47 +563,62 @@ bool ProjectManager::LoadProject(const std::string &projectFilePath)
return false; return false;
} }
// Read settings
std::string savedName = config["Name"].as<std::string>(projectName); std::string savedName = config["Name"].as<std::string>(projectName);
std::string defaultScene = config["s_defaultScene"].as<std::string>(""); std::string defaultScene = config["s_defaultScene"].as<std::string>("");
Logger::LogInfo("Loading Project: '%s' from '%s'", Logger::LogDebug("Loading '%s'",
savedName.c_str(), projFile.string().c_str()); savedName.c_str());
// update project pointers
s_currentProjectPath = baseDir.string(); s_currentProjectPath = baseDir.string();
s_currentProjectName = savedName; s_currentProjectName = savedName;
// ensure folders
CreateDirectories(baseDir);
// --- Load the asset manifest from Assets/asset_manifest.yaml ---
{
fs::path manifest = baseDir / "Assets" / ASSET_MANEFEST_NAME;
if (fs::exists(manifest)) {
YAML::Node doc = YAML::LoadFile(manifest.string());
if (doc["Assets"]) {
AssetManager::Load(doc["Assets"]);
Logger::LogDebug("Loaded asset manifest: %s",
manifest.string().c_str());
}
} else {
Logger::LogDebug("No asset manifest found at '%s', skipping.",
manifest.string().c_str());
}
}
// finally load the default scene (if any)
if (!defaultScene.empty()) { if (!defaultScene.empty()) {
s_defaultScene = ResolveResPath(defaultScene); s_defaultScene = ResolveResPath(defaultScene);
Logger::LogDebug("Loading Default Scene: %s", s_defaultScene.c_str()); Logger::LogDebug("Loading Default Scene: %s", s_defaultScene.c_str());
if (!fs::exists(s_defaultScene)) { if (!fs::exists(s_defaultScene)) {
Logger::LogError("Default scene does not exist: %s", s_defaultScene.c_str()); Logger::LogError("Default scene does not exist: %s", s_defaultScene.c_str());
return false; return false;
} }
SceneLoader::LoadScene(s_defaultScene); SceneLoader::LoadScene(s_defaultScene);
} }
Logger::LogInfo("Successfully loaded project '%s'", savedName.c_str()); Logger::LogOk("loaded project '%s'", savedName.c_str());
CreateDirectories(baseDir);
SceneLoader::LoadScene(s_defaultScene);
return true; return true;
} }
bool ProjectManager::CreateProject(const std::string &projectPath,
bool ProjectManager::CreateProject(
const std::string &projectPath,
const std::string &projectName) const std::string &projectName)
{ {
fs::path baseDir = fs::path(projectPath) / projectName; fs::path baseDir = fs::path(projectPath) / projectName;
fs::path projFile = baseDir / (projectName + ".cproj"); fs::path projFile = baseDir / (projectName + ".cproj");
std::error_code ec; std::error_code ec;
if (!fs::create_directories(baseDir, ec) && ec) // 1) create project root
{ if (!fs::create_directories(baseDir, ec) && ec) {
Logger::LogError("Failed to create project directory '%s': %s", Logger::LogError("Failed to create project directory '%s': %s",
projectPath.c_str(), ec.message().c_str()); projectPath.c_str(), ec.message().c_str());
return false; return false;
@ -598,18 +627,74 @@ bool ProjectManager::CreateProject(const std::string &projectPath,
s_currentProjectPath = baseDir.string(); s_currentProjectPath = baseDir.string();
s_currentProjectName = projectName; s_currentProjectName = projectName;
fs::path scenePath = baseDir / "Assets" / "Scenes" / projectName / ".cene"; CreateDirectories(baseDir);
fs::path sceneDir = baseDir / "Assets" / "Scenes";
fs::path sceneFile = sceneDir / (projectName + ".cene");
s_defaultScene = sceneFile.string();
s_defaultScene = scenePath.string(); {
// Write .cproj YAML
YAML::Emitter out; YAML::Emitter out;
out << YAML::BeginMap; out << YAML::BeginMap;
out << YAML::Key << "Name" << YAML::Value << projectName; out << YAML::Key << "Name" << YAML::Value << projectName;
out << YAML::Key << "EngineVersion" << YAML::Value << g_engineConfig.version; out << YAML::Key << "EngineVersion" << YAML::Value << g_engineConfig.version;
fs::path relPath = fs::relative(scenePath, baseDir);
out << YAML::Key << "s_defaultScene" << YAML::Value << ("res://" + relPath.generic_string()); fs::path rel = fs::relative(sceneFile, baseDir);
out << YAML::Key << "s_defaultScene"
<< YAML::Value << ("res://" + rel.generic_string());
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
#if defined(_WIN32)
gmtime_s(&tm, &t);
#else
gmtime_r(&t, &tm);
#endif
char buf[32];
std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &tm);
out << YAML::Key << "CreatedDate" << YAML::Value << buf;
out << YAML::EndMap;
std::ofstream fout(projFile);
if (!fout.is_open()) {
Logger::LogError("Could not open project file for writing: %s",
projFile.string().c_str());
return false;
}
fout << out.c_str();
}
Logger::LogOk("Created project '%s' at '%s'",
projectName.c_str(), projectPath.c_str());
SceneLoader::SaveScene(sceneFile.string());
SceneLoader::LoadScene(sceneFile.string());
return true;
}
bool ProjectManager::SaveCurrentProject()
{
if (s_currentProjectPath.empty() || s_currentProjectName.empty())
{
Logger::LogError("Cannot save project: no project loaded");
return false;
}
fs::path baseDir = fs::path(s_currentProjectPath);
fs::path projFile = baseDir / (s_currentProjectName + ".cproj");
{
YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "Name" << YAML::Value << s_currentProjectName;
out << YAML::Key << "EngineVersion" << YAML::Value << g_engineConfig.version;
fs::path rel = fs::relative(s_defaultScene, baseDir);
out << YAML::Key << "s_defaultScene"
<< YAML::Value << ("res://" + rel.generic_string());
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now); auto t = std::chrono::system_clock::to_time_t(now);
@ -632,46 +717,63 @@ bool ProjectManager::CreateProject(const std::string &projectPath,
return false; return false;
} }
fout << out.c_str(); fout << out.c_str();
fout.close(); }
Logger::LogOk("Created project '%s' at '%s'", Logger::LogOk("Saved project");
projectName.c_str(), projectPath.c_str());
CreateDirectories(baseDir); CreateDirectories(baseDir);
SceneLoader::SaveScene(scenePath.string()); {
fs::path manifest = baseDir / "Assets" / ASSET_MANEFEST_NAME;
YAML::Emitter manOut;
manOut << YAML::BeginMap;
AssetManager::Save(manOut);
manOut << YAML::EndMap;
std::ofstream mf(manifest);
SceneLoader::LoadScene(scenePath.string()); if (!mf.is_open())
{
Logger::LogError("Failed to write asset manifest: %s",
manifest.string().c_str());
}
else
{
mf << manOut.c_str();
Logger::LogDebug("Wrote asset manifest: %s",
manifest.string().c_str());
}
}
return true; return true;
} }
bool ProjectManager::SaveProject(const std::string &projectPath,
bool ProjectManager::SaveProject(
const std::string &projectPath,
const std::string &projectName) const std::string &projectName)
{ {
fs::path baseDir = fs::path(projectPath) / projectName; fs::path baseDir = fs::path(projectPath) / projectName;
fs::path projFile = baseDir / (projectName + ".cproj"); fs::path projFile = baseDir / (projectName + ".cproj");
std::error_code ec; std::error_code ec;
if (!fs::create_directories(baseDir, ec) && ec) if (!fs::create_directories(baseDir, ec) && ec) {
{
Logger::LogError("Failed to create project directory '%s': %s", Logger::LogError("Failed to create project directory '%s': %s",
projectPath.c_str(), ec.message().c_str()); projectPath.c_str(), ec.message().c_str());
return false; return false;
} }
YAML::Emitter out; YAML::Emitter out;
out << YAML::BeginMap; out << YAML::BeginMap;
out << YAML::Key << "Name" << YAML::Value << projectName; out << YAML::Key << "Name" << YAML::Value << projectName;
out << YAML::Key << "EngineVersion" << YAML::Value << g_engineConfig.version; out << YAML::Key << "EngineVersion" << YAML::Value << g_engineConfig.version;
fs::path relPath = fs::relative(s_defaultScene,
fs::path(s_currentProjectPath) / s_currentProjectName);
out << YAML::Key << "s_defaultScene" << YAML::Value << ("res://" + relPath.generic_string());
fs::path sceneRel = fs::relative(s_defaultScene,
fs::path(s_currentProjectPath) / s_currentProjectName);
out << YAML::Key << "s_defaultScene"
<< YAML::Value << ("res://" + sceneRel.generic_string());
// timestamp
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now); auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{}; std::tm tm{};
@ -686,25 +788,44 @@ bool ProjectManager::SaveProject(const std::string &projectPath,
out << YAML::EndMap; out << YAML::EndMap;
std::ofstream fout(projFile); std::ofstream fout(projFile);
if (!fout.is_open()) if (!fout.is_open()) {
{
Logger::LogError("Could not open project file for writing: %s", Logger::LogError("Could not open project file for writing: %s",
projFile.string().c_str()); projFile.string().c_str());
return false; return false;
} }
fout << out.c_str(); fout << out.c_str();
fout.close(); fout.close();
Logger::LogOk("Saved project '%s'",
Logger::LogOk("Saved project '%s' at '%s'", projectName.c_str());
projectName.c_str(), projectPath.c_str());
CreateDirectories(baseDir); CreateDirectories(baseDir);
{
fs::path manifest = baseDir / "Assets" / ASSET_MANEFEST_NAME;
YAML::Emitter assetOut;
assetOut << YAML::BeginMap;
AssetManager::Save(assetOut);
assetOut << YAML::EndMap;
std::ofstream mf(manifest);
if (!mf.is_open()) {
Logger::LogError("Failed to write asset manifest: %s",
manifest.string().c_str());
} else {
mf << assetOut.c_str();
mf.close();
Logger::LogDebug("Wrote asset manifest: %s",
manifest.string().c_str());
}
}
// update our in-memory project pointers
s_currentProjectPath = projectPath; s_currentProjectPath = projectPath;
s_currentProjectName = projectName; s_currentProjectName = projectName;
return true; return true;
} }
const std::string &ProjectManager::GetCurrentProjectPath() const std::string &ProjectManager::GetCurrentProjectPath()
{ {
return s_currentProjectPath; return s_currentProjectPath;

View File

@ -57,7 +57,6 @@ private:
static std::string IconFileForPath(const fs::path &path); static std::string IconFileForPath(const fs::path &path);
static void DrawFolderTree(const fs::path &path); static void DrawFolderTree(const fs::path &path);
// Initializes file queue by scanning res:// recursively
static void InitLoading(); static void InitLoading();
}; };
@ -76,6 +75,8 @@ public:
static bool SaveProject(const std::string& projectPath, static bool SaveProject(const std::string& projectPath,
const std::string& projectName); const std::string& projectName);
static bool SaveCurrentProject();
// Creates a project at the path with the name // Creates a project at the path with the name
// makes a .cproj file at the path in the name folder // makes a .cproj file at the path in the name folder
static bool CreateProject(const std::string& projectPath, static bool CreateProject(const std::string& projectPath,

View File

@ -38,7 +38,7 @@ bool VerifySceneHash(const YAML::Node &root)
return hashHex.str() == root["scene_hash"].as<std::string>(); return hashHex.str() == root["scene_hash"].as<std::string>();
} }
void SceneLoader::SaveScene(const std::string &path) void SceneLoader::SaveScene(const std::string &path, bool standalone)
{ {
YAML::Emitter out; YAML::Emitter out;
YAML::Emitter sceneData; YAML::Emitter sceneData;
@ -78,7 +78,8 @@ void SceneLoader::SaveScene(const std::string &path)
out << YAML::Key << "threshold" << YAML::Value << Renderer::GetColorCorrection()->threshold; out << YAML::Key << "threshold" << YAML::Value << Renderer::GetColorCorrection()->threshold;
out << YAML::EndMap; out << YAML::EndMap;
//AssetManager::Save(out); if (standalone)
AssetManager::Save(out);
out << YAML::EndMap; out << YAML::EndMap;
@ -88,7 +89,7 @@ void SceneLoader::SaveScene(const std::string &path)
Logger::LogOk("Scene Saved"); Logger::LogOk("Scene Saved");
} }
void SceneLoader::LoadScene(const std::string &path) void SceneLoader::LoadScene(const std::string &path, bool standalone)
{ {
bool legacyLoad = false; bool legacyLoad = false;
@ -152,16 +153,17 @@ void SceneLoader::LoadScene(const std::string &path)
{ {
Logger::LogWarning("Scene hash does not match! File may be corrupted or tampered."); Logger::LogWarning("Scene hash does not match! File may be corrupted or tampered.");
} }
if (standalone) {
if (root["Assets"])
{
currentStep = "Loading Assets";
currentDetail = "Parsing asset data...";
loadingUI.Update(currentStep, currentDetail, 0.05f);
//if (root["Assets"]) Logger::LogDebug("Loading Assets");
//{ AssetManager::Load(root["Assets"]);
// currentStep = "Loading Assets"; }
// currentDetail = "Parsing asset data..."; }
// loadingUI.Update(currentStep, currentDetail, 0.05f);
//
// Logger::LogDebug("Loading Assets");
// AssetManager::Load(root["Assets"]);
//}
Logger::LogDebug("Reseting Scene."); Logger::LogDebug("Reseting Scene.");
currentStep = "Clearing Previous Scene"; currentStep = "Clearing Previous Scene";

View File

@ -5,7 +5,7 @@
class SceneLoader class SceneLoader
{ {
public: public:
static void SaveScene(const std::string &path); static void SaveScene(const std::string &path, bool standalone = false);
static void LoadScene(const std::string &path); static void LoadScene(const std::string &path, bool standalone = false);
}; };

View File

@ -9,6 +9,8 @@
#include <stb_image.h> #include <stb_image.h>
#include "../../Entitys/Object.h" #include "../../Entitys/Object.h"
#include "../../core/functions/ProjectManager.h"
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
@ -21,6 +23,10 @@ std::unordered_map<std::string, uint64_t> AssetManager::s_PathToUAID;
uint64_t AssetManager::s_NextUAID = 1; uint64_t AssetManager::s_NextUAID = 1;
std::string AssetManager::GetFileExtension(const std::string& filepath) std::string AssetManager::GetFileExtension(const std::string& filepath)
{ {
std::string ext = fs::path(filepath).extension().string(); // ".PNG" std::string ext = fs::path(filepath).extension().string(); // ".PNG"
@ -600,19 +606,29 @@ void AssetManager::ClearAllAssets()
} }
namespace fs = std::filesystem;
void AssetManager::Save(YAML::Emitter &out) void AssetManager::Save(YAML::Emitter &out)
{ {
// Write out each assets UAID and a res:// path
out << YAML::Key << "Assets" << YAML::BeginSeq; out << YAML::Key << "Assets" << YAML::BeginSeq;
fs::path projectRoot = fs::path(ProjectManager::GetCurrentProjectPath());
for (const auto &[uaid, asset] : s_Assets) for (const auto &[uaid, asset] : s_Assets)
{ {
// compute res:// relative path
fs::path fullPath = asset->path;
fs::path rel = fs::relative(fullPath, projectRoot);
std::string resPath = std::string("res://") + rel.generic_string();
out << YAML::BeginMap; out << YAML::BeginMap;
out << YAML::Key << "uaid" << YAML::Value << asset->uaid; out << YAML::Key << "uaid" << YAML::Value << asset->uaid;
out << YAML::Key << "path" << YAML::Value << asset->path; out << YAML::Key << "path" << YAML::Value << resPath;
out << YAML::Key << "filename" << YAML::Value << asset->filename; out << YAML::Key << "filename" << YAML::Value << asset->filename;
out << YAML::Key << "filetype" << YAML::Value << asset->filetype; out << YAML::Key << "filetype" << YAML::Value << asset->filetype;
out << YAML::Key << "type" << YAML::Value << static_cast<int>(asset->type); out << YAML::Key << "type" << YAML::Value << static_cast<int>(asset->type);
out << YAML::Key << "hash" << YAML::Value << asset->hash; out << YAML::Key << "hash" << YAML::Value << asset->hash;
out << YAML::Key << "lastModified" << YAML::Value << asset->lastModified; out << YAML::Key << "lastModified" << YAML::Value << asset->lastModified;
// let each subclass dump its own data
asset->Save(out); asset->Save(out);
out << YAML::EndMap; out << YAML::EndMap;
} }
@ -621,66 +637,69 @@ void AssetManager::Save(YAML::Emitter &out)
void AssetManager::Load(const YAML::Node &node) void AssetManager::Load(const YAML::Node &node)
{ {
if (!node) if (!node) return;
return;
LoadingWindow loadingUI; LoadingWindow loadingUI;
loadingUI.Create("Loading Assets"); loadingUI.Create("Loading Assets");
fs::path projectRoot = fs::path(ProjectManager::GetCurrentProjectPath());
const size_t total = node.size(); const size_t total = node.size();
size_t index = 0; size_t index = 0;
for (const auto &item : node) for (const auto &item : node)
{ {
// 1) read the res:// path and convert to real FS path
std::string resPath = item["path"].as<std::string>();
std::string fsPath = ProjectManager::ResolveResPath(resPath);
uint64_t uaid = item["uaid"].as<uint64_t>(); uint64_t uaid = item["uaid"].as<uint64_t>();
std::string path = item["path"].as<std::string>();
AssetType type = static_cast<AssetType>(item["type"].as<int>()); AssetType type = static_cast<AssetType>(item["type"].as<int>());
// 2) allocate the right subclass
std::shared_ptr<AssetInfo> asset; std::shared_ptr<AssetInfo> asset;
if (type == AssetType::Image) switch (type) {
asset = std::make_shared<ImageAssetInfo>(); case AssetType::Image: asset = std::make_shared<ImageAssetInfo>(); break;
else if (type == AssetType::Audio) case AssetType::Audio: asset = std::make_shared<AudioAssetInfo>(); break;
asset = std::make_shared<AudioAssetInfo>(); case AssetType::Prefab: asset = std::make_shared<PrefabAssetInfo>(); break;
else if (type == AssetType::Prefab) case AssetType::Font: asset = std::make_shared<FontAssetInfo>(); break;
asset = std::make_shared<PrefabAssetInfo>(); default: continue;
else if (type == AssetType::Font) }
asset = std::make_shared<FontAssetInfo>();
else
continue;
float progress = static_cast<float>(index) / total; // 3) progress UI
loadingUI.Update("Loading Asset", path, progress); float progress = static_cast<float>(index++) / total;
loadingUI.Update("Loading Asset", fsPath, progress);
// 4) populate fields
asset->uaid = uaid; asset->uaid = uaid;
asset->path = path; asset->path = fsPath; // now points at real file
asset->filename = item["filename"] ? item["filename"].as<std::string>() : GetFilenameFromPath(path); asset->filename = item["filename"] ? item["filename"].as<std::string>()
asset->filetype = item["filetype"] ? item["filetype"].as<std::string>() : GetFileExtension(path); : GetFilenameFromPath(fsPath);
asset->filetype = item["filetype"] ? item["filetype"].as<std::string>()
: GetFileExtension(fsPath);
asset->hash = item["hash"] ? item["hash"].as<std::string>() : ""; asset->hash = item["hash"] ? item["hash"].as<std::string>() : "";
asset->lastModified = item["lastModified"] ? item["lastModified"].as<std::time_t>() : 0; asset->lastModified = item["lastModified"] ? item["lastModified"].as<std::time_t>() : 0;
asset->loaded = false; asset->loaded = false;
// 5) subclassspecific load
asset->Load(item); asset->Load(item);
// 6) insert into maps
s_Assets [uaid] = asset; s_Assets [uaid] = asset;
s_PathToUAID[path] = uaid; s_PathToUAID[asset->path] = uaid;
if (uaid >= s_NextUAID) s_NextUAID = uaid + 1;
if (uaid >= s_NextUAID) // 7) internal loading
s_NextUAID = uaid + 1; switch (type) {
case AssetType::Image: LoadImageInternal(fsPath, uaid); break;
if (type == AssetType::Image) case AssetType::Audio: LoadAudioInternal(fsPath, uaid); break;
LoadImageInternal(path, uaid); case AssetType::Prefab: LoadPrefabInternal(fsPath, uaid); break;
else if (type == AssetType::Audio) case AssetType::Font: LoadFontInternal(fsPath, uaid); break;
LoadAudioInternal(path, uaid); default:
else if (type == AssetType::Prefab) Logger::LogError("[Engine Error] Unhandled asset type %s",
LoadPrefabInternal(path, uaid); AssetTypeToString(type));
else if (type == AssetType::Font)
LoadFontInternal(path, uaid);
else
Logger::LogError("[Engine Error] Unhandled Internal Asset Loading. %s", AssetTypeToString(type));
++index;
} }
}
loadingUI.Destroy(); loadingUI.Destroy();
} }

View File

@ -158,6 +158,10 @@ public:
static uint64_t GenerateUAID(); static uint64_t GenerateUAID();
bool SaveManifest(const std::string &manifestPath);
bool LoadManifest(const std::string &manifestPath);
static std::string GetFileExtension(const std::string& filepath); static std::string GetFileExtension(const std::string& filepath);
static AssetType AssetTypeFromExtension(std::string ext); static AssetType AssetTypeFromExtension(std::string ext);

View File

@ -25,6 +25,7 @@ static std::unordered_map<FileDialogType, const char*> filters = {
{ FileDialogType::Fonts, "Font Files\0*.ttf;*.otf;*.fnt\0All Files\0*.*\0" }, { FileDialogType::Fonts, "Font Files\0*.ttf;*.otf;*.fnt\0All Files\0*.*\0" },
{ FileDialogType::Models, "3D Models\0*.obj;*.fbx;*.gltf;*.dae\0All Files\0*.*\0" }, { FileDialogType::Models, "3D Models\0*.obj;*.fbx;*.gltf;*.dae\0All Files\0*.*\0" },
{ FileDialogType::Project, "Project File\0*.cproj\0All Files\0*.*\0" }, { FileDialogType::Project, "Project File\0*.cproj\0All Files\0*.*\0" },
{ FileDialogType::AssetManifests, "Asset Manifest Files\0*.cmanifest;*.assetmanifest;*.yaml\0All Files\0*.*\0" },
{ FileDialogType::All, "All Files\0*.*\0" } { FileDialogType::All, "All Files\0*.*\0" }
}; };

View File

@ -14,6 +14,7 @@ enum class FileDialogType {
Fonts, // .ttf, .otf Fonts, // .ttf, .otf
Models, // .obj, .fbx, .gltf, etc. Models, // .obj, .fbx, .gltf, etc.
Project, Project,
AssetManifests,
All All
}; };

View File

@ -56,7 +56,7 @@ bool Logger::s_ShowWarning = true;
bool Logger::s_ShowError = true; bool Logger::s_ShowError = true;
bool Logger::s_ShowDebug = false; bool Logger::s_ShowDebug = false;
bool Logger::s_ShowVerbose = false; bool Logger::s_ShowVerbose = false;
bool Logger::s_PrintToTerminal = true; bool Logger::s_PrintToTerminal = false;
const char *Logger::ToString(Level level) const char *Logger::ToString(Level level)
{ {

View File

@ -16,7 +16,29 @@
//---- Define assertion handler. Defaults to calling assert(). //---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
#include <cstdio>
static inline void ImGuiAssertHandler(const char* expr, const char* file, int line)
{
std::fprintf(stderr,
"Assertion failed: (%s), at %s:%d\n",
expr, file, line);
#if defined(_MSC_VER)
__debugbreak(); // MSVC
#elif defined(__GNUC__) || defined(__clang__)
__builtin_trap(); // GCC / Clang
#else
std::abort(); // Fallback
#endif
}
#undef IM_ASSERT
#define IM_ASSERT(_EXPR) \
do { \
if (!(_EXPR)) \
ImGuiAssertHandler(#_EXPR, __FILE__, __LINE__); \
} while (0)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows