diff --git a/CMakeLists.txt b/CMakeLists.txt index bc227ae..3cd19ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ file(GLOB_RECURSE CORE_SOURCES CONFIGURE_DEPENDS add_library(Core STATIC ${CORE_SOURCES} + src/core/systems/MACROS.h ) target_include_directories(Core PUBLIC src/core) @@ -105,6 +106,8 @@ add_executable(Editor ${APP_SOURCES} main.cpp src/editor/Editor.cpp src/editor/Editor.h + src/editor/Windows/LoggerWindow.cpp + src/editor/Windows/LoggerWindow.h ) target_include_directories(Editor PRIVATE diff --git a/main.cpp b/main.cpp index fafaed0..e064a76 100644 --- a/main.cpp +++ b/main.cpp @@ -10,10 +10,10 @@ int main() { - OX::Core core("Application"); + OX::Core core("Obsidian Editor - Onyx Engine (2025.1)(0.0.4)"); - core.AddLayer(std::make_unique("Application")); + core.AddLayer(std::make_unique("Obsidian Editor")); core.Init(); diff --git a/src/core/Core.cpp b/src/core/Core.cpp index a794c92..be4b762 100644 --- a/src/core/Core.cpp +++ b/src/core/Core.cpp @@ -3,6 +3,9 @@ // #include "Core.h" + +#include "systems/MACROS.h" + namespace OX { @@ -19,8 +22,8 @@ void Core::Init() { - - for (auto& layer : m_layers) { + OX_ASSERT(!m_layers.empty(), "No Layers Attached"); + for (const auto& layer : m_layers) { Logger::LogDebug("Initializing Layer: '%s'", layer->GetName().c_str()); layer->Init(*this); } @@ -35,7 +38,7 @@ void Core::Run() { while (!window.ShouldClose()) { Profiler::BeginFrame(); { - PROFILE_LABEL("Frame"); + OX_PROFILE_LABEL("Frame"); Update(); window.BeginFrame(); @@ -52,14 +55,14 @@ void Core::Run() { } void Core::Update() { - PROFILE_FUNCTION(); + OX_PROFILE_FUNCTION(); for (auto& layer : m_layers) { layer->Update(*this); } } void Core::Draw() { - PROFILE_FUNCTION(); + OX_PROFILE_FUNCTION(); for (auto& layer : m_layers) { @@ -82,4 +85,4 @@ void Core::Shutdown() { Logger::LogOk("Core Shutdown Complete."); } -} \ No newline at end of file +} diff --git a/src/core/Core.h b/src/core/Core.h index 4a4b3ac..cf31e2f 100644 --- a/src/core/Core.h +++ b/src/core/Core.h @@ -12,6 +12,7 @@ #include "systems/Logger.h" #include "systems/Profiler.h" #include "systems/WindowManager.h" +#include "systems/MACROS.h" namespace OX { diff --git a/src/core/systems/Logger.cpp b/src/core/systems/Logger.cpp index 102aad1..5e9191a 100644 --- a/src/core/systems/Logger.cpp +++ b/src/core/systems/Logger.cpp @@ -3,6 +3,7 @@ #include #include #include + namespace OX { std::vector Logger::m_messages; @@ -137,4 +138,19 @@ namespace OX const std::vector& Logger::GetMessages() { return m_messages; } + + + + std::string Logger::MessageTypeToString(const 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"; + } + } + + } \ No newline at end of file diff --git a/src/core/systems/Logger.h b/src/core/systems/Logger.h index 466ffb3..b35a7cb 100644 --- a/src/core/systems/Logger.h +++ b/src/core/systems/Logger.h @@ -5,6 +5,8 @@ #include namespace OX { + + enum class MessageType { Info, Warning, @@ -30,6 +32,10 @@ public: static void LogDebug(const char* format, ...); static void LogOk(const char* format, ...); + static void Clear() {m_messages.clear();} + + static std::string MessageTypeToString(const MessageType type); + static const std::vector& GetMessages(); diff --git a/src/core/systems/MACROS.h b/src/core/systems/MACROS.h new file mode 100644 index 0000000..a7c53af --- /dev/null +++ b/src/core/systems/MACROS.h @@ -0,0 +1,48 @@ +// +// Created by spenc on 5/18/2025. +// + +#ifndef MACROS_H +#define MACROS_H + + +#include +#include + +#if defined(_WIN32) +#define OX_DEBUG_BREAK() __debugbreak() +#elif defined(__unix__) || defined(__APPLE__) +#include +#define OX_DEBUG_BREAK() raise(SIGTRAP) +#else +#define OX_DEBUG_BREAK() ((void)0) +#endif + +// ========== ASSERT ========== +// In debug builds: breaks and logs. +// In release builds: removed entirely. +#ifdef _DEBUG +#define OX_ASSERT(condition, message) \ +do { \ +if (!(condition)) { \ +std::cerr << "\n[ASSERTION] " << #condition << "\n" \ +<< "Message: " << message << "\n" \ +<< "File: " << __FILE__ << ":" << __LINE__ << "\n"; \ +OX_DEBUG_BREAK(); \ +} \ +} while (0) +#else +#define OX_ASSERT(condition, message) ((void)0) +#endif + +// ========== VERIFY ========== +// In debug builds: asserts. +// In release builds: just evaluates the condition. +#ifdef _DEBUG +#define OX_VERIFY(condition, message) OX_ASSERT(condition, message) +#else +#define OX_VERIFY(condition, message) ((void)(condition)) +#endif + + +#endif //MACROS_H diff --git a/src/core/systems/Profiler.h b/src/core/systems/Profiler.h index bf13206..bae3ded 100644 --- a/src/core/systems/Profiler.h +++ b/src/core/systems/Profiler.h @@ -58,13 +58,13 @@ namespace OX #if defined(_DEBUG) || defined(DEBUG) -#define PROFILE_FUNCTION() ProfileScope __profile_scope__##__LINE__(__FUNC_NAME__) -#define PROFILE_LABEL(name) ProfileScope __profile_scope__##__LINE__(name) +#define OX_PROFILE_FUNCTION() ProfileScope __profile_scope__##__LINE__(__FUNC_NAME__) +#define OX_PROFILE_LABEL(name) ProfileScope __profile_scope__##__LINE__(name) #else -#define PROFILE_FUNCTION() -#define PROFILE_LABEL(name) +#define OX_PROFILE_FUNCTION() +#define OX_PROFILE_LABEL(name) #endif } diff --git a/src/core/systems/WindowManager.cpp b/src/core/systems/WindowManager.cpp index 29bfc31..e127835 100644 --- a/src/core/systems/WindowManager.cpp +++ b/src/core/systems/WindowManager.cpp @@ -49,7 +49,7 @@ bool WindowManager::Init(const std::string& title, int width, int height) { void WindowManager::FramebufferSizeCallback(GLFWwindow* window, int width, int height) { - PROFILE_FUNCTION(); + OX_PROFILE_FUNCTION(); auto* wm = static_cast(glfwGetWindowUserPointer(window)); if (wm) { @@ -58,7 +58,7 @@ void WindowManager::FramebufferSizeCallback(GLFWwindow* window, int width, int h } void WindowManager::SetSize(int width, int height) { - PROFILE_FUNCTION(); + OX_PROFILE_FUNCTION(); m_width = width; m_height = height; @@ -66,7 +66,7 @@ void WindowManager::SetSize(int width, int height) { } void WindowManager::PollEvents() const { - PROFILE_FUNCTION(); + OX_PROFILE_FUNCTION(); glfwPollEvents(); } @@ -76,7 +76,7 @@ bool WindowManager::ShouldClose() const { } void WindowManager::BeginFrame() const { - PROFILE_FUNCTION(); + OX_PROFILE_FUNCTION(); PollEvents(); glViewport(0, 0, m_width, m_height); glClearColor(0.07f, 0.07f, 0.1f, 1.0f); @@ -84,10 +84,10 @@ void WindowManager::BeginFrame() const { } void WindowManager::EndFrame() const { + OX_PROFILE_FUNCTION(); + + glfwSwapBuffers(m_window); - { - glfwSwapBuffers(m_window); - } } void WindowManager::Shutdown() { diff --git a/src/editor/Editor.cpp b/src/editor/Editor.cpp index ddcda6c..17d7f96 100644 --- a/src/editor/Editor.cpp +++ b/src/editor/Editor.cpp @@ -3,27 +3,132 @@ // #include "Editor.h" - +#include +#include +#include +#include +#include "Windows/LoggerWindow.h" namespace OX { void Editor::Init(Core& core) { - Logger::LogOk("Editor::Init"); + Logger::LogInfo("%s Init", m_name.c_str()); + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + + ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + style.WindowRounding = 5.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + + ImGui_ImplGlfw_InitForOpenGL(core.GetWindow().GetHandle(), true); + ImGui_ImplOpenGL3_Init("#version 330 core"); } void Editor::Update(Core& core) { + OX_PROFILE_FUNCTION(); } void Editor::Draw(Core& core) { + OX_PROFILE_FUNCTION(); + + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + + + // === Main Docking Space === + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBackground; + + 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_WindowBorderSize, 0.0f); + windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + + ImGui::Begin("DockSpace Root", nullptr, windowFlags); + ImGui::PopStyleVar(2); + + ImGuiID dockspaceID = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(dockspaceID, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode); + + LoggerWindow::Draw(); + + + ImGui::Begin("Profiler"); + + 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); + + // 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 + + ImGui::PushStyleColor(ImGuiCol_Text, color); + + ImGui::Text("%s (%d): %.2f ms", node.name.c_str(), node.callCount, node.totalTime); + + ImGui::PopStyleColor(); + for (const auto& child : node.children) { + DrawNode(child, depth + 1); + } + + ImGui::Unindent(depth * 10.0f); + }; + + for (const auto& root : tree) { + DrawNode(root, 0); + } + } + + ImGui::End(); + + ImGui::End(); // DockSpace Root + + // --- Render ImGui onto FBO 0 --- + { + OX_PROFILE_LABEL("VSYNC Wait"); + + ImGui::EndFrame(); + ImGui::Render(); + ImGui::UpdatePlatformWindows(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } + } void Editor::Shutdown(Core& core) { Logger::LogOk("Editor::Shutdown"); + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); } diff --git a/src/editor/Windows/LoggerWindow.cpp b/src/editor/Windows/LoggerWindow.cpp new file mode 100644 index 0000000..65545b8 --- /dev/null +++ b/src/editor/Windows/LoggerWindow.cpp @@ -0,0 +1,150 @@ +// +// Created by spenc on 5/12/2025. +// + +#include "LoggerWindow.h" +#include "Core.h" +#include +#include + + + + +namespace OX::LoggerWindow { + + static bool s_AutoScroll = true; + static bool s_ShowOk = true; + static bool s_ShowInfo = true; + static bool s_ShowWarning = true; + static bool s_ShowError = true; + static bool s_ShowDebug = false; + static bool s_ShowVerbose = false; + static char s_FilterBuf[256] = ""; + + static ImVec4 GetColor(const MessageType level) { + switch (level) { + case MessageType::Ok: return {0.6f, 1.0f, 0.6f, 1.0f}; + case MessageType::Info: return {0.3f, 0.6f, 1.0f, 1.0f}; + case MessageType::Warning: return {1.0f, 0.8f, 0.2f, 1.0f}; + case MessageType::Error: return {1.0f, 0.2f, 0.2f, 1.0f}; + case MessageType::Debug: return {0.2f, 1.0f, 1.0f, 1.0f}; + default: return {1, 1, 1, 1}; + } + } + + void Draw(const char* title) + { + OX_PROFILE_FUNCTION(); + + if (!ImGui::Begin(title)) { + ImGui::End(); + return; + } + + // Controls + if (ImGui::Button("Clear Log")) Logger::Clear(); + ImGui::SameLine(); + ImGui::Checkbox("Auto-scroll", &s_AutoScroll); + ImGui::SameLine(); + ImGui::SetNextItemWidth(200); + ImGui::InputTextWithHint("##Filter", "Filter...", s_FilterBuf, sizeof(s_FilterBuf)); + + // Filter toggles + ImGui::Separator(); + ImGui::Text("Levels: "); + ImGui::SameLine(); ImGui::Checkbox("Ok", &s_ShowOk); + ImGui::SameLine(); ImGui::Checkbox("Info", &s_ShowInfo); + ImGui::SameLine(); ImGui::Checkbox("Warning", &s_ShowWarning); + ImGui::SameLine(); ImGui::Checkbox("Error", &s_ShowError); + ImGui::SameLine(); ImGui::Checkbox("Debug", &s_ShowDebug); + ImGui::SameLine(); ImGui::Checkbox("Verbose", &s_ShowVerbose); + ImGui::Separator(); + + constexpr ImGuiTableFlags flags = + ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | + ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter; + + if (const float tableHeight = ImGui::GetContentRegionAvail().y - 10.0f; ImGui::BeginTable("LogTable", 3, flags, ImVec2(0, tableHeight))) { + ImGui::TableSetupColumn("Level", ImGuiTableColumnFlags_WidthFixed, 80.0f); + ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_WidthFixed, 80.0f); + ImGui::TableSetupColumn("Message", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableHeadersRow(); + + for (const auto& message : Logger::GetMessages()) { + const MessageType level = message.type; + const std::string& text = message.text; + const std::string& timestamp = message.timestamp; + + const bool show = + (level == MessageType::Ok && s_ShowOk) || + (level == MessageType::Info && s_ShowInfo) || + (level == MessageType::Warning && s_ShowWarning) || + (level == MessageType::Error && s_ShowError) || + (level == MessageType::Debug && s_ShowDebug); + + if (!show) + continue; + + if (s_FilterBuf[0] != '\0' && + std::string(text).find(s_FilterBuf) == std::string::npos) + continue; + + + + + + + + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImVec4 col = GetColor(level); + const ImVec2 cursor = ImGui::GetCursorScreenPos(); + constexpr float barWidth = 4.0f; + const float textHeight = ImGui::GetTextLineHeight(); + ImVec2 barStart = cursor; + auto barEnd = ImVec2(cursor.x + barWidth, cursor.y + textHeight); + ImGui::GetWindowDrawList()->AddRectFilled(barStart, barEnd, ImGui::ColorConvertFloat4ToU32(col), 1.0f); + + ImGui::Dummy(ImVec2(barWidth + 6.0f, 0)); + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Text, col); + ImGui::TextUnformatted(Logger::MessageTypeToString(level).c_str()); + ImGui::PopStyleColor(); + + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s",timestamp.c_str()); + + ImGui::TableSetColumnIndex(2); + ImGui::TextWrapped("%s", text.c_str()); + + + + if (s_AutoScroll) { + if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 5.0f) { + ImGui::SetScrollHereY(1.0f); + } + } + + + + } + + + ImGui::EndTable(); + } + + + + + ImGui::End(); + } + + void ClearFilters() { + s_FilterBuf[0] = '\0'; + s_ShowOk = s_ShowInfo = s_ShowWarning = s_ShowError = s_ShowDebug = s_ShowVerbose = true; + } + +} + +// CE \ No newline at end of file diff --git a/src/editor/Windows/LoggerWindow.h b/src/editor/Windows/LoggerWindow.h new file mode 100644 index 0000000..79b8ff6 --- /dev/null +++ b/src/editor/Windows/LoggerWindow.h @@ -0,0 +1,17 @@ +// +// Created by spenc on 5/18/2025. +// + +#ifndef LOGGERWINDOW_H +#define LOGGERWINDOW_H + +namespace OX { + + namespace LoggerWindow { + void Draw(const char* title = "Console"); + void ClearFilters(); + }; + +} // OX + +#endif //LOGGERWINDOW_H