Started On Asset Manager

This commit is contained in:
OusmBlueNinja 2025-05-21 11:34:23 -05:00
parent f298c48564
commit 6225c07dbd
11 changed files with 352 additions and 42 deletions

View File

@ -105,6 +105,12 @@ add_library(Core STATIC
src/core/systems/MACROS.h
src/core/renderer/Renderer.cpp
src/core/renderer/Renderer.h
src/core/systems/AssetManager.cpp
src/core/systems/AssetManager.h
src/core/systems/Asset.cpp
src/core/systems/Asset.h
src/core/systems/assets/Texture2D.cpp
src/core/systems/assets/Texture2D.h
)
target_include_directories(Core PUBLIC src/core)

View File

@ -5,6 +5,7 @@
#include "Core.h"
#include "systems/MACROS.h"
#include "systems/AssetManager.h"
namespace OX
{
@ -43,6 +44,8 @@ namespace OX
window.SetWindowTitle(fullTitle);
Logger::LogOk("Core Initialization Complete.");
AssetManager::Init("C:/Users/spenc/OneDrive/Desktop/OnyxProject");
}
@ -67,6 +70,7 @@ namespace OX
void Core::Update()
{
OX_PROFILE_FUNCTION();
AssetManager::Tick();
for (auto &layer: m_layers) {
layer->Update(*this);
}
@ -93,6 +97,7 @@ namespace OX
}
m_layers.clear();
AssetManager::Shutdown();
window.Shutdown();

View File

@ -5,4 +5,5 @@
#include "Renderer.h"
namespace OX {
} // OX

View File

@ -0,0 +1,8 @@
//
// Created by spenc on 5/21/2025.
//
#include "Asset.h"
namespace OX {
} // OX

20
src/core/systems/Asset.h Normal file
View File

@ -0,0 +1,20 @@
//
// Created by spenc on 5/21/2025.
//
#ifndef ASSET_H
#define ASSET_H
#include "Logger.h"
namespace OX {
class Asset {
public:
virtual ~Asset() = default;
virtual std::string GetTypeName() const = 0;
virtual bool LoadFromFile(const std::string& path) = 0;
};
} // OX
#endif //ASSET_H

View File

@ -0,0 +1,110 @@
#include "AssetManager.h"
#include <yaml-cpp/yaml.h>
#include "assets/Texture2D.h"
#include "Logger.h"
#include "Profiler.h"
namespace fs = std::filesystem;
namespace OX {
std::unordered_map<std::string, std::shared_ptr<Asset>> AssetManager::s_AssetMap;
std::unordered_map<std::string, std::type_index> AssetManager::s_AssetTypes;
std::unordered_map<std::string, AssetManager::AssetMetadata> AssetManager::s_AssetDatabase;
std::mutex AssetManager::s_QueueMutex;
std::queue<std::pair<std::string, AssetManager::AssetMetadata>> AssetManager::s_LoadQueue;
std::thread AssetManager::s_BackgroundThread;
std::atomic<bool> AssetManager::s_Running = false;
fs::path AssetManager::s_ProjectDirectory;
void AssetManager::Init(const std::string& projectDir) {
OX_PROFILE_FUNCTION();
s_ProjectDirectory = fs::absolute(projectDir);
s_Running = true;
s_BackgroundThread = std::thread([] {
ScanAssetsRecursive(s_ProjectDirectory);
});
}
void AssetManager::Shutdown() {
OX_PROFILE_FUNCTION();
s_Running = false;
if (s_BackgroundThread.joinable())
s_BackgroundThread.join();
}
void AssetManager::ScanAssetsRecursive(const fs::path& root) {
OX_PROFILE_FUNCTION();
for (auto& entry : fs::recursive_directory_iterator(root)) {
if (!entry.is_regular_file()) continue;
const auto& path = entry.path();
std::string virtualPath = GetVirtualPath(path);
std::string type = DetectTypeFromExtension(path);
if (type.empty()) continue;
AssetMetadata meta{ type, path.string() };
std::lock_guard<std::mutex> lock(s_QueueMutex);
s_LoadQueue.push({ virtualPath, meta });
}
}
void AssetManager::Tick() {
OX_PROFILE_FUNCTION();
std::lock_guard<std::mutex> lock(s_QueueMutex);
if (!s_LoadQueue.empty()) {
auto [id, meta] = s_LoadQueue.front();
s_LoadQueue.pop();
RegisterAssetMetadata(id, meta.type, meta.path);
LoadAsset(id);
}
}
std::string AssetManager::GetVirtualPath(const fs::path& absolute) {
OX_PROFILE_FUNCTION();
fs::path relative = fs::relative(absolute, s_ProjectDirectory);
return "res://" + relative.generic_string();
}
std::string AssetManager::DetectTypeFromExtension(const fs::path& path) {
OX_PROFILE_FUNCTION();
std::string ext = path.extension().string();
if (ext == ".png" || ext == ".jpg") return "texture2D";
if (ext == ".wav" || ext == ".ogg") return "audio";
if (ext == ".prefab" || ext == ".yaml") return "prefab";
return "";
}
void AssetManager::RegisterAssetMetadata(const std::string& id, const std::string& type, const std::string& path) {
OX_PROFILE_FUNCTION();
s_AssetDatabase[id] = { type, path };
}
bool AssetManager::LoadAsset(const std::string& id) {
OX_PROFILE_FUNCTION();
auto it = s_AssetDatabase.find(id);
if (it == s_AssetDatabase.end()) return false;
const auto& meta = it->second;
std::shared_ptr<Asset> asset;
if (meta.type == "texture2D")
asset = std::make_shared<Texture2D>();
if (asset && asset->LoadFromFile(meta.path)) {
s_AssetMap[id] = asset;
s_AssetTypes.emplace(id, std::type_index(typeid(*asset)));
Logger::LogInfo("Loaded asset '%s' (%s)", id.c_str(), meta.path.c_str());
return true;
}
Logger::LogError("Failed to load asset '%s'", id.c_str());
return false;
}
} // namespace OX

