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/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/roms/Tetris.gb b/roms/Tetris.gb new file mode 100644 index 0000000..fbcef42 Binary files /dev/null and b/roms/Tetris.gb differ diff --git a/roms/dmg_boot.gb b/roms/dmg_boot.gb new file mode 100644 index 0000000..afa0ee4 Binary files /dev/null and b/roms/dmg_boot.gb differ 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 95% rename from src/gameBoy.cpp rename to src/core/gameBoy.cpp index 9f565db..3c8ac83 100644 --- a/src/gameBoy.cpp +++ b/src/core/gameBoy.cpp @@ -1,4 +1,4 @@ -#include "types.h" +#include "common/types.h" #include "cpu.h" #include "gameBoy.h" @@ -27,11 +27,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 @@ -107,7 +107,7 @@ void GBE::update() // Update function of the GBE // Will be called every frame // GB has 59.73 frames per second - while (true) +// while (true) { // Execute the next instruction s_Cycles += gbe_cpu->executeNextInstruction(); diff --git a/src/gameBoy.h b/src/core/gameBoy.h similarity index 86% rename from src/gameBoy.h rename to src/core/gameBoy.h index 35c22f8..2250872 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,10 +34,7 @@ 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 @@ -52,6 +49,14 @@ class GBE // Initializes the CPU GBE(); + // Update function of the GBE + // Will be called every frame + // GB has 59.73 frames per second + void 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 79% rename from src/graphics.cpp rename to src/core/graphics.cpp index e1030bb..16be0ee 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; @@ -35,38 +35,38 @@ PPU::PPU() bool PPU::init() { // Initialize SDL - if (SDL_Init(SDL_INIT_VIDEO) < 0) - { - printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); - return false; - } - - // Set hint to use hardware acceleration - if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) - { - printf("Hardware Acceleration 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))) - { - printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); - return false; - } - - if (!(renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC))) - { - printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); - return false; - } +// if (SDL_Init(SDL_INIT_VIDEO) < 0) +// { +// printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); +// return false; +// } +// +// // Set hint to use hardware acceleration +// if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) +// { +// printf("Hardware Acceleration 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))) +// { +// printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); +// return false; +// } +// +// if (!(renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC))) +// { +// printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); +// return false; +// } // Evaluate LCDC register Byte LCDC = mMap->getRegLCDC(); @@ -91,9 +91,9 @@ bool PPU::init() // Render the texture SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); +// SDL_RenderClear(renderer); +// SDL_RenderCopy(renderer, texture, NULL, NULL); +// SDL_RenderPresent(renderer); return true; } @@ -101,76 +101,76 @@ bool PPU::init() // And process them bool PPU::pollEvents() { - while (SDL_PollEvent(event)) - { - if (event->key.type == SDL_KEYDOWN) - { - switch (event->key.keysym.sym) - { - case SDLK_LEFT: - *(mMap->joyPadState) &= 0xFD; - break; - case SDLK_RIGHT: - *(mMap->joyPadState) &= 0xFE; - break; - case SDLK_UP: - *(mMap->joyPadState) &= 0xFB; - break; - case SDLK_DOWN: - *(mMap->joyPadState) &= 0xF7; - break; - case SDLK_a: - *(mMap->joyPadState) &= 0xEF; - break; - case SDLK_s: - *(mMap->joyPadState) &= 0xDF; - break; - case SDLK_LSHIFT: - *(mMap->joyPadState) &= 0xBF; - break; - case SDLK_SPACE: - *(mMap->joyPadState) &= 0x7F; - break; - case SDLK_ESCAPE: - exit(0); - default: - break; - } - } - else if (event->key.type == SDL_KEYUP) - { - switch (event->key.keysym.sym) - { - case SDLK_LEFT: - *(mMap->joyPadState) |= 0x02; - break; - case SDLK_RIGHT: - *(mMap->joyPadState) |= 0x01; - break; - case SDLK_UP: - *(mMap->joyPadState) |= 0x04; - break; - case SDLK_DOWN: - *(mMap->joyPadState) |= 0x08; - break; - case SDLK_a: - *(mMap->joyPadState) |= 0x10; - break; - case SDLK_s: - *(mMap->joyPadState) |= 0x20; - break; - case SDLK_LSHIFT: - *(mMap->joyPadState) |= 0x40; - break; - case SDLK_SPACE: - *(mMap->joyPadState) |= 0x80; - break; - default: - break; - } - } - } - return false; +// while (SDL_PollEvent(event)) +// { +// if (event->key.type == SDL_KEYDOWN) +// { +// switch (event->key.keysym.sym) +// { +// case SDLK_LEFT: +// *(mMap->joyPadState) &= 0xFD; +// break; +// case SDLK_RIGHT: +// *(mMap->joyPadState) &= 0xFE; +// break; +// case SDLK_UP: +// *(mMap->joyPadState) &= 0xFB; +// break; +// case SDLK_DOWN: +// *(mMap->joyPadState) &= 0xF7; +// break; +// case SDLK_a: +// *(mMap->joyPadState) &= 0xEF; +// break; +// case SDLK_s: +// *(mMap->joyPadState) &= 0xDF; +// break; +// case SDLK_LSHIFT: +// *(mMap->joyPadState) &= 0xBF; +// break; +// case SDLK_SPACE: +// *(mMap->joyPadState) &= 0x7F; +// break; +// case SDLK_ESCAPE: +// exit(0); +// default: +// break; +// } +// } +// else if (event->key.type == SDL_KEYUP) +// { +// switch (event->key.keysym.sym) +// { +// case SDLK_LEFT: +// *(mMap->joyPadState) |= 0x02; +// break; +// case SDLK_RIGHT: +// *(mMap->joyPadState) |= 0x01; +// break; +// case SDLK_UP: +// *(mMap->joyPadState) |= 0x04; +// break; +// case SDLK_DOWN: +// *(mMap->joyPadState) |= 0x08; +// break; +// case SDLK_a: +// *(mMap->joyPadState) |= 0x10; +// break; +// case SDLK_s: +// *(mMap->joyPadState) |= 0x20; +// break; +// case SDLK_LSHIFT: +// *(mMap->joyPadState) |= 0x40; +// break; +// case SDLK_SPACE: +// *(mMap->joyPadState) |= 0x80; +// break; +// default: +// break; +// } +// } +// } +// return false; } void PPU::renderScanline(Byte line) @@ -388,9 +388,9 @@ void PPU::executePPU(int cycles) if (!frameRendered) { SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); +// SDL_RenderClear(renderer); +// SDL_RenderCopy(renderer, texture, NULL, NULL); +// SDL_RenderPresent(renderer); frameRendered = true; } if (currentClock < 0) @@ -474,11 +474,11 @@ void PPU::close() // Destroy texture SDL_DestroyTexture(texture); - // Destroy renderer - SDL_DestroyRenderer(renderer); - - // Destroy window - SDL_DestroyWindow(window); +// // Destroy renderer +// SDL_DestroyRenderer(renderer); +// +// // Destroy window +// SDL_DestroyWindow(window); // Quit SDL subsystems SDL_Quit(); diff --git a/src/graphics.h b/src/core/graphics.h similarity index 93% rename from src/graphics.h rename to src/core/graphics.h index 79446e3..ebe5258 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 @@ -23,7 +23,7 @@ struct Sprite class PPU { private: - SDL_Window* window; +// SDL_Window* window; SDL_Renderer* renderer; SDL_Texture* texture; SDL_Event* event; @@ -64,7 +64,7 @@ class PPU // OBP1 register is the OBJ Palette 1 Data Byte objPalette1; - // Internal window line counter + // Internal gui line counter Byte hiddenWindowLineCounter; // The GameBoy screen @@ -122,4 +122,8 @@ 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; } + }; 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..21192a4 --- /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 + ${SDL2_LIBRARIES} + 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..b25d9cc --- /dev/null +++ b/src/gui/ui-flags/UIFlags.h @@ -0,0 +1,16 @@ +#pragma once + +#include "imgui.h" +#include "imgui/imgui/backends/imgui_impl_glfw.h" +#include "imgui/imgui/backends/imgui_impl_opengl3.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..947088b --- /dev/null +++ b/src/gui/window.cpp @@ -0,0 +1,166 @@ +#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(); + } + + void Window::Clear() const + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + void Window::Update(SDL_Texture* text) + { + 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(); + + 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)text, ImVec2(256, 256)); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 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..2b25fc1 --- /dev/null +++ b/src/gui/window.h @@ -0,0 +1,46 @@ +#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 + +namespace gbemuGUI +{ + class Window + { + private: + SDL_Window* m_Window; + int m_Width, m_Height; + const char* m_Title; + + + static Window* s_Instance; + SDL_GLContext m_GLContext; + + bool m_Done = false; + + public: + ~Window() = default; + + Window(int width, int height, const char* title); + +// void Run(); + + void Clear() const; + + void Update(SDL_Texture* text); + + 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..24d6477 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,23 @@ -#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* text = gbe->getSDLTexture(); + while(!window.IsDone()) + { + gbe->update(); + text = gbe->getSDLTexture(); + window.Update(text); + } + + window.Exit(); + + return 0; } \ No newline at end of file diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index ef964d8..a094473 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -3,6 +3,5 @@ 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(glew-cmake) -add_subdirectory(glfw) + add_subdirectory(imgui) diff --git a/vendor/glew-cmake b/vendor/glew-cmake deleted file mode 160000 index 9758219..0000000 --- a/vendor/glew-cmake +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9758219375d923a008feef93c0955931d801740f diff --git a/vendor/glfw b/vendor/glfw deleted file mode 160000 index 3eaf125..0000000 --- a/vendor/glfw +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3eaf1255b29fdf5c2895856c7be7d7185ef2b241 diff --git a/vendor/imgui/CMakeLists.txt b/vendor/imgui/CMakeLists.txt index d8b3ce0..fe48571 100644 --- a/vendor/imgui/CMakeLists.txt +++ b/vendor/imgui/CMakeLists.txt @@ -16,7 +16,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STR 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_glfw.cpp imgui/backends/imgui_impl_opengl3.cpp) +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) add_library( imgui @@ -24,18 +24,17 @@ add_library( ) 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/") +#target_include_directories(imgui PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../glfw/include/") +#target_include_directories(imgui PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../glad/include/") -target_link_libraries(imgui PRIVATE glfw) +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) - # NOTE: This code is essentially duplicated here and in polyscope/src/CMakeLists.txt - # 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) diff --git a/vendor/imgui/imgui b/vendor/imgui/imgui index 37ea320..1161301 160000 --- a/vendor/imgui/imgui +++ b/vendor/imgui/imgui @@ -1 +1 @@ -Subproject commit 37ea320b96a4b77581986f5581707021a7be94c9 +Subproject commit 11613013860d149667302a258041dcd832069f36