Compare commits

...

8 Commits

Author SHA1 Message Date
OusmBlueNinja
91f8eff00d Started in File Broswe Window 2025-05-21 11:49:17 -05:00
OusmBlueNinja
4e2f6b8cfd "Functonal" asset manager 2025-05-21 11:46:17 -05:00
OusmBlueNinja
6225c07dbd Started On Asset Manager 2025-05-21 11:34:23 -05:00
OusmBlueNinja
f298c48564 Fixed Spacing of Editor 2025-05-21 11:07:23 -05:00
OusmBlueNinja
5758f841ed Added viewport 2025-05-21 10:16:46 -05:00
OusmBlueNinja
6074fdc492 Added Demmy Layer for testing 2025-05-21 09:25:43 -05:00
OusmBlueNinja
f6e80b5ad0 Added Layer Based Dynamic Window Titles 2025-05-21 09:21:50 -05:00
OusmBlueNinja
623aa4694b Updated Color Scemes 2025-05-20 21:45:27 -05:00
22 changed files with 841 additions and 167 deletions

2
.idea/.name generated
View File

@ -1 +1 @@
CreatePBR
Onyx

View File

@ -27,8 +27,6 @@ set(CMAKE_C_EXTENSIONS OFF)
include_directories(${CMAKE_BINARY_DIR}/generated)
# Optional message
message(STATUS "Generated Onyx version: ${FULL_VERSION}")
# --- Output directories for multi-config builds ---
@ -103,6 +101,14 @@ file(GLOB_RECURSE CORE_SOURCES CONFIGURE_DEPENDS
add_library(Core STATIC
${CORE_SOURCES}
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)
@ -124,6 +130,10 @@ add_executable(Editor ${APP_SOURCES}
src/editor/Editor.h
src/editor/Windows/LoggerWindow.cpp
src/editor/Windows/LoggerWindow.h
src/editor/Windows/Viewport.cpp
src/editor/Windows/Viewport.h
src/editor/Windows/FileBrowser.cpp
src/editor/Windows/FileBrowser.h
)
target_include_directories(Editor PRIVATE
@ -137,6 +147,7 @@ target_link_libraries(Editor PRIVATE
glfw
assimp::assimp
GLEW::GLEW
yaml-cpp
${CMAKE_DL_LIBS}
)

View File

@ -8,13 +8,11 @@
int main()
{
OX::Core core{};
constexpr const char* VERSION = "0.0.7"; // Format: 0.<release><commit_count>
constexpr const char* TITLE = "Obsidian Editor - Onyx Engine (2025.1)";
core.AddLayer(std::make_unique<OX::Editor>(OX_EDITOR_VERSION));
OX::Core core(std::string(TITLE) + " (" + VERSION + ")");
core.AddLayer(std::make_unique<OX::Editor>("Obsidian Editor"));
//core.AddLayer(std::make_unique<OX::DummyLayer>("Dummy Layer"));
core.Init();
core.Run();

View File

@ -5,6 +5,7 @@
#include "Core.h"
#include "systems/MACROS.h"
#include "systems/AssetManager.h"
namespace OX
{
@ -21,25 +22,34 @@ namespace OX
if (!window.Init(m_name, 1280, 720))
return;
OX_ASSERT(!m_layers.empty(), "No Layers Attached");
for (const auto &layer: m_layers) {
std::string layerTitle;
for (size_t i = 0; i < m_layers.size(); ++i) {
if (i > 0)
layerTitle += " - ";
layerTitle += m_layers[i]->GetName();
}
for (const auto &layer : m_layers) {
Logger::LogDebug("Initializing Layer: '%s'", layer->GetName().c_str());
layer->Init(*this);
}
std::string fullTitle = layerTitle + " - " + m_name;
window.SetWindowTitle(fullTitle);
Logger::LogOk("Core Initialization Complete.");
Logger::LogInfo("Info: This is an informational message.");
Logger::LogWarning("Warning: Something might be wrong.");
Logger::LogError("Error: Something went wrong!");
Logger::LogDebug("Debug: Internal debug information.");
Logger::LogVerbose("Verbose: Extra detailed logs.");
Logger::LogOk("Ok: Everything succeeded!");
AssetManager::Init("C:/Users/spenc/OneDrive/Desktop/OnyxProject");
}
void Core::Run()
{
m_running = true;
@ -60,6 +70,7 @@ namespace OX
void Core::Update()
{
OX_PROFILE_FUNCTION();
AssetManager::Tick();
for (auto &layer: m_layers) {
layer->Update(*this);
}
@ -86,6 +97,7 @@ namespace OX
}
m_layers.clear();
AssetManager::Shutdown();
window.Shutdown();

View File

@ -9,41 +9,80 @@
#include <vector>
#include <memory>
#include "Layer.h"
#include "renderer/Renderer.h"
#include "systems/Logger.h"
#include "systems/Profiler.h"
#include "systems/WindowManager.h"
#include "systems/MACROS.h"
#define OX_ENGINE_VERSION "Onyx Engine (2025.1)"
namespace OX
{
class Core {
class Core
{
public:
Core(std::string name) : m_name(std::move(name)) {};
Core(std::string name) : m_name(std::move(name))
{
};
Core() : m_name(OX_ENGINE_VERSION)
{
};
~Core() = default;
void Init();
void Run();
void Shutdown();
WindowManager& GetWindow() {return window;}
WindowManager &GetWindow() { return window; }
Renderer &GetRenderer() { return renderer; }
void AddLayer(std::unique_ptr<Layer> layer);
private:
void Update();
void Draw();
std::vector<std::unique_ptr<Layer>> m_layers;
std::vector<std::unique_ptr<Layer> > m_layers;
WindowManager window;
Renderer renderer;
bool m_running = false;
std::string m_name = "Application";
};
class DummyLayer : public Layer
{
public:
explicit DummyLayer(const std::string &name = "Dummy Layer")
: Layer(name)
{
}
void Init(Core &core) override
{
Logger::LogDebug("%s Init", m_name.c_str());
}
void Update(Core &core) override
{
}
void Draw(Core &core) override
{
}
void Shutdown(Core &core) override
{
Logger::LogDebug("%s Shutdown", m_name.c_str());
}
};
}
#endif // CORE_H

View File

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

View File

@ -0,0 +1,26 @@
//
// Created by spenc on 5/21/2025.
//
#ifndef RENDERER_H
#define RENDERER_H
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
namespace OX {
class Renderer {
public:
Renderer() = default;
~Renderer() = default;
[[nodiscard]] GLuint GetRenderTarget() const {return m_renderTarget; }
private:
GLuint m_renderTarget;
};
} // OX
#endif //RENDERER_H

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,196 @@
#include "AssetManager.h"
#include "assets/Texture2D.h"
#include "Logger.h"
#include <string>
#include <fstream>
#include <yaml-cpp/yaml.h>
namespace fs = std::filesystem;
namespace OX
{
std::unordered_map<std::string, std::shared_ptr<Asset> > AssetManager::s_LoadedAssets;
std::unordered_map<std::string, AssetManager::AssetMetadata> AssetManager::s_MetadataMap;
std::shared_ptr<ResourceTreeNode> AssetManager::s_FileTree = std::make_shared<ResourceTreeNode>();
std::mutex AssetManager::s_QueueMutex;
std::queue<std::string> AssetManager::s_DeferredLoadQueue;
fs::path AssetManager::s_ProjectRoot;
std::atomic<bool> AssetManager::s_Scanning = false;
std::thread AssetManager::s_ScanThread;
void AssetManager::Init(const std::string &projectRoot)
{
s_ProjectRoot = fs::absolute(projectRoot);
s_Scanning = true;
s_FileTree = std::make_shared<ResourceTreeNode>();
s_FileTree->name = "res://";
s_FileTree->path = "res://";
s_FileTree->isDirectory = true;
s_ScanThread = std::thread([=]
{
BackgroundScan(s_ProjectRoot);
s_Scanning = false;
});
}
void AssetManager::Shutdown()
{
if (s_ScanThread.joinable())
s_ScanThread.join();
}
void AssetManager::Tick()
{
std::lock_guard<std::mutex> lock(s_QueueMutex);
if (!s_DeferredLoadQueue.empty()) {
auto resPath = s_DeferredLoadQueue.front();
s_DeferredLoadQueue.pop();
LoadAsset(resPath);
}
}
void AssetManager::SaveAssetPack(const std::string& outputPath)
{
YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "assets" << YAML::Value << YAML::BeginSeq;
for (const auto& [resPath, meta] : s_MetadataMap) {
fs::path relative = fs::relative(meta.absolutePath, s_ProjectRoot);
out << YAML::BeginMap;
out << YAML::Key << "id" << YAML::Value << resPath;
out << YAML::Key << "type" << YAML::Value << meta.type;
out << YAML::Key << "path" << YAML::Value << relative.generic_string();
out << YAML::EndMap;
}
out << YAML::EndSeq;
out << YAML::EndMap;
std::ofstream fout(outputPath);
fout << out.c_str();
fout.close();
Logger::LogInfo("Saved asset pack: %s", outputPath.c_str());
}
void AssetManager::Rescan()
{
if (s_Scanning) return;
s_Scanning = true;
s_FileTree = std::make_shared<ResourceTreeNode>();
s_FileTree->name = "res://";
s_FileTree->path = "res://";
s_FileTree->isDirectory = true;
s_ScanThread = std::thread([=]
{
BackgroundScan(s_ProjectRoot);
s_Scanning = false;
});
}
void AssetManager::BackgroundScan(const fs::path &root)
{
for (const auto &entry: fs::recursive_directory_iterator(root)) {
const auto &path = entry.path();
std::string resPath = MakeVirtualPath(path);
if (entry.is_directory()) {
AddToTree(resPath, true);
} else {
AddToTree(resPath, false);
std::string type = DetectAssetType(path);
if (!type.empty()) {
std::lock_guard<std::mutex> lock(s_QueueMutex);
s_MetadataMap[resPath] = {type, path.string()};
s_DeferredLoadQueue.push(resPath);
}
}
}
}
void AssetManager::AddToTree(const std::string &resPath, bool isDir)
{
std::vector<std::string> parts;
size_t pos = 0, next;
while ((next = resPath.find('/', pos)) != std::string::npos) {
if (next != pos) parts.push_back(resPath.substr(pos, next - pos));
pos = next + 1;
}
if (pos < resPath.length()) parts.push_back(resPath.substr(pos));
auto current = s_FileTree;
for (size_t i = 1; i < parts.size(); ++i) {
auto &name = parts[i];
auto it = std::find_if(current->children.begin(), current->children.end(), [&](auto &child)
{
return child->name == name;
});
if (it == current->children.end()) {
auto newNode = std::make_shared<ResourceTreeNode>();
newNode->name = name;
newNode->path = current->path + "/" + name;
newNode->isDirectory = (i < parts.size() - 1) || isDir;
current->children.push_back(newNode);
current = newNode;
} else {
current = *it;
}
}
}
std::string AssetManager::MakeVirtualPath(const fs::path &full)
{
fs::path rel = fs::relative(full, s_ProjectRoot);
return "res://" + rel.generic_string();
}
std::string AssetManager::DetectAssetType(const fs::path &ext)
{
auto e = ext.extension().string();
if (e == ".png" || e == ".jpg") return "texture2D";
if (e == ".ogg" || e == ".wav") return "audio";
return "";
}
std::shared_ptr<Asset> AssetManager::Get(const std::string &resPath)
{
auto it = s_LoadedAssets.find(resPath);
return (it != s_LoadedAssets.end()) ? it->second : nullptr;
}
bool AssetManager::LoadAsset(const std::string &resPath)
{
auto it = s_MetadataMap.find(resPath);
if (it == s_MetadataMap.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.absolutePath)) {
s_LoadedAssets[resPath] = asset;
Logger::LogDebug("Loaded asset: %s", resPath.c_str());
return true;
}
Logger::LogError("Failed to load: %s", resPath.c_str());
return false;
}
std::shared_ptr<ResourceTreeNode> AssetManager::GetFileTree()
{
return s_FileTree;
}
} // namespace OX

View File

@ -0,0 +1,61 @@
#pragma once
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <memory>
#include <filesystem>
#include <mutex>
#include <thread>
#include <atomic>
namespace OX {
class Asset;
struct ResourceTreeNode {
std::string name;
std::string path; // res://...
bool isDirectory = false;
std::vector<std::shared_ptr<ResourceTreeNode>> children;
};
class AssetManager {
public:
static void Init(const std::string& projectRoot);
static void Shutdown();
static void Tick(); // Call every frame
static void SaveAssetPack(const std::string& outputPath);
static std::shared_ptr<Asset> Get(const std::string& resPath);
static std::shared_ptr<ResourceTreeNode> GetFileTree();
static bool LoadAsset(const std::string& resPath);
static void Rescan(); // Rebuilds file tree (slow)
private:
struct AssetMetadata {
std::string type;
std::string absolutePath;
};
static void BackgroundScan(const std::filesystem::path& root);
static void AddToTree(const std::string& virtualPath, bool isDir);
static std::string DetectAssetType(const std::filesystem::path& ext);
static std::string MakeVirtualPath(const std::filesystem::path& full);
static std::unordered_map<std::string, std::shared_ptr<Asset>> s_LoadedAssets;
static std::unordered_map<std::string, AssetMetadata> s_MetadataMap;
static std::shared_ptr<ResourceTreeNode> s_FileTree;
static std::mutex s_QueueMutex;
static std::queue<std::string> s_DeferredLoadQueue;
static std::filesystem::path s_ProjectRoot;
static std::atomic<bool> s_Scanning;
static std::thread s_ScanThread;
};
} // namespace OX

View File

@ -48,31 +48,32 @@ namespace OX
Color color;
switch (type) {
case MessageType::Info:
color.r = 0.25f; color.g = 0.5f; color.b = 0.95f; // Calmer blue
color = { 0.0f, 0.478f, 1.0f }; // Standard blue (iOS info blue / material blue)
break;
case MessageType::Warning:
color.r = 1.0f; color.g = 0.75f; color.b = 0.1f; // Amber/goldenrod
color = { 1.0f, 0.647f, 0.0f }; // Orange / amber (alert warning)
break;
case MessageType::Error:
color.r = 0.9f; color.g = 0.2f; color.b = 0.3f; // Strong crimson red
color = { 0.863f, 0.078f, 0.235f }; // Crimson / alert red
break;
case MessageType::Debug:
color.r = 0.6f; color.g = 0.6f; color.b = 0.7f; // Neutral cool gray
color = { 0.56f, 0.56f, 0.58f }; // Slate gray
break;
case MessageType::Ok:
color.r = 0.3f; color.g = 0.85f; color.b = 0.4f; // Balanced green
color = { 0.0f, 0.8f, 0.4f }; // Vibrant green / success
break;
case MessageType::Verbose:
color.r = 0.7f; color.g = 0.7f; color.b = 0.9f; // Soft lavender/gray
color = { 0.6f, 0.6f, 0.7f }; // Dim blue-gray
break;
default:
color = Color::White(); // Default white
color = { 1.0f, 1.0f, 1.0f }; // Default white
break;
}
return color;
}
void Logger::LogInternal(const std::string& text, MessageType type) {
glm::vec3 color = Logger::GetRGBColor(type);
float r = color.r;

View File

@ -4,101 +4,120 @@
#include <GLFW/glfw3.h>
#include "Profiler.h"
namespace OX
{
bool WindowManager::Init(const std::string& title, int width, int height) {
m_width = width;
m_height = height;
bool WindowManager::Init(const std::string &title, int width, int height)
{
m_width = width;
m_height = height;
if (!glfwInit()) {
Logger::LogError("Failed to initialize GLFW");
return false;
if (!glfwInit()) {
Logger::LogError("Failed to initialize GLFW");
return false;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // REM title bar
m_window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (!m_window) {
Logger::LogError("Failed to create GLFW window");
glfwTerminate();
return false;
}
glfwMakeContextCurrent(m_window);
glfwSwapInterval(1); // Enable vsync
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (err != GLEW_OK) {
Logger::LogError("Failed to initialize GLEW: %s", reinterpret_cast<const char *>(glewGetErrorString(err)));
return false;
}
glfwSetFramebufferSizeCallback(m_window, FramebufferSizeCallback);
glfwSetWindowUserPointer(m_window, this);
glEnable(GL_DEPTH_TEST);
return true;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // REM title bar
void WindowManager::SetWindowTitle(const std::string &title) const
{
if (m_window) {
glfwSetWindowTitle(m_window, title.c_str());
}
else {
Logger::LogWarning("Failed to set Window Title: 'Not Initialized'");
}
}
void WindowManager::FramebufferSizeCallback(GLFWwindow *window, int width, int height)
{
OX_PROFILE_FUNCTION();
m_window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (!m_window) {
Logger::LogError("Failed to create GLFW window");
auto *wm = static_cast<WindowManager *>(glfwGetWindowUserPointer(window));
if (wm) {
wm->SetSize(width, height);
}
}
void WindowManager::SetSize(int width, int height)
{
OX_PROFILE_FUNCTION();
m_width = width;
m_height = height;
glViewport(0, 0, width, height);
}
void WindowManager::PollEvents() const
{
OX_PROFILE_FUNCTION();
glfwPollEvents();
}
bool WindowManager::ShouldClose() const
{
return glfwWindowShouldClose(m_window);
}
void WindowManager::BeginFrame() const
{
OX_PROFILE_FUNCTION();
PollEvents();
glViewport(0, 0, m_width, m_height);
glClearColor(0.07f, 0.07f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void WindowManager::EndFrame() const
{
OX_PROFILE_FUNCTION();
glfwSwapBuffers(m_window);
}
void WindowManager::Shutdown()
{
if (m_window) {
glfwDestroyWindow(m_window);
m_window = nullptr;
}
glfwTerminate();
return false;
}
glfwMakeContextCurrent(m_window);
glfwSwapInterval(1); // Enable vsync
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (err != GLEW_OK) {
Logger::LogError("Failed to initialize GLEW: %s", reinterpret_cast<const char*>(glewGetErrorString(err)));
return false;
}
glfwSetFramebufferSizeCallback(m_window, FramebufferSizeCallback);
glfwSetWindowUserPointer(m_window, this);
glEnable(GL_DEPTH_TEST);
return true;
}
void WindowManager::FramebufferSizeCallback(GLFWwindow* window, int width, int height) {
OX_PROFILE_FUNCTION();
auto* wm = static_cast<WindowManager*>(glfwGetWindowUserPointer(window));
if (wm) {
wm->SetSize(width, height);
WindowManager::~WindowManager()
{
Shutdown();
}
}
void WindowManager::SetSize(int width, int height) {
OX_PROFILE_FUNCTION();
m_width = width;
m_height = height;
glViewport(0, 0, width, height);
}
void WindowManager::PollEvents() const {
OX_PROFILE_FUNCTION();
glfwPollEvents();
}
bool WindowManager::ShouldClose() const {
return glfwWindowShouldClose(m_window);
}
void WindowManager::BeginFrame() const {
OX_PROFILE_FUNCTION();
PollEvents();
glViewport(0, 0, m_width, m_height);
glClearColor(0.07f, 0.07f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void WindowManager::EndFrame() const {
OX_PROFILE_FUNCTION();
glfwSwapBuffers(m_window);
}
void WindowManager::Shutdown() {
if (m_window) {
glfwDestroyWindow(m_window);
m_window = nullptr;
}
glfwTerminate();
}
WindowManager::~WindowManager() {
Shutdown();
}
}

View File

@ -15,6 +15,7 @@ namespace OX
void PollEvents() const;
[[nodiscard]] bool ShouldClose() const;
void SetWindowTitle(const std::string& title) const;
void BeginFrame() const;
void EndFrame() const;

View File

@ -0,0 +1,48 @@
//
// 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);
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

@ -7,25 +7,27 @@
#include <imgui.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_glfw.h>
#include "Windows/LoggerWindow.h"
#include "Windows/Viewport.h"
#include "Windows/FileBrowser.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;
@ -33,17 +35,17 @@ namespace OX {
ImGui_ImplGlfw_InitForOpenGL(core.GetWindow().GetHandle(), true);
ImGui_ImplOpenGL3_Init("#version 330 core");
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();
@ -52,65 +54,93 @@ 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);
ImGui::PopStyleVar(2);
ImGui::PopStyleVar(3);
ImGuiID dockspaceID = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspaceID, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);
LoggerWindow::Draw();
FileBrowser::Draw();
primaryViewport->Draw(core);
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 ---
@ -122,17 +152,17 @@ namespace OX {
ImGui::UpdatePlatformWindows();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
}
void Editor::Shutdown(Core& core)
void Editor::Shutdown(Core &core)
{
delete primaryViewport;
primaryViewport = nullptr;
Logger::LogOk("Editor::Shutdown");
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
}
} // OX
} // OX

View File

@ -4,24 +4,31 @@
#ifndef EDITOR_H
#define EDITOR_H
#define OX_EDITOR_VERSION "Obsidian Editor (0.1.5)"
#include "Layer.h"
#include "Core.h"
namespace OX {
namespace OX
{
class Viewport;
class Editor final : public Layer {
public:
using Layer::Layer;
class Editor final : public Layer
{
public:
using Layer::Layer;
void Init(Core& core) override;
void Update(Core& core) override;
void Draw(Core& core) override;
void Shutdown(Core& core) override;
};
void Init(Core &core) override;
void Update(Core &core) override;
void Draw(Core &core) override;
void Shutdown(Core &core) override;
private:
Viewport* primaryViewport;
};
} // OX
#endif //EDITOR_H

View File

@ -0,0 +1,70 @@
//
// Created by spenc on 5/21/2025.
//
#include "FileBrowser.h"
#include "systems/AssetManager.h"
#include "imgui.h"
#include <filesystem>
namespace OX {
static std::string s_CurrentPath = "res://";
static void DrawGrid(const std::shared_ptr<ResourceTreeNode>& node) {
const int columns = 4;
int itemIndex = 0;
ImGui::BeginChild("FileGrid", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysUseWindowPadding);
ImGui::Columns(columns, nullptr, false);
for (const auto& child : node->children) {
std::string label = (child->isDirectory ? "[folder] " : "[file] ") + child->name;
if (ImGui::Selectable(label.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick, ImVec2(0, 64))) {
if (child->isDirectory && ImGui::IsMouseDoubleClicked(0)) {
s_CurrentPath = child->path;
}
}
ImGui::NextColumn();
++itemIndex;
}
ImGui::Columns(1);
ImGui::EndChild();
}
static std::shared_ptr<ResourceTreeNode> FindNode(const std::shared_ptr<ResourceTreeNode>& root, const std::string& targetPath) {
if (root->path == targetPath) return root;
for (const auto& child : root->children) {
if (child->isDirectory) {
auto found = FindNode(child, targetPath);
if (found) return found;
}
}
return nullptr;
}
void FileBrowser::Draw() {
ImGui::Begin("File Browser");
ImGui::Text("Current Path: %s", s_CurrentPath.c_str());
ImGui::Separator();
auto root = AssetManager::GetFileTree();
auto currentNode = FindNode(root, s_CurrentPath);
if (currentNode) {
DrawGrid(currentNode);
} else {
ImGui::Text("Path not found: %s", s_CurrentPath.c_str());
}
ImGui::End();
}
} // namespace OX

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
namespace OX {
class FileBrowser {
public:
static void Draw();
};
} // namespace OX

View File

@ -0,0 +1,37 @@
//
// Created by spenc on 5/21/2025.
//
#include "Viewport.h"
#include <imgui.h>
namespace OX
{
int Viewport::s_nextID = 0;
void Viewport::Draw(Core &core) const
{
// Construct title
std::string title = m_name + " (" + std::to_string(m_id) + ")";
// Remove window padding
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
// Begin full viewport window
ImGui::Begin(title.c_str(), nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
// Get size of available region
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
// === Fullscreen image ===
GLuint textureID = core.GetRenderer().GetRenderTarget(); // You must define this method
if (textureID != 0) {
ImGui::Image(textureID, viewportSize, ImVec2(0, 1), ImVec2(1, 0));
} else {
ImGui::Text("No Render Target.");
}
ImGui::End();
ImGui::PopStyleVar(); // Restore padding
}
};

View File

@ -0,0 +1,36 @@
//
// Created by spenc on 5/21/2025.
//
#ifndef VIEWPORT_H
#define VIEWPORT_H
#include <string>
#include "Core.h"
namespace OX
{
class Viewport
{
public:
explicit Viewport(std::string name = "Viewport")
: m_name(std::move(name))
{
m_id = s_nextID++;
}
void Draw(Core& core) const;
[[nodiscard]] int GetID() const { return m_id; }
[[nodiscard]] const std::string& GetName() const { return m_name; }
private:
int m_id;
std::string m_name;
static int s_nextID;
};
};
#endif