View File

@ -0,0 +1,60 @@
#pragma once
#include <unordered_map>
#include <string>
#include <memory>
#include <typeindex>
#include <mutex>
#include <queue>
#include <filesystem>
#include <thread>
#include <atomic>
#include "Asset.h"
namespace OX {
class AssetManager {
public:
static void Init(const std::string& projectDir);
static void Shutdown();
static void Tick(); // Call once per frame
static void RegisterAssetMetadata(const std::string& id, const std::string& type, const std::string& path);
static bool LoadAsset(const std::string& id);
template<typename T>
static std::shared_ptr<T> Get(const std::string& id) {
if (s_AssetMap.find(id) == s_AssetMap.end())
LoadAsset(id);
auto it = s_AssetMap.find(id);
if (it == s_AssetMap.end()) return nullptr;
if (s_AssetTypes[id] != std::type_index(typeid(T))) return nullptr;
return std::static_pointer_cast<T>(it->second);
}
private:
struct AssetMetadata {
std::string type;
std::string path;
};
static void ScanAssetsRecursive(const std::filesystem::path& root);
static std::string DetectTypeFromExtension(const std::filesystem::path& path);
static std::string GetVirtualPath(const std::filesystem::path& absolute);
static std::unordered_map<std::string, std::shared_ptr<Asset>> s_AssetMap;
static std::unordered_map<std::string, std::type_index> s_AssetTypes;
static std::unordered_map<std::string, AssetMetadata> s_AssetDatabase;
static std::mutex s_QueueMutex;
static std::queue<std::pair<std::string, AssetMetadata>> s_LoadQueue;
static std::thread s_BackgroundThread;
static std::atomic<bool> s_Running;
static std::filesystem::path s_ProjectDirectory;
};
} // namespace OX

View File

