Added Project Manager mostly, just need to make a Asset Metadata save file so you can reload assets

This commit is contained in:
OusmBlueNinja 2025-05-11 13:33:07 -05:00
parent bb3ea1ba2c
commit 6a29c5bdee
11 changed files with 614 additions and 119 deletions

View File

@ -3,7 +3,9 @@
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="userId" value="-126d4bf4:196bd985816:-7fff" />
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="-126d4bf4:196bd985816:-7ffe" />
</MTProjectMetadataState>
</option>
</component>

127
imgui.ini
View File

@ -14,74 +14,74 @@ Size=1920,1158
Collapsed=0
[Window][Inspector]
Pos=1553,19
Size=367,537
Pos=1529,19
Size=391,636
Collapsed=0
DockId=0x0000001B,0
[Window][Scene Tree]
Pos=0,19
Size=341,444
Size=340,763
Collapsed=0
DockId=0x0000000F,0
[Window][Viewport]
Pos=343,19
Size=1208,659
Pos=342,19
Size=1185,488
Collapsed=0
DockId=0x00000017,0
DockId=0x00000011,0
[Window][##MainMenuBar]
Size=1920,19
Size=1280,19
Collapsed=0
[Window][Performance Info]
Pos=1628,680
Size=292,268
Pos=1587,794
Size=333,220
Collapsed=0
DockId=0x00000019,0
DockId=0x00000015,0
[Window][Console]
Pos=343,680
Size=1207,497
Pos=342,509
Size=1185,273
Collapsed=0
DockId=0x00000013,0
DockId=0x00000012,0
[Window][Tilemap Editor]
Pos=265,19
Size=1263,674
Collapsed=0
DockId=0x00000017,1
DockId=0x00000011,1
[Window][Profiler]
Pos=343,955
Size=1232,222
Collapsed=0
DockId=0x00000011,0
DockId=0x00000008,0
[Window][Profiler Timeline]
Pos=265,69
Size=623,651
Collapsed=0
DockId=0x00000015,1
DockId=0x00000008,1
[Window][Profiler (Unity Style)]
Pos=265,430
Size=623,290
Collapsed=0
DockId=0x00000015,1
DockId=0x00000008,1
[Window][Profiler Timeline View]
Pos=265,526
Size=1263,651
Collapsed=0
DockId=0x00000015,1
DockId=0x00000008,1
[Window][Color Correction]
Pos=1628,950
Size=292,227
Pos=1587,1016
Size=333,161
Collapsed=0
DockId=0x0000001A,0
DockId=0x00000016,0
[Window][Asset Browser]
Pos=0,658
@ -113,10 +113,10 @@ Collapsed=0
DockId=0x0000000E,0
[Window][Audio Output]
Pos=1552,680
Size=74,497
Pos=1529,794
Size=56,383
Collapsed=0
DockId=0x00000014,0
DockId=0x00000019,0
[Window][Master Bus]
Pos=1003,570
@ -128,7 +128,7 @@ DockId=0x0000000D,0
Pos=0,465
Size=341,712
Collapsed=0
DockId=0x00000010,0
DockId=0x00000003,0
[Window][Import Preview]
Pos=584,22
@ -136,10 +136,10 @@ Size=550,695
Collapsed=0
[Window][Lua Globals]
Pos=1553,558
Size=367,120
Pos=1529,657
Size=391,135
Collapsed=0
DockId=0x0000001C,0
DockId=0x00000013,0
[Window][Import Assets]
Pos=626,263
@ -151,6 +151,17 @@ Pos=298,22
Size=600,209
Collapsed=0
[Window][File Explorer]
Pos=0,784
Size=1527,393
Collapsed=0
DockId=0x0000001E,0
[Window][ConfirmClearScene]
Pos=808,551
Size=304,75
Collapsed=0
[Table][0x96376740,2]
RefScale=13
Column 0 Weight=1.0000
@ -186,33 +197,35 @@ Column 1 Width=86
Column 2 Weight=1.0000
[Docking][Data]
DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=X
DockNode ID=0x00000005 Parent=0x11111111 SizeRef=989,1158 Split=X
DockNode ID=0x00000001 Parent=0x00000005 SizeRef=341,701 Split=Y Selected=0x12EF0F59
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,637 Split=Y Selected=0x12EF0F59
DockNode ID=0x0000000F Parent=0x00000003 SizeRef=341,444 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000010 Parent=0x00000003 SizeRef=341,712 HiddenTabBar=1 Selected=0x30401527
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,519 HiddenTabBar=1 Selected=0x36AF052B
DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1577,701 Split=Y Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,659 Split=X Selected=0xC450F867
DockNode ID=0x00000017 Parent=0x00000007 SizeRef=1208,860 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x00000018 Parent=0x00000007 SizeRef=367,860 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000001B Parent=0x00000018 SizeRef=367,537 HiddenTabBar=1 Selected=0x36DC96AB
DockNode ID=0x0000001C Parent=0x00000018 SizeRef=367,120 HiddenTabBar=1 Selected=0x8CFF897F
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,497 Split=X Selected=0xEA83D666
DockNode ID=0x00000015 Parent=0x00000008 SizeRef=1283,172 Split=X Selected=0xEA83D666
DockNode ID=0x00000011 Parent=0x00000015 SizeRef=1206,168 HiddenTabBar=1 Selected=0x9B5D3198
DockNode ID=0x00000012 Parent=0x00000015 SizeRef=75,168 Split=X Selected=0x56009A08
DockNode ID=0x00000013 Parent=0x00000012 SizeRef=1207,497 Selected=0xEA83D666
DockNode ID=0x00000014 Parent=0x00000012 SizeRef=74,497 HiddenTabBar=1 Selected=0x56009A08
DockNode ID=0x00000016 Parent=0x00000008 SizeRef=292,172 Split=Y Selected=0x3FC1A724
DockNode ID=0x00000019 Parent=0x00000016 SizeRef=314,268 HiddenTabBar=1 Selected=0x3FC1A724
DockNode ID=0x0000001A Parent=0x00000016 SizeRef=314,227 HiddenTabBar=1 Selected=0xA873C17F
DockNode ID=0x00000006 Parent=0x11111111 SizeRef=289,1158 Split=Y Selected=0x36DC96AB
DockNode ID=0x00000009 Parent=0x00000006 SizeRef=449,488 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000000B Parent=0x00000009 SizeRef=449,556 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000000D Parent=0x0000000B SizeRef=449,860 Selected=0x36DC96AB
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=449,296 HiddenTabBar=1 Selected=0x9D7E7171
DockNode ID=0x0000000C Parent=0x00000009 SizeRef=449,143 HiddenTabBar=1 Selected=0xB6C74292
DockNode ID=0x0000000A Parent=0x00000006 SizeRef=449,211 HiddenTabBar=1 Selected=0xD83E5DD3
DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=X
DockNode ID=0x00000005 Parent=0x11111111 SizeRef=989,1158 Split=X
DockNode ID=0x00000001 Parent=0x00000005 SizeRef=341,701 Split=Y Selected=0x12EF0F59
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,637 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,519 HiddenTabBar=1 Selected=0x36AF052B
DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1577,701 Split=Y Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,684 Split=X Selected=0xC450F867
DockNode ID=0x00000017 Parent=0x00000007 SizeRef=1184,860 Split=Y 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=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=0x00000012 Parent=0x00000010 SizeRef=1185,273 HiddenTabBar=1 Selected=0xEA83D666
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=0x0000001B Parent=0x00000018 SizeRef=367,636 HiddenTabBar=1 Selected=0x36DC96AB
DockNode ID=0x0000001C Parent=0x00000018 SizeRef=367,520 Split=Y Selected=0x8CFF897F
DockNode ID=0x00000013 Parent=0x0000001C SizeRef=367,135 HiddenTabBar=1 Selected=0x8CFF897F
DockNode ID=0x00000014 Parent=0x0000001C SizeRef=367,383 Split=X Selected=0x56009A08
DockNode ID=0x00000019 Parent=0x00000014 SizeRef=56,70 HiddenTabBar=1 Selected=0x56009A08
DockNode ID=0x0000001A Parent=0x00000014 SizeRef=333,70 Split=Y Selected=0x3FC1A724
DockNode ID=0x00000015 Parent=0x0000001A SizeRef=181,220 HiddenTabBar=1 Selected=0x3FC1A724
DockNode ID=0x00000016 Parent=0x0000001A SizeRef=181,161 HiddenTabBar=1 Selected=0xA873C17F
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,472 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x00000006 Parent=0x11111111 SizeRef=289,1158 Split=Y Selected=0x36DC96AB
DockNode ID=0x00000009 Parent=0x00000006 SizeRef=449,488 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000000B Parent=0x00000009 SizeRef=449,556 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000000D Parent=0x0000000B SizeRef=449,860 Selected=0x36DC96AB
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=449,296 HiddenTabBar=1 Selected=0x9D7E7171
DockNode ID=0x0000000C Parent=0x00000009 SizeRef=449,143 HiddenTabBar=1 Selected=0xB6C74292
DockNode ID=0x0000000A Parent=0x00000006 SizeRef=449,211 HiddenTabBar=1 Selected=0xD83E5DD3

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -550,7 +550,10 @@ void Engine::Init()
ProjectManager::Init();
Logger::LogVerbose("Init Script Core");
ScriptCore::Init();
Logger::LogVerbose("Resverving Objects");
Logger::LogVerbose("Init Files Core");
FileExplorer::Init();
Logger::LogVerbose("Reserving Objects");
// These values were AI Generated.
@ -570,7 +573,7 @@ void Engine::Init()
ProjectManager::LoadProject("C:/Users/spenc/OneDrive/Desktop", "TestProject");
//ProjectManager::LoadProject("C:/Users/spenc/OneDrive/Desktop", "TestProject");
}
core::types::Vec2 ScreenToWorld(const core::types::Vec2 &screenPos, const core::types::Vec2 &viewportSize, const core::types::Vec2 &cameraPos, float zoom)
@ -757,7 +760,16 @@ void Engine::Run()
ImGui::Separator();
ImGui::Spacing();
if (ImGui::MenuItem("Load Project"))
{
std::string file = OpenFileDialog(FileDialogType::Project);
Logger::LogInfo("Loading Project.");
if (!file.empty())
{
ProjectManager::LoadProject(file);
}
selected = nullptr;
}
if (ImGui::MenuItem("Create New Project"))
{
OpenNewProjectMenu = true;
@ -766,6 +778,8 @@ void Engine::Run()
ImGui::Separator();
ImGui::Spacing();
@ -915,6 +929,10 @@ void Engine::Run()
ShowAssetBrowser();
DrawAudioPlayingList();
DrawImGuiWindow();
FileExplorer::Update();
FileExplorer::Show(nullptr);
FileExplorer::ProcessLoadQueue();

View File

@ -1,10 +1,11 @@
// ProjectManager.cpp
#include "ProjectManager.h"
#include "../../core/utils/EngineConfig.h"
#include "../../core/utils/Logging.h"
#include "../../Engine.h"
#include "../../core/utils/FileDialog.h"
#include "../../core/utils/utils.h"
#include "../../core/utils/AssetManager.h"
#include "SceneSerializer.h"
#include <yaml-cpp/yaml.h>
@ -16,6 +17,351 @@
#include <cstring>
bool FileExplorer::s_initialized = false;
std::string FileExplorer::SelectedPath;
fs::path FileExplorer::s_root;
fs::path FileExplorer::s_currentDir;
std::unordered_set<std::string> FileExplorer::s_knownFiles;
const char* FileExplorer::ICONS_PATH = "./src/assets/icons/";
unsigned int FileExplorer::s_folderIcon = 0;
std::string FileExplorer::fileSearchQuery = "";
bool FileExplorer::sortAscending = true;
int FileExplorer::sortMode = 0;
bool FileExplorer::showScenes = true;
bool FileExplorer::showImages = true;
bool FileExplorer::showAudio = true;
bool FileExplorer::showScripts = true;
bool FileExplorer::showOther = true;
std::vector<fs::path> FileExplorer::s_loadQueue;
size_t FileExplorer::s_loadIndex = 0;
bool FileExplorer::s_loadingInit = false;
bool FileExplorer::Init()
{
if (s_initialized)
return false;
if (!ProjectManager::HasProject())
return false;
// Resolve and verify root…
s_root = fs::path(ProjectManager::ResolveResPath("res://"));
s_currentDir = s_root;
if (!fs::exists(s_root) || !fs::is_directory(s_root)) {
Logger::LogError("FileExplorer: invalid root path '%s'", s_root.string().c_str());
return false;
}
s_folderIcon = EngineLoadTextureIfNeeded(std::string(ICONS_PATH) + "folder-outline.png");
std::error_code ec;
fs::recursive_directory_iterator it(s_root, fs::directory_options::skip_permission_denied, ec);
fs::recursive_directory_iterator end;
for (; it != end; it.increment(ec)) {
if (ec) {
Logger::LogWarning("FileExplorer: skipping '%s': %s",
it->path().string().c_str(),
ec.message().c_str());
ec.clear();
continue;
}
if (it->is_regular_file())
s_loadQueue.push_back(it->path());
}
s_initialized = true;
return true;
}
void FileExplorer::Show(bool* p_open)
{
if (!s_initialized) return;
if (!ImGui::Begin("File Explorer", p_open,
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::End();
return;
}
// --- Left pane: folder tree ---
ImGui::BeginChild("##FolderPane", ImVec2(250, 0), true);
ImGui::TextUnformatted("res://");
ImGui::Separator();
DrawFolderTree(s_root);
ImGui::EndChild();
ImGui::SameLine();
// --- Right pane: content + toolbar ---
ImGui::BeginChild("##ContentPane", ImVec2(0, 0), false);
// Filters / Search
if (ImGui::Button("Filters / Sort")) ImGui::OpenPopup("FileFilterPopup");
ImGui::SameLine();
ImGui::SetNextItemWidth(200);
char buf[256];
strncpy(buf, fileSearchQuery.c_str(), sizeof(buf));
buf[sizeof(buf)-1] = '\0';
if (ImGui::InputTextWithHint("##Search", "Search...", buf, sizeof(buf)))
fileSearchQuery = buf;
if (ImGui::BeginPopup("FileFilterPopup")) {
ImGui::Text("Sort By:");
if (ImGui::Selectable("Name", sortMode == 0)) sortMode = 0;
if (ImGui::Selectable("Type", sortMode == 1)) sortMode = 1;
ImGui::Checkbox("Ascending", &sortAscending);
ImGui::Separator();
ImGui::Text("Type:");
ImGui::Checkbox("Scenes", &showScenes);
ImGui::Checkbox("Images", &showImages);
ImGui::Checkbox("Audio", &showAudio);
ImGui::Checkbox("Scripts", &showScripts);
ImGui::Checkbox("Other", &showOther);
ImGui::EndPopup();
}
ImGui::Separator();
// “Up” button
if (s_currentDir != s_root) {
if (ImGui::Button("Up")) s_currentDir = s_currentDir.parent_path();
ImGui::SameLine();
}
// Current path display
fs::path rel = fs::relative(s_currentDir, s_root);
std::string displayPath = "res://" +
(rel.empty() ? std::string() : rel.generic_string() + "/");
ImGui::TextUnformatted(displayPath.c_str());
ImGui::Separator();
// Gather & filter
std::vector<fs::directory_entry> entries;
for (auto &e : fs::directory_iterator(s_currentDir)) {
if (!e.is_directory()) {
AssetType t = AssetManager::AssetTypeFromPath(e.path().string());
switch (t) {
case AssetType::Scene: if (!showScenes) continue; break;
case AssetType::Image: if (!showImages) continue; break;
case AssetType::Audio: if (!showAudio) continue; break;
case AssetType::Script: if (!showScripts) continue; break;
default: if (!showOther) continue; break;
}
}
if (!fileSearchQuery.empty() &&
e.path().filename().string().find(fileSearchQuery) == std::string::npos)
continue;
entries.push_back(e);
}
// Sort
std::sort(entries.begin(), entries.end(), [&](auto &a, auto &b) {
if (sortMode == 1) {
bool da = a.is_directory(), db = b.is_directory();
if (da != db) return sortAscending ? da > db : da < db;
return sortAscending
? a.path().extension() < b.path().extension()
: a.path().extension() > b.path().extension();
}
return sortAscending
? a.path().filename() < b.path().filename()
: a.path().filename() > b.path().filename();
});
// Icon grid setup
constexpr float iconSize = 64.0f, padding = 8.0f;
float avail = ImGui::GetContentRegionAvail().x;
int cols = std::max(1, int(avail / (iconSize + padding)));
ImGui::Columns(cols, nullptr, false);
// Draw each entry
for (auto &e : entries) {
fs::path p = e.path();
std::string pathStr = p.string();
AssetType type = AssetManager::AssetTypeFromPath(pathStr);
ImGui::PushID(pathStr.c_str());
// --- IMAGE THUMBNAIL w/ InvisibleButton for clicks ---
if (type == AssetType::Image) {
if (auto* asset = AssetManager::GetAssetByPath(pathStr))
if (asset->type == AssetType::Image) {
auto* img = dynamic_cast<const ImageAssetInfo*>(asset);
if (img) {
if (ImGui::ImageButton("##Image", (ImTextureID)(intptr_t)img->textureID, ImVec2(iconSize, iconSize)))
{
s_currentDir = p;
}
}
}
}
else if (e.is_directory()) {
if (ImGui::ImageButton("##item",
(ImTextureID)(intptr_t)s_folderIcon,
ImVec2(iconSize, iconSize)))
{
s_currentDir = p;
}
}
else {
std::string label;
switch (type) {
case AssetType::Prefab: label = "Prefab"; break;
case AssetType::Scene: label = "Scene"; break;
case AssetType::Audio: label = "Audio"; break;
case AssetType::Script: label = "Script"; break;
case AssetType::Video: label = "Video"; break;
case AssetType::Font: label = "Font"; break;
case AssetType::Shader: label = "Shader"; break;
default:
label = p.extension().string();
if (!label.empty() && label.front()=='.')
label.erase(0,1);
break;
}
if (ImGui::Button(label.c_str(),
ImVec2(iconSize, iconSize)))
{
SelectedPath = pathStr;
}
}
if (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();
}
if (ImGui::BeginPopupContextItem()) {
if (e.is_directory() && ImGui::MenuItem("Open"))
s_currentDir = p;
if (!e.is_directory() && ImGui::MenuItem("Open File"))
SelectedPath = pathStr;
if (ImGui::MenuItem("Copy Path"))
ImGui::SetClipboardText(pathStr.c_str());
ImGui::EndPopup();
}
ImGui::TextWrapped("%s", p.filename().string().c_str());
ImGui::NextColumn();
ImGui::PopID();
}
ImGui::Columns(1);
ImGui::EndChild();
ImGui::End();
}
void FileExplorer::Update()
{
if (!s_initialized)
{
Init();
return;
}
std::error_code ec;
std::unordered_set<std::string> currentFiles;
for (auto it = fs::recursive_directory_iterator(s_root,
fs::directory_options::skip_permission_denied, ec),
end = fs::recursive_directory_iterator{};
it != end; it.increment(ec))
{
if (ec) {
Logger::LogWarning("FileExplorer: skipping '%s': %s",
it->path().string().c_str(),
ec.message().c_str());
ec.clear();
continue;
}
if (!it->is_regular_file()) continue;
auto pathStr = it->path().string();
currentFiles.insert(pathStr);
if (!s_knownFiles.count(pathStr)) {
s_knownFiles.insert(pathStr);
s_loadQueue.push_back(it->path());
Logger::LogVerbose("FileExplorer: added '%s'", pathStr.c_str());
}
}
// detect deletions
for (auto it = s_knownFiles.begin(); it != s_knownFiles.end(); )
{
if (!currentFiles.count(*it)) {
Logger::LogVerbose("FileExplorer: removed '%s'", it->c_str());
AssetManager::GetAssetByPath(*it);
it = s_knownFiles.erase(it);
}
else ++it;
}
}
bool FileExplorer::LoadingDone()
{
return s_initialized && s_loadIndex >= s_loadQueue.size();
}
void FileExplorer::DrawFolderTree(const fs::path &path)
{
for (auto &e : fs::directory_iterator(path)) {
if (!e.is_directory()) continue;
auto &d = e.path();
ImGui::PushID(d.string().c_str());
ImGui::Image((ImTextureID)(intptr_t)s_folderIcon, ImVec2(16,16)); ImGui::SameLine();
bool open = ImGui::TreeNodeEx(d.string().c_str(), ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth, "%s", d.filename().string().c_str());
if (ImGui::IsItemClicked()) s_currentDir = d;
if (open) { DrawFolderTree(d); ImGui::TreePop(); }
ImGui::PopID();
}
}
void FileExplorer::ProcessLoadQueue()
{
while (!s_loadQueue.empty()) {
fs::path p = s_loadQueue.back();
s_loadQueue.pop_back();
// Load the asset and get the type,... Long way...
AssetManager::LoadAssetAsync(p.string(), AssetManager::AssetTypeFromExtension(AssetManager::GetFileExtension(p.string())));
Logger::LogVerbose("FileExplorer: imported '%s'", p.string().c_str());
}
}
std::string FileExplorer::IconFileForPath(const fs::path &path)
{
auto ext = path.extension().string();
if (ext == ".cene") return "movie-open-outline.png";
return "error.png";
}
namespace fs = std::filesystem;
std::string ProjectManager::s_currentProjectPath;
@ -102,7 +448,7 @@ bool CreateDirectories(const fs::path &baseDir)
"Scenes",
"Images",
"Sounds",
"Fomts",
"Fonts",
"Shaders",
"Scripts",
"Videos",
@ -129,25 +475,49 @@ bool CreateDirectories(const fs::path &baseDir)
return allOk;
}
bool ProjectManager::HasProject() {
if (s_currentProjectPath.empty() || s_currentProjectName.empty())
{
return false;
}
return true;
}
std::string ProjectManager::ResolveResPath(const std::string &resPath)
{
constexpr const char *prefix = "res://";
// if its not a “res://” path, just return it
if (resPath.rfind(prefix, 0) != 0)
return resPath;
if (s_currentProjectPath.empty() || s_currentProjectName.empty())
if (s_currentProjectPath.empty())
{
Logger::LogError("Cannot resolve res path: 'project not loaded!'");
Logger::LogError("Cannot resolve res path: project not loaded!");
return resPath;
}
fs::path baseDir = fs::path(s_currentProjectPath) / s_currentProjectName;
std::string relativePart = resPath.substr(strlen(prefix));
fs::path fullPath = baseDir / relativePart;
// start from whatever s_currentProjectPath holds
fs::path baseDir{s_currentProjectPath};
return fullPath.lexically_normal().string();
// only append the project name if it isn't already the last path component
if (!s_currentProjectName.empty() &&
baseDir.filename().string() != s_currentProjectName)
{
baseDir /= s_currentProjectName;
}
// drop the "res://" prefix
std::string relativePart = resPath.substr(strlen(prefix));
// build & normalize
fs::path fullPath = (baseDir / relativePart).lexically_normal();
return fullPath.string();
}
bool ProjectManager::Init()
{
s_currentProjectPath.clear();
@ -157,63 +527,60 @@ bool ProjectManager::Init()
return true;
}
bool ProjectManager::LoadProject(const std::string &projectPath,
const std::string &projectName)
bool ProjectManager::LoadProject(const std::string &projectFilePath)
{
fs::path baseDir = fs::path(projectPath) / projectName;
fs::path projFile = baseDir / (projectName + ".cproj");
if (!fs::exists(projFile))
{
Logger::LogInfo("Project '%s' not found at '%s' creating new one.",
projectName.c_str(), projectPath.c_str());
return SaveProject(projectPath, projectName);
fs::path projFile{ projectFilePath };
if (projFile.extension() != ".cproj" || !fs::exists(projFile)) {
Logger::LogError("Project file not found or invalid: '%s'",
projectFilePath.c_str());
return false;
}
fs::path baseDir = projFile.parent_path();
std::string projectName = projFile.stem().string();
YAML::Node config;
try
{
try {
config = YAML::LoadFile(projFile.string());
}
catch (const YAML::Exception &e)
{
catch (const YAML::Exception &e) {
Logger::LogError("Failed to parse project file '%s': %s",
projFile.string().c_str(), e.what());
return false;
}
std::string savedName = config["Name"].as<std::string>(projectName);
std::string createdDate = config["CreatedDate"].as<std::string>("unknown");
std::string s_defaultScene = config["s_defaultScene"].as<std::string>("s_defaultScene");
// Read settings
std::string savedName = config["Name"].as<std::string>(projectName);
std::string defaultScene = config["s_defaultScene"].as<std::string>("");
Logger::LogInfo("Loading Project: '%s", savedName.c_str());
Logger::LogInfo("Loading Project: '%s' from '%s'",
savedName.c_str(), projFile.string().c_str());
s_currentProjectPath = projectPath;
s_currentProjectPath = baseDir.string();
s_currentProjectName = savedName;
std::string resScenePath = config["s_defaultScene"].as<std::string>("");
if (!defaultScene.empty()) {
s_defaultScene = ResolveResPath(defaultScene);
Logger::LogDebug("Loading Default Scene: %s", s_defaultScene.c_str());
if (!resScenePath.empty())
{
s_defaultScene = ResolveResPath(resScenePath);
Logger::LogDebug("Loading Scene: %s", s_defaultScene.c_str());
if (!std::filesystem::exists(s_defaultScene))
{
Logger::LogError("Scene file does not exist: %s", s_defaultScene.c_str());
if (!fs::exists(s_defaultScene)) {
Logger::LogError("Default scene does not exist: %s", s_defaultScene.c_str());
return false;
}
SceneLoader::LoadScene(s_defaultScene);
}
Logger::LogInfo("Loaded project '%s'", savedName.c_str());
Logger::LogInfo("Successfully loaded project '%s'", savedName.c_str());
CreateDirectories(baseDir);
SceneLoader::LoadScene(s_defaultScene);
return true;
}
bool ProjectManager::CreateProject(const std::string &projectPath,
const std::string &projectName)
{
@ -228,14 +595,11 @@ bool ProjectManager::CreateProject(const std::string &projectPath,
return false;
}
// Set current project path before scene creation
s_currentProjectPath = baseDir.string();
s_currentProjectName = projectName;
// Create initial scene
fs::path scenePath = baseDir / "Assets" / "Scenes" / "main.cene";
fs::path scenePath = baseDir / "Assets" / "Scenes" / projectName / ".cene";
SceneLoader::SaveScene(scenePath.string());
s_defaultScene = scenePath.string();
@ -275,6 +639,9 @@ bool ProjectManager::CreateProject(const std::string &projectPath,
CreateDirectories(baseDir);
SceneLoader::SaveScene(scenePath.string());
SceneLoader::LoadScene(scenePath.string());
return true;
@ -351,8 +718,16 @@ const std::string &ProjectManager::GetCurrentProjectName()
const std::string &ProjectManager::GetCurrentAssetsPath()
{
static std::string assetsPath;
fs::path p = fs::path(s_currentProjectPath) / s_currentProjectName / "Assets";
assetsPath = p.string();
return assetsPath;
fs::path baseDir{s_currentProjectPath};
if (!s_currentProjectName.empty() &&
baseDir.filename().string() != s_currentProjectName)
{
baseDir /= s_currentProjectName;
}
fs::path assetDir = (baseDir / "Assets").lexically_normal();
assetsPath = assetDir.string();
return assetsPath;
}

View File

@ -2,6 +2,65 @@
#include <string>
#include <filesystem>
#include <vector>
#include <unordered_set>
namespace fs = std::filesystem;
class FileExplorer
{
public:
// Shows the explorer window (call every frame)
static void Show(bool* p_open = nullptr);
static bool Init();
static void Update();
static void ProcessLoadQueue();
static bool LoadingDone();
static const std::vector<fs::path>& GetLoadQueue() { return s_loadQueue; }
static std::string SelectedPath;
private:
static bool s_initialized;
// File browsing state
static fs::path s_root;
static fs::path s_currentDir;
static std::unordered_set<std::string> s_knownFiles; // <-- new
// Icons
static const char* ICONS_PATH;
static unsigned int s_folderIcon;
// Filters & view state (unchanged)...
static std::string fileSearchQuery;
static bool sortAscending;
static int sortMode;
static bool showScenes, showImages, showAudio, showScripts, showOther;
// Asset loading queue
static std::vector<fs::path> s_loadQueue;
static size_t s_loadIndex;
static bool s_loadingInit;
static std::string IconFileForPath(const fs::path &path);
static void DrawFolderTree(const fs::path &path);
// Initializes file queue by scanning res:// recursively
static void InitLoading();
};
class ProjectManager
{
@ -10,8 +69,7 @@ public:
/// Load an existing project at projectPath/projectName.
/// Reads the .cproj, sets current path/name, and ensures folders.
static bool LoadProject(const std::string& projectPath,
const std::string& projectName);
static bool LoadProject(const std::string& projectFilePath);
/// Save (or create) a project at projectPath/projectName.
/// Writes the .cproj YAML, sets current path/name, and ensures folders.
@ -25,6 +83,8 @@ public:
static void ShowCreateProjectPopup();
static bool HasProject();
static const std::string& GetCurrentProjectPath();
static const std::string& GetCurrentProjectName();

View File

@ -43,6 +43,7 @@ void SceneLoader::SaveScene(const std::string &path)
YAML::Emitter out;
YAML::Emitter sceneData;
// Serialize object list only
sceneData << YAML::BeginSeq;
for (const auto &obj : objects)
@ -57,6 +58,8 @@ void SceneLoader::SaveScene(const std::string &path)
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
hashHex << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
out << YAML::BeginMap;
out << YAML::Key << "engine_version" << YAML::Value << g_engineConfig.version;
out << YAML::Key << "scene_name" << YAML::Value << std::filesystem::path(path).stem().string();
@ -75,7 +78,7 @@ void SceneLoader::SaveScene(const std::string &path)
out << YAML::Key << "threshold" << YAML::Value << Renderer::GetColorCorrection()->threshold;
out << YAML::EndMap;
AssetManager::Save(out);
//AssetManager::Save(out);
out << YAML::EndMap;
@ -150,15 +153,15 @@ void SceneLoader::LoadScene(const std::string &path)
Logger::LogWarning("Scene hash does not match! File may be corrupted or tampered.");
}
if (root["Assets"])
{
currentStep = "Loading Assets";
currentDetail = "Parsing asset data...";
loadingUI.Update(currentStep, currentDetail, 0.05f);
Logger::LogDebug("Loading Assets");
AssetManager::Load(root["Assets"]);
}
//if (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.");
currentStep = "Clearing Previous Scene";

View File

@ -4,6 +4,7 @@
#include <iostream>
#include "../audio/AudioEngine.h"
#include "LoadingWindow.h"
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include "../../Entitys/Object.h"
@ -11,12 +12,25 @@
#include <ft2build.h>
#include FT_FREETYPE_H
namespace fs = std::filesystem;
std::unordered_map<uint64_t, std::shared_ptr<AssetInfo>> AssetManager::s_Assets;
std::unordered_map<std::string, uint64_t> AssetManager::s_PathToUAID;
uint64_t AssetManager::s_NextUAID = 1;
std::string AssetManager::GetFileExtension(const std::string& filepath)
{
std::string ext = fs::path(filepath).extension().string(); // ".PNG"
if (!ext.empty() && ext.front() == '.')
ext.erase(0, 1); // "PNG"
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
return ext; // "png"
}
AssetType AssetManager::AssetTypeFromExtension(std::string ext)
{
@ -75,6 +89,12 @@ AssetType AssetManager::AssetTypeFromExtension(std::string ext)
AssetType AssetManager::AssetTypeFromPath(const std::string& filepath)
{
return AssetTypeFromExtension(GetFileExtension(filepath));
}
const char *MiniaudioResultToString(ma_result result)
{

View File

@ -157,8 +157,12 @@ public:
static uint64_t GenerateUAID();
static std::string GetFileExtension(const std::string& filepath);
static AssetType AssetTypeFromExtension(std::string ext);
static AssetType AssetTypeFromPath(const std::string& filepath);
private:
static std::unordered_map<uint64_t, std::shared_ptr<AssetInfo>> s_Assets;
static std::unordered_map<std::string, uint64_t> s_PathToUAID;

View File

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