Started On Asset Manager
This commit is contained in:
parent
f298c48564
commit
6225c07dbd
@ -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)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -5,4 +5,5 @@
|
||||
#include "Renderer.h"
|
||||
|
||||
namespace OX {
|
||||
|
||||
} // OX
|
8
src/core/systems/Asset.cpp
Normal file
8
src/core/systems/Asset.cpp
Normal 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
20
src/core/systems/Asset.h
Normal 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
|
110
src/core/systems/AssetManager.cpp
Normal file
110
src/core/systems/AssetManager.cpp
Normal 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
|
60
src/core/systems/AssetManager.h
Normal file
60
src/core/systems/AssetManager.h
Normal 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
|
49
src/core/systems/assets/Texture2D.cpp
Normal file
49
src/core/systems/assets/Texture2D.cpp
Normal 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
|
33
src/core/systems/assets/Texture2D.h
Normal file
33
src/core/systems/assets/Texture2D.h
Normal 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
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user