@ -0,0 +1,49 @@
//
// Created by spenc on 5/21/2025.
//
#include "Texture2D.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#include <GL/glew.h>
namespace OX
{
Texture2D::Texture2D() = default;
Texture2D::~Texture2D()
{
if (m_TextureID != 0) {
glDeleteTextures(1, &m_TextureID);
}
}
bool Texture2D::LoadFromFile(const std::string &path)
{
stbi_set_flip_vertically_on_load(1);
int channels;
unsigned char *data = stbi_load(path.c_str(), &m_Width, &m_Height, &channels, 0);
if (!data) {
Logger::LogError("Failed to load texture: %s", path.c_str());
return false;
}
GLenum format = (channels == 4) ? GL_RGBA : GL_RGB;
glGenTextures(1, &m_TextureID);
glBindTexture(GL_TEXTURE_2D, m_TextureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, m_Width, m_Height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
Logger::LogVerbose("Loaded texture: %s (%dx%d)", path.c_str(), m_Width, m_Height);
return true;
}
} // namespace OX

View File

@ -0,0 +1,33 @@
//
// Created by spenc on 5/21/2025.
//
#pragma once
#include "systems/Asset.h"
#include <string>
#include <cstdint>
namespace OX
{
class Texture2D : public Asset
{
public:
Texture2D();
~Texture2D();
bool LoadFromFile(const std::string &path) override;
std::string GetTypeName() const override { return "texture2D"; }
uint32_t GetID() const { return m_TextureID; }
int GetWidth() const { return m_Width; }
int GetHeight() const { return m_Height; }
private:
uint32_t m_TextureID = 0;
int m_Width = 0;
int m_Height = 0;
};
} // OX

View File

@ -11,22 +11,22 @@
#include "Windows/LoggerWindow.h"
#include "Windows/Viewport.h"
namespace OX {
void Editor::Init(Core& core)
namespace OX
{
void Editor::Init(Core &core)
{
Logger::LogInfo("%s Init", m_name.c_str());
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
ImGuiStyle &style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
style.WindowRounding = 5.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
@ -38,15 +38,13 @@ namespace OX {
primaryViewport = new Viewport(); // The first time ive ever use the new keywork...
}
void Editor::Update(Core& core)
void Editor::Update(Core &core)
{
OX_PROFILE_FUNCTION();
}
void Editor::Draw(Core& core)
void Editor::Draw(Core &core)
{
OX_PROFILE_FUNCTION();
@ -55,18 +53,19 @@ namespace OX {
ImGui::NewFrame();
// === Main Docking Space ===
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBackground;
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking |
ImGuiWindowFlags_NoBackground;
const ImGuiViewport* viewport = ImGui::GetMainViewport();
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove;
windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
ImGui::Begin("DockSpace Root", nullptr, windowFlags);
@ -81,41 +80,65 @@ namespace OX {
ImGui::Begin("Profiler");
const auto& tree = Profiler::GetSavedTree();
const auto &tree = Profiler::GetSavedTree();
if (tree.empty()) {
ImGui::Text("No samples yet.");
} else {
std::function<void(const Profiler::SavedSample&, int)> DrawNode;
DrawNode = [&](const Profiler::SavedSample& node, int depth) {
// Indentation
ImGui::Indent(depth * 10.0f);
if (ImGui::BeginTable("ProfilerTable", 3,
ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV)) {
ImGui::TableSetupColumn("Section");
ImGui::TableSetupColumn("Calls", ImGuiTableColumnFlags_WidthFixed, 60.0f);
ImGui::TableSetupColumn("Total Time (ms)", ImGuiTableColumnFlags_WidthFixed, 120.0f);
ImGui::TableHeadersRow();
// Color based on time
ImVec4 color;
if (node.totalTime > 10.0f) color = ImVec4(1.0f, 0.2f, 0.2f, 1.0f); // red
else if (node.totalTime > 5.0f) color = ImVec4(1.0f, 0.5f, 0.0f, 1.0f); // orange
else if (node.totalTime > 1.0f) color = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); // yellow
else color = ImVec4(0.2f, 1.0f, 0.2f, 1.0f); // green
std::function<void(const Profiler::SavedSample &)> DrawNode;
DrawNode = [&](const Profiler::SavedSample &node)
{
ImGui::TableNextRow();
ImGui::PushStyleColor(ImGuiCol_Text, color);
// === Section name with Tree UI ===
ImGui::TableNextColumn();
ImGui::Text("%s (%d): %.2f ms", node.name.c_str(), node.callCount, node.totalTime);
// Color based on total time
ImVec4 color;
if (node.totalTime > 10.0f) color = ImVec4(1.0f, 0.2f, 0.2f, 1.0f); // red
else if (node.totalTime > 5.0f) color = ImVec4(1.0f, 0.5f, 0.0f, 1.0f); // orange
else if (node.totalTime > 1.0f) color = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); // yellow
else color = ImVec4(0.4f, 1.0f, 0.4f, 1.0f); // green
ImGui::PopStyleColor();
for (const auto& child : node.children) {
DrawNode(child, depth + 1);
ImGui::PushStyleColor(ImGuiCol_Text, color);
bool open = ImGui::TreeNodeEx(node.name.c_str(),
ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen);
ImGui::PopStyleColor();
// === Call count ===
ImGui::TableNextColumn();
ImGui::Text("%d", node.callCount);
// === Total time ===
ImGui::TableNextColumn();
ImGui::Text("%.2f", node.totalTime);
// === Recurse if open ===
if (open) {
for (const auto &child: node.children) {
DrawNode(child);
}
ImGui::TreePop();
}
};
for (const auto &root: tree) {
DrawNode(root);
}
ImGui::Unindent(depth * 10.0f);
};
for (const auto& root : tree) {
DrawNode(root, 0);
ImGui::EndTable();
}
}
ImGui::End();
ImGui::End(); // DockSpace Root
// --- Render ImGui onto FBO 0 ---
@ -127,12 +150,10 @@ namespace OX {
ImGui::UpdatePlatformWindows();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
}
void Editor::Shutdown(Core& core)
void Editor::Shutdown(Core &core)
{
delete primaryViewport;
primaryViewport = nullptr;
@ -141,8 +162,5 @@ namespace OX {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
}
} // OX
} // OX

View File

@ -4,7 +4,7 @@
#ifndef EDITOR_H
#define EDITOR_H
#define OX_EDITOR_VERSION "Obsidian Editor (0.3.2)"
#define OX_EDITOR_VERSION "Obsidian Editor (0.1.5)"
#include "Layer.h"
#include "Core.h"