From e67631f7fc0be54f4d1469c0790e6f991b04271f Mon Sep 17 00:00:00 2001 From: Archit Gosain Date: Thu, 9 Nov 2023 22:06:58 +0530 Subject: [PATCH] Add imgui --- .gitignore | 2 +- .gitmodules | 9 ++ CMakeLists.txt | 4 +- src/CMakeLists.txt | 27 ++++-- src/common/maths/vec.h | 94 +++++++++++++++++++ src/{ => common}/types.h | 0 src/core/CMakeLists.txt | 30 ++++++ src/{ => core}/cpu.cpp | 2 +- src/{ => core}/cpu.h | 2 +- src/{ => core}/gameBoy.cpp | 18 +++- src/{ => core}/gameBoy.h | 15 +-- src/{ => core}/graphics.cpp | 18 ++-- src/{ => core}/graphics.h | 7 +- src/{ => core}/mmap.cpp | 0 src/{ => core}/mmap.h | 2 +- src/gui/CMakeLists.txt | 44 +++++++++ src/gui/dock/DockUI.cpp | 64 +++++++++++++ src/gui/dock/DockUI.h | 17 ++++ src/gui/gui.h | 5 + src/gui/menubar/MenuBar.cpp | 44 +++++++++ src/gui/menubar/MenuBar.h | 33 +++++++ src/gui/ui-flags/UIFlags.cpp | 74 +++++++++++++++ src/gui/ui-flags/UIFlags.h | 16 ++++ src/gui/window.cpp | 176 +++++++++++++++++++++++++++++++++++ src/gui/window.h | 50 ++++++++++ src/main.cpp | 27 +++++- vendor/CMakeLists.txt | 7 ++ vendor/imgui/CMakeLists.txt | 52 +++++++++++ vendor/imgui/imgui | 1 + 29 files changed, 801 insertions(+), 39 deletions(-) create mode 100644 .gitmodules create mode 100644 src/common/maths/vec.h rename src/{ => common}/types.h (100%) create mode 100644 src/core/CMakeLists.txt rename src/{ => core}/cpu.cpp (99%) rename src/{ => core}/cpu.h (99%) rename src/{ => core}/gameBoy.cpp (92%) rename src/{ => core}/gameBoy.h (85%) rename src/{ => core}/graphics.cpp (98%) rename src/{ => core}/graphics.h (95%) rename src/{ => core}/mmap.cpp (100%) rename src/{ => core}/mmap.h (99%) create mode 100644 src/gui/CMakeLists.txt create mode 100644 src/gui/dock/DockUI.cpp create mode 100644 src/gui/dock/DockUI.h create mode 100644 src/gui/gui.h create mode 100644 src/gui/menubar/MenuBar.cpp create mode 100644 src/gui/menubar/MenuBar.h create mode 100644 src/gui/ui-flags/UIFlags.cpp create mode 100644 src/gui/ui-flags/UIFlags.h create mode 100644 src/gui/window.cpp create mode 100644 src/gui/window.h create mode 100644 vendor/CMakeLists.txt create mode 100644 vendor/imgui/CMakeLists.txt create mode 160000 vendor/imgui/imgui diff --git a/.gitignore b/.gitignore index 478cd55..7b03933 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ build/ cmake-build-debug/ .idea/ src/.vscode/ -src/dmg_boot.gb \ No newline at end of file +roms/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e034d9d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "vendor/imgui/imgui"] + path = vendor/imgui/imgui + url = https://github.com/ocornut/imgui.git +[submodule "vendor/glfw"] + path = vendor/glfw + url = https://github.com/glfw/glfw.git +[submodule "vendor/glew-cmake"] + path = vendor/glew-cmake + url = https://github.com/Aviii06/glew-cmake.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f0ecf96..d1fab63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,5 +12,5 @@ if (DEBUG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG") endif() -add_executable(${PROJECT_NAME} src/main.cpp) -add_subdirectory(src) +add_subdirectory(vendor) +add_subdirectory(src) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2177555..b3bd168 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,17 +1,22 @@ +#add_subdirectory(core) + +add_executable(${PROJECT_NAME} main.cpp) + +add_subdirectory(gui) set(SOURCES # ------- # Source Files - cpu.cpp - gameBoy.cpp - mmap.cpp - graphics.cpp + core/cpu.cpp + core/gameBoy.cpp + core/mmap.cpp + core/graphics.cpp # ------- # Header Files - cpu.h - gameBoy.h - mmap.h - types.h - graphics.h + core/cpu.h + core/gameBoy.h + core/mmap.h + common/types.h + core/graphics.h ) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) @@ -19,12 +24,14 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod find_package(SDL2 REQUIRED) include_directories(${SDL2_INCLUDE_DIRS}) +file(COPY ../roms DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + if (MSVC) set_target_properties( ${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/") endif () -target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} gui_gbemu) set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include") diff --git a/src/common/maths/vec.h b/src/common/maths/vec.h new file mode 100644 index 0000000..1399760 --- /dev/null +++ b/src/common/maths/vec.h @@ -0,0 +1,94 @@ +#pragma once + +#include + +// Structure to standardize the vertices used in the meshes +struct Vec4 +{ + float x, y, z, w; + + Vec4() + : x(0.0f) + , y(0.0f) + , z(0.0f) + , w(0.0f) + { + } + + Vec4(float x, float y, float z, float w) + : x(x) + , y(y) + , z(z) + , w(w) + { + } + + Vec4 operator*(float scalar) { return Vec4(x * scalar, y * scalar, z * scalar, w * scalar); } + + Vec4 operator+(Vec4 other) { return Vec4(x + other.x, y + other.y, z + other.z, w + other.w); } +}; + +struct Vec3 +{ + float x, y, z; + + Vec3() + : x(0.0f) + , y(0.0f) + , z(0.0f) + { + } + + Vec3(float x, float y, float z) + : x(x) + , y(y) + , z(z) + { + } + + Vec3(const Vec3& other) + : x(other.x) + , y(other.y) + , z(other.z) + { + } + + Vec3 operator*(float scalar) { return Vec3(x * scalar, y * scalar, z * scalar); } + + Vec3 operator+(Vec3 other) { return Vec3(x + other.x, y + other.y, z + other.z); } + + Vec3 operator-(Vec3 other) { return Vec3(x - other.x, y - other.y, z - other.z); } +}; + +struct Vec2 +{ + float x, y; + + Vec2() + : x(0.0f) + , y(0.0f) + { + } + + Vec2(float x, float y) + : x(x) + , y(y) + { + } + + Vec2 operator*(float scalar) { return Vec2(x * scalar, y * scalar); } + + Vec2 operator*(double scalar) { return Vec2(x * scalar, y * scalar); } + + Vec2 operator+(Vec2 other) { return Vec2(x + other.x, y + other.y); } + + Vec2 operator-(Vec2 other) { return Vec2(x - other.x, y - other.y); } + + Vec2 Perpendicular() { return Vec2(-y, x); } + + Vec2 Normalize() + { + float length = std::sqrt(x * x + y * y); + return Vec2(x / length, y / length); + } +}; \ No newline at end of file diff --git a/src/types.h b/src/common/types.h similarity index 100% rename from src/types.h rename to src/common/types.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 0000000..e489e62 --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,30 @@ +set(SOURCES + # ------- + # Source Files + cpu.cpp + gameBoy.cpp + mmap.cpp + graphics.cpp + # ------- + # Header Files + cpu.h + gameBoy.h + mmap.h + ../common/types.h + graphics.h + ) + +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) + +if (MSVC) + set_target_properties( + ${PROJECT_NAME} PROPERTIES + VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/") +endif () + +target_link_libraries(${PROJECT_NAME} gui_gbemu) + +set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include") diff --git a/src/cpu.cpp b/src/core/cpu.cpp similarity index 99% rename from src/cpu.cpp rename to src/core/cpu.cpp index d2a6872..7280b77 100644 --- a/src/cpu.cpp +++ b/src/core/cpu.cpp @@ -1,4 +1,4 @@ -#include "types.h" +#include "common/types.h" #include "cpu.h" #include #ifndef DEBUG diff --git a/src/cpu.h b/src/core/cpu.h similarity index 99% rename from src/cpu.h rename to src/core/cpu.h index b7f3e48..3658314 100644 --- a/src/cpu.h +++ b/src/core/cpu.h @@ -1,5 +1,5 @@ #pragma once -#include "types.h" +#include "common/types.h" #include "mmap.h" #include "graphics.h" diff --git a/src/gameBoy.cpp b/src/core/gameBoy.cpp similarity index 92% rename from src/gameBoy.cpp rename to src/core/gameBoy.cpp index 9f565db..1235373 100644 --- a/src/gameBoy.cpp +++ b/src/core/gameBoy.cpp @@ -1,9 +1,11 @@ -#include "types.h" +#include "common/types.h" #include "cpu.h" #include "gameBoy.h" int GBE::s_Cycles; +static int i = 0; + GBE::GBE() { // Initialize the CPU @@ -27,11 +29,11 @@ GBE::GBE() gbe_graphics->init(); // Open the Boot ROM - if ((bootROM = fopen("../src/dmg_boot.gb", "rb")) == NULL) + if ((bootROM = fopen("./roms/dmg_boot.gb", "rb")) == NULL) printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) + if ((gameROM = fopen("./roms/Tetris.gb", "rb")) == NULL) printf("game rom file not opened"); // Set the Boot ROM @@ -102,15 +104,18 @@ GBE::GBE() update(); } -void GBE::update() +int GBE::update() { +// printf("Updating\n"); // Update function of the GBE // Will be called every frame // GB has 59.73 frames per second - while (true) +// while (true) + int cycle = 0; { // Execute the next instruction s_Cycles += gbe_cpu->executeNextInstruction(); + cycle += s_Cycles; // update the DIV and TIMA timers gbe_cpu->updateTimers(s_Cycles); @@ -118,7 +123,10 @@ void GBE::update() s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); gbe_graphics->pollEvents(); + i++; +// printf("%d\n", i); } + return cycle; } void GBE::executeBootROM() diff --git a/src/gameBoy.h b/src/core/gameBoy.h similarity index 85% rename from src/gameBoy.h rename to src/core/gameBoy.h index 35c22f8..16cf91e 100644 --- a/src/gameBoy.h +++ b/src/core/gameBoy.h @@ -1,5 +1,5 @@ #pragma once -#include "types.h" +#include "common/types.h" #include "cpu.h" #include "mmap.h" #include "graphics.h" @@ -34,11 +34,6 @@ class GBE // File pointer for game ROM FILE* gameROM; - // Update function of the GBE - // Will be called every frame - // GB has 59.73 frames per second - void update(); - // cycle counter of the gameboy // used by CPU, PPU, APU so declared here static int s_Cycles; @@ -52,6 +47,14 @@ class GBE // Initializes the CPU GBE(); + // Update function of the GBE + // Will be called every frame + // GB has 59.73 frames per second + int update(); + // Returns the CPU CPU* getCPU() { return gbe_cpu; }; + color* getRenderArray() const { return gbe_graphics->getRenderArray(); } + + SDL_Texture* getSDLTexture() const { return gbe_graphics->getSDLTexture(); } }; \ No newline at end of file diff --git a/src/graphics.cpp b/src/core/graphics.cpp similarity index 98% rename from src/graphics.cpp rename to src/core/graphics.cpp index e1030bb..14cf37c 100644 --- a/src/graphics.cpp +++ b/src/core/graphics.cpp @@ -1,10 +1,10 @@ -#include "types.h" +#include "common/types.h" #include "graphics.h" PPU::PPU() { // Initialize members - window = nullptr; +// window = nullptr; renderer = nullptr; texture = nullptr; isEnabled = false; @@ -48,12 +48,12 @@ bool PPU::init() return false; } - // Set hint for VSync - if (!SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1")) - { - printf("VSync not enabled! SDL_Error: %s\n", SDL_GetError()); - return false; - } +// // Set hint for VSync +// if (!SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1")) +// { +// printf("VSync not enabled! SDL_Error: %s\n", SDL_GetError()); +// return false; +// } // Create window and renderer if (!(window = SDL_CreateWindow("GameBoy Emulator", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2, SDL_WINDOW_SHOWN))) @@ -93,7 +93,7 @@ bool PPU::init() SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); +// SDL_RenderPresent(renderer); return true; } diff --git a/src/graphics.h b/src/core/graphics.h similarity index 95% rename from src/graphics.h rename to src/core/graphics.h index 79446e3..4b2d657 100644 --- a/src/graphics.h +++ b/src/core/graphics.h @@ -1,5 +1,5 @@ #pragma once -#include "types.h" +#include "common/types.h" #include "mmap.h" #include #include @@ -122,4 +122,7 @@ class PPU void setMemoryMap(MemoryMap* m) { mMap = m; } void executePPU(int cycles); Byte getPPUMode() { return ppuMode; } -}; + + color* getRenderArray() { return renderArray; } + SDL_Texture* getSDLTexture() { return texture; } +}; \ No newline at end of file diff --git a/src/mmap.cpp b/src/core/mmap.cpp similarity index 100% rename from src/mmap.cpp rename to src/core/mmap.cpp diff --git a/src/mmap.h b/src/core/mmap.h similarity index 99% rename from src/mmap.h rename to src/core/mmap.h index aee176a..fe93b39 100644 --- a/src/mmap.h +++ b/src/core/mmap.h @@ -1,5 +1,5 @@ #pragma once -#include "types.h" +#include "common/types.h" #include // The Memory Map for GBE diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt new file mode 100644 index 0000000..74a8dc2 --- /dev/null +++ b/src/gui/CMakeLists.txt @@ -0,0 +1,44 @@ +set(HEADERS_GUI_GBEMU + window.h + dock/DockUI.h + ui-flags/UIFlags.h + gui.h + menubar/MenuBar.h) +set(SOURCES_GUI_GBEMU + window.cpp + dock/DockUI.cpp + ui-flags/UIFlags.cpp + menubar/MenuBar.cpp + ) + +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) + +add_library(gui_gbemu STATIC ${HEADERS_GUI_GBEMU} ${SOURCES_GUI_GBEMU}) + + +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) + + +if (MSVC) + set_target_properties( + gui_gbemu PROPERTIES + VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/") +endif () + +target_link_libraries(gui_gbemu + PUBLIC ${SDL2_LIBRARIES} + PUBLIC imgui) + + + +set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include") +target_include_directories(gui_gbemu PUBLIC + ${PROJECT_SOURCE_DIR}/vendor + ${PROJECT_SOURCE_DIR}/src + ) \ No newline at end of file diff --git a/src/gui/dock/DockUI.cpp b/src/gui/dock/DockUI.cpp new file mode 100644 index 0000000..67809cb --- /dev/null +++ b/src/gui/dock/DockUI.cpp @@ -0,0 +1,64 @@ +#include "DockUI.h" + +void gbemuGUI::InitUI() +{ + DockConfig(); + InitDock(); +} + +void gbemuGUI::DockConfig() +{ + // Initialize DockSpace + if (opt_fullscreen) + { + 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); + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + } + else + { + dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode; + } + + // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background + // and handle the pass-thru hole, so we ask Begin() to not render a background. + if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) + window_flags |= ImGuiWindowFlags_NoBackground; + + // Important: note that we proceed even if Begin() returns false (aka gui is collapsed). + // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, + // all active windows docked into it will lose their parent and become undocked. + // We cannot preserve the docking relationship between an active gui and an inactive docking, otherwise + // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible. + if (!opt_padding) + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); +} + +void gbemuGUI::InitDock() +{ + ImGui::Begin("GBEMU Dock", nullptr, window_flags); + if (!opt_padding) + ImGui::PopStyleVar(); + + if (opt_fullscreen) + ImGui::PopStyleVar(2); + + // Submit the DockSpace + ImGuiIO& io = ImGui::GetIO(); + + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); + } +} + +void gbemuGUI::EndUI() +{ + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/dock/DockUI.h b/src/gui/dock/DockUI.h new file mode 100644 index 0000000..12539eb --- /dev/null +++ b/src/gui/dock/DockUI.h @@ -0,0 +1,17 @@ +#pragma once + +#include "gui/ui-flags/UIFlags.h" +#include + +namespace gbemuGUI +{ + void InitUI(); + + void DockConfig(); + + void InitDock(); + + void Draw(); + + void EndUI(); +}; diff --git a/src/gui/gui.h b/src/gui/gui.h new file mode 100644 index 0000000..b090c41 --- /dev/null +++ b/src/gui/gui.h @@ -0,0 +1,5 @@ +#pragma once + +#include "window.h" +#include "dock/DockUI.h" +#include "ui-flags/UIFlags.h" diff --git a/src/gui/menubar/MenuBar.cpp b/src/gui/menubar/MenuBar.cpp new file mode 100644 index 0000000..dec350e --- /dev/null +++ b/src/gui/menubar/MenuBar.cpp @@ -0,0 +1,44 @@ +#include "MenuBar.h" + +#include "imgui.h" + + +namespace gbemuGUI +{ + void MainMenuBar() + { + if (ImGui::BeginMainMenuBar()) + { + // Iterate through the tabs + for (int i = 0; i < static_cast(MenuBarTabs::NUMBER_OF_TABS); i++) + { + // Draw the tabs - very bad way fix this later. TODO. + if (ImGui::BeginMenu(menubarTabNames[i])) + { + switch (static_cast(i)) + { + case MenuBarTabs::FILE: +// FileTab(); + break; + case MenuBarTabs::EDIT: +// EditTab(); + break; + case MenuBarTabs::VIEW: +// ViewTab(); + break; + case MenuBarTabs::HELP: +// HelpTab(); + break; + default: + break; + } + + // End the tab + ImGui::EndMenu(); + } + } + ImGui::EndMainMenuBar(); + } + } +} + diff --git a/src/gui/menubar/MenuBar.h b/src/gui/menubar/MenuBar.h new file mode 100644 index 0000000..5702fc6 --- /dev/null +++ b/src/gui/menubar/MenuBar.h @@ -0,0 +1,33 @@ +#pragma once + +#include "gui/ui-flags/UIFlags.h" +#include + +// Using X macros to define the tab names +#define MENU_BAR_TABS \ +X(FILE, "File") \ +X(EDIT, "Edit") \ +X(VIEW, "View") \ +X(HELP, "Help") \ +X(NUMBER_OF_TABS, "") \ + +namespace gbemuGUI +{ +#define X(en, str) en, + enum class MenuBarTabs : size_t { + MENU_BAR_TABS + }; +#undef X + +#define X(en, str) str, + static const char* menubarTabNames[] = { + MENU_BAR_TABS + }; +#undef X + void MainMenuBar(); + + void FileTab(); + void EditTab(); + void ViewTab(); + void HelpTab(); +}; diff --git a/src/gui/ui-flags/UIFlags.cpp b/src/gui/ui-flags/UIFlags.cpp new file mode 100644 index 0000000..9ce1d7d --- /dev/null +++ b/src/gui/ui-flags/UIFlags.cpp @@ -0,0 +1,74 @@ +#include "UIFlags.h" + +void gbemuGUI::ImGuiThemeSetup() +{ + ImVec4* colors = ImGui::GetStyle().Colors; + colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + colors[ImGuiCol_WindowBg] = ImVec4(0.16f, 0.16f, 0.16f, 0.94f); + colors[ImGuiCol_ChildBg] = ImVec4(0.16f, 0.16f, 0.16f, 0.94f); + colors[ImGuiCol_PopupBg] = ImVec4(0.16f, 0.16f, 0.16f, 0.94f); + colors[ImGuiCol_Border] = ImVec4(0.56f, 0.56f, 0.56f, 0.50f); + colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_FrameBg] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.72f, 0.72f, 0.73f, 0.40f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 0.67f); + colors[ImGuiCol_TitleBg] = ImVec4(0.21f, 0.21f, 0.21f, 1.00f); + colors[ImGuiCol_TitleBgActive] = ImVec4(0.29f, 0.29f, 0.29f, 1.00f); + colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); + colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); + colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); + colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); + colors[ImGuiCol_CheckMark] = ImVec4(0.74f, 0.74f, 0.74f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.29f, 0.29f, 0.29f, 1.00f); + colors[ImGuiCol_Button] = ImVec4(1.00f, 1.00f, 1.00f, 0.40f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.36f, 0.37f, 0.37f, 1.00f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_Tab] = ImVec4(0.33f, 0.33f, 0.33f, 0.86f); + colors[ImGuiCol_TabHovered] = ImVec4(0.48f, 0.48f, 0.48f, 1.00f); + colors[ImGuiCol_TabActive] = ImVec4(0.64f, 0.64f, 0.64f, 1.00f); + colors[ImGuiCol_TabUnfocused] = ImVec4(0.33f, 0.33f, 0.33f, 0.86f); + colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.33f, 0.33f, 0.33f, 0.86f); + colors[ImGuiCol_DockingPreview] = ImVec4(0.31f, 0.31f, 0.31f, 0.70f); + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); + colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); + colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); + colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); + colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); + colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f); + colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f); + colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); + colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); + colors[ImGuiCol_TextSelectedBg] = ImVec4(0.68f, 0.68f, 0.68f, 0.35f); + colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_NavHighlight] = ImVec4(0.69f, 0.69f, 0.69f, 1.00f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); + + ImGuiStyle* style = &ImGui::GetStyle(); + style->WindowPadding = ImVec2(8, 8); + style->FramePadding = ImVec2(4, 6); + style->CellPadding = ImVec2(4, 2); + style->ItemSpacing = ImVec2(4, 4); + style->ItemInnerSpacing = ImVec2(4, 4); + style->WindowRounding = 10; + style->FrameRounding = 7; + style->ScrollbarRounding = 12; + style->GrabRounding = 4; + style->WindowMenuButtonPosition = ImGuiDir_None; + style->DockingSeparatorSize = 2; +} diff --git a/src/gui/ui-flags/UIFlags.h b/src/gui/ui-flags/UIFlags.h new file mode 100644 index 0000000..f0dd64a --- /dev/null +++ b/src/gui/ui-flags/UIFlags.h @@ -0,0 +1,16 @@ +#pragma once + +#include "imgui.h" +#include "imgui/imgui/backends/imgui_impl_opengl3.h" +#include "imgui/imgui/backends/imgui_impl_sdlrenderer2.h" + +namespace gbemuGUI +{ + static bool opt_fullscreen = true; + static bool opt_padding = false; + static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; + + static ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + + void ImGuiThemeSetup(); +}; diff --git a/src/gui/window.cpp b/src/gui/window.cpp new file mode 100644 index 0000000..a11d0e1 --- /dev/null +++ b/src/gui/window.cpp @@ -0,0 +1,176 @@ +#include "Window.h" +#include + +#include +#include "dock/DockUI.h" +#include "menubar/MenuBar.h" + + + +namespace gbemuGUI +{ + Window::Window(int width, int height, const char* title) + { + m_Width = width; + m_Height = height; + m_Title = title; + + // Setup SDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) + { + printf("Error: %s\n", SDL_GetError()); + return; + } + + // Decide GL+GLSL versions +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GL ES 2.0 + GLSL 100 + const char* glsl_version = "#version 100"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(__APPLE__) + // GL 3.2 Core + GLSL 150 + const char* glsl_version = "#version 150"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); +#else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#endif + +// From 2.0.18: Enable native IME. +#ifdef SDL_HINT_IME_SHOW_UI + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); +#endif + + // Create gui with graphics context + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + m_Window = SDL_CreateWindow(m_Title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + m_GLContext = SDL_GL_CreateContext(m_Window); + SDL_GL_MakeCurrent(m_Window, m_GLContext); +// SDL_GL_SetSwapInterval(1); // Enable vsync + + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGui::SetCurrentContext(ImGui::GetCurrentContext()); + ImGui_ImplSDL2_InitForOpenGL(m_Window, m_GLContext); + ImGui_ImplOpenGL3_Init(glsl_version); + ImGui::StyleColorsDark(); + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + gbemuGUI::ImGuiThemeSetup(); + m_Texture = SDL_CreateTexture(m_Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, 160, 144); + + } + + void Window::Clear() const + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + void Window::Update(color* renderArray) + { +// SDL_Event event; +// while (SDL_PollEvent(&event)) +// { +// ImGui_ImplSDL2_ProcessEvent(&event); +// if (event.type == SDL_QUIT) +// m_Done = true; +// if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(m_Window)) +// m_Done = true; +// } + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + gbemuGUI::InitUI(); + + // Update the texture +// for (int i = 0; i < 160 * 144; i++) +// { +// renderArray[i] = 0x00FF00FF; +// } + SDL_UpdateTexture(m_Texture, NULL, renderArray, 160 * 4); + + bool show_demo_window = true; + // 2. Show a simple gui that we create ourselves. We use a Begin/End pair to create a named gui. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + +// ImGui::Image((ImTextureID)(intptr_t)m_Texture, ImVec2(512, 512)); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + printf("Application average %.3f ms/frame (%.1f FPS)\n", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); + } + + gbemuGUI::MainMenuBar(); + gbemuGUI::EndUI(); + + ImGui::Render(); + glViewport(0, 0, (int)ImGui::GetIO().DisplaySize.x, (int)ImGui::GetIO().DisplaySize.y); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call SDL_GL_MakeCurrent(gui, gl_context) directly) + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); + SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(backup_current_window, backup_current_context); + } + + SDL_GL_SwapWindow(m_Window); + } + + void Window::Exit() + { + if (m_Done) + { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + SDL_GL_DeleteContext(m_GLContext); + SDL_DestroyWindow(m_Window); + SDL_Quit(); + } + + } + +// void Window::Run() +// { +// while (!m_Done) +// { +// Update(); +// } +// Exit(); +// } +} \ No newline at end of file diff --git a/src/gui/window.h b/src/gui/window.h new file mode 100644 index 0000000..c93e5e7 --- /dev/null +++ b/src/gui/window.h @@ -0,0 +1,50 @@ +#include "imgui.h" +#include "imgui/imgui/backends/imgui_impl_sdl2.h" +#include "imgui/imgui/backends/imgui_impl_opengl3.h" +#include +#include "gui/ui-flags/UIFlags.h" +#include "common/maths/vec.h" +#include "common/types.h" + +#include + +namespace gbemuGUI +{ + class Window + { + private: + SDL_Window* m_Window; + SDL_GLContext m_GLContext; + SDL_Renderer* m_Renderer; + SDL_Texture* m_Texture; + + int m_Width, m_Height; + const char* m_Title; + + + static Window* s_Instance; + + bool m_Done = false; + + public: + ~Window() = default; + + Window(int width, int height, const char* title); + +// void Run(); + + void Clear() const; + + void Update(color* renderArray); + + void Exit(); + + int GetWidth() const { return m_Width; } + + int GetHeight() const { return m_Height; } + + float GetAspectRatio() const { return (float)m_Width / (float)m_Height; } + + bool IsDone() const { return m_Done; } + }; +}; diff --git a/src/main.cpp b/src/main.cpp index e29a31a..f846b03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,33 @@ -#include "gameBoy.h" +#include "gui/gui.h" +#include "core/gameBoy.h" int main(int argv, char** argc) { + + gbemuGUI::Window window = gbemuGUI::Window(1280, 720, "gbemu"); + GBE* gbe = new GBE(); + SDL_Texture* texture = nullptr; + color* renderArray = gbe->getRenderArray(); + SDL_Renderer* renderer = nullptr; + SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + int cyclesForPPUEExecution = 0; + while(!window.IsDone()) + { + cyclesForPPUEExecution += gbe->update(); + renderArray = gbe->getRenderArray(); + if (cyclesForPPUEExecution >= (456 + 204 + 80 + 172)) + { + window.Update(renderArray); + cyclesForPPUEExecution = 0; + } + } + + window.Exit(); + + return 0; } \ No newline at end of file diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt new file mode 100644 index 0000000..a094473 --- /dev/null +++ b/vendor/CMakeLists.txt @@ -0,0 +1,7 @@ +set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) +set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) + + +add_subdirectory(imgui) diff --git a/vendor/imgui/CMakeLists.txt b/vendor/imgui/CMakeLists.txt new file mode 100644 index 0000000..5c82678 --- /dev/null +++ b/vendor/imgui/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.5) + +# Maybe stop from CMAKEing in the wrong place +if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR) + message(FATAL_ERROR "Source and build directories cannot be the same. Go use the /build directory.") +endif () + + +# Append Wno-error to avoid compiling imgui with Werror; imgui seems to trigger lots of warnings +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error") + + # apparently, this stronger command is needed to accomplish the same in Clang + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything") + endif () +endif () + +set(SRCS imgui/imgui.cpp imgui/imgui_draw.cpp imgui/imgui_tables.cpp imgui/imgui_widgets.cpp imgui/imgui_demo.cpp imgui/backends/imgui_impl_opengl3.cpp imgui/backends/imgui_impl_sdl2.cpp imgui/backends/imgui_impl_sdlrenderer2.cpp) + +add_library( + imgui + ${SRCS} +) + +target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/imgui") +#target_include_directories(imgui PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../glfw/include/") +#target_include_directories(imgui PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../glad/include/") + +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) +target_link_libraries(imgui PRIVATE ${SDL2_LIBRARIES}) + +if (APPLE) + # On macOS, get openGL & friends from Frameworks; do not use GLAD at all + add_definitions(-DGLFW_INCLUDE_GLCOREARB) + + # Apple is playing hardball and deprecating openGL... we'll cross that bridge when we come to it + # Silence warnings about openGL deprecation + add_definitions(-DGL_SILENCE_DEPRECATION) + find_library(opengl_library OpenGL) + target_link_libraries(imgui PRIVATE ${opengl_library}) +else () + # On Windows/Linux, use the glad openGL loader + + add_definitions(-DIMGUI_IMPL_OPENGL_LOADER_GLAD) + target_link_libraries(imgui PRIVATE glad) +endif () + + +set_target_properties(imgui PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/vendor/imgui/imgui b/vendor/imgui/imgui new file mode 160000 index 0000000..1161301 --- /dev/null +++ b/vendor/imgui/imgui @@ -0,0 +1 @@ +Subproject commit 11613013860d149667302a258041dcd832069f36