diff --git a/CMakeLists.txt b/CMakeLists.txt index 442afcc..694ade7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/src/core/Core.cpp b/src/core/Core.cpp index 93a9f53..6aca208 100644 --- a/src/core/Core.cpp +++ b/src/core/Core.cpp @@ -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(); diff --git a/src/core/renderer/Renderer.cpp b/src/core/renderer/Renderer.cpp index 19a79ad..474b9f5 100644 --- a/src/core/renderer/Renderer.cpp +++ b/src/core/renderer/Renderer.cpp @@ -5,4 +5,5 @@ #include "Renderer.h" namespace OX { + } // OX \ No newline at end of file diff --git a/src/core/systems/Asset.cpp b/src/core/systems/Asset.cpp new file mode 100644 index 0000000..2f44973 --- /dev/null +++ b/src/core/systems/Asset.cpp @@ -0,0 +1,8 @@ +// +// Created by spenc on 5/21/2025. +// + +#include "Asset.h" + +namespace OX { +} // OX \ No newline at end of file diff --git a/src/core/systems/Asset.h b/src/core/systems/Asset.h new file mode 100644 index 0000000..204ff23 --- /dev/null +++ b/src/core/systems/Asset.h @@ -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 diff --git a/src/core/systems/AssetManager.cpp b/src/core/systems/AssetManager.cpp new file mode 100644 index 0000000..5bb5a7e --- /dev/null +++ b/src/core/systems/AssetManager.cpp @@ -0,0 +1,110 @@ +#include "AssetManager.h" +#include +#include "assets/Texture2D.h" +#include "Logger.h" +#include "Profiler.h" + +namespace fs = std::filesystem; + +namespace OX { + +std::unordered_map> AssetManager::s_AssetMap; +std::unordered_map AssetManager::s_AssetTypes; +std::unordered_map AssetManager::s_AssetDatabase; + +std::mutex AssetManager::s_QueueMutex; +std::queue> AssetManager::s_LoadQueue; + +std::thread AssetManager::s_BackgroundThread; +std::atomic 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 lock(s_QueueMutex); + s_LoadQueue.push({ virtualPath, meta }); + } +} + +void AssetManager::Tick() { + OX_PROFILE_FUNCTION(); + std::lock_guard 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; + + if (meta.type == "texture2D") + asset = std::make_shared(); + + + 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 diff --git a/src/core/systems/AssetManager.h b/src/core/systems/AssetManager.h new file mode 100644 index 0000000..e3cb49a --- /dev/null +++ b/src/core/systems/AssetManager.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + static std::shared_ptr 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(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> s_AssetMap; + static std::unordered_map s_AssetTypes; + static std::unordered_map s_AssetDatabase; + + static std::mutex s_QueueMutex; + static std::queue> s_LoadQueue; + + static std::thread s_BackgroundThread; + static std::atomic s_Running; + + static std::filesystem::path s_ProjectDirectory; + }; + +} // namespace OX diff --git a/src/core/systems/assets/Texture2D.cpp b/src/core/systems/assets/Texture2D.cpp new file mode 100644 index 0000000..1301e1c --- /dev/null +++ b/src/core/systems/assets/Texture2D.cpp @@ -0,0 +1,49 @@ +// +// Created by spenc on 5/21/2025. +// + + +#include "Texture2D.h" +#define STB_IMAGE_IMPLEMENTATION +#include +#include + +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 diff --git a/src/core/systems/assets/Texture2D.h b/src/core/systems/assets/Texture2D.h new file mode 100644 index 0000000..4ee6575 --- /dev/null +++ b/src/core/systems/assets/Texture2D.h @@ -0,0 +1,33 @@ +// +// Created by spenc on 5/21/2025. +// + +#pragma once +#include "systems/Asset.h" +#include +#include + + +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 diff --git a/src/editor/Editor.cpp b/src/editor/Editor.cpp index ce1146a..7f5c04f 100644 --- a/src/editor/Editor.cpp +++ b/src/editor/Editor.cpp @@ -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 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 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 \ No newline at end of file +} // OX diff --git a/src/editor/Editor.h b/src/editor/Editor.h index eef8f72..df2e872 100644 --- a/src/editor/Editor.h +++ b/src/editor/Editor.h @@ -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"