From 8c43baedea7a6014e906480a682209a0f0c33dec Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Thu, 1 Jun 2023 23:22:31 -0500 Subject: [PATCH] [nostalgia] Break part of core out into Turbine and TeaGBA libraries --- CMakeLists.txt | 2 +- deps/gbastartup/CMakeLists.txt | 12 - deps/teagba/CMakeLists.txt | 1 + .../teagba/include/teagba}/addresses.hpp | 2 +- deps/teagba/include/teagba/bios.hpp | 22 + deps/teagba/include/teagba/gfx.hpp | 46 ++ .../teagba/include/teagba}/irq.hpp | 8 +- .../teagba/include/teagba}/registers.hpp | 4 +- deps/teagba/src/CMakeLists.txt | 21 + deps/teagba/src/bios.s | 37 ++ deps/{gbastartup => teagba/src}/cstartup.cpp | 2 +- deps/{gbastartup => teagba/src}/gba_crt0.s | 0 deps/teagba/src/gfx.cpp | 40 ++ src/CMakeLists.txt | 3 +- src/keel/keel.hpp | 2 + src/nostalgia/core/CMakeLists.txt | 7 +- src/nostalgia/core/clipboard.hpp | 36 -- src/nostalgia/core/config.hpp | 24 +- src/nostalgia/core/context.cpp | 16 + src/nostalgia/core/context.hpp | 131 +--- src/nostalgia/core/core.hpp | 21 +- src/nostalgia/core/event.hpp | 17 - src/nostalgia/core/gba/CMakeLists.txt | 10 +- src/nostalgia/core/gba/bios.hpp | 22 - src/nostalgia/core/gba/bios.s | 37 -- src/nostalgia/core/gba/core.arm.cpp | 42 -- src/nostalgia/core/gba/core.cpp | 85 +-- src/nostalgia/core/gba/core.hpp | 15 - src/nostalgia/core/gba/gfx.cpp | 143 ++--- src/nostalgia/core/gba/gfx.hpp | 38 +- src/nostalgia/core/gba/irq.arm.cpp | 46 -- src/nostalgia/core/gba/panic.cpp | 25 +- src/nostalgia/core/gba/tests.cpp | 38 -- src/nostalgia/core/gfx.cpp | 15 +- src/nostalgia/core/gfx.hpp | 26 - src/nostalgia/core/glfw/clipboard.cpp | 27 - src/nostalgia/core/glfw/core.cpp | 82 --- src/nostalgia/core/glfw/core.hpp | 20 - src/nostalgia/core/initparams.hpp | 13 + src/nostalgia/core/opengl/CMakeLists.txt | 2 +- src/nostalgia/core/opengl/context.hpp | 26 + src/nostalgia/core/opengl/core.cpp | 21 + src/nostalgia/core/opengl/gfx.cpp | 519 +++++++++++++++- src/nostalgia/core/opengl/gfx.hpp | 56 +- src/nostalgia/core/opengl/gfx_opengl.cpp | 585 ------------------ src/nostalgia/core/sdl/core.cpp | 88 --- src/nostalgia/core/sdl/core.hpp | 21 - src/nostalgia/core/sdl/gfx.cpp | 71 --- src/nostalgia/core/studio/module.cpp | 6 +- src/nostalgia/core/studio/module.hpp | 6 +- .../core/studio/paletteeditor-imgui.cpp | 13 +- .../core/studio/paletteeditor-imgui.hpp | 8 +- .../core/studio/tilesheeteditor-imgui.cpp | 28 +- .../core/studio/tilesheeteditor-imgui.hpp | 8 +- .../core/studio/tilesheeteditormodel.cpp | 14 +- .../core/studio/tilesheeteditormodel.hpp | 4 +- .../core/studio/tilesheeteditorview.cpp | 2 +- .../core/studio/tilesheeteditorview.hpp | 2 +- src/nostalgia/glutils/glutils.cpp | 18 + src/nostalgia/glutils/glutils.hpp | 10 + src/nostalgia/player/app.cpp | 24 +- src/nostalgia/scene/studio/module.cpp | 6 +- src/nostalgia/scene/studio/module.hpp | 6 +- .../scene/studio/sceneeditor-imgui.cpp | 6 +- .../scene/studio/sceneeditor-imgui.hpp | 6 +- src/nostalgia/scene/studio/sceneeditor.cpp | 2 +- src/nostalgia/scene/studio/sceneeditor.hpp | 4 +- .../scene/studio/sceneeditorview.cpp | 12 +- .../scene/studio/sceneeditorview.hpp | 4 +- src/nostalgia/studio/aboutpopup.cpp | 4 +- src/nostalgia/studio/aboutpopup.hpp | 8 +- src/nostalgia/studio/clawviewer.cpp | 4 +- src/nostalgia/studio/clawviewer.hpp | 6 +- src/nostalgia/studio/filedialogmanager.cpp | 8 +- src/nostalgia/studio/filedialogmanager.hpp | 6 +- src/nostalgia/studio/lib/configio.hpp | 2 +- src/nostalgia/studio/lib/editor.cpp | 4 +- src/nostalgia/studio/lib/editor.hpp | 4 +- src/nostalgia/studio/lib/imguiuitl.hpp | 6 +- src/nostalgia/studio/lib/imguiutil.cpp | 6 +- src/nostalgia/studio/lib/itemmaker.hpp | 8 +- src/nostalgia/studio/lib/module.cpp | 4 +- src/nostalgia/studio/lib/module.hpp | 7 +- src/nostalgia/studio/lib/popup.hpp | 6 +- src/nostalgia/studio/lib/task.cpp | 4 +- src/nostalgia/studio/lib/task.hpp | 6 +- src/nostalgia/studio/lib/widget.hpp | 4 +- src/nostalgia/studio/main.cpp | 35 +- src/nostalgia/studio/newmenu.cpp | 10 +- src/nostalgia/studio/newmenu.hpp | 14 +- src/nostalgia/studio/projectexplorer.cpp | 4 +- src/nostalgia/studio/projectexplorer.hpp | 8 +- src/nostalgia/studio/projecttreemodel.cpp | 4 +- src/nostalgia/studio/projecttreemodel.hpp | 6 +- src/nostalgia/studio/studioapp.cpp | 40 +- src/nostalgia/studio/studioapp.hpp | 8 +- src/turbine/CMakeLists.txt | 40 ++ src/turbine/clipboard.hpp | 32 + src/turbine/config.hpp | 25 + src/turbine/context.hpp | 93 +++ src/turbine/event.cpp | 14 + src/turbine/event.hpp | 17 + src/turbine/gba/CMakeLists.txt | 16 + src/turbine/gba/clipboard.cpp | 19 + .../core => turbine}/gba/context.hpp | 6 +- src/turbine/gba/gfx.cpp | 42 ++ src/turbine/gba/irq.arm.cpp | 36 ++ src/{nostalgia/core => turbine}/gba/irq.s | 10 +- src/turbine/gba/turbine.arm.cpp | 41 ++ src/turbine/gba/turbine.cpp | 80 +++ src/turbine/gba/turbine.hpp | 15 + src/turbine/gfx.hpp | 42 ++ .../core => turbine}/glfw/CMakeLists.txt | 7 +- src/turbine/glfw/clipboard.cpp | 27 + src/turbine/glfw/context.hpp | 25 + src/{nostalgia/core => turbine}/glfw/gfx.cpp | 116 ++-- src/turbine/glfw/turbine.cpp | 109 ++++ src/{nostalgia/core => turbine}/input.hpp | 8 +- src/turbine/turbine.hpp | 29 + 119 files changed, 1918 insertions(+), 1873 deletions(-) delete mode 100644 deps/gbastartup/CMakeLists.txt create mode 100644 deps/teagba/CMakeLists.txt rename {src/nostalgia/core/gba => deps/teagba/include/teagba}/addresses.hpp (98%) create mode 100644 deps/teagba/include/teagba/bios.hpp create mode 100644 deps/teagba/include/teagba/gfx.hpp rename {src/nostalgia/core/gba => deps/teagba/include/teagba}/irq.hpp (76%) rename {src/nostalgia/core/gba => deps/teagba/include/teagba}/registers.hpp (93%) create mode 100644 deps/teagba/src/CMakeLists.txt create mode 100644 deps/teagba/src/bios.s rename deps/{gbastartup => teagba/src}/cstartup.cpp (96%) rename deps/{gbastartup => teagba/src}/gba_crt0.s (100%) create mode 100644 deps/teagba/src/gfx.cpp delete mode 100644 src/nostalgia/core/clipboard.hpp create mode 100644 src/nostalgia/core/context.cpp delete mode 100644 src/nostalgia/core/event.hpp delete mode 100644 src/nostalgia/core/gba/bios.hpp delete mode 100644 src/nostalgia/core/gba/bios.s delete mode 100644 src/nostalgia/core/gba/core.arm.cpp delete mode 100644 src/nostalgia/core/gba/core.hpp delete mode 100644 src/nostalgia/core/gba/irq.arm.cpp delete mode 100644 src/nostalgia/core/gba/tests.cpp delete mode 100644 src/nostalgia/core/glfw/clipboard.cpp delete mode 100644 src/nostalgia/core/glfw/core.cpp delete mode 100644 src/nostalgia/core/glfw/core.hpp create mode 100644 src/nostalgia/core/initparams.hpp create mode 100644 src/nostalgia/core/opengl/context.hpp create mode 100644 src/nostalgia/core/opengl/core.cpp delete mode 100644 src/nostalgia/core/opengl/gfx_opengl.cpp delete mode 100644 src/nostalgia/core/sdl/core.cpp delete mode 100644 src/nostalgia/core/sdl/core.hpp delete mode 100644 src/nostalgia/core/sdl/gfx.cpp create mode 100644 src/turbine/CMakeLists.txt create mode 100644 src/turbine/clipboard.hpp create mode 100644 src/turbine/config.hpp create mode 100644 src/turbine/context.hpp create mode 100644 src/turbine/event.cpp create mode 100644 src/turbine/event.hpp create mode 100644 src/turbine/gba/CMakeLists.txt create mode 100644 src/turbine/gba/clipboard.cpp rename src/{nostalgia/core => turbine}/gba/context.hpp (81%) create mode 100644 src/turbine/gba/gfx.cpp create mode 100644 src/turbine/gba/irq.arm.cpp rename src/{nostalgia/core => turbine}/gba/irq.s (92%) create mode 100644 src/turbine/gba/turbine.arm.cpp create mode 100644 src/turbine/gba/turbine.cpp create mode 100644 src/turbine/gba/turbine.hpp create mode 100644 src/turbine/gfx.hpp rename src/{nostalgia/core => turbine}/glfw/CMakeLists.txt (60%) create mode 100644 src/turbine/glfw/clipboard.cpp create mode 100644 src/turbine/glfw/context.hpp rename src/{nostalgia/core => turbine}/glfw/gfx.cpp (79%) create mode 100644 src/turbine/glfw/turbine.cpp rename src/{nostalgia/core => turbine}/input.hpp (79%) create mode 100644 src/turbine/turbine.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a57bac88..4532f339 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ include_directories( ) if(BUILDCORE_TARGET STREQUAL "gba") - add_subdirectory(deps/gbastartup) + add_subdirectory(deps/teagba) else() include_directories( SYSTEM diff --git a/deps/gbastartup/CMakeLists.txt b/deps/gbastartup/CMakeLists.txt deleted file mode 100644 index c49f1629..00000000 --- a/deps/gbastartup/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -enable_language(CXX ASM) - -add_library( - GbaStartup OBJECT - gba_crt0.s - cstartup.cpp -) - -target_link_libraries( - GbaStartup PUBLIC - OxStd -) diff --git a/deps/teagba/CMakeLists.txt b/deps/teagba/CMakeLists.txt new file mode 100644 index 00000000..febd4f0a --- /dev/null +++ b/deps/teagba/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(src) diff --git a/src/nostalgia/core/gba/addresses.hpp b/deps/teagba/include/teagba/addresses.hpp similarity index 98% rename from src/nostalgia/core/gba/addresses.hpp rename to deps/teagba/include/teagba/addresses.hpp index 02d66d2c..6d3bf183 100644 --- a/src/nostalgia/core/gba/addresses.hpp +++ b/deps/teagba/include/teagba/addresses.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once diff --git a/deps/teagba/include/teagba/bios.hpp b/deps/teagba/include/teagba/bios.hpp new file mode 100644 index 00000000..4ab73561 --- /dev/null +++ b/deps/teagba/include/teagba/bios.hpp @@ -0,0 +1,22 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +// Functions for accessing BIOS calls + +extern "C" { + +// waits for any interrupt +void teagba_halt(); + +void teagba_stop(); + +// waits for interrupts specified in interSubs +void teagba_intrwait(unsigned discardExistingIntrs, unsigned intrSubs); + +// waits for vblank interrupt +void teagba_vblankintrwait(); + +} diff --git a/deps/teagba/include/teagba/gfx.hpp b/deps/teagba/include/teagba/gfx.hpp new file mode 100644 index 00000000..fdee0378 --- /dev/null +++ b/deps/teagba/include/teagba/gfx.hpp @@ -0,0 +1,46 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include + +namespace teagba { + +enum DispCtl { + DispCtl_Mode0 = 0, + DispCtl_Mode1 = 1, + DispCtl_Mode2 = 2, + DispCtl_Mode3 = 3, + DispCtl_Mode4 = 4, + DispCtl_Mode5 = 5, + + DispCtl_SpriteMap1D = 1 << 6, + + DispCtl_Bg0 = 1 << 8, + DispCtl_Bg1 = 1 << 9, + DispCtl_Bg2 = 1 << 10, + DispCtl_Bg3 = 1 << 11, + + DispCtl_Obj = 1 << 12, +}; + +struct OX_ALIGN8 GbaSpriteAttrUpdate { + uint16_t attr0 = 0; + uint16_t attr1 = 0; + uint16_t attr2 = 0; + uint16_t idx = 0; + +}; + +extern volatile uint16_t g_spriteUpdates; +extern ox::Array g_spriteBuffer; + +void addSpriteUpdate(const GbaSpriteAttrUpdate &upd) noexcept; + +void applySpriteUpdates() noexcept; + +} diff --git a/src/nostalgia/core/gba/irq.hpp b/deps/teagba/include/teagba/irq.hpp similarity index 76% rename from src/nostalgia/core/gba/irq.hpp rename to deps/teagba/include/teagba/irq.hpp index acb47160..c697dbd7 100644 --- a/src/nostalgia/core/gba/irq.hpp +++ b/deps/teagba/include/teagba/irq.hpp @@ -1,10 +1,14 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include -namespace nostalgia::core { +namespace teagba { + +constexpr uint16_t DispStat_irq_vblank = 1 << 3; +constexpr uint16_t DispStat_irq_hblank = 1 << 4; +constexpr uint16_t DispStat_irq_vcount = 1 << 5; constexpr uint16_t Int_vblank = 1 << 0; constexpr uint16_t Int_hblank = 1 << 1; diff --git a/src/nostalgia/core/gba/registers.hpp b/deps/teagba/include/teagba/registers.hpp similarity index 93% rename from src/nostalgia/core/gba/registers.hpp rename to deps/teagba/include/teagba/registers.hpp index 0fb30426..e19c5ec7 100644 --- a/src/nostalgia/core/gba/registers.hpp +++ b/deps/teagba/include/teagba/registers.hpp @@ -1,12 +1,12 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once #include "addresses.hpp" -namespace nostalgia::core { +namespace teagba { inline auto bgSetSbb(volatile BgCtl *bgCtl, unsigned sbb) noexcept { *bgCtl = (*bgCtl & ~0b11111'0000'0000u) | (sbb << 8); diff --git a/deps/teagba/src/CMakeLists.txt b/deps/teagba/src/CMakeLists.txt new file mode 100644 index 00000000..bc66dc31 --- /dev/null +++ b/deps/teagba/src/CMakeLists.txt @@ -0,0 +1,21 @@ +enable_language(CXX ASM) + +set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm) + +add_library( + TeaGBA + bios.s + gba_crt0.s + cstartup.cpp + gfx.cpp +) + +target_include_directories( + TeaGBA PUBLIC + ../include +) + +target_link_libraries( + TeaGBA PUBLIC + OxStd +) diff --git a/deps/teagba/src/bios.s b/deps/teagba/src/bios.s new file mode 100644 index 00000000..be46ba5e --- /dev/null +++ b/deps/teagba/src/bios.s @@ -0,0 +1,37 @@ +// +// Copyright 2016 - 2023 gary@drinkingtea.net +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +.section .iwram, "ax", %progbits +.thumb +.align + +.global teagba_halt +.type teagba_halt, %function +teagba_halt: + swi 2 + bx lr + +.global teagba_stop +.type teagba_stop, %function +teagba_stop: + swi 3 + bx lr + +.global teagba_intrwait +.type teagba_intrwait, %function +teagba_intrwait: + swi 4 + bx lr + +.global teagba_vblankintrwait +.type teagba_vblankintrwait, %function +teagba_vblankintrwait: + swi 5 + bx lr + +// vim: ft=armv4 diff --git a/deps/gbastartup/cstartup.cpp b/deps/teagba/src/cstartup.cpp similarity index 96% rename from deps/gbastartup/cstartup.cpp rename to deps/teagba/src/cstartup.cpp index 1ffb0942..b9e3f983 100644 --- a/deps/gbastartup/cstartup.cpp +++ b/deps/teagba/src/cstartup.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2021 gary@drinkingtea.net + * Copyright 2016 - 2023 gary@drinkingtea.net * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/deps/gbastartup/gba_crt0.s b/deps/teagba/src/gba_crt0.s similarity index 100% rename from deps/gbastartup/gba_crt0.s rename to deps/teagba/src/gba_crt0.s diff --git a/deps/teagba/src/gfx.cpp b/deps/teagba/src/gfx.cpp new file mode 100644 index 00000000..2148dfac --- /dev/null +++ b/deps/teagba/src/gfx.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include +#include + +#include + +namespace teagba { + +volatile uint16_t g_spriteUpdates = 0; +ox::Array g_spriteBuffer; + +void addSpriteUpdate(const GbaSpriteAttrUpdate &upd) noexcept { + // block until g_spriteUpdates is less than buffer len + if (g_spriteUpdates >= g_spriteBuffer.size()) [[unlikely]] { + teagba_vblankintrwait(); + } + const auto ie = REG_IE; // disable vblank interrupt handler + REG_IE = REG_IE & static_cast(~teagba::Int_vblank); // disable vblank interrupt handler + const auto updateCnt = g_spriteUpdates; + g_spriteBuffer[updateCnt] = upd; + g_spriteUpdates = updateCnt + 1; + REG_IE = ie; // enable vblank interrupt handler +} + +void applySpriteUpdates() noexcept { + // copy g_spriteUpdates to allow it to use a register instead of reading + // from memory every iteration of the loop, needed because g_spriteUpdates + // is volatile + const unsigned updates = g_spriteUpdates; + for (unsigned i = 0; i < updates; ++i) { + const auto &oa = g_spriteBuffer[i]; + MEM_OAM[oa.idx] = *reinterpret_cast(&oa); + } +} + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d455c7f6..f8615a62 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ include_directories(".") add_subdirectory(keel) -add_subdirectory(nostalgia) \ No newline at end of file +add_subdirectory(nostalgia) +add_subdirectory(turbine) \ No newline at end of file diff --git a/src/keel/keel.hpp b/src/keel/keel.hpp index 2cb36c5b..ab048e12 100644 --- a/src/keel/keel.hpp +++ b/src/keel/keel.hpp @@ -18,6 +18,7 @@ ox::Result> init(ox::UPtr &&fs, ox::CRStringView a auto ctx = ox::make_unique(); ctx->appName = appName; oxIgnoreError(setRomFs(ctx.get(), std::move(fs))); +#ifndef OX_BARE_METAL const auto &mods = modules(); for (auto &mod : mods) { // register type converters @@ -29,6 +30,7 @@ ox::Result> init(ox::UPtr &&fs, ox::CRStringView a ctx->packTransforms.emplace_back(c); } } +#endif return ctx; } diff --git a/src/nostalgia/core/CMakeLists.txt b/src/nostalgia/core/CMakeLists.txt index f6d7a3dd..0c3948bb 100644 --- a/src/nostalgia/core/CMakeLists.txt +++ b/src/nostalgia/core/CMakeLists.txt @@ -1,5 +1,6 @@ add_library( NostalgiaCore + context.cpp gfx.cpp module.cpp tilesheet.cpp @@ -10,7 +11,6 @@ add_library( if(NOSTALGIA_BUILD_TYPE STREQUAL "GBA") add_subdirectory(gba) else() - add_subdirectory(glfw) add_subdirectory(opengl) endif() @@ -21,6 +21,7 @@ endif() target_link_libraries( NostalgiaCore PUBLIC Keel + Turbine ) if(NOSTALGIA_BUILD_STUDIO) @@ -29,16 +30,14 @@ endif() install( FILES - clipboard.hpp color.hpp config.hpp consts.hpp context.hpp core.hpp - event.hpp gfx.hpp + initparams.hpp ptidxconv.hpp - input.hpp tilesheet.hpp typeconv.hpp typestore.hpp diff --git a/src/nostalgia/core/clipboard.hpp b/src/nostalgia/core/clipboard.hpp deleted file mode 100644 index 96fd8eba..00000000 --- a/src/nostalgia/core/clipboard.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -#include -#include -#include - -#include "context.hpp" - -namespace nostalgia::core { - -ox::String getClipboardText(class Context *ctx) noexcept; - -void setClipboardText(class Context *ctx, ox::CRStringView text) noexcept; - -template -void setClipboardObject([[maybe_unused]] class Context *ctx, [[maybe_unused]] ox::UniquePtr obj) noexcept { -#ifndef OX_BARE_METAL - ctx->clipboard = std::move(obj); -#endif -} - -template -ox::Result getClipboardObject([[maybe_unused]] class Context *ctx) noexcept { -#ifndef OX_BARE_METAL - if (ctx->clipboard && ctx->clipboard->typeMatch(T::TypeName, T::TypeVersion)) { - return static_cast(ctx->clipboard.get()); - } -#endif - return OxError(1); -} - -} diff --git a/src/nostalgia/core/config.hpp b/src/nostalgia/core/config.hpp index 5387b3f1..57276d2a 100644 --- a/src/nostalgia/core/config.hpp +++ b/src/nostalgia/core/config.hpp @@ -1,33 +1,11 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once -#if __has_include() -#endif - #include namespace nostalgia::core::config { -constexpr auto ImGuiEnabled = -#if __has_include() -true; -#else -false; -#endif - -enum class SdlVsync { - Adaptive = -1, - Off = 0, - On = 1, -}; - -constexpr auto GbaSpriteBufferLen = 128; -constexpr auto GbaEventLoopTimerBased = false; -constexpr auto GbaTimerBits = 32; -constexpr auto UserlandFpsPrint = false; -constexpr auto SdlVsyncOption = static_cast(SdlVsync::Adaptive); - } diff --git a/src/nostalgia/core/context.cpp b/src/nostalgia/core/context.cpp new file mode 100644 index 00000000..802dafea --- /dev/null +++ b/src/nostalgia/core/context.cpp @@ -0,0 +1,16 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include "context.hpp" +#include "gfx.hpp" + +#include "core.hpp" + +namespace nostalgia::core { + +Context::~Context() noexcept { + shutdownGfx(this); +} + +} diff --git a/src/nostalgia/core/context.hpp b/src/nostalgia/core/context.hpp index c15ed1bc..1d7c16ab 100644 --- a/src/nostalgia/core/context.hpp +++ b/src/nostalgia/core/context.hpp @@ -10,68 +10,28 @@ #include #include +#include -#include "event.hpp" -#include "input.hpp" +#include "initparams.hpp" + +namespace nostalgia::core { +class Context; +} namespace nostalgia::core::gl { -void setMainViewEnabled(core::Context *ctx, bool enabled) noexcept; void drawMainView(core::Context*) noexcept; void setRenderSize(core::Context*, int width, int height) noexcept; ox::Size getRenderSize(core::Context*) noexcept; void clearRenderSize(core::Context *ctx) noexcept; } -namespace nostalgia::core::renderer { -ox::Error init(Context *ctx) noexcept; -} - -namespace ox { -class Size; -} - namespace nostalgia::core { -class BaseClipboardObject { - public: - virtual ~BaseClipboardObject() noexcept = default; - - [[nodiscard]] - virtual ox::String typeId() const noexcept = 0; - - [[nodiscard]] - constexpr auto typeMatch(auto name, auto version) const noexcept { - return typeId() == ox::buildTypeId(name, version); - } -}; - -template -class ClipboardObject: public BaseClipboardObject { - [[nodiscard]] - ox::String typeId() const noexcept final { - return ox::buildTypeId(T::TypeName, T::TypeVersion); - } -}; - -class Drawer; - -struct BgCbbData { - unsigned bpp = 4; -}; - // User Input Output -class Context: public keel::Context { - friend constexpr void setApplicationData(Context *ctx, void *applicationData) noexcept; - template - friend constexpr T *applicationData(Context *ctx) noexcept; - friend constexpr void setConstantRefresh(Context *ctx, bool) noexcept; +class Context { friend bool bgStatus(Context *ctx, unsigned bg) noexcept; - friend bool buttonDown(Context *ctx, Key) noexcept; - friend ox::Size getScreenSize(Context *ctx) noexcept; - friend int getScreenHeight(Context *ctx) noexcept; - friend int getScreenWidth(Context *ctx) noexcept; - friend ox::Error initGfx(Context *ctx) noexcept; - friend ox::Error renderer::init(Context *ctx) noexcept; + friend ox::Error initGfx(Context *ctx, const InitParams&) noexcept; + friend void shutdownGfx(Context *ctx) noexcept; friend ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheetPath, @@ -81,21 +41,12 @@ class Context: public keel::Context { const ox::FileAddress &paletteAddr) noexcept; friend ox::Result loadTileSheet(Context *ctx, const struct CompactTileSheet &tilesheetAddr) noexcept; - friend ox::Error run(Context *ctx) noexcept; - friend void shutdown(Context *ctx) noexcept; - friend ox::Result> init(ox::UPtr fs, ox::CRStringView appName) noexcept; - friend ox::String getClipboardText(Context *ctx) noexcept; - friend uint64_t ticksMs(Context *ctx) noexcept; friend uint8_t bgStatus(Context *ctx) noexcept; friend void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept; friend void draw(Context *ctx) noexcept; - friend void focusWindow(Context *ctx) noexcept; friend void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbb) noexcept; friend void setBgStatus(Context *ctx, uint32_t status) noexcept; friend void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept; - friend void setUpdateHandler(Context *ctx, UpdateHandler h) noexcept; - friend constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept; - friend constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept; friend void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; friend void setSprite(Context *ctx, unsigned idx, @@ -106,30 +57,13 @@ class Context: public keel::Context { unsigned spriteSize, unsigned flipX) noexcept; friend void hideSprite(Context *ctx, unsigned idx) noexcept; - friend void gl::setMainViewEnabled(core::Context *ctx, bool enabled) noexcept; friend void gl::drawMainView(core::Context*) noexcept; friend void gl::setRenderSize(core::Context*, int width, int height) noexcept; friend ox::Size gl::getRenderSize(core::Context*) noexcept; friend void gl::clearRenderSize(core::Context *ctx) noexcept; public: - ox::Vector drawers; - -#ifndef OX_BARE_METAL - int uninterruptedRefreshes = 3; - ox::UPtr clipboard; -#endif - protected: -#ifndef OX_BARE_METAL - // sets screen refresh to constant instead of only on event - bool constantRefresh = true; -#endif - KeyEventHandler m_keyEventHandler = nullptr; - void *m_customData = nullptr; - - private: - void *m_windowerData = nullptr; - void *m_rendererData = nullptr; + turbine::Context *turbineCtx = nullptr; public: Context() noexcept = default; @@ -138,52 +72,13 @@ class Context: public keel::Context { Context(const Context &other) noexcept = delete; Context(const Context &&other) noexcept = delete; - template - [[nodiscard]] - constexpr T *windowerData() noexcept { - return static_cast(m_windowerData); - } + ~Context() noexcept; - protected: - constexpr void setWindowerData(void *windowerData) noexcept { - m_windowerData = windowerData; - } - - constexpr void setRendererData(void *rendererData) noexcept { - m_rendererData = rendererData; - } - - template - [[nodiscard]] - constexpr T *rendererData() noexcept { - return static_cast(m_rendererData); + auto &rom() noexcept { + return *turbineCtx->rom; } }; -constexpr void setApplicationData(Context *ctx, void *applicationData) noexcept { - ctx->m_customData = applicationData; -} - -template -[[nodiscard]] -constexpr T *applicationData(Context *ctx) noexcept { - return static_cast(ctx->m_customData); -} - -constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept { - ctx->m_keyEventHandler = h; -} - -constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept { - return ctx->m_keyEventHandler; -} - -constexpr void setConstantRefresh([[maybe_unused]] Context *ctx, [[maybe_unused]] bool r) noexcept { -#ifndef OX_BARE_METAL - ctx->constantRefresh = r; -#endif -} - } diff --git a/src/nostalgia/core/core.hpp b/src/nostalgia/core/core.hpp index 4d41ecaa..43551503 100644 --- a/src/nostalgia/core/core.hpp +++ b/src/nostalgia/core/core.hpp @@ -4,27 +4,10 @@ #pragma once -#include - -#include "clipboard.hpp" -#include "consts.hpp" -#include "event.hpp" -#include "gfx.hpp" -#include "input.hpp" -#include "module.hpp" -#include "typeconv.hpp" +#include "context.hpp" namespace nostalgia::core { -ox::Result> init(ox::UniquePtr fs, ox::CRStringView appName = "Nostalgia") noexcept; - -ox::Error run(Context *ctx) noexcept; - -// Returns the number of milliseconds that have passed since the start of the -// program. -[[nodiscard]] -uint64_t ticksMs(Context *ctx) noexcept; - -void shutdown(Context *ctx) noexcept; +ox::Result> init(turbine::Context *tctx, const InitParams& = {}) noexcept; } diff --git a/src/nostalgia/core/event.hpp b/src/nostalgia/core/event.hpp deleted file mode 100644 index ea1e7546..00000000 --- a/src/nostalgia/core/event.hpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -namespace nostalgia::core { - -class Context; - -using UpdateHandler = int(*)(Context*); - -// Sets event handler that sleeps for the time given in the return value. The -// sleep time is a minimum of ~16 milliseconds. -void setUpdateHandler(Context *ctx, UpdateHandler) noexcept; - -} diff --git a/src/nostalgia/core/gba/CMakeLists.txt b/src/nostalgia/core/gba/CMakeLists.txt index 87f5a58a..a325d939 100644 --- a/src/nostalgia/core/gba/CMakeLists.txt +++ b/src/nostalgia/core/gba/CMakeLists.txt @@ -1,16 +1,12 @@ enable_language(CXX ASM) -set_source_files_properties(core.arm.cpp irq.arm.cpp PROPERTIES COMPILE_FLAGS -marm) +set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm) target_sources( NostalgiaCore PRIVATE - bios.s - core.arm.cpp core.cpp gfx.cpp - irq.arm.cpp - irq.s panic.cpp ) target_link_libraries( NostalgiaCore PUBLIC - GbaStartup -) + TeaGBA +) \ No newline at end of file diff --git a/src/nostalgia/core/gba/bios.hpp b/src/nostalgia/core/gba/bios.hpp deleted file mode 100644 index 4604b634..00000000 --- a/src/nostalgia/core/gba/bios.hpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -// Functions for accessing BIOS calls - -extern "C" { - -// waits for any interrupt -void nostalgia_core_halt(); - -void nostalgia_core_stop(); - -// waits for interrupts specified in interSubs -void nostalgia_core_intrwait(unsigned discardExistingIntrs, unsigned intrSubs); - -// waits for vblank interrupt -void nostalgia_core_vblankintrwait(); - -} diff --git a/src/nostalgia/core/gba/bios.s b/src/nostalgia/core/gba/bios.s deleted file mode 100644 index 2380bcd2..00000000 --- a/src/nostalgia/core/gba/bios.s +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright 2016 - 2020 gary@drinkingtea.net -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// - -.section .iwram, "ax", %progbits -.thumb -.align - -.global nostalgia_core_halt -.type nostalgia_core_halt, %function -nostalgia_core_halt: - swi 2 - bx lr - -.global nostalgia_core_stop -.type nostalgia_core_stop, %function -nostalgia_core_stop: - swi 3 - bx lr - -.global nostalgia_core_intrwait -.type nostalgia_core_intrwait, %function -nostalgia_core_intrwait: - swi 4 - bx lr - -.global nostalgia_core_vblankintrwait -.type nostalgia_core_vblankintrwait, %function -nostalgia_core_vblankintrwait: - swi 5 - bx lr - -// vim: ft=armv4 diff --git a/src/nostalgia/core/gba/core.arm.cpp b/src/nostalgia/core/gba/core.arm.cpp deleted file mode 100644 index 4e8d3720..00000000 --- a/src/nostalgia/core/gba/core.arm.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include -#include - -#include "addresses.hpp" -#include "context.hpp" -#include "bios.hpp" -#include "irq.hpp" -#include "core.hpp" - -namespace nostalgia::core { - -extern volatile gba_timer_t g_timerMs; -gba_timer_t g_wakeupTime; -UpdateHandler g_updateHandler = nullptr; - -ox::Error run(Context *ctx) noexcept { - g_wakeupTime = 0; - const auto gbaCtx = static_cast(ctx); - while (gbaCtx->running) { - if (g_wakeupTime <= g_timerMs && g_updateHandler) { - auto sleepTime = g_updateHandler(ctx); - if (sleepTime >= 0) { - g_wakeupTime = g_timerMs + static_cast(sleepTime); - } else { - g_wakeupTime = ~gba_timer_t(0); - } - } - if constexpr(config::GbaEventLoopTimerBased) { - // wait for timer interrupt - nostalgia_core_intrwait(0, Int_timer0 | Int_timer1 | Int_timer2 | Int_timer3); - } else { - nostalgia_core_vblankintrwait(); - } - } - return OxError(0); -} - -} diff --git a/src/nostalgia/core/gba/core.cpp b/src/nostalgia/core/gba/core.cpp index b3daf23a..6280fe3a 100644 --- a/src/nostalgia/core/gba/core.cpp +++ b/src/nostalgia/core/gba/core.cpp @@ -1,86 +1,21 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include -#include -#include +#include "../context.hpp" +#include "../gfx.hpp" -#include "addresses.hpp" -#include "bios.hpp" -#include "irq.hpp" -#include "context.hpp" -#include "core.hpp" - -extern "C" void isr(); +#include "../core.hpp" namespace nostalgia::core { -// Timer Consts -constexpr int NanoSecond = 1000000000; -constexpr int MilliSecond = 1000; -constexpr int TicksMs59ns = 65535 - (NanoSecond / MilliSecond) / 59.59; +ox::Error initGfx(Context *ctx, const InitParams&) noexcept; -extern UpdateHandler g_updateHandler; - -extern volatile gba_timer_t g_timerMs; - -static void initIrq() noexcept { - REG_ISR = isr; - REG_IME = 1; // enable interrupts -} - -static void initTimer() noexcept { - // make timer0 a ~1 millisecond timer - REG_TIMER0 = TicksMs59ns; - REG_TIMER0CTL = 0b11000000; - // enable interrupt for timer0 - REG_IE = REG_IE | Int_timer0; -} - -static ox::Result findPreloadSection() noexcept { - // put the header in the wrong order to prevent mistaking this code for the - // media section - constexpr auto headerP2 = "DER_____________"; - constexpr auto headerP1 = "KEEL_PRELOAD_HEA"; - constexpr auto headerP1Len = ox_strlen(headerP2); - constexpr auto headerP2Len = ox_strlen(headerP1); - constexpr auto headerLen = headerP1Len + headerP2Len; - for (auto current = MEM_ROM; current < reinterpret_cast(0x0a000000); current += headerLen) { - if (ox_memcmp(current, headerP1, headerP1Len) == 0 && - ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) { - return reinterpret_cast(current + headerLen); - } - } - return OxError(1); -} - -ox::Result> init(ox::UniquePtr fs, ox::CRStringView appName) noexcept { - ox::UPtr ctx(ox::make()); - ctx->rom = std::move(fs); - ctx->appName = appName; - oxReturnError(initGfx(ctx.get())); - initTimer(); - initIrq(); - oxReturnError(findPreloadSection().moveTo(&ctx->preloadSectionOffset)); +ox::Result> init(turbine::Context *tctx, const InitParams ¶ms) noexcept { + ox::UPtr ctx = ox::make_unique(); + ctx->turbineCtx = tctx; + oxReturnError(initGfx(ctx.get(), params)); return ctx; } -void setUpdateHandler(Context*, UpdateHandler h) noexcept { - g_updateHandler = h; -} - -uint64_t ticksMs(Context*) noexcept { - return g_timerMs; -} - -bool buttonDown(Context*, Key k) noexcept { - return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast(k))); -} - -void shutdown(Context *ctx) noexcept { - const auto gbaCtx = static_cast(ctx); - gbaCtx->running = false; -} - -} +} \ No newline at end of file diff --git a/src/nostalgia/core/gba/core.hpp b/src/nostalgia/core/gba/core.hpp deleted file mode 100644 index 0b27def7..00000000 --- a/src/nostalgia/core/gba/core.hpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -#include - -#include - -namespace nostalgia::core { - -using gba_timer_t = ox::Uint; - -} \ No newline at end of file diff --git a/src/nostalgia/core/gba/gfx.cpp b/src/nostalgia/core/gba/gfx.cpp index a3e80f99..4fc861d7 100644 --- a/src/nostalgia/core/gba/gfx.cpp +++ b/src/nostalgia/core/gba/gfx.cpp @@ -6,27 +6,26 @@ #include #include +#include +#include +#include + #include +#include + #include #include -#include "addresses.hpp" -#include "bios.hpp" -#include "gfx.hpp" -#include "irq.hpp" -#include "registers.hpp" - namespace nostalgia::core { +struct BgCbbData { + unsigned bpp = 4; +}; static ox::Array g_cbbData; constexpr auto GbaTileColumns = 32; constexpr auto GbaTileRows = 32; -constexpr uint16_t DispStat_irq_vblank = 1 << 3; -constexpr uint16_t DispStat_irq_hblank = 1 << 4; -constexpr uint16_t DispStat_irq_vcount = 1 << 5; - struct GbaPaletteTarget { static constexpr auto TypeName = Palette::TypeName; static constexpr auto TypeVersion = Palette::TypeVersion; @@ -68,40 +67,14 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) return io->template field("tileMap", handleTileMap); } -ox::Error initGfx(Context*) noexcept { - REG_DISPCTL = DispCtl_Mode0 - | DispCtl_SpriteMap1D - | DispCtl_Obj; - // tell display to trigger vblank interrupts - REG_DISPSTAT = REG_DISPSTAT | DispStat_irq_vblank; - // enable vblank interrupt - REG_IE = REG_IE | Int_vblank; +ox::Error initGfx(Context*, const InitParams&) noexcept { for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) { - bgSetSbb(bgCtl, 28); + teagba::bgSetSbb(bgCtl, 28); } return {}; } -ox::Error shutdownGfx(Context*) noexcept { - return {}; -} - -void setWindowTitle(Context*, ox::CRStringView) noexcept { -} - -void focusWindow(Context*) noexcept { -} - -int getScreenWidth(Context*) noexcept { - return 240; -} - -int getScreenHeight(Context*) noexcept { - return 160; -} - -ox::Size getScreenSize(Context*) noexcept { - return {240, 160}; +void shutdownGfx(Context*) noexcept { } uint8_t bgStatus(Context*) noexcept { @@ -129,30 +102,35 @@ ox::Error initConsole(Context *ctx) noexcept { constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal"); setBgStatus(ctx, 0b0001); if (!ctx) { - ctx = new (ox_alloca(sizeof(Context))) Context(); + ctx = new(ox_alloca(sizeof(Context))) Context; oxRequire(rom, keel::loadRom()); ox::FileStore32 fs(rom, 32 * ox::units::MB); - auto romFs = new (ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs); - new (&ctx->rom) ox::UniquePtr(romFs); + auto romFs = new(ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs); + oxRequireM(tctx, turbine::init(ox::UPtr(romFs))); + ctx->turbineCtx = tctx.release(); + oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr)); + setBgCbb(ctx, 0, 0); + return {}; + } else { + oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr)); + setBgCbb(ctx, 0, 0); + return {}; } - oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr)); - setBgCbb(ctx, 0, 0); - return {}; } void setBgCbb(Context*, unsigned bgIdx, unsigned cbb) noexcept { auto &bgCtl = regBgCtl(bgIdx); const auto &cbbData = g_cbbData[cbb]; - bgSetBpp(&bgCtl, cbbData.bpp); - bgSetCbb(&bgCtl, cbb); + teagba::bgSetBpp(&bgCtl, cbbData.bpp); + teagba::bgSetCbb(&bgCtl, cbb); } ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheetAddr, const ox::FileAddress &paletteAddr) noexcept { - oxRequire(tsStat, ctx->rom->stat(tilesheetAddr)); - oxRequire(ts, static_cast(ctx->rom.get())->directAccess(tilesheetAddr)); + oxRequire(tsStat, ctx->rom().stat(tilesheetAddr)); + oxRequire(ts, static_cast(&ctx->rom())->directAccess(tilesheetAddr)); GbaTileMapTarget target; target.pal.palette = MEM_BG_PALETTE; target.cbbData = &g_cbbData[cbb]; @@ -160,15 +138,15 @@ ox::Error loadBgTileSheet(Context *ctx, oxReturnError(ox::readMC(ts, tsStat.size, &target)); // load external palette if available if (paletteAddr) { - oxRequire(palStat, ctx->rom->stat(paletteAddr)); - oxRequire(pal, static_cast(ctx->rom.get())->directAccess(paletteAddr)); + oxRequire(palStat, ctx->rom().stat(paletteAddr)); + oxRequire(pal, static_cast(&ctx->rom())->directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); } // update bpp of all bgs with the updated cbb const auto bpp = g_cbbData[cbb].bpp; - iterateBgCtl([bpp, cbb](auto bgCtl) { - if (bgCbb(bgCtl) == cbb) { - bgSetBpp(bgCtl, bpp); + teagba::iterateBgCtl([bpp, cbb](auto bgCtl) { + if (teagba::bgCbb(bgCtl) == cbb) { + teagba::bgSetBpp(bgCtl, bpp); } }); return {}; @@ -177,16 +155,16 @@ ox::Error loadBgTileSheet(Context *ctx, ox::Error loadSpriteTileSheet(Context *ctx, const ox::FileAddress &tilesheetAddr, const ox::FileAddress &paletteAddr) noexcept { - oxRequire(tsStat, ctx->rom->stat(tilesheetAddr)); - oxRequire(ts, static_cast(ctx->rom.get())->directAccess(tilesheetAddr)); + oxRequire(tsStat, ctx->rom().stat(tilesheetAddr)); + oxRequire(ts, static_cast(&ctx->rom())->directAccess(tilesheetAddr)); GbaTileMapTarget target; target.pal.palette = MEM_SPRITE_PALETTE; target.tileMap = MEM_SPRITE_TILES; oxReturnError(ox::readMC(ts, tsStat.size, &target)); // load external palette if available if (paletteAddr) { - oxRequire(palStat, ctx->rom->stat(paletteAddr)); - oxRequire(pal, static_cast(ctx->rom.get())->directAccess(paletteAddr)); + oxRequire(palStat, ctx->rom().stat(paletteAddr)); + oxRequire(pal, static_cast(&ctx->rom())->directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); } return {}; @@ -195,8 +173,8 @@ ox::Error loadSpriteTileSheet(Context *ctx, ox::Error loadBgPalette(Context *ctx, unsigned, const ox::FileAddress &paletteAddr) noexcept { GbaPaletteTarget target; target.palette = MEM_BG_PALETTE; - oxRequire(palStat, ctx->rom->stat(paletteAddr)); - oxRequire(pal, static_cast(ctx->rom.get())->directAccess(paletteAddr)); + oxRequire(palStat, ctx->rom().stat(paletteAddr)); + oxRequire(pal, static_cast(&ctx->rom())->directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, palStat.size, &target)); return {}; } @@ -204,13 +182,12 @@ ox::Error loadBgPalette(Context *ctx, unsigned, const ox::FileAddress &paletteAd ox::Error loadSpritePalette(Context *ctx, unsigned cbb, const ox::FileAddress &paletteAddr) noexcept { GbaPaletteTarget target; target.palette = &MEM_SPRITE_PALETTE[cbb]; - oxRequire(palStat, ctx->rom->stat(paletteAddr)); - oxRequire(pal, static_cast(ctx->rom.get())->directAccess(paletteAddr)); + oxRequire(palStat, ctx->rom().stat(paletteAddr)); + oxRequire(pal, static_cast(&ctx->rom())->directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, palStat.size, &target)); return {}; } -// Do NOT use Context in the GBA version of this function. void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept { const auto col = static_cast(column); for (auto i = 0u; i < str.bytes(); i++) { @@ -231,25 +208,11 @@ void clearTileLayer(Context*, unsigned bgIdx) noexcept { [[maybe_unused]] void hideSprite(Context*, unsigned idx) noexcept { - oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); - GbaSpriteAttrUpdate oa; + //oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); + teagba::GbaSpriteAttrUpdate oa; oa.attr0 = 2 << 8; oa.idx = idx; - // block until g_spriteUpdates is less than buffer len - if (g_spriteUpdates >= config::GbaSpriteBufferLen) [[unlikely]] { - nostalgia_core_vblankintrwait(); - } - if constexpr(config::GbaEventLoopTimerBased) { - REG_IE = REG_IE & ~Int_vblank; // disable vblank interrupt handler - g_spriteBuffer[g_spriteUpdates] = oa; - REG_IE = REG_IE | Int_vblank; // enable vblank interrupt handler - } else { - const auto ie = REG_IE; // disable vblank interrupt handler - REG_IE = REG_IE & static_cast(~Int_vblank); // disable vblank interrupt handler - g_spriteBuffer[g_spriteUpdates] = oa; - REG_IE = ie; // enable vblank interrupt handler - } - g_spriteUpdates = g_spriteUpdates + 1; + teagba::addSpriteUpdate(oa); } void setSprite(Context*, @@ -260,8 +223,8 @@ void setSprite(Context*, unsigned spriteShape, unsigned spriteSize, unsigned flipX) noexcept { - oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); - GbaSpriteAttrUpdate oa; + //oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); + teagba::GbaSpriteAttrUpdate oa; oa.attr0 = static_cast(y & ox::onMask(0b111'1111)) | (static_cast(1) << 10) // enable alpha | (static_cast(spriteShape) << 14); @@ -270,21 +233,7 @@ void setSprite(Context*, | (static_cast(spriteSize) << 14); oa.attr2 = static_cast(tileIdx & ox::onMask(8)); oa.idx = idx; - // block until g_spriteUpdates is less than buffer len - if (g_spriteUpdates >= config::GbaSpriteBufferLen) [[unlikely]] { - nostalgia_core_vblankintrwait(); - } - if constexpr(config::GbaEventLoopTimerBased) { - REG_IE = REG_IE & ~Int_vblank; // disable vblank interrupt handler - g_spriteBuffer[g_spriteUpdates] = oa; - REG_IE = REG_IE | Int_vblank; // enable vblank interrupt handler - } else { - const auto ie = REG_IE; // disable vblank interrupt handler - REG_IE = REG_IE & static_cast(~Int_vblank); // disable vblank interrupt handler - g_spriteBuffer[g_spriteUpdates] = oa; - REG_IE = ie; // enable vblank interrupt handler - } - g_spriteUpdates = g_spriteUpdates + 1; + teagba::addSpriteUpdate(oa); } } diff --git a/src/nostalgia/core/gba/gfx.hpp b/src/nostalgia/core/gba/gfx.hpp index 54b9ca08..1e26ff6e 100644 --- a/src/nostalgia/core/gba/gfx.hpp +++ b/src/nostalgia/core/gba/gfx.hpp @@ -1,43 +1,11 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once -#include -#include - -#include +#include "../context.hpp" namespace nostalgia::core { - -enum DispCtl { - DispCtl_Mode0 = 0, - DispCtl_Mode1 = 1, - DispCtl_Mode2 = 2, - DispCtl_Mode3 = 3, - DispCtl_Mode4 = 4, - DispCtl_Mode5 = 5, - - DispCtl_SpriteMap1D = 1 << 6, - - DispCtl_Bg0 = 1 << 8, - DispCtl_Bg1 = 1 << 9, - DispCtl_Bg2 = 1 << 10, - DispCtl_Bg3 = 1 << 11, - - DispCtl_Obj = 1 << 12, -}; - -struct OX_ALIGN8 GbaSpriteAttrUpdate { - uint16_t attr0 = 0; - uint16_t attr1 = 0; - uint16_t attr2 = 0; - uint16_t idx = 0; - -}; - -extern volatile uint16_t g_spriteUpdates; -extern ox::Array g_spriteBuffer; - +ox::Error initGfx(Context *ctx, const InitParams &) noexcept; } diff --git a/src/nostalgia/core/gba/irq.arm.cpp b/src/nostalgia/core/gba/irq.arm.cpp deleted file mode 100644 index 57e937a5..00000000 --- a/src/nostalgia/core/gba/irq.arm.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -// NOTE: this file is compiled as ARM and not THUMB, so don't but too much in -// here - -#include "addresses.hpp" -#include "core.hpp" -#include "gfx.hpp" -#include "irq.hpp" - -namespace nostalgia::core { - -volatile uint16_t g_spriteUpdates = 0; -ox::Array g_spriteBuffer; - -volatile gba_timer_t g_timerMs = 0; - -} - -using namespace nostalgia::core; - -extern "C" { - -void nostalgia_core_isr_vblank() { - // copy g_spriteUpdates to allow it to use a register instead of reading - // from memory every iteration of the loop, needed because g_spriteUpdates - // is volatile - const unsigned updates = g_spriteUpdates; - for (unsigned i = 0; i < updates; ++i) { - const auto &oa = g_spriteBuffer[i]; - MEM_OAM[oa.idx] = *reinterpret_cast(&oa); - } - g_spriteUpdates = 0; - if constexpr(config::GbaEventLoopTimerBased) { - // disable vblank interrupt until it is needed again - REG_IE = REG_IE & ~Int_vblank; - } -} - -void nostalgia_core_isr_timer0() { - g_timerMs = g_timerMs + 1; -} - -} diff --git a/src/nostalgia/core/gba/panic.cpp b/src/nostalgia/core/gba/panic.cpp index 60938753..329b813f 100644 --- a/src/nostalgia/core/gba/panic.cpp +++ b/src/nostalgia/core/gba/panic.cpp @@ -7,32 +7,43 @@ #include #include -#include "addresses.hpp" -#include "bios.hpp" +#include +#include +#include + #include "gfx.hpp" namespace ox { using namespace nostalgia::core; -void panic(const char*, int, const char *msg, const ox::Error &err) noexcept { - oxIgnoreError(initGfx(nullptr)); +void panic(const char *file, int line, const char *panicMsg, const ox::Error &err) noexcept { + oxIgnoreError(initGfx(nullptr, {})); oxIgnoreError(initConsole(nullptr)); // enable only BG 0 - REG_DISPCTL = DispCtl_Bg0; + REG_DISPCTL = teagba::DispCtl_Bg0; clearTileLayer(nullptr, 0); ox::BString<23> serr = "Error code: "; serr += static_cast(err); puts(nullptr, 32 + 1, 1, "SADNESS..."); puts(nullptr, 32 + 1, 4, "UNEXPECTED STATE:"); - puts(nullptr, 32 + 2, 6, msg); + puts(nullptr, 32 + 2, 6, panicMsg); if (err) { puts(nullptr, 32 + 2, 8, serr); } puts(nullptr, 32 + 1, 15, "PLEASE RESTART THE SYSTEM"); + // print to terminal if in mGBA + oxErrf("\033[31;1;1mPANIC:\033[0m [{}:{}]: {}\n", file, line, panicMsg); + if (err.msg) { + oxErrf("\tError Message:\t{}\n", err.msg); + } + oxErrf("\tError Code:\t{}\n", static_cast(err)); + if (err.file != nullptr) { + oxErrf("\tError Location:\t{}:{}\n", err.file, err.line); + } // disable all interrupt handling and IntrWait on no interrupts REG_IE = 0; - nostalgia_core_intrwait(0, 0); + teagba_intrwait(0, 0); } } diff --git a/src/nostalgia/core/gba/tests.cpp b/src/nostalgia/core/gba/tests.cpp deleted file mode 100644 index f29ad9b4..00000000 --- a/src/nostalgia/core/gba/tests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include -#include - -#include - -namespace ox::heapmgr { - -[[nodiscard]] -void *malloc(std::size_t allocSize) noexcept; - -void free(void *ptr) noexcept; - -void initHeap(char *heapBegin, char *heapEnd) noexcept; - -} - -std::map tests = { -}; - -int main(int argc, const char **args) { - int retval = -1; - if (argc > 1) { - auto testName = args[1]; - std::string testArg = ""; - if (args[2]) { - testArg = args[2]; - } - if (tests.find(testName) != tests.end()) { - retval = tests[testName](testArg); - } - } - return retval; -} - diff --git a/src/nostalgia/core/gfx.cpp b/src/nostalgia/core/gfx.cpp index 7ad75c8a..7d4719bf 100644 --- a/src/nostalgia/core/gfx.cpp +++ b/src/nostalgia/core/gfx.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include "gfx.hpp" @@ -137,19 +137,6 @@ ox::Array charMap = { 0, // ~ }; -void addCustomDrawer(Context *ctx, Drawer *cd) noexcept { - ctx->drawers.emplace_back(cd); -} - -void removeCustomDrawer(Context *ctx, Drawer *cd) noexcept { - for (auto i = 0u; i < ctx->drawers.size(); ++i) { - if (ctx->drawers[i] == cd) { - oxIgnoreError(ctx->drawers.erase(i)); - break; - } - } -} - void setSprite(Context *c, const Sprite &s) noexcept { setSprite(c, s.idx, s.x, s.y, s.tileIdx, s.spriteShape, s.spriteSize, s.flipX); } diff --git a/src/nostalgia/core/gfx.hpp b/src/nostalgia/core/gfx.hpp index 1eca9bad..0620337d 100644 --- a/src/nostalgia/core/gfx.hpp +++ b/src/nostalgia/core/gfx.hpp @@ -19,13 +19,6 @@ namespace nostalgia::core { extern ox::Array charMap; -class Drawer { - public: - virtual ~Drawer() = default; - - virtual void draw(Context*) noexcept = 0; -}; - enum class TileSheetSpace { Background, Sprite @@ -51,25 +44,6 @@ oxModelBegin(Sprite) oxModelField(flipX) oxModelEnd() -ox::Error initGfx(Context *ctx) noexcept; - -void addCustomDrawer(Context *ctx, Drawer *cd) noexcept; - -void removeCustomDrawer(Context *ctx, Drawer *cd) noexcept; - -void setWindowTitle(Context *ctx, ox::CRStringView title) noexcept; - -void focusWindow(Context *ctx) noexcept; - -[[nodiscard]] -int getScreenWidth(Context *ctx) noexcept; - -[[nodiscard]] -int getScreenHeight(Context *ctx) noexcept; - -[[nodiscard]] -ox::Size getScreenSize(Context *ctx) noexcept; - [[nodiscard]] uint8_t bgStatus(Context *ctx) noexcept; diff --git a/src/nostalgia/core/glfw/clipboard.cpp b/src/nostalgia/core/glfw/clipboard.cpp deleted file mode 100644 index 259d7907..00000000 --- a/src/nostalgia/core/glfw/clipboard.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include - -#include - -#include "core.hpp" - -namespace nostalgia::core { - -ox::String getClipboardText(Context *ctx) noexcept { - const auto id = ctx->windowerData(); - return glfwGetClipboardString(id->window); -} - -void setClipboardText(Context *ctx, ox::CRStringView text) noexcept { - const auto id = ctx->windowerData(); - auto cstr = ox_malloca(text.bytes() + 1, char); - ox_strncpy(cstr.get(), text.data(), text.bytes()); - glfwSetClipboardString(id->window, cstr); -} - -} diff --git a/src/nostalgia/core/glfw/core.cpp b/src/nostalgia/core/glfw/core.cpp deleted file mode 100644 index ac2bc62d..00000000 --- a/src/nostalgia/core/glfw/core.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include -#include -#include - -#include "core.hpp" - -namespace nostalgia::core { - -ox::Result> init(ox::UPtr fs, ox::CRStringView appName) noexcept { - oxRequireM(ctx, keel::init(std::move(fs), appName)); - const auto id = ox::make(); - ctx->setWindowerData(id); - using namespace std::chrono; - id->startTime = duration_cast(system_clock::now().time_since_epoch()).count(); - glfwInit(); - oxReturnError(initGfx(ctx.get())); - return std::move(ctx); -} - -ox::Error run(Context *ctx) noexcept { - const auto id = ctx->windowerData(); - int sleepTime = 0; - while (!glfwWindowShouldClose(id->window)) { - glfwPollEvents(); - const auto ticks = ticksMs(ctx); - if (id->wakeupTime <= ticks) { - sleepTime = id->eventHandler(ctx); - if (sleepTime >= 0) { - id->wakeupTime = ticks + static_cast(sleepTime); - } else { - id->wakeupTime = ~uint64_t(0); - } - } else { - sleepTime = 10; - } - draw(ctx); - glfwSwapBuffers(id->window); - if (!ctx->constantRefresh) { - if (ctx->uninterruptedRefreshes) { - --ctx->uninterruptedRefreshes; - } else { - glfwWaitEventsTimeout(sleepTime); - } - } - } - // destroy GLFW window - renderer::shutdown(ctx, ctx->rendererData()); - glfwDestroyWindow(id->window); - ctx->setWindowerData(nullptr); - ox::safeDelete(id); - return OxError(0); -} - -void setUpdateHandler(Context *ctx, UpdateHandler h) noexcept { - const auto id = ctx->windowerData(); - id->eventHandler = h; -} - -uint64_t ticksMs(Context *ctx) noexcept { - using namespace std::chrono; - const auto id = ctx->windowerData(); - const auto now = duration_cast(system_clock::now().time_since_epoch()).count(); - return static_cast(now - id->startTime); -} - -bool buttonDown(Context *ctx, Key key) noexcept { - const auto id = ctx->windowerData(); - return (id->keysDown >> static_cast(key)) & 1; -} - -void shutdown(Context *ctx) noexcept { - const auto id = ctx->windowerData(); - glfwSetWindowShouldClose(id->window, true); -} - -} diff --git a/src/nostalgia/core/glfw/core.hpp b/src/nostalgia/core/glfw/core.hpp deleted file mode 100644 index b7edaa92..00000000 --- a/src/nostalgia/core/glfw/core.hpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -#include - -namespace nostalgia::core { - -struct GlfwImplData { - struct GLFWwindow *window = nullptr; - int64_t startTime = 0; - UpdateHandler eventHandler = [](Context*) -> int {return 0;}; - KeyEventHandler keyEventHandler = nullptr; - uint64_t wakeupTime = 0; - uint64_t keysDown = 0; -}; - -} diff --git a/src/nostalgia/core/initparams.hpp b/src/nostalgia/core/initparams.hpp new file mode 100644 index 00000000..a43ea28c --- /dev/null +++ b/src/nostalgia/core/initparams.hpp @@ -0,0 +1,13 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +namespace nostalgia::core { + +struct InitParams { + bool glInstallDrawer = true; +}; + +} \ No newline at end of file diff --git a/src/nostalgia/core/opengl/CMakeLists.txt b/src/nostalgia/core/opengl/CMakeLists.txt index adbcaacf..c6558664 100644 --- a/src/nostalgia/core/opengl/CMakeLists.txt +++ b/src/nostalgia/core/opengl/CMakeLists.txt @@ -1,7 +1,7 @@ target_sources( NostalgiaCore PRIVATE + core.cpp gfx.cpp - gfx_opengl.cpp ) target_link_libraries( NostalgiaCore PUBLIC diff --git a/src/nostalgia/core/opengl/context.hpp b/src/nostalgia/core/opengl/context.hpp new file mode 100644 index 00000000..dfe0b374 --- /dev/null +++ b/src/nostalgia/core/opengl/context.hpp @@ -0,0 +1,26 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include + +#include "gfx.hpp" + +namespace nostalgia::core { + +struct GlContext: public core::Context { + glutils::GLProgram bgShader; + glutils::GLProgram spriteShader; + ox::Array cbbs; + renderer::SpriteBlockset spriteBlocks; + ox::Array spriteStates; + ox::Array backgrounds; + ox::Optional renderSize; + renderer::Drawer drawer; +}; + +} diff --git a/src/nostalgia/core/opengl/core.cpp b/src/nostalgia/core/opengl/core.cpp new file mode 100644 index 00000000..62f82717 --- /dev/null +++ b/src/nostalgia/core/opengl/core.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include + +#include + +#include "context.hpp" + +namespace nostalgia::core { + +ox::Result> init(turbine::Context *tctx, const InitParams ¶ms) noexcept { + ox::UPtr ctx = ox::make_unique(); + ctx->turbineCtx = tctx; + oxReturnError(initGfx(ctx.get(), params)); + return ctx; +} + +} \ No newline at end of file diff --git a/src/nostalgia/core/opengl/gfx.cpp b/src/nostalgia/core/opengl/gfx.cpp index 7e0c6555..e11942a4 100644 --- a/src/nostalgia/core/opengl/gfx.cpp +++ b/src/nostalgia/core/opengl/gfx.cpp @@ -2,13 +2,337 @@ * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include +#include + +#include +#include +#include + #include -#include "gfx.hpp" +#include + +#include +#include + +#include "context.hpp" namespace nostalgia::core { +void draw(Context *ctx) noexcept; + +namespace renderer { + +void Drawer::draw(turbine::Context&) noexcept { + core::draw(m_ctx); +} + +constexpr ox::StringView bgvshadTmpl = R"( + {} + in vec2 vTexCoord; + in vec2 vPosition; + out vec2 fTexCoord; + uniform float vXScale; + uniform float vTileHeight; + void main() { + float xScaleInvert = 1.0 - vXScale; + gl_Position = vec4( + vPosition.x * vXScale - xScaleInvert, vPosition.y, + 0.0, 1.0); + fTexCoord = vTexCoord * vec2(1, vTileHeight); + })"; + +constexpr ox::StringView bgfshadTmpl = R"( + {} + out vec4 outColor; + in vec2 fTexCoord; + uniform sampler2D image; + uniform vec4 fPalette[256]; + void main() { + int idx = int(texture(image, fTexCoord).rgb.r * 256); + outColor = fPalette[idx]; + //outColor = vec4(0.0, 0.7, 1.0, 1.0); + })"; + +constexpr ox::StringView spritevshadTmpl = R"( + {} + in float vEnabled; + in vec2 vTexCoord; + in vec2 vPosition; + out vec2 fTexCoord; + uniform float vXScale; + uniform float vTileHeight; + void main() { + float xScaleInvert = 1.0 - vXScale; + gl_Position = vec4( + vPosition.x * vXScale - xScaleInvert, vPosition.y, + 0.0, 1.0); + fTexCoord = vTexCoord * vec2(1, vTileHeight) * vec2(vEnabled, vEnabled); + })"; + +constexpr ox::StringView spritefshadTmpl = bgfshadTmpl; + +[[nodiscard]] +static constexpr auto bgVertexRow(unsigned x, unsigned y) noexcept { + return y * TileRows + x; +} + +static void setSpriteBufferObject(Context*, + unsigned vi, + float enabled, + float x, float y, + unsigned textureRow, + unsigned flipX, + float *vbo, + GLuint *ebo) noexcept { + // don't worry, this memcpy gets optimized to something much more ideal + constexpr float xmod = 0.1f; + constexpr float ymod = 0.1f; + x *= xmod; + y *= -ymod; + x -= 1.f; + y += 1.f - ymod; + const auto textureRowf = static_cast(textureRow); + const float L = flipX ? 1 : 0; + const float R = flipX ? 0 : 1; + const ox::Array vertices { + enabled, x, y, L, textureRowf + 1, // bottom left + enabled, x + xmod, y, R, textureRowf + 1, // bottom right + enabled, x + xmod, y + ymod, R, textureRowf + 0, // top right + enabled, x, y + ymod, L, textureRowf + 0, // top left + }; + memcpy(vbo, vertices.data(), sizeof(vertices)); + const ox::Array elms { + vi + 0, vi + 1, vi + 2, + vi + 2, vi + 3, vi + 0, + }; + memcpy(ebo, elms.data(), sizeof(elms)); +} + +static void setTileBufferObject(Context*, + unsigned vi, + float x, + float y, + unsigned textureRow, + float *vbo, + GLuint *ebo) noexcept { + // don't worry, this memcpy gets optimized to something much more ideal + constexpr float ymod = 0.1f; + constexpr float xmod = 0.1f; + x *= xmod; + y *= -ymod; + x -= 1.0f; + y += 1.0f - ymod; + const auto textureRowf = static_cast(textureRow); + const ox::Array vertices { + x, y, 0, textureRowf + 1, // bottom left + x + xmod, y, 1, textureRowf + 1, // bottom right + x + xmod, y + ymod, 1, textureRowf + 0, // top right + x, y + ymod, 0, textureRowf + 0, // top left + }; + memcpy(vbo, vertices.data(), sizeof(vertices)); + const ox::Array elms { + vi + 0, vi + 1, vi + 2, + vi + 2, vi + 3, vi + 0, + }; + memcpy(ebo, elms.data(), sizeof(elms)); +} + +static void initSpriteBufferObjects(Context *ctx, glutils::BufferSet *bs) noexcept { + for (auto i = 0u; i < SpriteCount; ++i) { + auto vbo = &bs->vertices[i * static_cast(SpriteVertexVboLength)]; + auto ebo = &bs->elements[i * static_cast(SpriteVertexEboLength)]; + setSpriteBufferObject(ctx, i * SpriteVertexVboRows, 0, 0, 0, 0, false, vbo, ebo); + } +} + +static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept { + for (auto x = 0u; x < TileColumns; ++x) { + for (auto y = 0u; y < TileRows; ++y) { + const auto i = bgVertexRow(x, y); + auto vbo = &bg->vertices[i * static_cast(BgVertexVboLength)]; + auto ebo = &bg->elements[i * static_cast(BgVertexEboLength)]; + setTileBufferObject(ctx, i * BgVertexVboRows, static_cast(x), static_cast(y), 0, vbo, ebo); + } + } +} + +static void initSpritesBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bs) noexcept { + // vao + bs->vao = glutils::generateVertexArrayObject(); + glBindVertexArray(bs->vao); + // vbo & ebo + bs->vbo = glutils::generateBuffer(); + bs->ebo = glutils::generateBuffer(); + initSpriteBufferObjects(ctx, bs); + glutils::sendVbo(*bs); + glutils::sendEbo(*bs); + // vbo layout + auto enabledAttr = static_cast(glGetAttribLocation(shader, "vEnabled")); + glEnableVertexAttribArray(enabledAttr); + glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr); + auto posAttr = static_cast(glGetAttribLocation(shader, "vPosition")); + glEnableVertexAttribArray(posAttr); + glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), + reinterpret_cast(1 * sizeof(float))); + auto texCoordAttr = static_cast(glGetAttribLocation(shader, "vTexCoord")); + glEnableVertexAttribArray(texCoordAttr); + glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), + reinterpret_cast(3 * sizeof(float))); +} + +static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bg) noexcept { + // vao + bg->vao = glutils::generateVertexArrayObject(); + glBindVertexArray(bg->vao); + // vbo & ebo + bg->vbo = glutils::generateBuffer(); + bg->ebo = glutils::generateBuffer(); + initBackgroundBufferObjects(ctx, bg); + glutils::sendVbo(*bg); + glutils::sendEbo(*bg); + // vbo layout + auto posAttr = static_cast(glGetAttribLocation(shader, "vPosition")); + glEnableVertexAttribArray(posAttr); + glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), nullptr); + auto texCoordAttr = static_cast(glGetAttribLocation(shader, "vTexCoord")); + glEnableVertexAttribArray(texCoordAttr); + glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), + reinterpret_cast(2 * sizeof(float))); +} + +static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, const void *pixels) noexcept { + GLuint texId = 0; + glGenTextures(1, &texId); + glutils::GLTexture tex(texId); + tex.width = w; + tex.height = h; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex.id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return tex; +} + +static void drawBackground(CBB *cbb) noexcept { + glBindVertexArray(cbb->vao); + if (cbb->updated) { + cbb->updated = false; + glutils::sendVbo(*cbb); + } + glBindTexture(GL_TEXTURE_2D, cbb->tex); + glDrawElements(GL_TRIANGLES, static_cast(cbb->elements.size()), GL_UNSIGNED_INT, nullptr); +} + +static void drawBackgrounds(GlContext *gctx) noexcept { + // load background shader and its uniforms + glUseProgram(gctx->bgShader); + const auto uniformXScale = static_cast(glGetUniformLocation(gctx->bgShader, "vXScale")); + const auto uniformTileHeight = static_cast(glGetUniformLocation(gctx->bgShader, "vTileHeight")); + const auto [wi, hi] = gl::getRenderSize(gctx); + const auto wf = static_cast(wi); + const auto hf = static_cast(hi); + glUniform1f(uniformXScale, hf / wf); + for (const auto &bg : gctx->backgrounds) { + if (bg.enabled) { + auto &cbb = gctx->cbbs[bg.cbbIdx]; + const auto tileRows = cbb.tex.height / TileHeight; + glUniform1f(uniformTileHeight, 1.0f / static_cast(tileRows)); + drawBackground(&cbb); + } + } +} + +static void drawSprites(GlContext *gctx) noexcept { + glUseProgram(gctx->spriteShader); + auto &sb = gctx->spriteBlocks; + const auto uniformXScale = static_cast(glGetUniformLocation(gctx->bgShader, "vXScale")); + const auto uniformTileHeight = static_cast(glGetUniformLocation(gctx->spriteShader, "vTileHeight")); + const auto [wi, hi] = gl::getRenderSize(gctx); + const auto wf = static_cast(wi); + const auto hf = static_cast(hi); + glUniform1f(uniformXScale, hf / wf); + // update vbo + glBindVertexArray(sb.vao); + if (sb.updated) { + sb.updated = false; + glutils::sendVbo(sb); + } + // set vTileHeight uniform + const auto tileRows = sb.tex.height / TileHeight; + glUniform1f(uniformTileHeight, 1.0f / static_cast(tileRows)); + // draw + glBindTexture(GL_TEXTURE_2D, sb.tex); + glDrawElements(GL_TRIANGLES, static_cast(sb.elements.size()), GL_UNSIGNED_INT, nullptr); +} + +static void loadPalette(GLuint shaderPgrm, const Palette &pal, bool firstIsTransparent = false) noexcept { + static constexpr std::size_t ColorCnt = 256; + ox::Array palette{}; + for (auto i = 0u; const auto c : pal.colors) { + palette[i++] = redf(c); + palette[i++] = greenf(c); + palette[i++] = bluef(c); + palette[i++] = 255; + } + if (firstIsTransparent) { + palette[3] = 0; + } + glUseProgram(shaderPgrm); + const auto uniformPalette = static_cast(glGetUniformLocation(shaderPgrm, "fPalette")); + glUniform4fv(uniformPalette, ColorCnt, palette.data()); +} + +static void loadBgPalette(GlContext *gctx, const Palette &pal) noexcept { + loadPalette(gctx->bgShader, pal); +} + +static void loadSpritePalette(GlContext *gctx, const Palette &pal) noexcept { + loadPalette(gctx->spriteShader, pal, true); +} + +static void loadBgTexture(GlContext *gctx, unsigned cbbIdx, const void *pixels, int w, int h) noexcept { + oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h); + gctx->cbbs[cbbIdx].tex = loadTexture(w, h, pixels); +} + +static void loadSpriteTexture(GlContext *gctx, const void *pixels, int w, int h) noexcept { + oxTracef("nostalgia::core::gfx::gl", "loadSpriteTexture: { w: {}, h: {} }", w, h); + gctx->spriteBlocks.tex = loadTexture(w, h, pixels); +} + +} + +ox::Error initGfx(Context *ctx, const InitParams &initParams) noexcept { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + const auto bgVshad = ox::sfmt(renderer::bgvshadTmpl, glutils::GlslVersion); + const auto bgFshad = ox::sfmt(renderer::bgfshadTmpl, glutils::GlslVersion); + const auto spriteVshad = ox::sfmt(renderer::spritevshadTmpl, glutils::GlslVersion); + const auto spriteFshad = ox::sfmt(renderer::spritefshadTmpl, glutils::GlslVersion); + const auto gctx = static_cast(ctx); + oxReturnError(glutils::buildShaderProgram(bgVshad.c_str(), bgFshad.c_str()).moveTo(&gctx->bgShader)); + oxReturnError(glutils::buildShaderProgram(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&gctx->spriteShader)); + for (auto &bg : gctx->cbbs) { + initBackgroundBufferset(ctx, gctx->bgShader, &bg); + } + gctx->drawer.m_ctx = ctx; + if (initParams.glInstallDrawer) { + turbine::gl::addDrawer(*ctx->turbineCtx, &gctx->drawer); + initSpritesBufferset(ctx, gctx->spriteShader, &gctx->spriteBlocks); + } + ImGui_ImplOpenGL3_Init(glutils::GlslVersion); + return {}; +} + +void shutdownGfx(Context *ctx) noexcept { + const auto gctx = static_cast(ctx); + turbine::gl::removeDrawer(*ctx->turbineCtx, &gctx->drawer); +} + ox::Error initConsole(Context *ctx) noexcept { constexpr ox::FileAddress TilesheetAddr("/TileSheets/Charset.ng"); constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal"); @@ -24,6 +348,7 @@ struct TileSheetData { }; ox::Result loadTileSheet(Context *ctx, const CompactTileSheet &tilesheet) noexcept { + const auto gctx = static_cast(ctx); const unsigned bytesPerTile = tilesheet.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2; const auto tiles = tilesheet.pixels.size() / bytesPerTile; constexpr int width = 8; @@ -41,8 +366,7 @@ ox::Result loadTileSheet(Context *ctx, const CompactTileSheet &ti pixels[i * 2 + 1] = tilesheet.pixels[i] >> 4; } } - const auto rd = ctx->rendererData(); - renderer::loadSpriteTexture(rd, pixels.data(), width, height); + renderer::loadSpriteTexture(gctx, pixels.data(), width, height); return TileSheetData{std::move(pixels), width, height}; } @@ -50,24 +374,26 @@ ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheetAddr, const ox::FileAddress &paletteAddr) noexcept { - oxRequire(tilesheet, readObj(ctx, tilesheetAddr)); - oxRequire(palette, readObj(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); + auto &kctx = *ctx->turbineCtx; + const auto gctx = static_cast(ctx); + oxRequire(tilesheet, readObj(&kctx, tilesheetAddr)); + oxRequire(palette, readObj(&kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); oxRequire(tsd, loadTileSheet(ctx, *tilesheet)); - const auto rd = ctx->rendererData(); - renderer::loadBgTexture(rd, cbb, tsd.pixels.data(), tsd.width, tsd.height); - renderer::loadBgPalette(rd, *palette); + renderer::loadBgTexture(gctx, cbb, tsd.pixels.data(), tsd.width, tsd.height); + renderer::loadBgPalette(gctx, *palette); return {}; } ox::Error loadSpriteTileSheet(Context *ctx, const ox::FileAddress &tilesheetAddr, const ox::FileAddress &paletteAddr) noexcept { - oxRequire(tilesheet, readObj(ctx, tilesheetAddr)); - oxRequire(palette, readObj(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); + auto &kctx = *ctx->turbineCtx; + const auto gctx = static_cast(ctx); + oxRequire(tilesheet, readObj(&kctx, tilesheetAddr)); + oxRequire(palette, readObj(&kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); oxRequire(tsd, loadTileSheet(ctx, *tilesheet)); - const auto rd = ctx->rendererData(); - renderer::loadSpriteTexture(rd, tsd.pixels.data(), tsd.width, tsd.height); - renderer::loadSpritePalette(rd, *palette); + renderer::loadSpriteTexture(gctx, tsd.pixels.data(), tsd.width, tsd.height); + renderer::loadSpritePalette(gctx, *palette); return {}; } @@ -77,5 +403,170 @@ void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept { setTile(ctx, 0, static_cast(col + i), row, static_cast(charMap[static_cast(str[i])])); } } +void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbbIdx) noexcept { + const auto gctx = static_cast(ctx); + auto &bg = gctx->backgrounds[bgIdx]; + bg.cbbIdx = cbbIdx; +} + +uint8_t bgStatus(Context *ctx) noexcept { + const auto gctx = static_cast(ctx); + uint8_t out = 0; + for (unsigned i = 0; i < gctx->cbbs.size(); ++i) { + out |= gctx->backgrounds[i].enabled << i; + } + return out; +} + +void setBgStatus(Context *ctx, uint32_t status) noexcept { + const auto gctx = static_cast(ctx); + for (unsigned i = 0; i < gctx->cbbs.size(); ++i) { + gctx->backgrounds[i].enabled = (status >> i) & 1; + } +} + +bool bgStatus(Context *ctx, unsigned bg) noexcept { + const auto gctx = static_cast(ctx); + return gctx->backgrounds[bg].enabled; +} + +void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept { + const auto gctx = static_cast(ctx); + gctx->backgrounds[bg].enabled = status; +} + + +void draw(Context *ctx) noexcept { + gl::drawMainView(ctx); +} + +void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept { + const auto gctx = static_cast(ctx); + auto &bg = gctx->cbbs[static_cast(bgIdx)]; + initBackgroundBufferObjects(ctx, &bg); + bg.updated = true; +} + +void hideSprite(Context *ctx, unsigned idx) noexcept { + auto &gctx = *static_cast(ctx); + auto vbo = &gctx.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength]; + auto ebo = &gctx.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength]; + renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 0, + 0, 0, 0, false, vbo, ebo); + gctx.spriteBlocks.updated = true; +} + +void setSprite(Context *ctx, + unsigned idx, + int x, + int y, + unsigned tileIdx, + [[maybe_unused]] unsigned spriteShape, + [[maybe_unused]] unsigned spriteSize, + unsigned flipX) noexcept { + //oxTracef("nostalgia::core::gfx::gl", "setSprite(ctx, {}, {}, {}, {}, {}, {}, {})", + // idx, x, y, tileIdx, spriteShape, spriteSize, flipX); + // Tonc Table 8.4 + static constexpr ox::Array, 12> dimensions{ + // col 0 + {1, 1}, // 0, 0 + {2, 2}, // 0, 1 + {4, 4}, // 0, 2 + {8, 8}, // 0, 3 + // col 1 + {2, 1}, // 1, 0 + {4, 1}, // 1, 1 + {4, 2}, // 1, 2 + {8, 4}, // 1, 3 + // col 2 + {1, 1}, // 2, 0 + {1, 4}, // 2, 1 + {2, 4}, // 2, 2 + {4, 8}, // 2, 3 + }; + const auto dim = dimensions[(spriteShape << 2) | spriteSize]; + const auto uX = static_cast(x) % 255; + const auto uY = static_cast(y + 8) % 255 - 8; + auto &gctx = static_cast(*ctx); + auto i = 0u; + const auto set = [&](int xIt, int yIt) { + const auto fX = static_cast(uX + xIt * 8) / 8; + const auto fY = static_cast(uY + yIt * 8) / 8; + const auto cidx = idx + i; + auto vbo = &gctx.spriteBlocks.vertices[cidx * renderer::SpriteVertexVboLength]; + auto ebo = &gctx.spriteBlocks.elements[cidx * renderer::SpriteVertexEboLength]; + renderer::setSpriteBufferObject(ctx, cidx * renderer::SpriteVertexVboRows, 1, + fX, fY, tileIdx + i, flipX, vbo, ebo); + ++i; + }; + if (!flipX) { + for (auto yIt = 0; yIt < static_cast(dim.y); ++yIt) { + for (auto xIt = 0u; xIt < dim.x; ++xIt) { + set(static_cast(xIt), static_cast(yIt)); + } + } + } else { + for (auto yIt = 0u; yIt < dim.y; ++yIt) { + for (auto xIt = dim.x - 1; xIt < ~0u; --xIt) { + set(static_cast(xIt), static_cast(yIt)); + } + } + } + gctx.spriteBlocks.updated = true; +} + +void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { + oxTracef( + "nostalgia::core::gfx::setTile", + "bgIdx: {}, column: {}, row: {}, tile: {}", + bgIdx, column, row, tile); + const auto gctx = static_cast(ctx); + const auto z = static_cast(bgIdx); + const auto y = static_cast(row); + const auto x = static_cast(column); + const auto i = renderer::bgVertexRow(x, y); + auto &bg = gctx->cbbs[z]; + auto vbo = &bg.vertices[i * renderer::BgVertexVboLength]; + auto ebo = &bg.elements[i * renderer::BgVertexEboLength]; + renderer::setTileBufferObject( + ctx, i * renderer::BgVertexVboRows, + static_cast(x), static_cast(y), + tile, vbo, ebo); + bg.updated = true; +} + +namespace gl { + +void drawMainView(core::Context *ctx) noexcept { + // clear screen + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + const auto gctx = static_cast(ctx); + renderer::drawBackgrounds(gctx); + if (gctx->spriteBlocks.tex) { + renderer::drawSprites(gctx); + } +} + +void setRenderSize(core::Context *ctx, int width, int height) noexcept { + const auto gctx = static_cast(ctx); + gctx->renderSize.emplace(width, height); +} + +void clearRenderSize(core::Context *ctx) noexcept { + const auto gctx = static_cast(ctx); + gctx->renderSize.reset(); +} + +ox::Size getRenderSize(core::Context *ctx) noexcept { + const auto gctx = static_cast(ctx); + if (gctx->renderSize.has_value()) { + return gctx->renderSize.value(); + } else { + return turbine::getScreenSize(*ctx->turbineCtx); + } +} } + +} \ No newline at end of file diff --git a/src/nostalgia/core/opengl/gfx.hpp b/src/nostalgia/core/opengl/gfx.hpp index 2c6b0b0c..36114bf2 100644 --- a/src/nostalgia/core/opengl/gfx.hpp +++ b/src/nostalgia/core/opengl/gfx.hpp @@ -1,29 +1,63 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once #include -#include +#include -namespace nostalgia::core { -struct Palette; -} +#include namespace nostalgia::core::renderer { -ox::Error init(Context *ctx, void **rendererData) noexcept; +constexpr uint64_t TileRows = 128; +constexpr uint64_t TileColumns = 128; +constexpr uint64_t TileCount = TileRows * TileColumns; +constexpr uint64_t SpriteCount = 128; +constexpr uint64_t BgVertexVboRows = 4; +constexpr uint64_t BgVertexVboRowLength = 4; +constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; +constexpr uint64_t BgVertexEboLength = 6; +constexpr uint64_t SpriteVertexVboRows = 256; +constexpr uint64_t SpriteVertexVboRowLength = 5; +constexpr uint64_t SpriteVertexVboLength = SpriteVertexVboRows * SpriteVertexVboRowLength; +constexpr uint64_t SpriteVertexEboLength = 6; -void shutdown(Context *ctx, void *rendererData) noexcept; +struct CBB: public glutils::BufferSet { + bool updated = false; + constexpr CBB() noexcept { + vertices.resize(TileCount * BgVertexVboLength); + elements.resize(TileCount * BgVertexEboLength); + } +}; -void loadBgPalette(void *rendererData, const Palette &pal) noexcept; +struct SpriteBlockset: public glutils::BufferSet { + bool updated = false; + constexpr SpriteBlockset() noexcept { + vertices.resize(SpriteCount * SpriteVertexVboLength); + elements.resize(SpriteCount * SpriteVertexEboLength); + } +}; -void loadBgTexture(void *rendererData, unsigned cbb, const void *pixels, int w, int h) noexcept; +struct Background { + bool enabled = false; + unsigned cbbIdx = 0; +}; -void loadSpritePalette(void *rendererData, const Palette &pal) noexcept; +struct Sprite { + bool enabled = false; +}; -void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept; +class Drawer: public turbine::gl::Drawer { + public: + Context *m_ctx = nullptr; + void draw(turbine::Context&) noexcept final; +}; } + +namespace nostalgia::core { +ox::Error initGfx(Context *ctx, const InitParams &) noexcept; +} \ No newline at end of file diff --git a/src/nostalgia/core/opengl/gfx_opengl.cpp b/src/nostalgia/core/opengl/gfx_opengl.cpp deleted file mode 100644 index ecf1c3e0..00000000 --- a/src/nostalgia/core/opengl/gfx_opengl.cpp +++ /dev/null @@ -1,585 +0,0 @@ -/* - * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include -#include -#include - -#include - -#include -#include -#include - -namespace nostalgia::core { - -void ImGui_Impl_NewFrame() noexcept; - -namespace renderer { - -constexpr uint64_t TileRows = 128; -constexpr uint64_t TileColumns = 128; -constexpr uint64_t TileCount = TileRows * TileColumns; -constexpr uint64_t SpriteCount = 128; -constexpr uint64_t BgVertexVboRows = 4; -constexpr uint64_t BgVertexVboRowLength = 4; -constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; -constexpr uint64_t BgVertexEboLength = 6; -constexpr uint64_t SpriteVertexVboRows = 256; -constexpr uint64_t SpriteVertexVboRowLength = 5; -constexpr uint64_t SpriteVertexVboLength = SpriteVertexVboRows * SpriteVertexVboRowLength; -constexpr uint64_t SpriteVertexEboLength = 6; - -struct CBB: public glutils::BufferSet { - bool updated = false; - constexpr CBB() noexcept { - vertices.resize(TileCount * BgVertexVboLength); - elements.resize(TileCount * BgVertexEboLength); - } -}; - -struct SpriteBlockset: public glutils::BufferSet { - bool updated = false; - constexpr SpriteBlockset() noexcept { - vertices.resize(SpriteCount * SpriteVertexVboLength); - elements.resize(SpriteCount * SpriteVertexEboLength); - } -}; - -struct Background { - bool enabled = false; - unsigned cbbIdx = 0; -}; - -struct Sprite { - bool enabled = false; -}; - -struct GlImplData { - glutils::GLProgram bgShader; - glutils::GLProgram spriteShader; - int64_t prevFpsCheckTime = 0; - uint64_t draws = 0; - bool mainViewEnabled = true; - ox::Array cbbs; - SpriteBlockset spriteBlocks; - ox::Array spriteStates; - ox::Array backgrounds; - ox::Optional renderSize; -}; - -constexpr ox::StringView bgvshadTmpl = R"( - {} - in vec2 vTexCoord; - in vec2 vPosition; - out vec2 fTexCoord; - uniform float vXScale; - uniform float vTileHeight; - void main() { - float xScaleInvert = 1.0 - vXScale; - gl_Position = vec4( - vPosition.x * vXScale - xScaleInvert, vPosition.y, - 0.0, 1.0); - fTexCoord = vTexCoord * vec2(1, vTileHeight); - })"; - -constexpr ox::StringView bgfshadTmpl = R"( - {} - out vec4 outColor; - in vec2 fTexCoord; - uniform sampler2D image; - uniform vec4 fPalette[256]; - void main() { - int idx = int(texture(image, fTexCoord).rgb.r * 256); - outColor = fPalette[idx]; - //outColor = vec4(0.0, 0.7, 1.0, 1.0); - })"; - -constexpr ox::StringView spritevshadTmpl = R"( - {} - in float vEnabled; - in vec2 vTexCoord; - in vec2 vPosition; - out vec2 fTexCoord; - uniform float vXScale; - uniform float vTileHeight; - void main() { - float xScaleInvert = 1.0 - vXScale; - gl_Position = vec4( - vPosition.x * vXScale - xScaleInvert, vPosition.y, - 0.0, 1.0); - fTexCoord = vTexCoord * vec2(1, vTileHeight) * vec2(vEnabled, vEnabled); - })"; - -constexpr ox::StringView spritefshadTmpl = bgfshadTmpl; - -[[nodiscard]] -static constexpr auto bgVertexRow(unsigned x, unsigned y) noexcept { - return y * TileRows + x; -} - -static void setSpriteBufferObject(Context*, - unsigned vi, - float enabled, - float x, float y, - unsigned textureRow, - unsigned flipX, - float *vbo, - GLuint *ebo) noexcept { - // don't worry, this memcpy gets optimized to something much more ideal - constexpr float xmod = 0.1f; - constexpr float ymod = 0.1f; - x *= xmod; - y *= -ymod; - x -= 1.f; - y += 1.f - ymod; - const auto textureRowf = static_cast(textureRow); - const float L = flipX ? 1 : 0; - const float R = flipX ? 0 : 1; - const ox::Array vertices { - enabled, x, y, L, textureRowf + 1, // bottom left - enabled, x + xmod, y, R, textureRowf + 1, // bottom right - enabled, x + xmod, y + ymod, R, textureRowf + 0, // top right - enabled, x, y + ymod, L, textureRowf + 0, // top left - }; - memcpy(vbo, vertices.data(), sizeof(vertices)); - const ox::Array elms { - vi + 0, vi + 1, vi + 2, - vi + 2, vi + 3, vi + 0, - }; - memcpy(ebo, elms.data(), sizeof(elms)); -} - -static void setTileBufferObject(Context*, - unsigned vi, - float x, - float y, - unsigned textureRow, - float *vbo, - GLuint *ebo) noexcept { - // don't worry, this memcpy gets optimized to something much more ideal - constexpr float ymod = 0.1f; - constexpr float xmod = 0.1f; - x *= xmod; - y *= -ymod; - x -= 1.0f; - y += 1.0f - ymod; - const auto textureRowf = static_cast(textureRow); - const ox::Array vertices { - x, y, 0, textureRowf + 1, // bottom left - x + xmod, y, 1, textureRowf + 1, // bottom right - x + xmod, y + ymod, 1, textureRowf + 0, // top right - x, y + ymod, 0, textureRowf + 0, // top left - }; - memcpy(vbo, vertices.data(), sizeof(vertices)); - const ox::Array elms { - vi + 0, vi + 1, vi + 2, - vi + 2, vi + 3, vi + 0, - }; - memcpy(ebo, elms.data(), sizeof(elms)); -} - -static void initSpriteBufferObjects(Context *ctx, glutils::BufferSet *bs) noexcept { - for (auto i = 0u; i < SpriteCount; ++i) { - auto vbo = &bs->vertices[i * static_cast(SpriteVertexVboLength)]; - auto ebo = &bs->elements[i * static_cast(SpriteVertexEboLength)]; - setSpriteBufferObject(ctx, i * SpriteVertexVboRows, 0, 0, 0, 0, false, vbo, ebo); - } -} - -static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept { - for (auto x = 0u; x < TileColumns; ++x) { - for (auto y = 0u; y < TileRows; ++y) { - const auto i = bgVertexRow(x, y); - auto vbo = &bg->vertices[i * static_cast(BgVertexVboLength)]; - auto ebo = &bg->elements[i * static_cast(BgVertexEboLength)]; - setTileBufferObject(ctx, i * BgVertexVboRows, static_cast(x), static_cast(y), 0, vbo, ebo); - } - } -} - -static void initSpritesBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bs) noexcept { - // vao - bs->vao = glutils::generateVertexArrayObject(); - glBindVertexArray(bs->vao); - // vbo & ebo - bs->vbo = glutils::generateBuffer(); - bs->ebo = glutils::generateBuffer(); - initSpriteBufferObjects(ctx, bs); - glutils::sendVbo(*bs); - glutils::sendEbo(*bs); - // vbo layout - auto enabledAttr = static_cast(glGetAttribLocation(shader, "vEnabled")); - glEnableVertexAttribArray(enabledAttr); - glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr); - auto posAttr = static_cast(glGetAttribLocation(shader, "vPosition")); - glEnableVertexAttribArray(posAttr); - glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), - reinterpret_cast(1 * sizeof(float))); - auto texCoordAttr = static_cast(glGetAttribLocation(shader, "vTexCoord")); - glEnableVertexAttribArray(texCoordAttr); - glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), - reinterpret_cast(3 * sizeof(float))); -} - -static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bg) noexcept { - // vao - bg->vao = glutils::generateVertexArrayObject(); - glBindVertexArray(bg->vao); - // vbo & ebo - bg->vbo = glutils::generateBuffer(); - bg->ebo = glutils::generateBuffer(); - initBackgroundBufferObjects(ctx, bg); - glutils::sendVbo(*bg); - glutils::sendEbo(*bg); - // vbo layout - auto posAttr = static_cast(glGetAttribLocation(shader, "vPosition")); - glEnableVertexAttribArray(posAttr); - glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), nullptr); - auto texCoordAttr = static_cast(glGetAttribLocation(shader, "vTexCoord")); - glEnableVertexAttribArray(texCoordAttr); - glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), - reinterpret_cast(2 * sizeof(float))); -} - -static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, const void *pixels) noexcept { - GLuint texId = 0; - glGenTextures(1, &texId); - glutils::GLTexture tex(texId); - tex.width = w; - tex.height = h; - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex.id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - return tex; -} - -static void tickFps(GlImplData *id) noexcept { - ++id->draws; - if (id->draws >= 500) { - using namespace std::chrono; - const auto now = duration_cast(system_clock::now().time_since_epoch()).count(); - const auto duration = static_cast(now - id->prevFpsCheckTime) / 1000.0; - const auto fps = static_cast(static_cast(id->draws) / duration); - if constexpr(config::UserlandFpsPrint) { - oxOutf("FPS: {}\n", fps); - } - oxTracef("nostalgia::core::gfx::fps", "FPS: {}", fps); - id->prevFpsCheckTime = now; - id->draws = 0; - } -} - -static void drawBackground(CBB *cbb) noexcept { - glBindVertexArray(cbb->vao); - if (cbb->updated) { - cbb->updated = false; - glutils::sendVbo(*cbb); - } - glBindTexture(GL_TEXTURE_2D, cbb->tex); - glDrawElements(GL_TRIANGLES, static_cast(cbb->elements.size()), GL_UNSIGNED_INT, nullptr); -} - -static void drawBackgrounds(core::Context *ctx, GlImplData *id) noexcept { - // load background shader and its uniforms - glUseProgram(id->bgShader); - const auto uniformXScale = static_cast(glGetUniformLocation(id->bgShader, "vXScale")); - const auto uniformTileHeight = static_cast(glGetUniformLocation(id->bgShader, "vTileHeight")); - const auto [wi, hi] = gl::getRenderSize(ctx); - const auto wf = static_cast(wi); - const auto hf = static_cast(hi); - glUniform1f(uniformXScale, hf / wf); - for (const auto &bg : id->backgrounds) { - if (bg.enabled) { - auto &cbb = id->cbbs[bg.cbbIdx]; - const auto tileRows = cbb.tex.height / TileHeight; - glUniform1f(uniformTileHeight, 1.0f / static_cast(tileRows)); - drawBackground(&cbb); - } - } -} - -static void drawSprites(core::Context *ctx, GlImplData *id) noexcept { - glUseProgram(id->spriteShader); - auto &sb = id->spriteBlocks; - const auto uniformXScale = static_cast(glGetUniformLocation(id->bgShader, "vXScale")); - const auto uniformTileHeight = static_cast(glGetUniformLocation(id->spriteShader, "vTileHeight")); - const auto [wi, hi] = gl::getRenderSize(ctx); - const auto wf = static_cast(wi); - const auto hf = static_cast(hi); - glUniform1f(uniformXScale, hf / wf); - // update vbo - glBindVertexArray(sb.vao); - if (sb.updated) { - sb.updated = false; - glutils::sendVbo(sb); - } - // set vTileHeight uniform - const auto tileRows = sb.tex.height / TileHeight; - glUniform1f(uniformTileHeight, 1.0f / static_cast(tileRows)); - // draw - glBindTexture(GL_TEXTURE_2D, sb.tex); - glDrawElements(GL_TRIANGLES, static_cast(sb.elements.size()), GL_UNSIGNED_INT, nullptr); -} - -ox::Error init(Context *ctx) noexcept { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - const auto bgVshad = ox::sfmt(bgvshadTmpl, glutils::GlslVersion); - const auto bgFshad = ox::sfmt(bgfshadTmpl, glutils::GlslVersion); - const auto spriteVshad = ox::sfmt(spritevshadTmpl, glutils::GlslVersion); - const auto spriteFshad = ox::sfmt(spritefshadTmpl, glutils::GlslVersion); - const auto id = ox::make(); - ctx->setRendererData(id); - oxReturnError(glutils::buildShaderProgram(bgVshad.c_str(), bgFshad.c_str()).moveTo(&id->bgShader)); - oxReturnError(glutils::buildShaderProgram(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&id->spriteShader)); - for (auto &bg : id->cbbs) { - initBackgroundBufferset(ctx, id->bgShader, &bg); - } - initSpritesBufferset(ctx, id->spriteShader, &id->spriteBlocks); - ImGui_ImplOpenGL3_Init(glutils::GlslVersion); - return {}; -} - -void shutdown(Context*, void *rendererData) noexcept { - const auto id = reinterpret_cast(rendererData); - ox::safeDelete(id); -} - -static void loadPalette(GLuint shaderPgrm, const Palette &pal, bool firstIsTransparent = false) noexcept { - static constexpr std::size_t ColorCnt = 256; - ox::Array palette{}; - for (auto i = 0u; const auto c : pal.colors) { - palette[i++] = redf(c); - palette[i++] = greenf(c); - palette[i++] = bluef(c); - palette[i++] = 255; - } - if (firstIsTransparent) { - palette[3] = 0; - } - glUseProgram(shaderPgrm); - const auto uniformPalette = static_cast(glGetUniformLocation(shaderPgrm, "fPalette")); - glUniform4fv(uniformPalette, ColorCnt, palette.data()); -} - -void loadBgPalette(void *rendererData, const Palette &pal) noexcept { - const auto id = static_cast(rendererData); - loadPalette(id->bgShader, pal); -} - -void loadSpritePalette(void *rendererData, const Palette &pal) noexcept { - const auto id = static_cast(rendererData); - loadPalette(id->spriteShader, pal, true); -} - -void loadBgTexture(void *rendererData, unsigned cbbIdx, const void *pixels, int w, int h) noexcept { - oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h); - const auto id = static_cast(rendererData); - id->cbbs[cbbIdx].tex = loadTexture(w, h, pixels); -} - -void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept { - oxTracef("nostalgia::core::gfx::gl", "loadSpriteTexture: { w: {}, h: {} }", w, h); - const auto id = static_cast(rendererData); - id->spriteBlocks.tex = loadTexture(w, h, pixels); -} - -} - -void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbbIdx) noexcept { - const auto id = ctx->rendererData(); - auto &bg = id->backgrounds[bgIdx]; - bg.cbbIdx = cbbIdx; -} - -uint8_t bgStatus(Context *ctx) noexcept { - const auto id = ctx->rendererData(); - uint8_t out = 0; - for (unsigned i = 0; i < id->cbbs.size(); ++i) { - out |= id->backgrounds[i].enabled << i; - } - return out; -} - -void setBgStatus(Context *ctx, uint32_t status) noexcept { - const auto id = ctx->rendererData(); - for (unsigned i = 0; i < id->cbbs.size(); ++i) { - id->backgrounds[i].enabled = (status >> i) & 1; - } -} - -bool bgStatus(Context *ctx, unsigned bg) noexcept { - const auto id = ctx->rendererData(); - return id->backgrounds[bg].enabled; -} - -void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept { - const auto id = ctx->rendererData(); - id->backgrounds[bg].enabled = status; -} - - -void draw(Context *ctx) noexcept { - const auto id = ctx->rendererData(); - renderer::tickFps(id); - ImGui_ImplOpenGL3_NewFrame(); - ImGui_Impl_NewFrame(); - if constexpr(config::ImGuiEnabled) { - ImGui::NewFrame(); - } - // clear screen - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - // render - if (id->mainViewEnabled) { - gl::drawMainView(ctx); - } - for (const auto cd : ctx->drawers) { - cd->draw(ctx); - } - if constexpr(config::ImGuiEnabled) { - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - } -} - -void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept { - const auto id = ctx->rendererData(); - auto &bg = id->cbbs[static_cast(bgIdx)]; - initBackgroundBufferObjects(ctx, &bg); - bg.updated = true; -} - -void hideSprite(Context *ctx, unsigned idx) noexcept { - auto &id = *ctx->rendererData(); - auto vbo = &id.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength]; - auto ebo = &id.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength]; - renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 0, - 0, 0, 0, false, vbo, ebo); - id.spriteBlocks.updated = true; -} - -void setSprite(Context *ctx, - unsigned idx, - int x, - int y, - unsigned tileIdx, - [[maybe_unused]] unsigned spriteShape, - [[maybe_unused]] unsigned spriteSize, - unsigned flipX) noexcept { - //oxTracef("nostalgia::core::gfx::gl", "setSprite(ctx, {}, {}, {}, {}, {}, {}, {})", - // idx, x, y, tileIdx, spriteShape, spriteSize, flipX); - // Tonc Table 8.4 - static constexpr ox::Array, 12> dimensions{ - // col 0 - {1, 1}, // 0, 0 - {2, 2}, // 0, 1 - {4, 4}, // 0, 2 - {8, 8}, // 0, 3 - // col 1 - {2, 1}, // 1, 0 - {4, 1}, // 1, 1 - {4, 2}, // 1, 2 - {8, 4}, // 1, 3 - // col 2 - {1, 1}, // 2, 0 - {1, 4}, // 2, 1 - {2, 4}, // 2, 2 - {4, 8}, // 2, 3 - }; - const auto dim = dimensions[(spriteShape << 2) | spriteSize]; - const auto uX = static_cast(x) % 255; - const auto uY = static_cast(y + 8) % 255 - 8; - auto &id = *ctx->rendererData(); - auto i = 0u; - const auto set = [&](int xIt, int yIt) { - const auto fX = static_cast(uX + xIt * 8) / 8; - const auto fY = static_cast(uY + yIt * 8) / 8; - const auto cidx = idx + i; - auto vbo = &id.spriteBlocks.vertices[cidx * renderer::SpriteVertexVboLength]; - auto ebo = &id.spriteBlocks.elements[cidx * renderer::SpriteVertexEboLength]; - renderer::setSpriteBufferObject(ctx, cidx * renderer::SpriteVertexVboRows, 1, - fX, fY, tileIdx + i, flipX, vbo, ebo); - ++i; - }; - if (!flipX) { - for (auto yIt = 0; yIt < static_cast(dim.y); ++yIt) { - for (auto xIt = 0u; xIt < dim.x; ++xIt) { - set(static_cast(xIt), static_cast(yIt)); - } - } - } else { - for (auto yIt = 0u; yIt < dim.y; ++yIt) { - for (auto xIt = dim.x - 1; xIt < ~0u; --xIt) { - set(static_cast(xIt), static_cast(yIt)); - } - } - } - id.spriteBlocks.updated = true; -} - -void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { - oxTracef( - "nostalgia::core::gfx::setTile", - "bgIdx: {}, column: {}, row: {}, tile: {}", - bgIdx, column, row, tile); - const auto id = ctx->rendererData(); - const auto z = static_cast(bgIdx); - const auto y = static_cast(row); - const auto x = static_cast(column); - const auto i = renderer::bgVertexRow(x, y); - auto &bg = id->cbbs[z]; - auto vbo = &bg.vertices[i * renderer::BgVertexVboLength]; - auto ebo = &bg.elements[i * renderer::BgVertexEboLength]; - renderer::setTileBufferObject( - ctx, i * renderer::BgVertexVboRows, - static_cast(x), static_cast(y), - tile, vbo, ebo); - bg.updated = true; -} - -namespace gl { - -void setMainViewEnabled(core::Context *ctx, bool enabled) noexcept { - const auto id = ctx->rendererData(); - id->mainViewEnabled = enabled; -} - -void drawMainView(core::Context *ctx) noexcept { - const auto id = ctx->rendererData(); - renderer::drawBackgrounds(ctx, id); - if (id->spriteBlocks.tex) { - renderer::drawSprites(ctx, id); - } -} - -void setRenderSize(core::Context *ctx, int width, int height) noexcept { - const auto id = ctx->rendererData(); - id->renderSize.emplace(width, height); -} - -void clearRenderSize(core::Context *ctx) noexcept { - auto id = ctx->rendererData(); - id->renderSize.reset(); -} - -ox::Size getRenderSize(core::Context *ctx) noexcept { - const auto id = ctx->rendererData(); - if (id->renderSize.has_value()) { - return id->renderSize.value(); - } else { - return core::getScreenSize(ctx); - } -} - -} - -} diff --git a/src/nostalgia/core/sdl/core.cpp b/src/nostalgia/core/sdl/core.cpp deleted file mode 100644 index 7e22c945..00000000 --- a/src/nostalgia/core/sdl/core.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include -#include -#include -#include - -#include "core.hpp" - -namespace nostalgia::core { - -static event_handler g_eventHandler = nullptr; -static uint64_t g_wakeupTime; - -void draw(Context *ctx); - -ox::Result> init(ox::UniquePtr fs, const char *appName) noexcept { - auto ctx = ox::make_unique(); - ctx->rom = std::move(fs); - ctx->appName = appName; - const auto id = new SdlImplData; - ctx->setWindowerData(id); - oxReturnError(initGfx(ctx.get())); - return OxError(0); -} - -ox::Error run(Context *ctx) noexcept { - const auto id = ctx->windowerData(); - // try adaptive vsync - if (SDL_GL_SetSwapInterval(config::SdlVsyncOption) < 0) { - oxTrace("nostalgia::core::sdl", "Could not enable adaptive vsync, falling back on vsync"); - SDL_GL_SetSwapInterval(1); // fallback on normal vsync - } - id->running = true; - while (id->running) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - switch (event.type) { - case SDL_KEYDOWN: - if (event.key.keysym.sym == SDLK_q) { - id->running = false; - } - break; - case SDL_QUIT: { - id->running = false; - break; - } - } - } - const auto ticks = ticksMs(ctx); - if (g_wakeupTime <= ticks && g_eventHandler) { - auto sleepTime = g_eventHandler(ctx); - if (sleepTime >= 0) { - g_wakeupTime = ticks + static_cast(sleepTime); - } else { - g_wakeupTime = ~uint64_t(0); - } - } - draw(ctx); - SDL_GL_SwapWindow(id->window); - } - ctx->setWindowerData(nullptr); - delete id; - return OxError(0); -} - -void setEventHandler(event_handler h) noexcept { - g_eventHandler = h; -} - -uint64_t ticksMs() noexcept { - return SDL_GetTicks(); -} - -bool buttonDown(Key) noexcept { - return false; -} - -void shutdown(Context *ctx) noexcept { - const auto id = ctx->windowerData(); - id->running = false; -} - -} diff --git a/src/nostalgia/core/sdl/core.hpp b/src/nostalgia/core/sdl/core.hpp deleted file mode 100644 index f9b57c47..00000000 --- a/src/nostalgia/core/sdl/core.hpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -#include - -#include - -namespace nostalgia::core { - -struct SdlImplData { - SDL_Window *window = nullptr; - SDL_GLContext renderer = nullptr; - int64_t startTime = 0; - uint64_t wakeupTime = 0; - bool running = false; -}; - -} diff --git a/src/nostalgia/core/sdl/gfx.cpp b/src/nostalgia/core/sdl/gfx.cpp deleted file mode 100644 index cc65089d..00000000 --- a/src/nostalgia/core/sdl/gfx.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include - -#include - -#include -#include - -#include "core.hpp" - -namespace nostalgia::core { - -constexpr auto Scale = 5; - -ox::Error initGfx(Context *ctx) noexcept { - auto id = new SdlImplData; - ctx->setWindowerData(id); - id->window = SDL_CreateWindow("nostalgia", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - 240 * Scale, 160 * Scale, - SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); - if (id->window == nullptr) { - return OxError(1, SDL_GetError()); - } - 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); - id->renderer = SDL_GL_CreateContext(id->window); - if (id->renderer == nullptr) { - return OxError(1, SDL_GetError()); - } - oxReturnError(renderer::init(ctx)); - return OxError(0); -} - -ox::Error shutdownGfx(Context *ctx) noexcept { - oxReturnError(renderer::shutdown(ctx)); - auto id = ctx->windowerData(); - SDL_GL_DeleteContext(id->renderer); - SDL_DestroyWindow(id->window); - ctx->setWindowerData(nullptr); - delete id; - return OxError(0); -} - -int getScreenWidth(Context *ctx) noexcept { - auto id = ctx->windowerData(); - int x = 0, y = 0; - SDL_GetWindowSize(id->window, &x, &y); - return x; -} - -int getScreenHeight(Context *ctx) noexcept { - auto id = ctx->windowerData(); - int x = 0, y = 0; - SDL_GetWindowSize(id->window, &x, &y); - return y; -} - -common::Size getScreenSize(Context *ctx) noexcept { - auto id = ctx->windowerData(); - int x = 0, y = 0; - SDL_GetWindowSize(id->window, &x, &y); - return {x, y}; -} - -} diff --git a/src/nostalgia/core/studio/module.cpp b/src/nostalgia/core/studio/module.cpp index e7f2e0b4..54f47ce3 100644 --- a/src/nostalgia/core/studio/module.cpp +++ b/src/nostalgia/core/studio/module.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include @@ -11,7 +11,7 @@ namespace nostalgia::core { -ox::Vector StudioModule::editors(core::Context *ctx) noexcept { +ox::Vector StudioModule::editors(turbine::Context *ctx) noexcept { return { { {FileExt_ng}, @@ -32,7 +32,7 @@ ox::Vector StudioModule::editors(core::Context *ctx) noexce }; } -ox::Vector> StudioModule::itemMakers(core::Context*) noexcept { +ox::Vector> StudioModule::itemMakers(turbine::Context*) noexcept { ox::Vector> out; out.emplace_back(ox::make>("Tile Sheet", "TileSheets", "ng")); out.emplace_back(ox::make>("Palette", "Palettes", "npal")); diff --git a/src/nostalgia/core/studio/module.hpp b/src/nostalgia/core/studio/module.hpp index b59c7a4e..160fba32 100644 --- a/src/nostalgia/core/studio/module.hpp +++ b/src/nostalgia/core/studio/module.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -10,8 +10,8 @@ namespace nostalgia::core { class StudioModule: public studio::Module { public: - ox::Vector editors(core::Context *ctx) noexcept override; - ox::Vector> itemMakers(core::Context*) noexcept override; + ox::Vector editors(turbine::Context *ctx) noexcept override; + ox::Vector> itemMakers(turbine::Context*) noexcept override; }; } diff --git a/src/nostalgia/core/studio/paletteeditor-imgui.cpp b/src/nostalgia/core/studio/paletteeditor-imgui.cpp index d3430c7f..119cc5d4 100644 --- a/src/nostalgia/core/studio/paletteeditor-imgui.cpp +++ b/src/nostalgia/core/studio/paletteeditor-imgui.cpp @@ -13,8 +13,13 @@ namespace nostalgia::core { -ox::Result PaletteEditorImGui::make(Context *ctx, ox::CRStringView path) noexcept { - auto out = ox::UniquePtr(new PaletteEditorImGui); +ox::Result PaletteEditorImGui::make(turbine::Context *ctx, ox::CRStringView path) noexcept { + ox::UniquePtr out; + try { + out = ox::UniquePtr(new PaletteEditorImGui); + } catch (...) { + return OxError(1); + } out->m_ctx = ctx; out->m_itemPath = path; const auto lastSlash = std::find(out->m_itemPath.rbegin(), out->m_itemPath.rend(), '/').offset(); @@ -32,7 +37,7 @@ const ox::String &PaletteEditorImGui::itemDisplayName() const noexcept { return m_itemName; } -void PaletteEditorImGui::draw(core::Context*) noexcept { +void PaletteEditorImGui::draw(turbine::Context*) noexcept { static constexpr auto flags = ImGuiTableFlags_RowBg; const auto paneSize = ImGui::GetContentRegionAvail(); ImGui::BeginChild("PaletteEditor"); @@ -137,7 +142,7 @@ void PaletteEditorImGui::draw(core::Context*) noexcept { } ox::Error PaletteEditorImGui::saveItem() noexcept { - const auto sctx = applicationData(m_ctx); + const auto sctx = applicationData(*m_ctx); oxReturnError(sctx->project->writeObj(m_itemPath, &m_pal)); oxReturnError(m_ctx->assetManager.setAsset(m_itemPath, m_pal)); return OxError(0); diff --git a/src/nostalgia/core/studio/paletteeditor-imgui.hpp b/src/nostalgia/core/studio/paletteeditor-imgui.hpp index 198141f6..b8559747 100644 --- a/src/nostalgia/core/studio/paletteeditor-imgui.hpp +++ b/src/nostalgia/core/studio/paletteeditor-imgui.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -12,7 +12,7 @@ namespace nostalgia::core { class PaletteEditorImGui: public studio::Editor { private: - Context *m_ctx = nullptr; + turbine::Context *m_ctx = nullptr; ox::String m_itemName; ox::String m_itemPath; Palette m_pal; @@ -21,7 +21,7 @@ class PaletteEditorImGui: public studio::Editor { PaletteEditorImGui() noexcept = default; public: - static ox::Result make(Context *ctx, ox::CRStringView path) noexcept; + static ox::Result make(turbine::Context *ctx, ox::CRStringView path) noexcept; /** * Returns the name of item being edited. @@ -30,7 +30,7 @@ class PaletteEditorImGui: public studio::Editor { const ox::String &itemDisplayName() const noexcept final; - void draw(core::Context*) noexcept final; + void draw(turbine::Context*) noexcept final; protected: ox::Error saveItem() noexcept final; diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp index 8f80850f..6ac691de 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp @@ -38,14 +38,14 @@ ox::Error toPngFile(const ox::String &path, const TileSheet::SubSheet &s, const return OxError(lodepng_encode_file(path.c_str(), outData.data(), width, height, fmt, 8)); } -TileSheetEditorImGui::TileSheetEditorImGui(Context *ctx, ox::CRStringView path): m_tileSheetEditor(ctx, path) { +TileSheetEditorImGui::TileSheetEditorImGui(turbine::Context *ctx, ox::CRStringView path): m_tileSheetEditor(ctx, path) { m_ctx = ctx; m_itemPath = path; const auto lastSlash = ox::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset(); m_itemName = m_itemPath.substr(lastSlash + 1); // init palette idx const auto &palPath = model()->palPath(); - auto sctx = applicationData(m_ctx); + auto sctx = applicationData(*m_ctx); const auto &palList = sctx->project->fileList(core::FileExt_npal); for (std::size_t i = 0; const auto &pal : palList) { if (palPath == pal) { @@ -83,35 +83,35 @@ void TileSheetEditorImGui::paste() { model()->paste(); } -void TileSheetEditorImGui::keyStateChanged(core::Key key, bool down) { +void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) { if (!down) { return; } - if (key == core::Key::Escape) { + if (key == turbine::Key::Escape) { m_subsheetEditor.close(); } auto pal = model()->pal(); if (pal) { const auto colorCnt = pal->colors.size(); - if (key == core::Key::Alpha_D) { + if (key == turbine::Key::Alpha_D) { m_tool = Tool::Draw; model()->clearSelection(); - } else if (key == core::Key::Alpha_S) { + } else if (key == turbine::Key::Alpha_S) { m_tool = Tool::Select; - } else if (key == core::Key::Alpha_F) { + } else if (key == turbine::Key::Alpha_F) { m_tool = Tool::Fill; model()->clearSelection(); - } else if (key >= core::Key::Num_1 && key <= core::Key::Num_9 && key <= core::Key::Num_0 + colorCnt) { - auto idx = ox::min(static_cast(key - core::Key::Num_1), colorCnt - 1); + } else if (key >= turbine::Key::Num_1 && key <= turbine::Key::Num_9 && key <= turbine::Key::Num_0 + colorCnt) { + auto idx = ox::min(static_cast(key - turbine::Key::Num_1), colorCnt - 1); m_tileSheetEditor.setPalIdx(idx); - } else if (key == core::Key::Num_0 && colorCnt >= 10) { - auto idx = ox::min(static_cast(key - core::Key::Num_1 + 9), colorCnt - 1); + } else if (key == turbine::Key::Num_0 && colorCnt >= 10) { + auto idx = ox::min(static_cast(key - turbine::Key::Num_1 + 9), colorCnt - 1); m_tileSheetEditor.setPalIdx(idx); } } } -void TileSheetEditorImGui::draw(core::Context*) noexcept { +void TileSheetEditorImGui::draw(turbine::Context*) noexcept { const auto paneSize = ImGui::GetContentRegionAvail(); const auto tileSheetParentSize = ImVec2(paneSize.x - m_palViewWidth, paneSize.y); const auto fbSize = ox::Vec2(tileSheetParentSize.x - 16, tileSheetParentSize.y - 16); @@ -302,7 +302,7 @@ void TileSheetEditorImGui::drawTileSheet(const ox::Vec2 &fbSize) noexcept { const auto wheelh = io.MouseWheelH; if (wheel != 0) { const auto zoomMod = ox::defines::OS == ox::OS::Darwin ? - io.KeySuper : core::buttonDown(m_ctx, core::Key::Mod_Ctrl); + io.KeySuper : turbine::buttonDown(*m_ctx, turbine::Key::Mod_Ctrl); m_tileSheetEditor.scrollV(fbSize, wheel, zoomMod); } if (wheelh != 0) { @@ -342,7 +342,7 @@ void TileSheetEditorImGui::drawTileSheet(const ox::Vec2 &fbSize) noexcept { } void TileSheetEditorImGui::drawPaletteSelector() noexcept { - auto sctx = applicationData(m_ctx); + auto sctx = applicationData(*m_ctx); const auto &files = sctx->project->fileList(core::FileExt_npal); const auto first = m_selectedPaletteIdx < files.size() ? files[m_selectedPaletteIdx].c_str() : ""; diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp index 9800f029..065b6c81 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp @@ -43,7 +43,7 @@ class TileSheetEditorImGui: public studio::BaseEditor { void close() noexcept; }; std::size_t m_selectedPaletteIdx = 0; - Context *m_ctx = nullptr; + turbine::Context *m_ctx = nullptr; ox::Vector m_paletteList; SubSheetEditor m_subsheetEditor; ox::String m_itemPath; @@ -55,7 +55,7 @@ class TileSheetEditorImGui: public studio::BaseEditor { Tool m_tool = Tool::Draw; public: - TileSheetEditorImGui(Context *ctx, ox::CRStringView path); + TileSheetEditorImGui(turbine::Context *ctx, ox::CRStringView path); ~TileSheetEditorImGui() override = default; @@ -71,9 +71,9 @@ class TileSheetEditorImGui: public studio::BaseEditor { void paste() override; - void keyStateChanged(core::Key key, bool down) override; + void keyStateChanged(turbine::Key key, bool down) override; - void draw(core::Context*) noexcept override; + void draw(turbine::Context*) noexcept override; void drawSubsheetSelector(TileSheet::SubSheet*, TileSheet::SubSheetIdx *path); diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.cpp b/src/nostalgia/core/studio/tilesheeteditormodel.cpp index c72b6799..ae049886 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.cpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include "tilesheeteditormodel.hpp" @@ -19,7 +19,7 @@ const Palette TileSheetEditorModel::s_defaultPalette = { .colors = ox::Vector(128), }; -class TileSheetClipboard: public ClipboardObject { +class TileSheetClipboard: public turbine::ClipboardObject { public: static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard"; static constexpr auto TypeVersion = 1; @@ -535,7 +535,7 @@ class PaletteChangeCommand: public TileSheetCommand { }; -TileSheetEditorModel::TileSheetEditorModel(Context *ctx, ox::String path) { +TileSheetEditorModel::TileSheetEditorModel(turbine::Context *ctx, ox::String path) { m_ctx = ctx; m_path = std::move(path); oxRequireT(img, readObj(ctx, m_path)); @@ -564,7 +564,7 @@ void TileSheetEditorModel::cut() { } const auto pt1 = m_selectionOrigin == ox::Point(-1, -1) ? ox::Point(0, 0) : m_selectionOrigin; const auto pt2 = ox::Point(s->columns * TileWidth, s->rows * TileHeight); - setClipboardObject(m_ctx, std::move(cb)); + turbine::setClipboardObject(*m_ctx, std::move(cb)); pushCommand(ox::make>(&m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb)); } @@ -581,11 +581,11 @@ void TileSheetEditorModel::copy() { cb->addPixel(pt, c); } } - setClipboardObject(m_ctx, std::move(cb)); + turbine::setClipboardObject(*m_ctx, std::move(cb)); } void TileSheetEditorModel::paste() { - auto [cb, err] = getClipboardObject(m_ctx); + auto [cb, err] = turbine::getClipboardObject(*m_ctx); if (err) { oxLogError(err); oxErrf("Could not read clipboard: {}", toStr(err)); @@ -744,7 +744,7 @@ void TileSheetEditorModel::ackUpdate() noexcept { } ox::Error TileSheetEditorModel::saveFile() noexcept { - const auto sctx = applicationData(m_ctx); + const auto sctx = applicationData(*m_ctx); oxReturnError(sctx->project->writeObj(m_path, &m_img)); return m_ctx->assetManager.setAsset(m_path, m_img).error; } diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.hpp b/src/nostalgia/core/studio/tilesheeteditormodel.hpp index 704e9df6..d93b902a 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.hpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.hpp @@ -27,14 +27,14 @@ class TileSheetEditorModel: public ox::SignalHandler { studio::UndoStack m_undoStack; class DrawCommand *m_ongoingDrawCommand = nullptr; bool m_updated = false; - Context *m_ctx = nullptr; + turbine::Context *m_ctx = nullptr; ox::String m_path; bool m_selectionOngoing = false; ox::Point m_selectionOrigin = {-1, -1}; ox::Bounds m_selectionBounds = {{-1, -1}, {-1, -1}}; public: - TileSheetEditorModel(Context *ctx, ox::String path); + TileSheetEditorModel(turbine::Context *ctx, ox::String path); ~TileSheetEditorModel() override = default; diff --git a/src/nostalgia/core/studio/tilesheeteditorview.cpp b/src/nostalgia/core/studio/tilesheeteditorview.cpp index 02373c85..d1950b40 100644 --- a/src/nostalgia/core/studio/tilesheeteditorview.cpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.cpp @@ -11,7 +11,7 @@ namespace nostalgia::core { -TileSheetEditorView::TileSheetEditorView(Context *ctx, ox::CRStringView path): m_model(ctx, path), m_pixelsDrawer(&m_model) { +TileSheetEditorView::TileSheetEditorView(turbine::Context *ctx, ox::CRStringView path): m_model(ctx, path), m_pixelsDrawer(&m_model) { // build shaders oxThrowError(m_pixelsDrawer.buildShader()); oxThrowError(m_pixelGridDrawer.buildShader()); diff --git a/src/nostalgia/core/studio/tilesheeteditorview.hpp b/src/nostalgia/core/studio/tilesheeteditorview.hpp index 06d1c331..2437a07e 100644 --- a/src/nostalgia/core/studio/tilesheeteditorview.hpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.hpp @@ -49,7 +49,7 @@ class TileSheetEditorView: public ox::SignalHandler { std::size_t m_palIdx = 0; public: - TileSheetEditorView(Context *ctx, ox::CRStringView path); + TileSheetEditorView(turbine::Context *ctx, ox::CRStringView path); ~TileSheetEditorView() override = default; diff --git a/src/nostalgia/glutils/glutils.cpp b/src/nostalgia/glutils/glutils.cpp index 2aee3891..6e4c7404 100644 --- a/src/nostalgia/glutils/glutils.cpp +++ b/src/nostalgia/glutils/glutils.cpp @@ -46,6 +46,24 @@ template struct GLObject; template struct GLObject; template struct GLObject; +const FrameBuffer *FrameBufferBind::s_activeFb = nullptr; + +FrameBufferBind::FrameBufferBind(const FrameBuffer &fb) noexcept: m_restoreFb(s_activeFb) { + s_activeFb = &fb; + glBindFramebuffer(GL_FRAMEBUFFER, fb); + glViewport(0, 0, fb.width, fb.height); +} + +FrameBufferBind::~FrameBufferBind() noexcept { + s_activeFb = m_restoreFb; + if (s_activeFb) { + glBindFramebuffer(GL_FRAMEBUFFER, *s_activeFb); + glViewport(0, 0, s_activeFb->width, s_activeFb->height); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, s_activeFb->width, s_activeFb->height); + } +} void bind(const FrameBuffer &fb) noexcept { glBindFramebuffer(GL_FRAMEBUFFER, fb); diff --git a/src/nostalgia/glutils/glutils.hpp b/src/nostalgia/glutils/glutils.hpp index 54115e82..f59a168d 100644 --- a/src/nostalgia/glutils/glutils.hpp +++ b/src/nostalgia/glutils/glutils.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -134,6 +135,15 @@ struct FrameBuffer { } }; +class FrameBufferBind { + private: + static const FrameBuffer *s_activeFb; + const FrameBuffer *m_restoreFb = nullptr; + public: + explicit FrameBufferBind(const FrameBuffer &fb) noexcept; + ~FrameBufferBind() noexcept; +}; + void bind(const FrameBuffer &fb) noexcept; diff --git a/src/nostalgia/player/app.cpp b/src/nostalgia/player/app.cpp index 08404898..49d58fda 100644 --- a/src/nostalgia/player/app.cpp +++ b/src/nostalgia/player/app.cpp @@ -3,6 +3,7 @@ */ #include +#include #include #include @@ -12,7 +13,7 @@ using namespace nostalgia; static bool s_paused = false; static ox::Optional s_scene; -static int updateHandler(core::Context*) noexcept { +static int updateHandler(turbine::Context&) noexcept { constexpr auto sleepTime = 16; if (s_paused) { return sleepTime; @@ -21,11 +22,11 @@ static int updateHandler(core::Context*) noexcept { return sleepTime; } -static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexcept { +static void keyEventHandler(turbine::Context &tctx, turbine::Key key, bool down) noexcept { if (down) { - if (key == core::Key::Alpha_Q) { - core::shutdown(ctx); - } else if (key == core::Key::Alpha_P) { + if (key == turbine::Key::Alpha_Q) { + turbine::requestShutdown(tctx); + } else if (key == turbine::Key::Alpha_P) { s_paused = !s_paused; } } @@ -33,12 +34,13 @@ static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexce ox::Error run(ox::UniquePtr fs) noexcept { oxTraceInitHook(); - oxRequireM(ctx, core::init(std::move(fs))); + oxRequireM(tctx, turbine::init(std::move(fs))); + oxRequireM(cctx, core::init(tctx.get())); constexpr ox::FileAddress SceneAddr("/Scenes/Chester.nscn"); - oxRequire(scn, keel::readObj(ctx.get(), SceneAddr)); - core::setUpdateHandler(ctx.get(), updateHandler); - core::setKeyEventHandler(ctx.get(), keyEventHandler); + oxRequire(scn, keel::readObj(tctx.get(), SceneAddr)); + turbine::setUpdateHandler(*tctx, updateHandler); + turbine::setKeyEventHandler(*tctx, keyEventHandler); s_scene.emplace(*scn); - oxReturnError(s_scene->setupDisplay(ctx.get())); - return core::run(ctx.get()); + oxReturnError(s_scene->setupDisplay(cctx.get())); + return turbine::run(*tctx); } diff --git a/src/nostalgia/scene/studio/module.cpp b/src/nostalgia/scene/studio/module.cpp index 4144ae35..a3ae188d 100644 --- a/src/nostalgia/scene/studio/module.cpp +++ b/src/nostalgia/scene/studio/module.cpp @@ -2,14 +2,12 @@ * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include - #include "sceneeditor-imgui.hpp" #include "module.hpp" namespace nostalgia::scene { -ox::Vector StudioModule::editors(core::Context *ctx) noexcept { +ox::Vector StudioModule::editors(turbine::Context *ctx) noexcept { return { { {"nscn"}, @@ -20,7 +18,7 @@ ox::Vector StudioModule::editors(core::Context *ctx) noexce }; } -ox::Vector> StudioModule::itemMakers(core::Context*) noexcept { +ox::Vector> StudioModule::itemMakers(turbine::Context*) noexcept { ox::Vector> out; return out; } diff --git a/src/nostalgia/scene/studio/module.hpp b/src/nostalgia/scene/studio/module.hpp index bd0b14a5..0602e4c8 100644 --- a/src/nostalgia/scene/studio/module.hpp +++ b/src/nostalgia/scene/studio/module.hpp @@ -4,14 +4,16 @@ #pragma once +#include + #include namespace nostalgia::scene { class StudioModule: public studio::Module { public: - ox::Vector editors(core::Context *ctx) noexcept override; - ox::Vector> itemMakers(core::Context*) noexcept override; + ox::Vector editors(turbine::Context *ctx) noexcept override; + ox::Vector> itemMakers(turbine::Context*) noexcept override; }; } diff --git a/src/nostalgia/scene/studio/sceneeditor-imgui.cpp b/src/nostalgia/scene/studio/sceneeditor-imgui.cpp index e61395a7..0d5502d0 100644 --- a/src/nostalgia/scene/studio/sceneeditor-imgui.cpp +++ b/src/nostalgia/scene/studio/sceneeditor-imgui.cpp @@ -12,7 +12,7 @@ namespace nostalgia::scene { -SceneEditorImGui::SceneEditorImGui(core::Context *ctx, ox::CRStringView path): +SceneEditorImGui::SceneEditorImGui(turbine::Context *ctx, ox::CRStringView path): m_editor(ctx, path), m_view(ctx, m_editor.scene()) { m_ctx = ctx; @@ -30,7 +30,7 @@ ox::CRString SceneEditorImGui::itemDisplayName() const noexcept { return m_itemName; } -void SceneEditorImGui::draw(core::Context*) noexcept { +void SceneEditorImGui::draw(turbine::Context*) noexcept { const auto paneSize = ImGui::GetContentRegionAvail(); const ox::Size fbSize{ static_cast(paneSize.x), @@ -50,7 +50,7 @@ void SceneEditorImGui::onActivated() noexcept { } ox::Error SceneEditorImGui::saveItem() noexcept { - const auto sctx = applicationData(m_ctx); + const auto sctx = applicationData(*m_ctx); oxReturnError(sctx->project->writeObj(m_itemPath, &m_editor.scene())); oxReturnError(m_ctx->assetManager.setAsset(m_itemPath, m_editor.scene())); return {}; diff --git a/src/nostalgia/scene/studio/sceneeditor-imgui.hpp b/src/nostalgia/scene/studio/sceneeditor-imgui.hpp index 8fb2c817..c63bbecb 100644 --- a/src/nostalgia/scene/studio/sceneeditor-imgui.hpp +++ b/src/nostalgia/scene/studio/sceneeditor-imgui.hpp @@ -15,14 +15,14 @@ namespace nostalgia::scene { class SceneEditorImGui: public studio::Editor { private: - core::Context *m_ctx = nullptr; + turbine::Context *m_ctx = nullptr; ox::String m_itemName; ox::String m_itemPath; SceneEditor m_editor; SceneEditorView m_view; public: - SceneEditorImGui(core::Context *ctx, ox::CRStringView path); + SceneEditorImGui(turbine::Context *ctx, ox::CRStringView path); /** * Returns the name of item being edited. @@ -31,7 +31,7 @@ class SceneEditorImGui: public studio::Editor { ox::CRString itemDisplayName() const noexcept final; - void draw(core::Context*) noexcept final; + void draw(turbine::Context*) noexcept final; void onActivated() noexcept override; diff --git a/src/nostalgia/scene/studio/sceneeditor.cpp b/src/nostalgia/scene/studio/sceneeditor.cpp index e21f11bd..0b88534e 100644 --- a/src/nostalgia/scene/studio/sceneeditor.cpp +++ b/src/nostalgia/scene/studio/sceneeditor.cpp @@ -6,7 +6,7 @@ namespace nostalgia::scene { -SceneEditor::SceneEditor(core::Context *ctx, ox::CRStringView path) { +SceneEditor::SceneEditor(turbine::Context *ctx, ox::CRStringView path) { m_ctx = ctx; oxRequireT(scn, keel::readObj(m_ctx, path)); m_scene = *scn; diff --git a/src/nostalgia/scene/studio/sceneeditor.hpp b/src/nostalgia/scene/studio/sceneeditor.hpp index 8ee29c59..6fac862c 100644 --- a/src/nostalgia/scene/studio/sceneeditor.hpp +++ b/src/nostalgia/scene/studio/sceneeditor.hpp @@ -13,13 +13,13 @@ namespace nostalgia::scene { class SceneEditor { private: - core::Context *m_ctx = nullptr; + turbine::Context *m_ctx = nullptr; ox::String m_itemName; ox::String m_itemPath; SceneStatic m_scene; public: - SceneEditor(core::Context *ctx, ox::CRStringView path); + SceneEditor(turbine::Context *ctx, ox::CRStringView path); const SceneStatic &scene() noexcept { return m_scene; diff --git a/src/nostalgia/scene/studio/sceneeditorview.cpp b/src/nostalgia/scene/studio/sceneeditorview.cpp index dd6d1ebe..1121be11 100644 --- a/src/nostalgia/scene/studio/sceneeditorview.cpp +++ b/src/nostalgia/scene/studio/sceneeditorview.cpp @@ -2,20 +2,20 @@ * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include +#include #include "sceneeditorview.hpp" namespace nostalgia::scene { -SceneEditorView::SceneEditorView(core::Context *ctx, const SceneStatic &sceneStatic) noexcept: - m_ctx(ctx), +SceneEditorView::SceneEditorView(turbine::Context *tctx, const SceneStatic &sceneStatic): m_sceneStatic(sceneStatic), m_scene(m_sceneStatic) { + oxThrowError(core::init(tctx, {.glInstallDrawer = false}).moveTo(&m_cctx)); } ox::Error SceneEditorView::setupScene() noexcept { - return m_scene.setupDisplay(m_ctx); + return m_scene.setupDisplay(m_cctx.get()); } void SceneEditorView::draw(int width, int height) noexcept { @@ -23,8 +23,8 @@ void SceneEditorView::draw(int width, int height) noexcept { glutils::resizeInitFrameBuffer(&m_frameBuffer, width, height); } glutils::bind(m_frameBuffer); - core::gl::setRenderSize(m_ctx, width, height); - core::gl::drawMainView(m_ctx); + core::gl::setRenderSize(m_cctx.get(), width, height); + core::gl::drawMainView(m_cctx.get()); glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/src/nostalgia/scene/studio/sceneeditorview.hpp b/src/nostalgia/scene/studio/sceneeditorview.hpp index f031c067..ab8135d4 100644 --- a/src/nostalgia/scene/studio/sceneeditorview.hpp +++ b/src/nostalgia/scene/studio/sceneeditorview.hpp @@ -13,13 +13,13 @@ namespace nostalgia::scene { class SceneEditorView { private: - core::Context *const m_ctx = nullptr; + ox::UPtr m_cctx; const SceneStatic &m_sceneStatic; Scene m_scene; glutils::FrameBuffer m_frameBuffer; public: - SceneEditorView(core::Context *ctx, const SceneStatic &sceneStatic) noexcept; + SceneEditorView(turbine::Context *ctx, const SceneStatic &sceneStatic); ox::Error setupScene() noexcept; diff --git a/src/nostalgia/studio/aboutpopup.cpp b/src/nostalgia/studio/aboutpopup.cpp index e265ed71..0b3a3175 100644 --- a/src/nostalgia/studio/aboutpopup.cpp +++ b/src/nostalgia/studio/aboutpopup.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include @@ -21,7 +21,7 @@ bool AboutPopup::isOpen() const noexcept { return m_stage == Stage::Open; } -void AboutPopup::draw(core::Context *ctx) noexcept { +void AboutPopup::draw(turbine::Context *ctx) noexcept { switch (m_stage) { case Stage::Closed: break; diff --git a/src/nostalgia/studio/aboutpopup.hpp b/src/nostalgia/studio/aboutpopup.hpp index e4ce5e19..82e1f18e 100644 --- a/src/nostalgia/studio/aboutpopup.hpp +++ b/src/nostalgia/studio/aboutpopup.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -7,13 +7,13 @@ #include #include -#include +#include #include "lib/popup.hpp" namespace nostalgia { - class AboutPopup: public studio::Popup { +class AboutPopup: public studio::Popup { public: enum class Stage { Closed, @@ -32,7 +32,7 @@ namespace nostalgia { [[nodiscard]] bool isOpen() const noexcept override; - void draw(core::Context *ctx) noexcept override; + void draw(turbine::Context *ctx) noexcept override; }; diff --git a/src/nostalgia/studio/clawviewer.cpp b/src/nostalgia/studio/clawviewer.cpp index 14cbf511..ff0b77b4 100644 --- a/src/nostalgia/studio/clawviewer.cpp +++ b/src/nostalgia/studio/clawviewer.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include @@ -22,7 +22,7 @@ ox::CRString ClawEditor::itemDisplayName() const noexcept { return m_itemDisplayName; } -void ClawEditor::draw(core::Context*) noexcept { +void ClawEditor::draw(turbine::Context*) noexcept { //const auto paneSize = ImGui::GetContentRegionAvail(); ImGui::BeginChild("PaletteEditor"); static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; diff --git a/src/nostalgia/studio/clawviewer.hpp b/src/nostalgia/studio/clawviewer.hpp index 09267122..30d8edc6 100644 --- a/src/nostalgia/studio/clawviewer.hpp +++ b/src/nostalgia/studio/clawviewer.hpp @@ -1,11 +1,11 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once #include -#include +#include #include "lib/editor.hpp" @@ -27,7 +27,7 @@ class ClawEditor: public studio::Editor { ox::CRString itemDisplayName() const noexcept final; - void draw(core::Context*) noexcept final; + void draw(turbine::Context*) noexcept final; private: static void drawRow(const ox::ModelValue &value) noexcept; diff --git a/src/nostalgia/studio/filedialogmanager.cpp b/src/nostalgia/studio/filedialogmanager.cpp index 8a7934dd..6eb23329 100644 --- a/src/nostalgia/studio/filedialogmanager.cpp +++ b/src/nostalgia/studio/filedialogmanager.cpp @@ -1,14 +1,14 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include +#include #include "filedialogmanager.hpp" namespace nostalgia { -studio::TaskState FileDialogManager::update(core::Context *ctx) noexcept { +studio::TaskState FileDialogManager::update(turbine::Context *ctx) noexcept { switch (m_state) { case UpdateProjectPathState::EnableSystemCursor: { // switch to system cursor in this update and open file dialog in the next @@ -20,7 +20,7 @@ studio::TaskState FileDialogManager::update(core::Context *ctx) noexcept { // switch to system cursor auto [path, err] = studio::chooseDirectory(); // Mac file dialog doesn't restore focus to main window when closed... - core::focusWindow(ctx); + turbine::focusWindow(*ctx); if (!err) { err = pathChosen.emitCheckError(path); oxAssert(err, "Path chosen response failed"); diff --git a/src/nostalgia/studio/filedialogmanager.hpp b/src/nostalgia/studio/filedialogmanager.hpp index 68c77fe0..be13067f 100644 --- a/src/nostalgia/studio/filedialogmanager.hpp +++ b/src/nostalgia/studio/filedialogmanager.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -7,7 +7,7 @@ #include #include -#include +#include #include "lib/filedialog.hpp" #include "lib/task.hpp" @@ -33,7 +33,7 @@ class FileDialogManager : public nostalgia::studio::Task { ~FileDialogManager() noexcept override = default; - nostalgia::studio::TaskState update(nostalgia::core::Context *ctx) noexcept final; + nostalgia::studio::TaskState update(turbine::Context *ctx) noexcept final; // signals ox::Signal pathChosen; diff --git a/src/nostalgia/studio/lib/configio.hpp b/src/nostalgia/studio/lib/configio.hpp index 9954dfb2..86ecebc2 100644 --- a/src/nostalgia/studio/lib/configio.hpp +++ b/src/nostalgia/studio/lib/configio.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include namespace nostalgia::studio { diff --git a/src/nostalgia/studio/lib/editor.cpp b/src/nostalgia/studio/lib/editor.cpp index 353868d1..3d4b1bc2 100644 --- a/src/nostalgia/studio/lib/editor.cpp +++ b/src/nostalgia/studio/lib/editor.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include @@ -26,7 +26,7 @@ void BaseEditor::paste() { void BaseEditor::exportFile() { } -void BaseEditor::keyStateChanged(core::Key, bool) { +void BaseEditor::keyStateChanged(turbine::Key, bool) { } void BaseEditor::onActivated() noexcept { diff --git a/src/nostalgia/studio/lib/editor.hpp b/src/nostalgia/studio/lib/editor.hpp index 5fc6e5a4..e083300a 100644 --- a/src/nostalgia/studio/lib/editor.hpp +++ b/src/nostalgia/studio/lib/editor.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -49,7 +49,7 @@ class NOSTALGIASTUDIO_EXPORT BaseEditor: public Widget { virtual void exportFile(); - virtual void keyStateChanged(core::Key key, bool down); + virtual void keyStateChanged(turbine::Key key, bool down); virtual void onActivated() noexcept; diff --git a/src/nostalgia/studio/lib/imguiuitl.hpp b/src/nostalgia/studio/lib/imguiuitl.hpp index cfe8b55a..797db670 100644 --- a/src/nostalgia/studio/lib/imguiuitl.hpp +++ b/src/nostalgia/studio/lib/imguiuitl.hpp @@ -1,13 +1,13 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include -#include +#include namespace nostalgia::studio::ig { -void centerNextWindow(core::Context *ctx) noexcept; +void centerNextWindow(turbine::Context *ctx) noexcept; } \ No newline at end of file diff --git a/src/nostalgia/studio/lib/imguiutil.cpp b/src/nostalgia/studio/lib/imguiutil.cpp index 5f2c9110..316f88d9 100644 --- a/src/nostalgia/studio/lib/imguiutil.cpp +++ b/src/nostalgia/studio/lib/imguiutil.cpp @@ -4,12 +4,12 @@ #include -#include +#include namespace nostalgia::studio::ig { -void centerNextWindow(core::Context *ctx) noexcept { - const auto sz = core::getScreenSize(ctx); +void centerNextWindow(turbine::Context *ctx) noexcept { + const auto sz = turbine::getScreenSize(*ctx); const auto screenW = static_cast(sz.width); const auto screenH = static_cast(sz.height); const auto mod = ImGui::GetWindowDpiScale() * 2; diff --git a/src/nostalgia/studio/lib/itemmaker.hpp b/src/nostalgia/studio/lib/itemmaker.hpp index 6a7d74c9..52eb8599 100644 --- a/src/nostalgia/studio/lib/itemmaker.hpp +++ b/src/nostalgia/studio/lib/itemmaker.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include "context.hpp" @@ -24,7 +24,7 @@ class ItemMaker { fileExt(std::move(pFileExt)) { } virtual ~ItemMaker() noexcept = default; - virtual ox::Error write(core::Context *ctx, ox::CRStringView pName) const noexcept = 0; + virtual ox::Error write(turbine::Context *ctx, ox::CRStringView pName) const noexcept = 0; }; template @@ -47,9 +47,9 @@ class ItemMakerT: public ItemMaker { item(std::forward(pItem)), fmt(pFmt) { } - ox::Error write(core::Context *ctx, ox::CRStringView pName) const noexcept override { + ox::Error write(turbine::Context *ctx, ox::CRStringView pName) const noexcept override { const auto path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt); - auto sctx = core::applicationData(ctx); + auto sctx = turbine::applicationData(*ctx); keel::createUuidMapping(ctx, path, ox::UUID::generate().unwrap()); return sctx->project->writeObj(path, &item, fmt); } diff --git a/src/nostalgia/studio/lib/module.cpp b/src/nostalgia/studio/lib/module.cpp index 0bab0e62..07657e07 100644 --- a/src/nostalgia/studio/lib/module.cpp +++ b/src/nostalgia/studio/lib/module.cpp @@ -6,11 +6,11 @@ namespace nostalgia::studio { -ox::Vector Module::editors(core::Context*) { +ox::Vector Module::editors(turbine::Context*) { return {}; } -ox::Vector> Module::itemMakers(core::Context*) { +ox::Vector> Module::itemMakers(turbine::Context*) { return {}; } diff --git a/src/nostalgia/studio/lib/module.hpp b/src/nostalgia/studio/lib/module.hpp index 1c754ce3..c509d8ea 100644 --- a/src/nostalgia/studio/lib/module.hpp +++ b/src/nostalgia/studio/lib/module.hpp @@ -9,8 +9,9 @@ #include #include +#include + #include -#include namespace nostalgia::studio { @@ -26,9 +27,9 @@ class Module { public: virtual ~Module() noexcept = default; - virtual ox::Vector editors(core::Context *ctx); + virtual ox::Vector editors(turbine::Context *ctx); - virtual ox::Vector> itemMakers(core::Context*); + virtual ox::Vector> itemMakers(turbine::Context*); }; diff --git a/src/nostalgia/studio/lib/popup.hpp b/src/nostalgia/studio/lib/popup.hpp index 7aeebe15..f16a42dc 100644 --- a/src/nostalgia/studio/lib/popup.hpp +++ b/src/nostalgia/studio/lib/popup.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include "imguiuitl.hpp" @@ -32,7 +32,7 @@ class Popup { [[nodiscard]] virtual bool isOpen() const noexcept = 0; - virtual void draw(core::Context *ctx) noexcept = 0; + virtual void draw(turbine::Context *ctx) noexcept = 0; protected: constexpr void setSize(ox::Size sz) noexcept { @@ -47,7 +47,7 @@ class Popup { return m_title; } - void drawWindow(core::Context *ctx, bool *open, auto drawContents) { + void drawWindow(turbine::Context *ctx, bool *open, auto drawContents) { studio::ig::centerNextWindow(ctx); ImGui::SetNextWindowSize(static_cast(m_size)); constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; diff --git a/src/nostalgia/studio/lib/task.cpp b/src/nostalgia/studio/lib/task.cpp index 77f0c63b..84c8f921 100644 --- a/src/nostalgia/studio/lib/task.cpp +++ b/src/nostalgia/studio/lib/task.cpp @@ -8,8 +8,8 @@ namespace nostalgia::studio { -void TaskRunner::update(core::Context *ctx) noexcept { - oxIgnoreError(m_tasks.erase(std::remove_if(m_tasks.begin(), m_tasks.end(), [&](auto &t) { +void TaskRunner::update(turbine::Context *ctx) noexcept { + oxIgnoreError(m_tasks.erase(std::remove_if(m_tasks.begin(), m_tasks.end(), [&](ox::UPtr &t) { const auto done = t->update(ctx) == TaskState::Done; if (done) { t->finished.emit(); diff --git a/src/nostalgia/studio/lib/task.hpp b/src/nostalgia/studio/lib/task.hpp index 6380c9f5..5810c570 100644 --- a/src/nostalgia/studio/lib/task.hpp +++ b/src/nostalgia/studio/lib/task.hpp @@ -6,7 +6,7 @@ #include -#include +#include namespace nostalgia::studio { @@ -19,14 +19,14 @@ class Task: public ox::SignalHandler { public: ox::Signal finished; ~Task() noexcept override = default; - virtual TaskState update(nostalgia::core::Context *ctx) noexcept = 0; + virtual TaskState update(turbine::Context *ctx) noexcept = 0; }; class TaskRunner { private: ox::Vector> m_tasks; public: - void update(core::Context *ctx) noexcept; + void update(turbine::Context *ctx) noexcept; void add(Task *task) noexcept; }; diff --git a/src/nostalgia/studio/lib/widget.hpp b/src/nostalgia/studio/lib/widget.hpp index 542078e0..3f13aac4 100644 --- a/src/nostalgia/studio/lib/widget.hpp +++ b/src/nostalgia/studio/lib/widget.hpp @@ -6,14 +6,14 @@ #include -#include +#include namespace nostalgia::studio { class Widget: public ox::SignalHandler { public: ~Widget() noexcept override = default; - virtual void draw(core::Context*) noexcept = 0; + virtual void draw(turbine::Context*) noexcept = 0; }; } diff --git a/src/nostalgia/studio/main.cpp b/src/nostalgia/studio/main.cpp index 63524244..364b1489 100644 --- a/src/nostalgia/studio/main.cpp +++ b/src/nostalgia/studio/main.cpp @@ -7,56 +7,55 @@ #include #include #include +#include +#include #include -#include -#include #include "lib/context.hpp" #include "studioapp.hpp" namespace nostalgia { -class StudioUIDrawer: public core::Drawer { +class StudioUIDrawer: public turbine::gl::Drawer { private: StudioUI *m_ui = nullptr; public: explicit StudioUIDrawer(StudioUI *ui) noexcept: m_ui(ui) { } protected: - void draw(core::Context*) noexcept final { + void draw(turbine::Context&) noexcept final { m_ui->draw(); } }; -static int updateHandler(core::Context *ctx) noexcept { - auto sctx = core::applicationData(ctx); +static int updateHandler(turbine::Context &ctx) noexcept { + auto sctx = turbine::applicationData(ctx); auto ui = dynamic_cast(sctx->ui); ui->update(); return 16; } -static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexcept { - auto sctx = core::applicationData(ctx); +static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept { + auto sctx = turbine::applicationData(ctx); auto ui = dynamic_cast(sctx->ui); ui->handleKeyEvent(key, down); } static ox::Error run(ox::UniquePtr fs) noexcept { - oxRequireM(ctx, core::init(std::move(fs), "NostalgiaStudio")); - core::setWindowTitle(ctx.get(), "Nostalgia Studio"); - core::setUpdateHandler(ctx.get(), updateHandler); - core::setKeyEventHandler(ctx.get(), keyEventHandler); - core::setConstantRefresh(ctx.get(), false); - core::gl::setMainViewEnabled(ctx.get(), false); + oxRequireM(ctx, turbine::init(std::move(fs), "NostalgiaStudio")); + turbine::setWindowTitle(*ctx, "Nostalgia Studio"); + turbine::setUpdateHandler(*ctx, updateHandler); + turbine::setKeyEventHandler(*ctx, keyEventHandler); + turbine::setConstantRefresh(*ctx, false); studio::StudioContext studioCtx; - core::setApplicationData(ctx.get(), &studioCtx); + turbine::setApplicationData(*ctx, &studioCtx); StudioUI ui(ctx.get()); studioCtx.ui = &ui; StudioUIDrawer drawer(&ui); - core::addCustomDrawer(ctx.get(), &drawer); - auto err = core::run(ctx.get()); - core::removeCustomDrawer(ctx.get(), &drawer); + turbine::gl::addDrawer(*ctx, &drawer); + const auto err = turbine::run(*ctx); + turbine::gl::removeDrawer(*ctx, &drawer); return err; } diff --git a/src/nostalgia/studio/newmenu.cpp b/src/nostalgia/studio/newmenu.cpp index 96862be8..f6ec475b 100644 --- a/src/nostalgia/studio/newmenu.cpp +++ b/src/nostalgia/studio/newmenu.cpp @@ -30,7 +30,7 @@ bool NewMenu::isOpen() const noexcept { return m_open; } -void NewMenu::draw(core::Context *ctx) noexcept { +void NewMenu::draw(turbine::Context *ctx) noexcept { switch (m_stage) { case Stage::Opening: ImGui::OpenPopup(title().c_str()); @@ -53,7 +53,7 @@ void NewMenu::addItemMaker(ox::UniquePtr im) noexcept { m_types.emplace_back(std::move(im)); } -void NewMenu::drawNewItemType(core::Context *ctx) noexcept { +void NewMenu::drawNewItemType(turbine::Context *ctx) noexcept { drawWindow(ctx, &m_open, [this] { auto items = ox_malloca(m_types.size() * sizeof(const char*), const char*, nullptr); for (auto i = 0u; const auto &im : m_types) { @@ -66,7 +66,7 @@ void NewMenu::drawNewItemType(core::Context *ctx) noexcept { }); } -void NewMenu::drawNewItemName(core::Context *ctx) noexcept { +void NewMenu::drawNewItemName(turbine::Context *ctx) noexcept { drawWindow(ctx, &m_open, [this, ctx] { const auto typeIdx = static_cast(m_selectedType); if (typeIdx < m_types.size()) { @@ -90,7 +90,7 @@ void NewMenu::drawFirstPageButtons() noexcept { } } -void NewMenu::drawLastPageButtons(core::Context *ctx) noexcept { +void NewMenu::drawLastPageButtons(turbine::Context *ctx) noexcept { ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 138); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20); if (ImGui::Button("Back")) { @@ -107,7 +107,7 @@ void NewMenu::drawLastPageButtons(core::Context *ctx) noexcept { } } -void NewMenu::finish(core::Context *ctx) noexcept { +void NewMenu::finish(turbine::Context *ctx) noexcept { const auto itemName = ox::String(m_itemName); const auto err = m_types[static_cast(m_selectedType)]->write(ctx, itemName); if (err) { diff --git a/src/nostalgia/studio/newmenu.hpp b/src/nostalgia/studio/newmenu.hpp index 7505a89b..d3197e5c 100644 --- a/src/nostalgia/studio/newmenu.hpp +++ b/src/nostalgia/studio/newmenu.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -8,8 +8,6 @@ #include #include -#include - #include "lib/itemmaker.hpp" #include "lib/popup.hpp" @@ -45,7 +43,7 @@ class NewMenu: public studio::Popup { [[nodiscard]] bool isOpen() const noexcept override; - void draw(core::Context *ctx) noexcept override; + void draw(turbine::Context *ctx) noexcept override; template void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept; @@ -56,15 +54,15 @@ class NewMenu: public studio::Popup { void addItemMaker(ox::UniquePtr im) noexcept; private: - void drawNewItemType(core::Context *ctx) noexcept; + void drawNewItemType(turbine::Context *ctx) noexcept; - void drawNewItemName(core::Context *ctx) noexcept; + void drawNewItemName(turbine::Context *ctx) noexcept; void drawFirstPageButtons() noexcept; - void drawLastPageButtons(core::Context *ctx) noexcept; + void drawLastPageButtons(turbine::Context *ctx) noexcept; - void finish(core::Context *ctx) noexcept; + void finish(turbine::Context *ctx) noexcept; }; diff --git a/src/nostalgia/studio/projectexplorer.cpp b/src/nostalgia/studio/projectexplorer.cpp index e24556bf..d652ac3f 100644 --- a/src/nostalgia/studio/projectexplorer.cpp +++ b/src/nostalgia/studio/projectexplorer.cpp @@ -31,11 +31,11 @@ buildProjectTreeModel(ProjectExplorer *explorer, ox::CRStringView name, ox::CRSt return out; } -ProjectExplorer::ProjectExplorer(core::Context *ctx) noexcept { +ProjectExplorer::ProjectExplorer(turbine::Context *ctx) noexcept { m_ctx = ctx; } -void ProjectExplorer::draw(core::Context *ctx) noexcept { +void ProjectExplorer::draw(turbine::Context *ctx) noexcept { const auto viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + 20)); ImGui::SetNextWindowSize(ImVec2(313, viewport->Size.y - 20)); diff --git a/src/nostalgia/studio/projectexplorer.hpp b/src/nostalgia/studio/projectexplorer.hpp index 7f613407..b98e8382 100644 --- a/src/nostalgia/studio/projectexplorer.hpp +++ b/src/nostalgia/studio/projectexplorer.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -15,11 +15,11 @@ namespace nostalgia { class ProjectExplorer: public studio::Widget { private: ox::UniquePtr m_treeModel; - core::Context *m_ctx = nullptr; + turbine::Context *m_ctx = nullptr; public: - explicit ProjectExplorer(core::Context *ctx) noexcept; + explicit ProjectExplorer(turbine::Context *ctx) noexcept; - void draw(core::Context *ctx) noexcept override; + void draw(turbine::Context *ctx) noexcept override; void setModel(ox::UniquePtr model) noexcept; diff --git a/src/nostalgia/studio/projecttreemodel.cpp b/src/nostalgia/studio/projecttreemodel.cpp index 946262cb..c41d20c5 100644 --- a/src/nostalgia/studio/projecttreemodel.cpp +++ b/src/nostalgia/studio/projecttreemodel.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include @@ -23,7 +23,7 @@ ProjectTreeModel::ProjectTreeModel(ProjectTreeModel &&other) noexcept: m_children(std::move(other.m_children)) { } -void ProjectTreeModel::draw(core::Context *ctx) const noexcept { +void ProjectTreeModel::draw(turbine::Context *ctx) const noexcept { constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; if (!m_children.empty()) { if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) { diff --git a/src/nostalgia/studio/projecttreemodel.hpp b/src/nostalgia/studio/projecttreemodel.hpp index 5efc598d..bf70b853 100644 --- a/src/nostalgia/studio/projecttreemodel.hpp +++ b/src/nostalgia/studio/projecttreemodel.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -7,7 +7,7 @@ #include #include -#include +#include namespace nostalgia { @@ -23,7 +23,7 @@ class ProjectTreeModel { ProjectTreeModel(ProjectTreeModel &&other) noexcept; - void draw(core::Context *ctx) const noexcept; + void draw(turbine::Context *ctx) const noexcept; void setChildren(ox::Vector> children) noexcept; diff --git a/src/nostalgia/studio/studioapp.cpp b/src/nostalgia/studio/studioapp.cpp index 1648e657..0fdf1b15 100644 --- a/src/nostalgia/studio/studioapp.cpp +++ b/src/nostalgia/studio/studioapp.cpp @@ -4,8 +4,8 @@ #include -#include #include +#include #include "lib/configio.hpp" #include "builtinmodules.hpp" @@ -31,7 +31,7 @@ oxModelBegin(StudioConfig) oxModelFieldRename(show_project_explorer, showProjectExplorer) oxModelEnd() -StudioUI::StudioUI(core::Context *ctx) noexcept { +StudioUI::StudioUI(turbine::Context *ctx) noexcept { m_ctx = ctx; m_projectExplorer = ox::make_unique(m_ctx); m_projectExplorer->fileChosen.connect(this, &StudioUI::openFile); @@ -61,11 +61,11 @@ void StudioUI::update() noexcept { m_taskRunner.update(m_ctx); } -void StudioUI::handleKeyEvent(core::Key key, bool down) noexcept { - const auto ctrlDown = core::buttonDown(m_ctx, core::Key::Mod_Ctrl); +void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept { + const auto ctrlDown = turbine::buttonDown(*m_ctx, turbine::Key::Mod_Ctrl); for (auto p : m_popups) { if (p->isOpen()) { - if (key == core::Key::Escape) { + if (key == turbine::Key::Escape) { p->close(); } return; @@ -73,34 +73,34 @@ void StudioUI::handleKeyEvent(core::Key key, bool down) noexcept { } if (down && ctrlDown) { switch (key) { - case core::Key::Num_1: + case turbine::Key::Num_1: toggleProjectExplorer(); break; - case core::Key::Alpha_C: + case turbine::Key::Alpha_C: m_activeEditor->copy(); break; - case core::Key::Alpha_N: + case turbine::Key::Alpha_N: m_newMenu.open(); break; - case core::Key::Alpha_O: + case turbine::Key::Alpha_O: m_taskRunner.add(ox::make(this, &StudioUI::openProject)); break; - case core::Key::Alpha_Q: - core::shutdown(m_ctx); + case turbine::Key::Alpha_Q: + turbine::requestShutdown(*m_ctx); break; - case core::Key::Alpha_S: + case turbine::Key::Alpha_S: save(); break; - case core::Key::Alpha_V: + case turbine::Key::Alpha_V: m_activeEditor->paste(); break; - case core::Key::Alpha_X: + case turbine::Key::Alpha_X: m_activeEditor->cut(); break; - case core::Key::Alpha_Y: + case turbine::Key::Alpha_Y: redo(); break; - case core::Key::Alpha_Z: + case turbine::Key::Alpha_Z: undo(); break; default: @@ -141,7 +141,7 @@ void StudioUI::drawMenu() noexcept { m_activeEditor->save(); } if (ImGui::MenuItem("Quit", "Ctrl+Q")) { - core::shutdown(m_ctx); + turbine::requestShutdown(*m_ctx); } ImGui::EndMenu(); } @@ -219,7 +219,7 @@ void StudioUI::drawTabs() noexcept { } if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] { m_activeEditor->onActivated(); - core::setConstantRefresh(m_ctx, m_activeEditor->requiresConstantRefresh()); + turbine::setConstantRefresh(*m_ctx, m_activeEditor->requiresConstantRefresh()); } e->draw(m_ctx); m_activeEditorOnLastDraw = e.get(); @@ -292,9 +292,9 @@ void StudioUI::save() noexcept { ox::Error StudioUI::openProject(ox::CRStringView path) noexcept { oxRequireM(fs, keel::loadRomFs(path)); oxReturnError(keel::setRomFs(m_ctx, std::move(fs))); - core::setWindowTitle(m_ctx, ox::sfmt("Nostalgia Studio - {}", path)); + turbine::setWindowTitle(*m_ctx, ox::sfmt("Nostalgia Studio - {}", path)); m_project = ox::make_unique(m_ctx, path); - auto sctx = applicationData(m_ctx); + auto sctx = applicationData(*m_ctx); sctx->project = m_project.get(); m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel); m_project->fileDeleted.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel); diff --git a/src/nostalgia/studio/studioapp.hpp b/src/nostalgia/studio/studioapp.hpp index 82fcc8c0..2b74bc01 100644 --- a/src/nostalgia/studio/studioapp.hpp +++ b/src/nostalgia/studio/studioapp.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once @@ -25,7 +25,7 @@ class StudioUI: public ox::SignalHandler { private: ox::UniquePtr m_project; studio::TaskRunner m_taskRunner; - core::Context *m_ctx = nullptr; + turbine::Context *m_ctx = nullptr; ox::Vector> m_editors; ox::Vector> m_widgets; ox::HashMap m_editorMakers; @@ -43,11 +43,11 @@ class StudioUI: public ox::SignalHandler { bool m_showProjectExplorer = true; public: - explicit StudioUI(core::Context *ctx) noexcept; + explicit StudioUI(turbine::Context *ctx) noexcept; void update() noexcept; - void handleKeyEvent(core::Key, bool down) noexcept; + void handleKeyEvent(turbine::Key, bool down) noexcept; [[nodiscard]] constexpr auto project() noexcept { diff --git a/src/turbine/CMakeLists.txt b/src/turbine/CMakeLists.txt new file mode 100644 index 00000000..2df5d051 --- /dev/null +++ b/src/turbine/CMakeLists.txt @@ -0,0 +1,40 @@ +add_library( + Turbine + event.cpp +) + +if(NOSTALGIA_BUILD_TYPE STREQUAL "GBA") + add_subdirectory(gba) +else() + add_subdirectory(glfw) +endif() + +if(NOT MSVC) + target_compile_options(Turbine PRIVATE -Wsign-conversion) +endif() + +target_link_libraries( + Turbine PUBLIC + Keel +) + +install( + FILES + clipboard.hpp + config.hpp + context.hpp + event.hpp + gfx.hpp + input.hpp + turbine.hpp + DESTINATION + include/turbine +) + +install( + TARGETS + Turbine + DESTINATION + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/src/turbine/clipboard.hpp b/src/turbine/clipboard.hpp new file mode 100644 index 00000000..c6107c4f --- /dev/null +++ b/src/turbine/clipboard.hpp @@ -0,0 +1,32 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include + +#include "context.hpp" + +namespace turbine { + +ox::String getClipboardText(class Context &ctx) noexcept; + +void setClipboardText(class Context &ctx, ox::CRStringView text) noexcept; + +template +void setClipboardObject([[maybe_unused]] class Context &ctx, [[maybe_unused]] ox::UniquePtr obj) noexcept { + ctx.clipboard = std::move(obj); +} + +template +ox::Result getClipboardObject([[maybe_unused]] class Context &ctx) noexcept { + if (ctx.clipboard && ctx.clipboard->typeMatch(T::TypeName, T::TypeVersion)) { + return static_cast(ctx.clipboard.get()); + } + return OxError(1); +} + +} diff --git a/src/turbine/config.hpp b/src/turbine/config.hpp new file mode 100644 index 00000000..dad2d9a1 --- /dev/null +++ b/src/turbine/config.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#if __has_include() +#endif + +#include + +namespace turbine::config { + +constexpr auto ImGuiEnabled = +#if __has_include() +true; +#else +false; +#endif + +constexpr auto GbaEventLoopTimerBased = false; +constexpr auto GbaTimerBits = 32; +constexpr auto GlFpsPrint = false; + +} diff --git a/src/turbine/context.hpp b/src/turbine/context.hpp new file mode 100644 index 00000000..9ca246ae --- /dev/null +++ b/src/turbine/context.hpp @@ -0,0 +1,93 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "event.hpp" +#include "input.hpp" + +namespace turbine { + +class BaseClipboardObject { + public: + virtual ~BaseClipboardObject() noexcept = default; + + [[nodiscard]] + virtual ox::String typeId() const noexcept = 0; + + [[nodiscard]] + constexpr auto typeMatch(auto name, auto version) const noexcept { + return typeId() == ox::buildTypeId(name, version); + } +}; + +template +class ClipboardObject: public BaseClipboardObject { + [[nodiscard]] + ox::String typeId() const noexcept final { + return ox::buildTypeId(T::TypeName, T::TypeVersion); + } +}; + +void shutdown(Context &ctx) noexcept; + +// User Input Output +class Context: public keel::Context { + friend constexpr void setApplicationData(Context &ctx, void *applicationData) noexcept; + template + friend constexpr T *applicationData(Context &ctx) noexcept; + friend void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept; + friend constexpr void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept; + friend constexpr KeyEventHandler keyEventHandler(Context &ctx) noexcept; + + public: + UpdateHandler updateHandler = [](Context&) -> int {return 0;}; + ox::UPtr clipboard; + + protected: + KeyEventHandler m_keyEventHandler = nullptr; + void *m_applicationData = nullptr; + + public: + Context() noexcept = default; + + Context(Context &other) noexcept = delete; + Context(const Context &other) noexcept = delete; + Context(const Context &&other) noexcept = delete; + + inline ~Context() noexcept { + shutdown(*this); + } + +}; + +constexpr void setApplicationData(Context &ctx, void *applicationData) noexcept { + ctx.m_applicationData = applicationData; +} + +template +[[nodiscard]] +constexpr T *applicationData(Context &ctx) noexcept { + return static_cast(ctx.m_applicationData); +} + +constexpr void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept { + ctx.m_keyEventHandler = h; +} + +constexpr KeyEventHandler keyEventHandler(Context &ctx) noexcept { + return ctx.m_keyEventHandler; +} + +void setConstantRefresh(Context &ctx, bool r) noexcept; + +} + diff --git a/src/turbine/event.cpp b/src/turbine/event.cpp new file mode 100644 index 00000000..d0f3c04c --- /dev/null +++ b/src/turbine/event.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include "context.hpp" +#include "event.hpp" + +namespace turbine { + +void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept { + ctx.updateHandler = h; +} + +} \ No newline at end of file diff --git a/src/turbine/event.hpp b/src/turbine/event.hpp new file mode 100644 index 00000000..5d4cfbca --- /dev/null +++ b/src/turbine/event.hpp @@ -0,0 +1,17 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +namespace turbine { + +class Context; + +using UpdateHandler = int(*)(Context&); + +// Sets event handler that sleeps for the time given in the return value. The +// sleep time is a minimum of ~16 milliseconds. +void setUpdateHandler(Context &ctx, UpdateHandler) noexcept; + +} diff --git a/src/turbine/gba/CMakeLists.txt b/src/turbine/gba/CMakeLists.txt new file mode 100644 index 00000000..769ec8fa --- /dev/null +++ b/src/turbine/gba/CMakeLists.txt @@ -0,0 +1,16 @@ +enable_language(CXX ASM) +set_source_files_properties(turbine.arm.cpp irq.arm.cpp PROPERTIES COMPILE_FLAGS -marm) +target_sources( + Turbine PRIVATE + clipboard.cpp + gfx.cpp + irq.arm.cpp + irq.s + turbine.arm.cpp + turbine.cpp +) + +target_link_libraries( + Turbine PUBLIC + TeaGBA +) diff --git a/src/turbine/gba/clipboard.cpp b/src/turbine/gba/clipboard.cpp new file mode 100644 index 00000000..947ce099 --- /dev/null +++ b/src/turbine/gba/clipboard.cpp @@ -0,0 +1,19 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +namespace turbine { + +ox::String getClipboardText(Context&) noexcept { + return {}; +} + +void setClipboardText(Context&, ox::CRStringView) noexcept { + +} + +} diff --git a/src/nostalgia/core/gba/context.hpp b/src/turbine/gba/context.hpp similarity index 81% rename from src/nostalgia/core/gba/context.hpp rename to src/turbine/gba/context.hpp index d78459ee..8eba212e 100644 --- a/src/nostalgia/core/gba/context.hpp +++ b/src/turbine/gba/context.hpp @@ -11,11 +11,11 @@ #include -#include +#include "../context.hpp" -namespace nostalgia::core::gba { +namespace turbine::gba { -class Context: public core::Context { +class Context: public turbine::Context { public: bool running = true; diff --git a/src/turbine/gba/gfx.cpp b/src/turbine/gba/gfx.cpp new file mode 100644 index 00000000..8fefc9bb --- /dev/null +++ b/src/turbine/gba/gfx.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include + +#include +#include +#include + +#include + +namespace turbine { + +ox::Error initGfx(Context&) noexcept { + REG_DISPCTL = teagba::DispCtl_Mode0 + | teagba::DispCtl_SpriteMap1D + | teagba::DispCtl_Obj; + // tell display to trigger vblank interrupts + REG_DISPSTAT = REG_DISPSTAT | teagba::DispStat_irq_vblank; + // enable vblank interrupt + REG_IE = REG_IE | teagba::Int_vblank; + return {}; +} + +void setWindowTitle(Context&, ox::CRStringView) noexcept { +} + +int getScreenWidth(Context&) noexcept { + return 240; +} + +int getScreenHeight(Context&) noexcept { + return 160; +} + +ox::Size getScreenSize(Context&) noexcept { + return {240, 160}; +} + +} diff --git a/src/turbine/gba/irq.arm.cpp b/src/turbine/gba/irq.arm.cpp new file mode 100644 index 00000000..0f2aa56f --- /dev/null +++ b/src/turbine/gba/irq.arm.cpp @@ -0,0 +1,36 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +// NOTE: this file is compiled as ARM and not THUMB, so don't but too much in +// here + +#include +#include +#include + +#include "turbine.hpp" + +namespace turbine { + +volatile gba_timer_t g_timerMs = 0; + +} + +using namespace turbine; + +extern "C" { + +void turbine_isr_vblank() { + teagba::applySpriteUpdates(); + if constexpr(config::GbaEventLoopTimerBased) { + // disable vblank interrupt until it is needed again + REG_IE = REG_IE & ~teagba::Int_vblank; + } +} + +void turbine_isr_timer0() { + g_timerMs = g_timerMs + 1; +} + +} diff --git a/src/nostalgia/core/gba/irq.s b/src/turbine/gba/irq.s similarity index 92% rename from src/nostalgia/core/gba/irq.s rename to src/turbine/gba/irq.s index 0d9cf93f..6937bc93 100644 --- a/src/nostalgia/core/gba/irq.s +++ b/src/turbine/gba/irq.s @@ -1,5 +1,5 @@ // -// Copyright 2016 - 2020 gary@drinkingtea.net +// Copyright 2016 - 2023 gary@drinkingtea.net // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -10,8 +10,8 @@ .arm .align -.extern nostalgia_core_isr_vblank -.extern nostalgia_core_isr_timer0 +.extern turbine_isr_vblank +.extern turbine_isr_timer0 .equ REG_IFBIOS, 0x03007ff8 @@ -62,11 +62,11 @@ isr: //////////////////////////////////////////////////// cmp r1, #Int_timer0 - ldreq r0, =nostalgia_core_isr_timer0 + ldreq r0, =turbine_isr_timer0 beq isr_call_handler cmp r1, #Int_vblank - ldreq r0, =nostalgia_core_isr_vblank + ldreq r0, =turbine_isr_vblank beq isr_call_handler //////////////////////////////////////////////////// diff --git a/src/turbine/gba/turbine.arm.cpp b/src/turbine/gba/turbine.arm.cpp new file mode 100644 index 00000000..e3cf2520 --- /dev/null +++ b/src/turbine/gba/turbine.arm.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include +#include + +#include "context.hpp" +#include "turbine.hpp" + +namespace turbine { + +extern volatile gba_timer_t g_timerMs; +gba_timer_t g_wakeupTime; + +ox::Error run(Context &ctx) noexcept { + g_wakeupTime = 0; + const auto gbaCtx = static_cast(&ctx); + while (gbaCtx->running) { + if (g_wakeupTime <= g_timerMs && ctx.updateHandler) { + auto sleepTime = ctx.updateHandler(ctx); + if (sleepTime >= 0) { + g_wakeupTime = g_timerMs + static_cast(sleepTime); + } else { + g_wakeupTime = ~gba_timer_t(0); + } + } + if constexpr(config::GbaEventLoopTimerBased) { + // wait for timer interrupt + teagba_intrwait( + 0, + teagba::Int_timer0 | teagba::Int_timer1 | teagba::Int_timer2 | teagba::Int_timer3); + } else { + teagba_vblankintrwait(); + } + } + return {}; +} + +} diff --git a/src/turbine/gba/turbine.cpp b/src/turbine/gba/turbine.cpp new file mode 100644 index 00000000..981d0341 --- /dev/null +++ b/src/turbine/gba/turbine.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include + +#include +#include + +#include "context.hpp" +#include "turbine.hpp" + +extern "C" void isr(); + +namespace turbine { + +// Timer Consts +constexpr int NanoSecond = 1000000000; +constexpr int MilliSecond = 1000; +constexpr int TicksMs59ns = 65535 - (NanoSecond / MilliSecond) / 59.59; + +extern volatile gba_timer_t g_timerMs; + +static void initIrq() noexcept { + REG_ISR = isr; + REG_IME = 1; // enable interrupts +} + +static void initTimer() noexcept { + // make timer0 a ~1 millisecond timer + REG_TIMER0 = TicksMs59ns; + REG_TIMER0CTL = 0b11000000; + // enable interrupt for timer0 + REG_IE = REG_IE | teagba::Int_timer0; +} + +static ox::Result findPreloadSection() noexcept { + // put the header in the wrong order to prevent mistaking this code for the + // media section + constexpr auto headerP2 = "DER_____________"; + constexpr auto headerP1 = "KEEL_PRELOAD_HEA"; + constexpr auto headerP1Len = ox_strlen(headerP2); + constexpr auto headerP2Len = ox_strlen(headerP1); + constexpr auto headerLen = headerP1Len + headerP2Len; + for (auto current = MEM_ROM; current < reinterpret_cast(0x0a000000); current += headerLen) { + if (ox_memcmp(current, headerP1, headerP1Len) == 0 && + ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) { + return reinterpret_cast(current + headerLen); + } + } + return OxError(1); +} + +ox::Result> init(ox::UPtr fs, ox::CRStringView appName) noexcept { + oxRequireM(ctx, keel::init(std::move(fs), appName)); + oxReturnError(findPreloadSection().moveTo(&ctx->preloadSectionOffset)); + oxReturnError(initGfx(*ctx)); + initTimer(); + initIrq(); + return ox::UPtr(std::move(ctx)); +} + +void shutdown(Context&) noexcept { +} + +uint64_t ticksMs(Context&) noexcept { + return g_timerMs; +} + +bool buttonDown(Context&, Key k) noexcept { + return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast(k))); +} + +void requestShutdown(Context &ctx) noexcept { + const auto gbaCtx = static_cast(&ctx); + gbaCtx->running = false; +} + +} diff --git a/src/turbine/gba/turbine.hpp b/src/turbine/gba/turbine.hpp new file mode 100644 index 00000000..f41b7234 --- /dev/null +++ b/src/turbine/gba/turbine.hpp @@ -0,0 +1,15 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include "../config.hpp" + +namespace turbine { + +using gba_timer_t = ox::Uint; + +} diff --git a/src/turbine/gfx.hpp b/src/turbine/gfx.hpp new file mode 100644 index 00000000..00b3be65 --- /dev/null +++ b/src/turbine/gfx.hpp @@ -0,0 +1,42 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "context.hpp" + +namespace turbine { + +namespace gl { +class Drawer { + public: + virtual ~Drawer() = default; + virtual void draw(Context&) noexcept = 0; +}; +void addDrawer(Context &ctx, Drawer *cd) noexcept; +void removeDrawer(Context &ctx, Drawer *cd) noexcept; +} + +ox::Error initGfx(Context &ctx) noexcept; + +void setWindowTitle(Context &ctx, ox::CRStringView title) noexcept; + +void focusWindow(Context &ctx) noexcept; + +[[nodiscard]] +int getScreenWidth(Context &ctx) noexcept; + +[[nodiscard]] +int getScreenHeight(Context &ctx) noexcept; + +[[nodiscard]] +ox::Size getScreenSize(Context &ctx) noexcept; + +} diff --git a/src/nostalgia/core/glfw/CMakeLists.txt b/src/turbine/glfw/CMakeLists.txt similarity index 60% rename from src/nostalgia/core/glfw/CMakeLists.txt rename to src/turbine/glfw/CMakeLists.txt index 60e5a47b..abddf74e 100644 --- a/src/nostalgia/core/glfw/CMakeLists.txt +++ b/src/turbine/glfw/CMakeLists.txt @@ -1,11 +1,12 @@ target_sources( - NostalgiaCore PRIVATE + Turbine PRIVATE clipboard.cpp - core.cpp gfx.cpp + turbine.cpp ) target_link_libraries( - NostalgiaCore PUBLIC + Turbine PUBLIC + glad glfw imgui ) diff --git a/src/turbine/glfw/clipboard.cpp b/src/turbine/glfw/clipboard.cpp new file mode 100644 index 00000000..894ad604 --- /dev/null +++ b/src/turbine/glfw/clipboard.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +#include + +#include "context.hpp" + +namespace turbine { + +ox::String getClipboardText(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); + return glfwGetClipboardString(gctx.window); +} + +void setClipboardText(Context &ctx, ox::CRStringView text) noexcept { + auto &gctx = static_cast(ctx); + auto cstr = ox_malloca(text.bytes() + 1, char); + ox_strncpy(cstr.get(), text.data(), text.bytes()); + glfwSetClipboardString(gctx.window, cstr); +} + +} diff --git a/src/turbine/glfw/context.hpp b/src/turbine/glfw/context.hpp new file mode 100644 index 00000000..65e9bd22 --- /dev/null +++ b/src/turbine/glfw/context.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +namespace turbine { + +struct GlfwContext: public turbine::Context { + int uninterruptedRefreshes = 3; + ox::UPtr clipboard; + struct GLFWwindow *window = nullptr; + // sets screen refresh to constant instead of only on event + bool constantRefresh = true; + ox::Vector drawers; + int64_t startTime = 0; + uint64_t wakeupTime = 0; + uint64_t keysDown = 0; + uint64_t prevFpsCheckTime = 0; + uint64_t draws = 0; +}; + +} diff --git a/src/nostalgia/core/glfw/gfx.cpp b/src/turbine/glfw/gfx.cpp similarity index 79% rename from src/nostalgia/core/glfw/gfx.cpp rename to src/turbine/glfw/gfx.cpp index e1b06922..7e1aa149 100644 --- a/src/nostalgia/core/glfw/gfx.cpp +++ b/src/turbine/glfw/gfx.cpp @@ -8,12 +8,30 @@ #include -#include -#include +#include -#include "core.hpp" +#include "context.hpp" -namespace nostalgia::core { +namespace turbine { + +namespace gl { + +void addDrawer(Context &ctx, Drawer *cd) noexcept { + auto &gctx = static_cast(ctx); + gctx.drawers.emplace_back(cd); +} + +void removeDrawer(Context &ctx, Drawer *cd) noexcept { + auto &gctx = static_cast(ctx); + for (auto i = 0u; i < gctx.drawers.size(); ++i) { + if (gctx.drawers[i] == cd) { + oxIgnoreError(gctx.drawers.erase(i)); + break; + } + } +} + +} constexpr auto Scale = 5; @@ -21,11 +39,11 @@ static void handleGlfwError(int err, const char *desc) noexcept { oxErrf("GLFW error ({}): {}\n", err, desc); } -static auto setKeyDownStatus(GlfwImplData *id, Key key, bool down) noexcept { +static auto setKeyDownStatus(GlfwContext *gctx, Key key, bool down) noexcept { if (down) { - id->keysDown |= 1llu << static_cast(key); + gctx->keysDown |= 1llu << static_cast(key); } else { - id->keysDown &= ~(1llu << static_cast(key)); + gctx->keysDown &= ~(1llu << static_cast(key)); } } @@ -47,18 +65,18 @@ static void handleKeyPress(Context *ctx, int key, bool down) noexcept { map[GLFW_KEY_ESCAPE] = Key::Escape; return map; }(); - const auto eventHandler = keyEventHandler(ctx); - const auto id = ctx->windowerData(); + const auto eventHandler = keyEventHandler(*ctx); + auto &gctx = static_cast(*ctx); const auto k = keyMap[static_cast(key)]; - setKeyDownStatus(id, k, down); + setKeyDownStatus(&gctx, k, down); if (eventHandler) { - eventHandler(ctx, k, down); + eventHandler(*ctx, k, down); } //if constexpr(ox::defines::Debug) { // switch (key) { // case GLFW_KEY_ESCAPE: // case GLFW_KEY_Q: - // oxIgnoreError(shutdown(ctx)); + // oxIgnoreError(requestShutdown(ctx)); // break; // default: // break; @@ -70,7 +88,7 @@ static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept { } static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept { - const auto ctx = static_cast(glfwGetWindowUserPointer(window)); + const auto ctx = static_cast(glfwGetWindowUserPointer(window)); ctx->uninterruptedRefreshes = 25; } @@ -78,7 +96,7 @@ static void handleGlfwWindowResize(GLFWwindow*, int, int) noexcept { } static void handleGlfwKeyEvent(GLFWwindow *window, int key, int, int action, int) noexcept { - const auto ctx = static_cast(glfwGetWindowUserPointer(window)); + const auto ctx = static_cast(glfwGetWindowUserPointer(window)); ctx->uninterruptedRefreshes = 25; if (action == GLFW_PRESS) { handleKeyPress(ctx, key, true); @@ -178,12 +196,8 @@ static void themeImgui() noexcept { style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.3499999940395355); } -void ImGui_Impl_NewFrame() noexcept { - ImGui_ImplGlfw_NewFrame(); -} - -ox::Error initGfx(Context *ctx) noexcept { - auto id = ctx->windowerData(); +ox::Error initGfx(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); glfwSetErrorCallback(handleGlfwError); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); @@ -192,18 +206,18 @@ ox::Error initGfx(Context *ctx) noexcept { glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); } glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - auto cstr = ox_malloca(ctx->appName.bytes() + 1, char); - ox_strncpy(cstr.get(), ctx->appName.data(), ctx->appName.bytes()); - id->window = glfwCreateWindow(240 * Scale, 160 * Scale, cstr, nullptr, nullptr); - if (id->window == nullptr) { + auto cstr = ox_malloca(ctx.appName.bytes() + 1, char); + ox_strncpy(cstr.get(), ctx.appName.data(), ctx.appName.bytes()); + gctx.window = glfwCreateWindow(240 * Scale, 160 * Scale, cstr, nullptr, nullptr); + if (gctx.window == nullptr) { return OxError(1, "Could not open GLFW window"); } - glfwSetCursorPosCallback(id->window, handleGlfwCursorPosEvent); - glfwSetMouseButtonCallback(id->window, handleGlfwMouseButtonEvent); - glfwSetWindowSizeCallback(id->window, handleGlfwWindowResize); - glfwSetKeyCallback(id->window, handleGlfwKeyEvent); - glfwSetWindowUserPointer(id->window, ctx); - glfwMakeContextCurrent(id->window); + glfwSetCursorPosCallback(gctx.window, handleGlfwCursorPosEvent); + glfwSetMouseButtonCallback(gctx.window, handleGlfwMouseButtonEvent); + glfwSetWindowSizeCallback(gctx.window, handleGlfwWindowResize); + glfwSetKeyCallback(gctx.window, handleGlfwKeyEvent); + glfwSetWindowUserPointer(gctx.window, &ctx); + glfwMakeContextCurrent(gctx.window); if (!gladLoadGLES2Loader(reinterpret_cast(glfwGetProcAddress))) { return OxError(2, "Could not init Glad"); } @@ -213,44 +227,48 @@ ox::Error initGfx(Context *ctx) noexcept { auto &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; //io.MouseDrawCursor = true; - ImGui_ImplGlfw_InitForOpenGL(id->window, true); + ImGui_ImplGlfw_InitForOpenGL(gctx.window, true); + themeImgui(); } - themeImgui(); - oxReturnError(renderer::init(ctx)); - return OxError(0); + return {}; } -void setWindowTitle(Context *ctx, ox::CRStringView title) noexcept { - const auto id = ctx->windowerData(); +void setWindowTitle(Context &ctx, ox::CRStringView title) noexcept { + auto &gctx = static_cast(ctx); auto cstr = ox_malloca(title.bytes() + 1, char); ox_strncpy(cstr.get(), title.data(), title.bytes()); - glfwSetWindowTitle(id->window, cstr); + glfwSetWindowTitle(gctx.window, cstr); } -void focusWindow(Context *ctx) noexcept { - const auto id = ctx->windowerData(); - glfwFocusWindow(id->window); +void focusWindow(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); + glfwFocusWindow(gctx.window); } -int getScreenWidth(Context *ctx) noexcept { - auto id = ctx->windowerData(); +int getScreenWidth(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); int w = 0, h = 0; - glfwGetFramebufferSize(id->window, &w, &h); + glfwGetFramebufferSize(gctx.window, &w, &h); return w; } -int getScreenHeight(Context *ctx) noexcept { - auto id = ctx->windowerData(); +int getScreenHeight(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); int w = 0, h = 0; - glfwGetFramebufferSize(id->window, &w, &h); + glfwGetFramebufferSize(gctx.window, &w, &h); return h; } -ox::Size getScreenSize(Context *ctx) noexcept { - auto id = ctx->windowerData(); +ox::Size getScreenSize(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); int w = 0, h = 0; - glfwGetFramebufferSize(id->window, &w, &h); + glfwGetFramebufferSize(gctx.window, &w, &h); return {w, h}; } +void setConstantRefresh(Context &ctx, bool r) noexcept { + auto &gctx = static_cast(ctx); + gctx.constantRefresh = r; +} + } diff --git a/src/turbine/glfw/turbine.cpp b/src/turbine/glfw/turbine.cpp new file mode 100644 index 00000000..53e053fb --- /dev/null +++ b/src/turbine/glfw/turbine.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include +#include + +#include +#include + +#include "../config.hpp" +#include "context.hpp" + +namespace turbine { + +ox::Result> init(ox::UPtr fs, ox::CRStringView appName) noexcept { + oxRequireM(ctx, keel::init(std::move(fs), appName)); + using namespace std::chrono; + ctx->startTime = duration_cast(system_clock::now().time_since_epoch()).count(); + glfwInit(); + oxReturnError(initGfx(*ctx)); + return ox::UPtr(ctx.release()); +} + +static void tickFps(GlfwContext &gctx, uint64_t nowMs) noexcept { + ++gctx.draws; + if (gctx.draws >= 500) { + const auto duration = static_cast(nowMs - gctx.prevFpsCheckTime) / 1000.0; + const auto fps = static_cast(static_cast(gctx.draws) / duration); + if constexpr(config::GlFpsPrint) { + oxOutf("FPS: {}\n", fps); + } + oxTracef("turbine::fps", "FPS: {}", fps); + gctx.prevFpsCheckTime = nowMs; + gctx.draws = 0; + } +} + +ox::Error run(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); + int sleepTime = 0; + while (!glfwWindowShouldClose(gctx.window)) { + glfwPollEvents(); + const auto ticks = ticksMs(gctx); + if (gctx.wakeupTime <= ticks) { + sleepTime = gctx.updateHandler(gctx); + if (sleepTime >= 0) { + gctx.wakeupTime = ticks + static_cast(sleepTime); + } else { + gctx.wakeupTime = ~uint64_t(0); + } + } else { + sleepTime = 10; + } + tickFps(gctx, ticks); + // draw start + if constexpr(turbine::config::ImGuiEnabled) { + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + } + for (auto d : gctx.drawers) { + d->draw(gctx); + } + if constexpr(turbine::config::ImGuiEnabled) { + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } + // draw end + glfwSwapBuffers(gctx.window); + if (!gctx.constantRefresh) { + if (gctx.uninterruptedRefreshes) { + --gctx.uninterruptedRefreshes; + } else { + glfwWaitEventsTimeout(sleepTime); + } + } + } + shutdown(gctx); + return {}; +} + +void shutdown(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); + if (gctx.window) { + glfwDestroyWindow(gctx.window); + gctx.window = nullptr; + } +} + +uint64_t ticksMs(const Context &ctx) noexcept { + using namespace std::chrono; + auto &gctx = static_cast(ctx); + const auto now = duration_cast(system_clock::now().time_since_epoch()).count(); + return static_cast(now - gctx.startTime); +} + +bool buttonDown(const Context &ctx, Key key) noexcept { + auto &gctx = static_cast(ctx); + return (gctx.keysDown >> static_cast(key)) & 1; +} + +void requestShutdown(Context &ctx) noexcept { + auto &gctx = static_cast(ctx); + glfwSetWindowShouldClose(gctx.window, true); +} + +} diff --git a/src/nostalgia/core/input.hpp b/src/turbine/input.hpp similarity index 79% rename from src/nostalgia/core/input.hpp rename to src/turbine/input.hpp index b28da1f1..15f65d67 100644 --- a/src/nostalgia/core/input.hpp +++ b/src/turbine/input.hpp @@ -1,12 +1,12 @@ /* - * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once #include -namespace nostalgia::core { +namespace turbine { enum Key { // GBA implementation currently relies on GamePad entry order @@ -72,8 +72,8 @@ enum Key { class Context; [[nodiscard]] -bool buttonDown(Context *ctx, Key) noexcept; +bool buttonDown(const Context &ctx, Key) noexcept; -using KeyEventHandler = void(*)(Context*, Key, bool); +using KeyEventHandler = void(*)(Context&, Key, bool); } diff --git a/src/turbine/turbine.hpp b/src/turbine/turbine.hpp new file mode 100644 index 00000000..e80f83fd --- /dev/null +++ b/src/turbine/turbine.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include "clipboard.hpp" +#include "event.hpp" +#include "gfx.hpp" +#include "input.hpp" + +namespace turbine { + +ox::Result> init(ox::UniquePtr fs, ox::CRStringView appName = "Nostalgia") noexcept; + +void shutdown(Context &ctx) noexcept; + +ox::Error run(Context &ctx) noexcept; + +// Returns the number of milliseconds that have passed since the start of the +// program. +[[nodiscard]] +uint64_t ticksMs(const Context &ctx) noexcept; + +void requestShutdown(Context &ctx) noexcept; + +}