diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..7a68be2
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+CreatePBR
\ No newline at end of file
diff --git a/.idea/Onyx-Engine.iml b/.idea/Onyx-Engine.iml
index bc2cd87..f08604b 100644
--- a/.idea/Onyx-Engine.iml
+++ b/.idea/Onyx-Engine.iml
@@ -1,8 +1,2 @@
-
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..970f097
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,341 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
index 7c223d5..5ce3305 100644
--- a/.idea/material_theme_project_new.xml
+++ b/.idea/material_theme_project_new.xml
@@ -3,7 +3,9 @@
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..0b76fe5
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..bc227ae
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,129 @@
+cmake_minimum_required(VERSION 3.16)
+project(CreatePBR VERSION 0.1.0 LANGUAGES C CXX)
+
+# Build type (for single-config generators like Make/Ninja)
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the build type (Debug or Release)" FORCE)
+endif()
+
+# Language standards
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+set(CMAKE_C_STANDARD 23)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(CMAKE_C_EXTENSIONS OFF)
+
+# --- Output directories for multi-config builds ---
+foreach(OUTPUTCONFIG IN ITEMS Debug Release)
+ string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin/${OUTPUTCONFIG})
+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib/${OUTPUTCONFIG})
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib/${OUTPUTCONFIG})
+endforeach()
+
+# --- External Libraries ---
+include(FetchContent)
+
+# GLFW
+FetchContent_Declare(
+ glfw
+ GIT_REPOSITORY https://github.com/glfw/glfw.git
+ GIT_TAG latest
+)
+FetchContent_MakeAvailable(glfw)
+
+# Assimp
+FetchContent_Declare(
+ assimp
+ GIT_REPOSITORY https://github.com/assimp/assimp.git
+ GIT_TAG master
+)
+set(ASSIMP_NO_EXPORT ON CACHE BOOL "" FORCE)
+set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "" FORCE)
+set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
+set(ASSIMP_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(assimp)
+
+# ImGui (Docking)
+FetchContent_Declare(
+ imgui
+ GIT_REPOSITORY https://github.com/ocornut/imgui.git
+ GIT_TAG docking
+)
+FetchContent_MakeAvailable(imgui)
+
+# Required packages
+find_package(yaml-cpp REQUIRED)
+find_package(GLEW REQUIRED)
+
+# --- ImGui Static Library ---
+add_library(ImGui STATIC
+ ${imgui_SOURCE_DIR}/imgui.cpp
+ ${imgui_SOURCE_DIR}/imgui_draw.cpp
+ ${imgui_SOURCE_DIR}/imgui_demo.cpp
+ ${imgui_SOURCE_DIR}/imgui_tables.cpp
+ ${imgui_SOURCE_DIR}/imgui_widgets.cpp
+ ${imgui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp
+ ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp
+)
+
+target_include_directories(ImGui PUBLIC
+ ${imgui_SOURCE_DIR}
+ ${imgui_SOURCE_DIR}/backends
+)
+
+target_compile_definitions(ImGui PUBLIC
+ IMGUI_IMPL_OPENGL_LOADER_GLEW
+)
+
+# --- Core Engine Library ---
+file(GLOB_RECURSE CORE_SOURCES CONFIGURE_DEPENDS
+ src/core/*.cpp
+ src/core/*.h
+)
+
+add_library(Core STATIC
+ ${CORE_SOURCES}
+)
+
+target_include_directories(Core PUBLIC src/core)
+
+# Add _DEBUG define only for debug builds
+target_compile_definitions(Core PRIVATE
+ $<$:_DEBUG>
+)
+
+# --- Editor Executable ---
+file(GLOB_RECURSE APP_SOURCES CONFIGURE_DEPENDS
+ src/editor/*.cpp
+ src/editor/*.h
+)
+
+add_executable(Editor ${APP_SOURCES}
+ main.cpp
+ src/editor/Editor.cpp
+ src/editor/Editor.h
+)
+
+target_include_directories(Editor PRIVATE
+ src/editor
+ src/core
+)
+
+target_link_libraries(Editor PRIVATE
+ Core
+ ImGui
+ glfw
+ assimp::assimp
+ GLEW::GLEW
+ ${CMAKE_DL_LIBS}
+)
+
+target_compile_definitions(Editor PRIVATE
+ $<$:_DEBUG>
+)
+
+# --- Install ---
+install(TARGETS Editor RUNTIME DESTINATION bin)
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..fafaed0
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,23 @@
+//
+// Created by spenc on 5/14/2025.
+//
+
+#include "Editor.h"
+#include "Layer.h"
+#include "Core.h"
+
+
+int main()
+{
+
+ OX::Core core("Application");
+
+
+ core.AddLayer(std::make_unique("Application"));
+
+
+ core.Init();
+ core.Run();
+ core.Shutdown();
+
+}
\ No newline at end of file
diff --git a/src/core/Core.cpp b/src/core/Core.cpp
new file mode 100644
index 0000000..a794c92
--- /dev/null
+++ b/src/core/Core.cpp
@@ -0,0 +1,85 @@
+//
+// Created by spenc on 5/14/2025.
+//
+
+#include "Core.h"
+namespace OX
+{
+
+void Core::AddLayer(std::unique_ptr layer) {
+ Logger::LogDebug("Added Layer: '%s'", layer->GetName().c_str());
+ m_layers.emplace_back(std::move(layer));
+}
+
+void Core::Init() {
+ Logger::LogInfo("Initializing Core...");
+
+ if (!window.Init(m_name, 1280, 720))
+ return;
+
+
+
+
+ for (auto& layer : m_layers) {
+ Logger::LogDebug("Initializing Layer: '%s'", layer->GetName().c_str());
+ layer->Init(*this);
+ }
+
+
+ Logger::LogOk("Core Initialization Complete.");
+}
+
+void Core::Run() {
+ m_running = true;
+
+ while (!window.ShouldClose()) {
+ Profiler::BeginFrame();
+ {
+ PROFILE_LABEL("Frame");
+ Update();
+ window.BeginFrame();
+
+ Draw();
+ window.EndFrame();
+ }
+ Profiler::EndFrame();
+
+ }
+
+
+
+
+}
+
+void Core::Update() {
+ PROFILE_FUNCTION();
+ for (auto& layer : m_layers) {
+ layer->Update(*this);
+ }
+}
+
+void Core::Draw() {
+ PROFILE_FUNCTION();
+
+
+ for (auto& layer : m_layers) {
+ layer->Draw(*this);
+ }
+}
+
+void Core::Shutdown() {
+ Logger::LogDebug("Shutting down Core...");
+
+
+ for (auto& layer : m_layers) {
+ Logger::LogDebug("Shutting down Layer: '%s'", layer->GetName().c_str());
+ layer->Shutdown(*this);
+ }
+
+ m_layers.clear();
+
+ window.Shutdown();
+
+ Logger::LogOk("Core Shutdown Complete.");
+}
+}
\ No newline at end of file
diff --git a/src/core/Core.h b/src/core/Core.h
new file mode 100644
index 0000000..4a4b3ac
--- /dev/null
+++ b/src/core/Core.h
@@ -0,0 +1,48 @@
+//
+// Created by spenc on 5/14/2025.
+//
+
+#ifndef CORE_H
+#define CORE_H
+
+#include
+#include
+#include
+#include "Layer.h"
+#include "systems/Logger.h"
+#include "systems/Profiler.h"
+#include "systems/WindowManager.h"
+
+namespace OX
+{
+ class Core {
+ public:
+ Core(std::string name) : m_name(std::move(name)) {};
+ ~Core() = default;
+
+ void Init();
+ void Run();
+ void Shutdown();
+
+
+ WindowManager& GetWindow() {return window;}
+
+
+
+
+ void AddLayer(std::unique_ptr layer);
+
+ private:
+ void Update();
+ void Draw();
+
+
+ std::vector> m_layers;
+ WindowManager window;
+
+ bool m_running = false;
+ std::string m_name = "Application";
+ };
+}
+#endif // CORE_H
+
diff --git a/src/core/Layer.cpp b/src/core/Layer.cpp
new file mode 100644
index 0000000..e730712
--- /dev/null
+++ b/src/core/Layer.cpp
@@ -0,0 +1,5 @@
+//
+// Created by spenc on 5/14/2025.
+//
+
+#include "Layer.h"
diff --git a/src/core/Layer.h b/src/core/Layer.h
new file mode 100644
index 0000000..e37eb01
--- /dev/null
+++ b/src/core/Layer.h
@@ -0,0 +1,45 @@
+// Created by spenc on 5/14/2025.
+
+
+#ifndef LAYER_H
+#define LAYER_H
+
+#include
+#include
+
+namespace OX
+{
+
+class Core;
+
+class Layer
+{
+public:
+ explicit Layer(std::string name = "New Layer")
+ : m_name(std::move(name))
+ {
+ }
+
+ explicit Layer(const char *name)
+ : m_name(name)
+ {
+ }
+
+ virtual ~Layer() = default;
+
+ virtual void Init(Core &core) = 0;
+
+ virtual void Update(Core &core) = 0;
+
+ virtual void Draw(Core &core) = 0;
+
+ virtual void Shutdown(Core &core) = 0;
+
+ [[nodiscard]] const std::string &GetName() const { return m_name; }
+
+protected:
+ std::string m_name;
+};
+}
+#endif // LAYER_H
+
diff --git a/src/core/systems/Logger.cpp b/src/core/systems/Logger.cpp
new file mode 100644
index 0000000..102aad1
--- /dev/null
+++ b/src/core/systems/Logger.cpp
@@ -0,0 +1,140 @@
+#include "Logger.h"
+#include
+#include
+#include
+#include
+namespace OX
+{
+ std::vector Logger::m_messages;
+
+ inline const char* ToString(MessageType type) {
+ switch (type) {
+ case MessageType::Info: return "Info";
+ case MessageType::Warning: return "Warning";
+ case MessageType::Error: return "Error";
+ case MessageType::Debug: return "Debug";
+ case MessageType::Ok: return "Ok";
+ default: return "Unknown";
+ }
+ }
+
+
+
+ static std::string GetTimestamp() {
+ auto now = std::chrono::system_clock::now();
+ std::time_t t = std::chrono::system_clock::to_time_t(now);
+ std::tm* tm = std::localtime(&t);
+ char buffer[20];
+ std::strftime(buffer, sizeof(buffer), "%H:%M:%S", tm);
+ return buffer;
+ }
+
+ static const char* GetAnsiColor(MessageType type) {
+ switch (type) {
+ case MessageType::Info: return "\033[1;34m";
+ case MessageType::Warning: return "\033[1;33m";
+ case MessageType::Error: return "\033[1;31m";
+ case MessageType::Debug: return "\033[1;90m";
+ case MessageType::Ok: return "\033[1;32m";
+ default: return "\033[0m";
+ }
+ }
+
+
+ static void GetRGBColor(MessageType type, float& r, float& g, float& b) {
+ switch (type) {
+ case MessageType::Info: r = 0.2f; g = 0.6f; b = 1.0f; break;
+ case MessageType::Warning: r = 1.0f; g = 0.8f; b = 0.2f; break;
+ case MessageType::Error: r = 1.0f; g = 0.2f; b = 0.2f; break;
+ case MessageType::Debug: r = 0.7f; g = 0.7f; b = 0.7f; break;
+ case MessageType::Ok: r = 0.6f; g = 1.0f; b = 0.6f; break;
+ default: r = g = b = 1.0f; break;
+ }
+ }
+
+
+ void Logger::LogInternal(const std::string& text, MessageType type) {
+ float r, g, b;
+ GetRGBColor(type, r, g, b);
+
+ Message message {
+ GetTimestamp(),
+ text,
+ type,
+ r, g, b
+ };
+
+ m_messages.push_back(message);
+
+ std::cout << "\033[37m[\033[35mONYX\033[37m]"
+ << "\033[37m[\033[36m" << message.timestamp
+ << "\033[37m]\033[37m[\033[33m"
+ << GetAnsiColor(type)
+ << ToString(message.type)
+ << "\033[37m] "
+ << GetAnsiColor(type)
+ << text
+ << "\033[0m\n";
+
+ }
+
+ void Logger::Logf(MessageType type, const char* format, ...) {
+ char buffer[1024];
+
+ va_list args;
+ va_start(args, format);
+ std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ LogInternal(std::string(buffer), type);
+ }
+
+ void Logger::LogInfo(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buffer[1024];
+ std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ LogInternal(buffer, MessageType::Info);
+ }
+
+ void Logger::LogWarning(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buffer[1024];
+ std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ LogInternal(buffer, MessageType::Warning);
+ }
+
+ void Logger::LogError(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buffer[1024];
+ std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ LogInternal(buffer, MessageType::Error);
+ }
+
+ void Logger::LogDebug(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buffer[1024];
+ std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ LogInternal(buffer, MessageType::Debug);
+ }
+
+ void Logger::LogOk(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buffer[1024];
+ std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ LogInternal(buffer, MessageType::Ok);
+ }
+
+ const std::vector& Logger::GetMessages() {
+ return m_messages;
+ }
+}
\ No newline at end of file
diff --git a/src/core/systems/Logger.h b/src/core/systems/Logger.h
new file mode 100644
index 0000000..466ffb3
--- /dev/null
+++ b/src/core/systems/Logger.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include
+#include
+#include
+namespace OX
+{
+enum class MessageType {
+ Info,
+ Warning,
+ Error,
+ Debug,
+ Ok
+};
+
+struct Message {
+ std::string timestamp;
+ std::string text;
+ MessageType type;
+ float r, g, b;
+};
+
+class Logger {
+public:
+ static void Logf(MessageType type, const char* format, ...);
+
+ static void LogInfo(const char* format, ...);
+ static void LogWarning(const char* format, ...);
+ static void LogError(const char* format, ...);
+ static void LogDebug(const char* format, ...);
+ static void LogOk(const char* format, ...);
+
+
+ static const std::vector& GetMessages();
+
+private:
+ static void LogInternal(const std::string& text, MessageType type);
+ static std::vector m_messages;
+};
+}
\ No newline at end of file
diff --git a/src/core/systems/Profiler.cpp b/src/core/systems/Profiler.cpp
new file mode 100644
index 0000000..fbba8f4
--- /dev/null
+++ b/src/core/systems/Profiler.cpp
@@ -0,0 +1,111 @@
+#include "Profiler.h"
+#include
+#include
+#include
+#include
+#include
+namespace OX
+{
+ void Profiler::BeginFrame() {
+ s_samples.clear();
+ s_startTimes.clear();
+ s_formatted.clear();
+ s_savedTree.clear();
+ s_stack.clear();
+ }
+
+ void Profiler::StartSample(const std::string& name) {
+ s_startTimes[name] = std::chrono::high_resolution_clock::now();
+
+ if (!s_stack.empty()) {
+ const std::string& parent = s_stack.back();
+
+ if (parent != name) { // Prevent self-cycle
+ auto& parentSample = s_samples[parent];
+ if (std::find(parentSample.children.begin(), parentSample.children.end(), name) == parentSample.children.end()) {
+ parentSample.children.push_back(name);
+ }
+ }
+ }
+
+
+ s_stack.push_back(name);
+ }
+
+ void Profiler::EndSample(const std::string& name) {
+ auto end = std::chrono::high_resolution_clock::now();
+ auto start = s_startTimes[name];
+ double elapsed = std::chrono::duration(end - start).count();
+
+ auto& sample = s_samples[name];
+ sample.totalTime += elapsed;
+ sample.callCount++;
+
+ if (!s_stack.empty() && s_stack.back() == name) {
+ s_stack.pop_back();
+ }
+ }
+
+ void Profiler::EndFrame() {
+ s_formatted.clear();
+ s_savedTree.clear();
+
+ std::unordered_set childSet;
+ for (const auto& [name, sample] : s_samples) {
+ for (const auto& child : sample.children) {
+ childSet.insert(child);
+ }
+ }
+
+ std::vector roots;
+ for (const auto& [name, sample] : s_samples) {
+ if (childSet.find(name) == childSet.end()) {
+ roots.push_back(name);
+ }
+ }
+
+ std::function recurse;
+ recurse = [&](const std::string& name, int depth) {
+ const auto& sample = s_samples[name];
+
+ std::ostringstream ss;
+ ss << std::string(depth * 2, ' ') << name << " (" << sample.callCount << "): "
+ << std::fixed << std::setprecision(2) << sample.totalTime << " ms";
+ s_formatted.push_back(ss.str());
+
+ SavedSample node;
+ node.name = name;
+ node.totalTime = sample.totalTime;
+ node.callCount = sample.callCount;
+
+ for (const auto& child : sample.children) {
+ node.children.push_back(recurse(child, depth + 1));
+ }
+
+ return node;
+ };
+
+ for (const auto& root : roots) {
+ s_savedTree.push_back(recurse(root, 0));
+ }
+
+ s_lastFormatted = s_formatted;
+ s_lastSavedTree = s_savedTree;
+ }
+
+ const std::vector& Profiler::GetFormattedData() {
+ return s_lastFormatted;
+ }
+
+ const std::vector& Profiler::GetSavedTree() {
+ return s_lastSavedTree;
+ }
+
+ ProfileScope::ProfileScope(const std::string& name) : m_name(name) {
+ Profiler::StartSample(name);
+ }
+
+ ProfileScope::~ProfileScope() {
+ Profiler::EndSample(m_name);
+ }
+}
\ No newline at end of file
diff --git a/src/core/systems/Profiler.h b/src/core/systems/Profiler.h
new file mode 100644
index 0000000..bf13206
--- /dev/null
+++ b/src/core/systems/Profiler.h
@@ -0,0 +1,70 @@
+#pragma once
+#include
+#include
+#include
+#include
+namespace OX
+{
+ class Profiler {
+ public:
+ static void BeginFrame();
+ static void EndFrame();
+
+ static void StartSample(const std::string& name);
+ static void EndSample(const std::string& name);
+
+ static const std::vector& GetFormattedData();
+
+ struct SavedSample {
+ std::string name;
+ double totalTime;
+ int callCount;
+ std::vector children;
+ };
+
+ static const std::vector& GetSavedTree();
+
+ private:
+ struct Sample {
+ double totalTime = 0.0;
+ int callCount = 0;
+ std::vector children;
+ };
+
+ static inline std::unordered_map s_samples;
+ static inline std::unordered_map s_startTimes;
+ static inline std::vector s_stack;
+ static inline std::vector s_formatted;
+ static inline std::vector s_lastFormatted;
+ static inline std::vector s_savedTree;
+ static inline std::vector s_lastSavedTree;
+ };
+
+ class ProfileScope {
+ public:
+ explicit ProfileScope(const std::string& name);
+ ~ProfileScope();
+
+ private:
+ std::string m_name;
+ };
+
+
+#if defined(_MSC_VER)
+#define __FUNC_NAME__ __FUNCSIG__
+#else
+#define __FUNC_NAME__ __PRETTY_FUNCTION__
+#endif
+
+#if defined(_DEBUG) || defined(DEBUG)
+
+#define PROFILE_FUNCTION() ProfileScope __profile_scope__##__LINE__(__FUNC_NAME__)
+#define PROFILE_LABEL(name) ProfileScope __profile_scope__##__LINE__(name)
+
+#else
+
+#define PROFILE_FUNCTION()
+#define PROFILE_LABEL(name)
+
+#endif
+}
diff --git a/src/core/systems/WindowManager.cpp b/src/core/systems/WindowManager.cpp
new file mode 100644
index 0000000..29bfc31
--- /dev/null
+++ b/src/core/systems/WindowManager.cpp
@@ -0,0 +1,104 @@
+#include
+#include "WindowManager.h"
+#include "../Systems/Logger.h"
+#include
+
+#include "Profiler.h"
+namespace OX
+{
+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;
+ }
+
+ 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(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) {
+ PROFILE_FUNCTION();
+
+ auto* wm = static_cast(glfwGetWindowUserPointer(window));
+ if (wm) {
+ wm->SetSize(width, height);
+ }
+}
+
+void WindowManager::SetSize(int width, int height) {
+ PROFILE_FUNCTION();
+
+ m_width = width;
+ m_height = height;
+ glViewport(0, 0, width, height);
+}
+
+void WindowManager::PollEvents() const {
+ PROFILE_FUNCTION();
+
+ glfwPollEvents();
+}
+
+bool WindowManager::ShouldClose() const {
+ return glfwWindowShouldClose(m_window);
+}
+
+void WindowManager::BeginFrame() const {
+ 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 {
+
+ {
+ glfwSwapBuffers(m_window);
+ }
+}
+
+void WindowManager::Shutdown() {
+ if (m_window) {
+ glfwDestroyWindow(m_window);
+ m_window = nullptr;
+ }
+ glfwTerminate();
+}
+
+WindowManager::~WindowManager() {
+ Shutdown();
+}
+}
\ No newline at end of file
diff --git a/src/core/systems/WindowManager.h b/src/core/systems/WindowManager.h
new file mode 100644
index 0000000..f8a5fee
--- /dev/null
+++ b/src/core/systems/WindowManager.h
@@ -0,0 +1,36 @@
+#ifndef WINDOWMANAGER_H
+#define WINDOWMANAGER_H
+
+#include
+#include
+namespace OX
+{
+ class WindowManager {
+ public:
+ WindowManager() = default;
+ ~WindowManager();
+
+ bool Init(const std::string& title, int width, int height);
+ void Shutdown();
+
+ void PollEvents() const;
+ [[nodiscard]] bool ShouldClose() const;
+
+ void BeginFrame() const;
+ void EndFrame() const;
+
+ [[nodiscard]] GLFWwindow* GetHandle() const { return m_window; }
+ [[nodiscard]] int GetWidth() const { return m_width; }
+ [[nodiscard]] int GetHeight() const { return m_height; }
+
+ static void FramebufferSizeCallback(GLFWwindow* window, int width, int height);
+
+ private:
+ void SetSize(int width, int height);
+
+ GLFWwindow* m_window = nullptr;
+ int m_width = 1280;
+ int m_height = 720;
+ };
+}
+#endif // WINDOWMANAGER_H
diff --git a/src/editor/Editor.cpp b/src/editor/Editor.cpp
new file mode 100644
index 0000000..ddcda6c
--- /dev/null
+++ b/src/editor/Editor.cpp
@@ -0,0 +1,31 @@
+//
+// Created by spenc on 5/18/2025.
+//
+
+#include "Editor.h"
+
+namespace OX {
+
+ void Editor::Init(Core& core)
+ {
+ Logger::LogOk("Editor::Init");
+ }
+
+ void Editor::Update(Core& core)
+ {
+
+ }
+
+ void Editor::Draw(Core& core)
+ {
+
+ }
+
+ void Editor::Shutdown(Core& core)
+ {
+ Logger::LogOk("Editor::Shutdown");
+
+
+ }
+
+} // OX
\ No newline at end of file
diff --git a/src/editor/Editor.h b/src/editor/Editor.h
new file mode 100644
index 0000000..1f688a6
--- /dev/null
+++ b/src/editor/Editor.h
@@ -0,0 +1,27 @@
+//
+// Created by spenc on 5/18/2025.
+//
+
+#ifndef EDITOR_H
+#define EDITOR_H
+#include "Layer.h"
+#include "Core.h"
+namespace OX {
+
+
+
+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;
+};
+
+
+
+} // OX
+
+#endif //EDITOR_H