[nostalgia] Break part of core out into Turbine and TeaGBA libraries
This commit is contained in:
parent
07284ac595
commit
8c43baedea
@ -47,7 +47,7 @@ include_directories(
|
||||
)
|
||||
|
||||
if(BUILDCORE_TARGET STREQUAL "gba")
|
||||
add_subdirectory(deps/gbastartup)
|
||||
add_subdirectory(deps/teagba)
|
||||
else()
|
||||
include_directories(
|
||||
SYSTEM
|
||||
|
12
deps/gbastartup/CMakeLists.txt
vendored
12
deps/gbastartup/CMakeLists.txt
vendored
@ -1,12 +0,0 @@
|
||||
enable_language(CXX ASM)
|
||||
|
||||
add_library(
|
||||
GbaStartup OBJECT
|
||||
gba_crt0.s
|
||||
cstartup.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
GbaStartup PUBLIC
|
||||
OxStd
|
||||
)
|
1
deps/teagba/CMakeLists.txt
vendored
Normal file
1
deps/teagba/CMakeLists.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(src)
|
@ -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
|
22
deps/teagba/include/teagba/bios.hpp
vendored
Normal file
22
deps/teagba/include/teagba/bios.hpp
vendored
Normal file
@ -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();
|
||||
|
||||
}
|
46
deps/teagba/include/teagba/gfx.hpp
vendored
Normal file
46
deps/teagba/include/teagba/gfx.hpp
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/stddef.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
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<GbaSpriteAttrUpdate, 128> g_spriteBuffer;
|
||||
|
||||
void addSpriteUpdate(const GbaSpriteAttrUpdate &upd) noexcept;
|
||||
|
||||
void applySpriteUpdates() noexcept;
|
||||
|
||||
}
|
@ -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 <ox/std/types.hpp>
|
||||
|
||||
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;
|
@ -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);
|
21
deps/teagba/src/CMakeLists.txt
vendored
Normal file
21
deps/teagba/src/CMakeLists.txt
vendored
Normal file
@ -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
|
||||
)
|
37
deps/teagba/src/bios.s
vendored
Normal file
37
deps/teagba/src/bios.s
vendored
Normal file
@ -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
|
@ -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
|
40
deps/teagba/src/gfx.cpp
vendored
Normal file
40
deps/teagba/src/gfx.cpp
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/bios.hpp>
|
||||
#include <teagba/irq.hpp>
|
||||
|
||||
#include <teagba/gfx.hpp>
|
||||
|
||||
namespace teagba {
|
||||
|
||||
volatile uint16_t g_spriteUpdates = 0;
|
||||
ox::Array<GbaSpriteAttrUpdate, 128> 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<uint16_t>(~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<const uint64_t*>(&oa);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
include_directories(".")
|
||||
|
||||
add_subdirectory(keel)
|
||||
add_subdirectory(nostalgia)
|
||||
add_subdirectory(nostalgia)
|
||||
add_subdirectory(turbine)
|
@ -18,6 +18,7 @@ ox::Result<ox::UPtr<Ctx>> init(ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView a
|
||||
auto ctx = ox::make_unique<Ctx>();
|
||||
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<ox::UPtr<Ctx>> init(ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView a
|
||||
ctx->packTransforms.emplace_back(c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
ox::String getClipboardText(class Context *ctx) noexcept;
|
||||
|
||||
void setClipboardText(class Context *ctx, ox::CRStringView text) noexcept;
|
||||
|
||||
template<typename T>
|
||||
void setClipboardObject([[maybe_unused]] class Context *ctx, [[maybe_unused]] ox::UniquePtr<T> obj) noexcept {
|
||||
#ifndef OX_BARE_METAL
|
||||
ctx->clipboard = std::move(obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Result<T*> getClipboardObject([[maybe_unused]] class Context *ctx) noexcept {
|
||||
#ifndef OX_BARE_METAL
|
||||
if (ctx->clipboard && ctx->clipboard->typeMatch(T::TypeName, T::TypeVersion)) {
|
||||
return static_cast<T*>(ctx->clipboard.get());
|
||||
}
|
||||
#endif
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
}
|
@ -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(<imgui.h>)
|
||||
#endif
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
namespace nostalgia::core::config {
|
||||
|
||||
constexpr auto ImGuiEnabled =
|
||||
#if __has_include(<imgui.h>)
|
||||
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<int>(SdlVsync::Adaptive);
|
||||
|
||||
}
|
||||
|
16
src/nostalgia/core/context.cpp
Normal file
16
src/nostalgia/core/context.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -10,68 +10,28 @@
|
||||
#include <ox/std/size.hpp>
|
||||
|
||||
#include <keel/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#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<typename T>
|
||||
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<typename T>
|
||||
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<struct TileSheetData> loadTileSheet(Context *ctx,
|
||||
const struct CompactTileSheet &tilesheetAddr) noexcept;
|
||||
friend ox::Error run(Context *ctx) noexcept;
|
||||
friend void shutdown(Context *ctx) noexcept;
|
||||
friend ox::Result<ox::UPtr<Context>> init(ox::UPtr<ox::FileSystem> 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<Drawer*, 5> drawers;
|
||||
|
||||
#ifndef OX_BARE_METAL
|
||||
int uninterruptedRefreshes = 3;
|
||||
ox::UPtr<BaseClipboardObject> 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<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *windowerData() noexcept {
|
||||
return static_cast<T*>(m_windowerData);
|
||||
}
|
||||
~Context() noexcept;
|
||||
|
||||
protected:
|
||||
constexpr void setWindowerData(void *windowerData) noexcept {
|
||||
m_windowerData = windowerData;
|
||||
}
|
||||
|
||||
constexpr void setRendererData(void *rendererData) noexcept {
|
||||
m_rendererData = rendererData;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *rendererData() noexcept {
|
||||
return static_cast<T*>(m_rendererData);
|
||||
auto &rom() noexcept {
|
||||
return *turbineCtx->rom;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
constexpr void setApplicationData(Context *ctx, void *applicationData) noexcept {
|
||||
ctx->m_customData = applicationData;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *applicationData(Context *ctx) noexcept {
|
||||
return static_cast<T*>(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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,27 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#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<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> 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<ox::UniquePtr<Context>> init(turbine::Context *tctx, const InitParams& = {}) noexcept;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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
|
||||
)
|
@ -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();
|
||||
|
||||
}
|
@ -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
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <nostalgia/core/config.hpp>
|
||||
#include <nostalgia/core/core.hpp>
|
||||
|
||||
#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<gba::Context*>(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<unsigned>(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);
|
||||
}
|
||||
|
||||
}
|
@ -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 <nostalgia/core/config.hpp>
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <nostalgia/core/input.hpp>
|
||||
#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<std::size_t> 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<char*>(0x0a000000); current += headerLen) {
|
||||
if (ox_memcmp(current, headerP1, headerP1Len) == 0 &&
|
||||
ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
|
||||
return reinterpret_cast<std::size_t>(current + headerLen);
|
||||
}
|
||||
}
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
|
||||
ox::UPtr<Context> ctx(ox::make<gba::Context>());
|
||||
ctx->rom = std::move(fs);
|
||||
ctx->appName = appName;
|
||||
oxReturnError(initGfx(ctx.get()));
|
||||
initTimer();
|
||||
initIrq();
|
||||
oxReturnError(findPreloadSection().moveTo(&ctx->preloadSectionOffset));
|
||||
ox::Result<ox::UniquePtr<Context>> init(turbine::Context *tctx, const InitParams ¶ms) noexcept {
|
||||
ox::UPtr<Context> ctx = ox::make_unique<Context>();
|
||||
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<int>(k)));
|
||||
}
|
||||
|
||||
void shutdown(Context *ctx) noexcept {
|
||||
const auto gbaCtx = static_cast<gba::Context*>(ctx);
|
||||
gbaCtx->running = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
#include <nostalgia/core/config.hpp>
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
using gba_timer_t = ox::Uint<config::GbaTimerBits>;
|
||||
|
||||
}
|
@ -6,27 +6,26 @@
|
||||
#include <ox/mc/mc.hpp>
|
||||
#include <ox/std/array.hpp>
|
||||
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/gfx.hpp>
|
||||
#include <teagba/registers.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
|
||||
#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<BgCbbData, 4> 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<GbaTileMapTarget> auto *t)
|
||||
return io->template field<uint8_t, decltype(handleTileMap)>("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<ox::FileSystem>(romFs);
|
||||
auto romFs = new(ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs);
|
||||
oxRequireM(tctx, turbine::init(ox::UPtr<ox::FileSystem>(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<ox::MemFS*>(ctx->rom.get())->directAccess(tilesheetAddr));
|
||||
oxRequire(tsStat, ctx->rom().stat(tilesheetAddr));
|
||||
oxRequire(ts, static_cast<ox::MemFS*>(&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<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&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<ox::MemFS*>(ctx->rom.get())->directAccess(tilesheetAddr));
|
||||
oxRequire(tsStat, ctx->rom().stat(tilesheetAddr));
|
||||
oxRequire(ts, static_cast<ox::MemFS*>(&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<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&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<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&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<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&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<unsigned>(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<uint16_t>(~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<uint16_t>(y & ox::onMask<uint8_t>(0b111'1111))
|
||||
| (static_cast<uint16_t>(1) << 10) // enable alpha
|
||||
| (static_cast<uint16_t>(spriteShape) << 14);
|
||||
@ -270,21 +233,7 @@ void setSprite(Context*,
|
||||
| (static_cast<uint16_t>(spriteSize) << 14);
|
||||
oa.attr2 = static_cast<uint16_t>(tileIdx & ox::onMask<uint16_t>(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<uint16_t>(~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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <ox/std/stddef.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
#include <nostalgia/core/config.hpp>
|
||||
#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<GbaSpriteAttrUpdate, config::GbaSpriteBufferLen> g_spriteBuffer;
|
||||
|
||||
ox::Error initGfx(Context *ctx, const InitParams &) noexcept;
|
||||
}
|
||||
|
@ -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<GbaSpriteAttrUpdate, config::GbaSpriteBufferLen> 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<const uint64_t*>(&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;
|
||||
}
|
||||
|
||||
}
|
@ -7,32 +7,43 @@
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
|
||||
#include "addresses.hpp"
|
||||
#include "bios.hpp"
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/bios.hpp>
|
||||
#include <teagba/gfx.hpp>
|
||||
|
||||
#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<int64_t>(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<ErrorCode>(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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <ox/std/std.hpp>
|
||||
|
||||
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<std::string, int(*)(std::string)> 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;
|
||||
}
|
||||
|
@ -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<char, 128> 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);
|
||||
}
|
||||
|
@ -19,13 +19,6 @@ namespace nostalgia::core {
|
||||
|
||||
extern ox::Array<char, 128> 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;
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
ox::String getClipboardText(Context *ctx) noexcept {
|
||||
const auto id = ctx->windowerData<GlfwImplData>();
|
||||
return glfwGetClipboardString(id->window);
|
||||
}
|
||||
|
||||
void setClipboardText(Context *ctx, ox::CRStringView text) noexcept {
|
||||
const auto id = ctx->windowerData<GlfwImplData>();
|
||||
auto cstr = ox_malloca(text.bytes() + 1, char);
|
||||
ox_strncpy(cstr.get(), text.data(), text.bytes());
|
||||
glfwSetClipboardString(id->window, cstr);
|
||||
}
|
||||
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <keel/keel.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
#include <nostalgia/core/opengl/gfx.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
ox::Result<ox::UPtr<Context>> init(ox::UPtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
|
||||
oxRequireM(ctx, keel::init<Context>(std::move(fs), appName));
|
||||
const auto id = ox::make<GlfwImplData>();
|
||||
ctx->setWindowerData(id);
|
||||
using namespace std::chrono;
|
||||
id->startTime = duration_cast<milliseconds>(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<GlfwImplData>();
|
||||
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<unsigned>(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<void>());
|
||||
glfwDestroyWindow(id->window);
|
||||
ctx->setWindowerData(nullptr);
|
||||
ox::safeDelete(id);
|
||||
return OxError(0);
|
||||
}
|
||||
|
||||
void setUpdateHandler(Context *ctx, UpdateHandler h) noexcept {
|
||||
const auto id = ctx->windowerData<GlfwImplData>();
|
||||
id->eventHandler = h;
|
||||
}
|
||||
|
||||
uint64_t ticksMs(Context *ctx) noexcept {
|
||||
using namespace std::chrono;
|
||||
const auto id = ctx->windowerData<GlfwImplData>();
|
||||
const auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
return static_cast<uint64_t>(now - id->startTime);
|
||||
}
|
||||
|
||||
bool buttonDown(Context *ctx, Key key) noexcept {
|
||||
const auto id = ctx->windowerData<GlfwImplData>();
|
||||
return (id->keysDown >> static_cast<int>(key)) & 1;
|
||||
}
|
||||
|
||||
void shutdown(Context *ctx) noexcept {
|
||||
const auto id = ctx->windowerData<GlfwImplData>();
|
||||
glfwSetWindowShouldClose(id->window, true);
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
13
src/nostalgia/core/initparams.hpp
Normal file
13
src/nostalgia/core/initparams.hpp
Normal file
@ -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;
|
||||
};
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
target_sources(
|
||||
NostalgiaCore PRIVATE
|
||||
core.cpp
|
||||
gfx.cpp
|
||||
gfx_opengl.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
NostalgiaCore PUBLIC
|
||||
|
26
src/nostalgia/core/opengl/context.hpp
Normal file
26
src/nostalgia/core/opengl/context.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
#include <nostalgia/glutils/glutils.hpp>
|
||||
|
||||
#include "gfx.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
struct GlContext: public core::Context {
|
||||
glutils::GLProgram bgShader;
|
||||
glutils::GLProgram spriteShader;
|
||||
ox::Array<renderer::CBB, 4> cbbs;
|
||||
renderer::SpriteBlockset spriteBlocks;
|
||||
ox::Array<Sprite, 128> spriteStates;
|
||||
ox::Array<renderer::Background, 4> backgrounds;
|
||||
ox::Optional<ox::Size> renderSize;
|
||||
renderer::Drawer drawer;
|
||||
};
|
||||
|
||||
}
|
21
src/nostalgia/core/opengl/core.cpp
Normal file
21
src/nostalgia/core/opengl/core.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
ox::Result<ox::UniquePtr<Context>> init(turbine::Context *tctx, const InitParams ¶ms) noexcept {
|
||||
ox::UPtr<Context> ctx = ox::make_unique<GlContext>();
|
||||
ctx->turbineCtx = tctx;
|
||||
oxReturnError(initGfx(ctx.get(), params));
|
||||
return ctx;
|
||||
}
|
||||
|
||||
}
|
@ -2,13 +2,337 @@
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/fmt.hpp>
|
||||
#include <ox/std/vec.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
|
||||
#include "gfx.hpp"
|
||||
#include <nostalgia/glutils/glutils.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
|
||||
#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<float>(textureRow);
|
||||
const float L = flipX ? 1 : 0;
|
||||
const float R = flipX ? 0 : 1;
|
||||
const ox::Array<float, SpriteVertexVboLength> 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<GLuint, SpriteVertexEboLength> 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<float>(textureRow);
|
||||
const ox::Array<float, BgVertexVboLength> 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<GLuint, BgVertexEboLength> 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<std::size_t>(SpriteVertexVboLength)];
|
||||
auto ebo = &bs->elements[i * static_cast<std::size_t>(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<std::size_t>(BgVertexVboLength)];
|
||||
auto ebo = &bg->elements[i * static_cast<std::size_t>(BgVertexEboLength)];
|
||||
setTileBufferObject(ctx, i * BgVertexVboRows, static_cast<float>(x), static_cast<float>(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<GLuint>(glGetAttribLocation(shader, "vEnabled"));
|
||||
glEnableVertexAttribArray(enabledAttr);
|
||||
glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr);
|
||||
auto posAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vPosition"));
|
||||
glEnableVertexAttribArray(posAttr);
|
||||
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(1 * sizeof(float)));
|
||||
auto texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord"));
|
||||
glEnableVertexAttribArray(texCoordAttr);
|
||||
glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(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<GLuint>(glGetAttribLocation(shader, "vPosition"));
|
||||
glEnableVertexAttribArray(posAttr);
|
||||
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), nullptr);
|
||||
auto texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord"));
|
||||
glEnableVertexAttribArray(texCoordAttr);
|
||||
glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(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<GLsizei>(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<GLint>(glGetUniformLocation(gctx->bgShader, "vXScale"));
|
||||
const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(gctx->bgShader, "vTileHeight"));
|
||||
const auto [wi, hi] = gl::getRenderSize(gctx);
|
||||
const auto wf = static_cast<float>(wi);
|
||||
const auto hf = static_cast<float>(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<float>(tileRows));
|
||||
drawBackground(&cbb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawSprites(GlContext *gctx) noexcept {
|
||||
glUseProgram(gctx->spriteShader);
|
||||
auto &sb = gctx->spriteBlocks;
|
||||
const auto uniformXScale = static_cast<GLint>(glGetUniformLocation(gctx->bgShader, "vXScale"));
|
||||
const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(gctx->spriteShader, "vTileHeight"));
|
||||
const auto [wi, hi] = gl::getRenderSize(gctx);
|
||||
const auto wf = static_cast<float>(wi);
|
||||
const auto hf = static_cast<float>(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<float>(tileRows));
|
||||
// draw
|
||||
glBindTexture(GL_TEXTURE_2D, sb.tex);
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(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<GLfloat, ColorCnt * 4> 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<GLint>(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<GlContext*>(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<GlContext*>(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<TileSheetData> loadTileSheet(Context *ctx, const CompactTileSheet &tilesheet) noexcept {
|
||||
const auto gctx = static_cast<GlContext*>(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<TileSheetData> loadTileSheet(Context *ctx, const CompactTileSheet &ti
|
||||
pixels[i * 2 + 1] = tilesheet.pixels[i] >> 4;
|
||||
}
|
||||
}
|
||||
const auto rd = ctx->rendererData<void>();
|
||||
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<CompactTileSheet>(ctx, tilesheetAddr));
|
||||
oxRequire(palette, readObj<Palette>(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||
auto &kctx = *ctx->turbineCtx;
|
||||
const auto gctx = static_cast<GlContext*>(ctx);
|
||||
oxRequire(tilesheet, readObj<CompactTileSheet>(&kctx, tilesheetAddr));
|
||||
oxRequire(palette, readObj<Palette>(&kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
|
||||
const auto rd = ctx->rendererData<void>();
|
||||
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<CompactTileSheet>(ctx, tilesheetAddr));
|
||||
oxRequire(palette, readObj<Palette>(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||
auto &kctx = *ctx->turbineCtx;
|
||||
const auto gctx = static_cast<GlContext*>(ctx);
|
||||
oxRequire(tilesheet, readObj<CompactTileSheet>(&kctx, tilesheetAddr));
|
||||
oxRequire(palette, readObj<Palette>(&kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
|
||||
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
|
||||
const auto rd = ctx->rendererData<void>();
|
||||
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<int>(col + i), row, static_cast<uint8_t>(charMap[static_cast<uint8_t>(str[i])]));
|
||||
}
|
||||
}
|
||||
void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbbIdx) noexcept {
|
||||
const auto gctx = static_cast<GlContext*>(ctx);
|
||||
auto &bg = gctx->backgrounds[bgIdx];
|
||||
bg.cbbIdx = cbbIdx;
|
||||
}
|
||||
|
||||
uint8_t bgStatus(Context *ctx) noexcept {
|
||||
const auto gctx = static_cast<GlContext*>(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<GlContext*>(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<GlContext*>(ctx);
|
||||
return gctx->backgrounds[bg].enabled;
|
||||
}
|
||||
|
||||
void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept {
|
||||
const auto gctx = static_cast<GlContext*>(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<GlContext*>(ctx);
|
||||
auto &bg = gctx->cbbs[static_cast<std::size_t>(bgIdx)];
|
||||
initBackgroundBufferObjects(ctx, &bg);
|
||||
bg.updated = true;
|
||||
}
|
||||
|
||||
void hideSprite(Context *ctx, unsigned idx) noexcept {
|
||||
auto &gctx = *static_cast<GlContext*>(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<ox::Vec<unsigned>, 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<int>(x) % 255;
|
||||
const auto uY = static_cast<int>(y + 8) % 255 - 8;
|
||||
auto &gctx = static_cast<GlContext&>(*ctx);
|
||||
auto i = 0u;
|
||||
const auto set = [&](int xIt, int yIt) {
|
||||
const auto fX = static_cast<float>(uX + xIt * 8) / 8;
|
||||
const auto fY = static_cast<float>(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<int>(dim.y); ++yIt) {
|
||||
for (auto xIt = 0u; xIt < dim.x; ++xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(yIt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto yIt = 0u; yIt < dim.y; ++yIt) {
|
||||
for (auto xIt = dim.x - 1; xIt < ~0u; --xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(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<GlContext*>(ctx);
|
||||
const auto z = static_cast<unsigned>(bgIdx);
|
||||
const auto y = static_cast<unsigned>(row);
|
||||
const auto x = static_cast<unsigned>(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<float>(x), static_cast<float>(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<GlContext*>(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<GlContext*>(ctx);
|
||||
gctx->renderSize.emplace(width, height);
|
||||
}
|
||||
|
||||
void clearRenderSize(core::Context *ctx) noexcept {
|
||||
const auto gctx = static_cast<GlContext*>(ctx);
|
||||
gctx->renderSize.reset();
|
||||
}
|
||||
|
||||
ox::Size getRenderSize(core::Context *ctx) noexcept {
|
||||
const auto gctx = static_cast<GlContext*>(ctx);
|
||||
if (gctx->renderSize.has_value()) {
|
||||
return gctx->renderSize.value();
|
||||
} else {
|
||||
return turbine::getScreenSize(*ctx->turbineCtx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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 <ox/std/types.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/gfx.hpp>
|
||||
|
||||
namespace nostalgia::core {
|
||||
struct Palette;
|
||||
}
|
||||
#include <nostalgia/glutils/glutils.hpp>
|
||||
|
||||
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;
|
||||
}
|
@ -1,585 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <imgui_impl_opengl3.h>
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/fmt.hpp>
|
||||
#include <ox/std/vec.hpp>
|
||||
|
||||
#include <nostalgia/glutils/glutils.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <nostalgia/core/config.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
|
||||
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<CBB, 4> cbbs;
|
||||
SpriteBlockset spriteBlocks;
|
||||
ox::Array<Sprite, 128> spriteStates;
|
||||
ox::Array<Background, 4> backgrounds;
|
||||
ox::Optional<ox::Size> 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<float>(textureRow);
|
||||
const float L = flipX ? 1 : 0;
|
||||
const float R = flipX ? 0 : 1;
|
||||
const ox::Array<float, SpriteVertexVboLength> 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<GLuint, SpriteVertexEboLength> 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<float>(textureRow);
|
||||
const ox::Array<float, BgVertexVboLength> 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<GLuint, BgVertexEboLength> 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<std::size_t>(SpriteVertexVboLength)];
|
||||
auto ebo = &bs->elements[i * static_cast<std::size_t>(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<std::size_t>(BgVertexVboLength)];
|
||||
auto ebo = &bg->elements[i * static_cast<std::size_t>(BgVertexEboLength)];
|
||||
setTileBufferObject(ctx, i * BgVertexVboRows, static_cast<float>(x), static_cast<float>(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<GLuint>(glGetAttribLocation(shader, "vEnabled"));
|
||||
glEnableVertexAttribArray(enabledAttr);
|
||||
glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr);
|
||||
auto posAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vPosition"));
|
||||
glEnableVertexAttribArray(posAttr);
|
||||
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(1 * sizeof(float)));
|
||||
auto texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord"));
|
||||
glEnableVertexAttribArray(texCoordAttr);
|
||||
glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(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<GLuint>(glGetAttribLocation(shader, "vPosition"));
|
||||
glEnableVertexAttribArray(posAttr);
|
||||
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), nullptr);
|
||||
auto texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord"));
|
||||
glEnableVertexAttribArray(texCoordAttr);
|
||||
glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(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<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
const auto duration = static_cast<double>(now - id->prevFpsCheckTime) / 1000.0;
|
||||
const auto fps = static_cast<int>(static_cast<double>(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<GLsizei>(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<GLint>(glGetUniformLocation(id->bgShader, "vXScale"));
|
||||
const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vTileHeight"));
|
||||
const auto [wi, hi] = gl::getRenderSize(ctx);
|
||||
const auto wf = static_cast<float>(wi);
|
||||
const auto hf = static_cast<float>(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<float>(tileRows));
|
||||
drawBackground(&cbb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawSprites(core::Context *ctx, GlImplData *id) noexcept {
|
||||
glUseProgram(id->spriteShader);
|
||||
auto &sb = id->spriteBlocks;
|
||||
const auto uniformXScale = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vXScale"));
|
||||
const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->spriteShader, "vTileHeight"));
|
||||
const auto [wi, hi] = gl::getRenderSize(ctx);
|
||||
const auto wf = static_cast<float>(wi);
|
||||
const auto hf = static_cast<float>(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<float>(tileRows));
|
||||
// draw
|
||||
glBindTexture(GL_TEXTURE_2D, sb.tex);
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(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<GlImplData>();
|
||||
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<GlImplData*>(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<GLfloat, ColorCnt * 4> 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<GLint>(glGetUniformLocation(shaderPgrm, "fPalette"));
|
||||
glUniform4fv(uniformPalette, ColorCnt, palette.data());
|
||||
}
|
||||
|
||||
void loadBgPalette(void *rendererData, const Palette &pal) noexcept {
|
||||
const auto id = static_cast<GlImplData*>(rendererData);
|
||||
loadPalette(id->bgShader, pal);
|
||||
}
|
||||
|
||||
void loadSpritePalette(void *rendererData, const Palette &pal) noexcept {
|
||||
const auto id = static_cast<GlImplData*>(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<GlImplData*>(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<GlImplData*>(rendererData);
|
||||
id->spriteBlocks.tex = loadTexture(w, h, pixels);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbbIdx) noexcept {
|
||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
auto &bg = id->backgrounds[bgIdx];
|
||||
bg.cbbIdx = cbbIdx;
|
||||
}
|
||||
|
||||
uint8_t bgStatus(Context *ctx) noexcept {
|
||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
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<renderer::GlImplData>();
|
||||
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<renderer::GlImplData>();
|
||||
return id->backgrounds[bg].enabled;
|
||||
}
|
||||
|
||||
void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept {
|
||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
id->backgrounds[bg].enabled = status;
|
||||
}
|
||||
|
||||
|
||||
void draw(Context *ctx) noexcept {
|
||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
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<renderer::GlImplData>();
|
||||
auto &bg = id->cbbs[static_cast<std::size_t>(bgIdx)];
|
||||
initBackgroundBufferObjects(ctx, &bg);
|
||||
bg.updated = true;
|
||||
}
|
||||
|
||||
void hideSprite(Context *ctx, unsigned idx) noexcept {
|
||||
auto &id = *ctx->rendererData<renderer::GlImplData>();
|
||||
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<ox::Vec<unsigned>, 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<int>(x) % 255;
|
||||
const auto uY = static_cast<int>(y + 8) % 255 - 8;
|
||||
auto &id = *ctx->rendererData<renderer::GlImplData>();
|
||||
auto i = 0u;
|
||||
const auto set = [&](int xIt, int yIt) {
|
||||
const auto fX = static_cast<float>(uX + xIt * 8) / 8;
|
||||
const auto fY = static_cast<float>(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<int>(dim.y); ++yIt) {
|
||||
for (auto xIt = 0u; xIt < dim.x; ++xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(yIt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto yIt = 0u; yIt < dim.y; ++yIt) {
|
||||
for (auto xIt = dim.x - 1; xIt < ~0u; --xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(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<renderer::GlImplData>();
|
||||
const auto z = static_cast<unsigned>(bgIdx);
|
||||
const auto y = static_cast<unsigned>(row);
|
||||
const auto x = static_cast<unsigned>(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<float>(x), static_cast<float>(y),
|
||||
tile, vbo, ebo);
|
||||
bg.updated = true;
|
||||
}
|
||||
|
||||
namespace gl {
|
||||
|
||||
void setMainViewEnabled(core::Context *ctx, bool enabled) noexcept {
|
||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
id->mainViewEnabled = enabled;
|
||||
}
|
||||
|
||||
void drawMainView(core::Context *ctx) noexcept {
|
||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
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<renderer::GlImplData>();
|
||||
id->renderSize.emplace(width, height);
|
||||
}
|
||||
|
||||
void clearRenderSize(core::Context *ctx) noexcept {
|
||||
auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
id->renderSize.reset();
|
||||
}
|
||||
|
||||
ox::Size getRenderSize(core::Context *ctx) noexcept {
|
||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||
if (id->renderSize.has_value()) {
|
||||
return id->renderSize.value();
|
||||
} else {
|
||||
return core::getScreenSize(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <nostalgia/core/config.hpp>
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
#include <nostalgia/core/input.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
static event_handler g_eventHandler = nullptr;
|
||||
static uint64_t g_wakeupTime;
|
||||
|
||||
void draw(Context *ctx);
|
||||
|
||||
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, const char *appName) noexcept {
|
||||
auto ctx = ox::make_unique<Context>();
|
||||
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<SdlImplData>();
|
||||
// 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<unsigned>(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<SdlImplData>();
|
||||
id->running = false;
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
struct SdlImplData {
|
||||
SDL_Window *window = nullptr;
|
||||
SDL_GLContext renderer = nullptr;
|
||||
int64_t startTime = 0;
|
||||
uint64_t wakeupTime = 0;
|
||||
bool running = false;
|
||||
};
|
||||
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <ox/claw/read.hpp>
|
||||
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
#include <nostalgia/core/userland/gfx.hpp>
|
||||
|
||||
#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<SdlImplData>();
|
||||
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<SdlImplData>();
|
||||
int x = 0, y = 0;
|
||||
SDL_GetWindowSize(id->window, &x, &y);
|
||||
return x;
|
||||
}
|
||||
|
||||
int getScreenHeight(Context *ctx) noexcept {
|
||||
auto id = ctx->windowerData<SdlImplData>();
|
||||
int x = 0, y = 0;
|
||||
SDL_GetWindowSize(id->window, &x, &y);
|
||||
return y;
|
||||
}
|
||||
|
||||
common::Size getScreenSize(Context *ctx) noexcept {
|
||||
auto id = ctx->windowerData<SdlImplData>();
|
||||
int x = 0, y = 0;
|
||||
SDL_GetWindowSize(id->window, &x, &y);
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
}
|
@ -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 <ox/std/memory.hpp>
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
ox::Vector<studio::EditorMaker> StudioModule::editors(core::Context *ctx) noexcept {
|
||||
ox::Vector<studio::EditorMaker> StudioModule::editors(turbine::Context *ctx) noexcept {
|
||||
return {
|
||||
{
|
||||
{FileExt_ng},
|
||||
@ -32,7 +32,7 @@ ox::Vector<studio::EditorMaker> StudioModule::editors(core::Context *ctx) noexce
|
||||
};
|
||||
}
|
||||
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> StudioModule::itemMakers(core::Context*) noexcept {
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> StudioModule::itemMakers(turbine::Context*) noexcept {
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> out;
|
||||
out.emplace_back(ox::make<studio::ItemMakerT<core::TileSheet>>("Tile Sheet", "TileSheets", "ng"));
|
||||
out.emplace_back(ox::make<studio::ItemMakerT<core::Palette>>("Palette", "Palettes", "npal"));
|
||||
|
@ -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<studio::EditorMaker> editors(core::Context *ctx) noexcept override;
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> itemMakers(core::Context*) noexcept override;
|
||||
ox::Vector<studio::EditorMaker> editors(turbine::Context *ctx) noexcept override;
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> itemMakers(turbine::Context*) noexcept override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,8 +13,13 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
ox::Result<PaletteEditorImGui*> PaletteEditorImGui::make(Context *ctx, ox::CRStringView path) noexcept {
|
||||
auto out = ox::UniquePtr<PaletteEditorImGui>(new PaletteEditorImGui);
|
||||
ox::Result<PaletteEditorImGui*> PaletteEditorImGui::make(turbine::Context *ctx, ox::CRStringView path) noexcept {
|
||||
ox::UniquePtr<PaletteEditorImGui> out;
|
||||
try {
|
||||
out = ox::UniquePtr<PaletteEditorImGui>(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<studio::StudioContext>(m_ctx);
|
||||
const auto sctx = applicationData<studio::StudioContext>(*m_ctx);
|
||||
oxReturnError(sctx->project->writeObj(m_itemPath, &m_pal));
|
||||
oxReturnError(m_ctx->assetManager.setAsset(m_itemPath, m_pal));
|
||||
return OxError(0);
|
||||
|
@ -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<PaletteEditorImGui*> make(Context *ctx, ox::CRStringView path) noexcept;
|
||||
static ox::Result<PaletteEditorImGui*> 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;
|
||||
|
@ -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<studio::StudioContext>(m_ctx);
|
||||
auto sctx = applicationData<studio::StudioContext>(*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<std::size_t>(static_cast<uint32_t>(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<std::size_t>(static_cast<uint32_t>(key - turbine::Key::Num_1), colorCnt - 1);
|
||||
m_tileSheetEditor.setPalIdx(idx);
|
||||
} else if (key == core::Key::Num_0 && colorCnt >= 10) {
|
||||
auto idx = ox::min<std::size_t>(static_cast<uint32_t>(key - core::Key::Num_1 + 9), colorCnt - 1);
|
||||
} else if (key == turbine::Key::Num_0 && colorCnt >= 10) {
|
||||
auto idx = ox::min<std::size_t>(static_cast<uint32_t>(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<studio::StudioContext>(m_ctx);
|
||||
auto sctx = applicationData<studio::StudioContext>(*m_ctx);
|
||||
const auto &files = sctx->project->fileList(core::FileExt_npal);
|
||||
const auto first = m_selectedPaletteIdx < files.size() ?
|
||||
files[m_selectedPaletteIdx].c_str() : "";
|
||||
|
@ -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<ox::String> 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);
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <ox/std/buffer.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
|
||||
#include <nostalgia/core/clipboard.hpp>
|
||||
#include <turbine/clipboard.hpp>
|
||||
#include <keel/media.hpp>
|
||||
|
||||
#include "tilesheeteditormodel.hpp"
|
||||
@ -19,7 +19,7 @@ const Palette TileSheetEditorModel::s_defaultPalette = {
|
||||
.colors = ox::Vector<Color16>(128),
|
||||
};
|
||||
|
||||
class TileSheetClipboard: public ClipboardObject<TileSheetClipboard> {
|
||||
class TileSheetClipboard: public turbine::ClipboardObject<TileSheetClipboard> {
|
||||
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<TileSheet>(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<CutPasteCommand<CommandId::Cut>>(&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<TileSheetClipboard>(m_ctx);
|
||||
auto [cb, err] = turbine::getClipboardObject<TileSheetClipboard>(*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<studio::StudioContext>(m_ctx);
|
||||
const auto sctx = applicationData<studio::StudioContext>(*m_ctx);
|
||||
oxReturnError(sctx->project->writeObj(m_path, &m_img));
|
||||
return m_ctx->assetManager.setAsset(m_path, m_img).error;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
||||
|
@ -46,6 +46,24 @@ template struct GLObject<deleteVertexArray>;
|
||||
template struct GLObject<deleteProgram>;
|
||||
template struct GLObject<deleteShader>;
|
||||
|
||||
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);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/bounds.hpp>
|
||||
#include <ox/std/defines.hpp>
|
||||
|
||||
#include <glad/glad.h>
|
||||
@ -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;
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <nostalgia/scene/scene.hpp>
|
||||
@ -12,7 +13,7 @@ using namespace nostalgia;
|
||||
static bool s_paused = false;
|
||||
static ox::Optional<scene::Scene> 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<ox::FileSystem> 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<scene::SceneStatic>(ctx.get(), SceneAddr));
|
||||
core::setUpdateHandler(ctx.get(), updateHandler);
|
||||
core::setKeyEventHandler(ctx.get(), keyEventHandler);
|
||||
oxRequire(scn, keel::readObj<scene::SceneStatic>(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);
|
||||
}
|
||||
|
@ -2,14 +2,12 @@
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ox/std/memory.hpp>
|
||||
|
||||
#include "sceneeditor-imgui.hpp"
|
||||
#include "module.hpp"
|
||||
|
||||
namespace nostalgia::scene {
|
||||
|
||||
ox::Vector<studio::EditorMaker> StudioModule::editors(core::Context *ctx) noexcept {
|
||||
ox::Vector<studio::EditorMaker> StudioModule::editors(turbine::Context *ctx) noexcept {
|
||||
return {
|
||||
{
|
||||
{"nscn"},
|
||||
@ -20,7 +18,7 @@ ox::Vector<studio::EditorMaker> StudioModule::editors(core::Context *ctx) noexce
|
||||
};
|
||||
}
|
||||
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> StudioModule::itemMakers(core::Context*) noexcept {
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> StudioModule::itemMakers(turbine::Context*) noexcept {
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> out;
|
||||
return out;
|
||||
}
|
||||
|
@ -4,14 +4,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include <nostalgia/studio/studio.hpp>
|
||||
|
||||
namespace nostalgia::scene {
|
||||
|
||||
class StudioModule: public studio::Module {
|
||||
public:
|
||||
ox::Vector<studio::EditorMaker> editors(core::Context *ctx) noexcept override;
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> itemMakers(core::Context*) noexcept override;
|
||||
ox::Vector<studio::EditorMaker> editors(turbine::Context *ctx) noexcept override;
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> itemMakers(turbine::Context*) noexcept override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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<int>(paneSize.x),
|
||||
@ -50,7 +50,7 @@ void SceneEditorImGui::onActivated() noexcept {
|
||||
}
|
||||
|
||||
ox::Error SceneEditorImGui::saveItem() noexcept {
|
||||
const auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
const auto sctx = applicationData<studio::StudioContext>(*m_ctx);
|
||||
oxReturnError(sctx->project->writeObj(m_itemPath, &m_editor.scene()));
|
||||
oxReturnError(m_ctx->assetManager.setAsset(m_itemPath, m_editor.scene()));
|
||||
return {};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<SceneStatic>(m_ctx, path));
|
||||
m_scene = *scn;
|
||||
|
@ -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;
|
||||
|
@ -2,20 +2,20 @@
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
#include <nostalgia/core/core.hpp>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,13 @@ namespace nostalgia::scene {
|
||||
class SceneEditorView {
|
||||
|
||||
private:
|
||||
core::Context *const m_ctx = nullptr;
|
||||
ox::UPtr<core::Context> 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;
|
||||
|
||||
|
@ -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 <imgui.h>
|
||||
@ -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;
|
||||
|
@ -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 <ox/event/signal.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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 <imgui.h>
|
||||
@ -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;
|
||||
|
@ -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 <ox/model/modelvalue.hpp>
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#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;
|
||||
|
@ -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 <nostalgia/core/gfx.hpp>
|
||||
#include <turbine/gfx.hpp>
|
||||
|
||||
#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");
|
||||
|
@ -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 <ox/event/signal.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#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<ox::Error(const ox::String &)> pathChosen;
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <keel/context.hpp>
|
||||
|
||||
namespace nostalgia::studio {
|
||||
|
||||
|
@ -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 <algorithm>
|
||||
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 <imgui.h>
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
namespace nostalgia::studio::ig {
|
||||
|
||||
void centerNextWindow(core::Context *ctx) noexcept;
|
||||
void centerNextWindow(turbine::Context *ctx) noexcept;
|
||||
|
||||
}
|
@ -4,12 +4,12 @@
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <turbine/gfx.hpp>
|
||||
|
||||
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<float>(sz.width);
|
||||
const auto screenH = static_cast<float>(sz.height);
|
||||
const auto mod = ImGui::GetWindowDpiScale() * 2;
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <ox/claw/claw.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#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<typename T>
|
||||
@ -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<studio::StudioContext>(ctx);
|
||||
auto sctx = turbine::applicationData<studio::StudioContext>(*ctx);
|
||||
keel::createUuidMapping(ctx, path, ox::UUID::generate().unwrap());
|
||||
return sctx->project->writeObj(path, &item, fmt);
|
||||
}
|
||||
|
@ -6,11 +6,11 @@
|
||||
|
||||
namespace nostalgia::studio {
|
||||
|
||||
ox::Vector<EditorMaker> Module::editors(core::Context*) {
|
||||
ox::Vector<EditorMaker> Module::editors(turbine::Context*) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Vector<ox::UniquePtr<ItemMaker>> Module::itemMakers(core::Context*) {
|
||||
ox::Vector<ox::UniquePtr<ItemMaker>> Module::itemMakers(turbine::Context*) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,9 @@
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#include <nostalgia/studio/studio.hpp>
|
||||
#include <nostalgia/core/context.hpp>
|
||||
|
||||
namespace nostalgia::studio {
|
||||
|
||||
@ -26,9 +27,9 @@ class Module {
|
||||
public:
|
||||
virtual ~Module() noexcept = default;
|
||||
|
||||
virtual ox::Vector<EditorMaker> editors(core::Context *ctx);
|
||||
virtual ox::Vector<EditorMaker> editors(turbine::Context *ctx);
|
||||
|
||||
virtual ox::Vector<ox::UniquePtr<ItemMaker>> itemMakers(core::Context*);
|
||||
virtual ox::Vector<ox::UniquePtr<ItemMaker>> itemMakers(turbine::Context*);
|
||||
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/vec.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#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<ImVec2>(m_size));
|
||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||
|
@ -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<studio::Task> &t) {
|
||||
const auto done = t->update(ctx) == TaskState::Done;
|
||||
if (done) {
|
||||
t->finished.emit();
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include <ox/event/signal.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
namespace nostalgia::studio {
|
||||
|
||||
@ -19,14 +19,14 @@ class Task: public ox::SignalHandler {
|
||||
public:
|
||||
ox::Signal<ox::Error()> 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<ox::UniquePtr<studio::Task>> m_tasks;
|
||||
public:
|
||||
void update(core::Context *ctx) noexcept;
|
||||
void update(turbine::Context *ctx) noexcept;
|
||||
void add(Task *task) noexcept;
|
||||
};
|
||||
|
||||
|
@ -6,14 +6,14 @@
|
||||
|
||||
#include <ox/event/signal.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -7,56 +7,55 @@
|
||||
#include <ox/logconn/logconn.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/uuid.hpp>
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include <nostalgia/appmodules/appmodules.hpp>
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <keel/media.hpp>
|
||||
|
||||
#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<studio::StudioContext>(ctx);
|
||||
static int updateHandler(turbine::Context &ctx) noexcept {
|
||||
auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
|
||||
auto ui = dynamic_cast<StudioUI*>(sctx->ui);
|
||||
ui->update();
|
||||
return 16;
|
||||
}
|
||||
|
||||
static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexcept {
|
||||
auto sctx = core::applicationData<studio::StudioContext>(ctx);
|
||||
static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept {
|
||||
auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
|
||||
auto ui = dynamic_cast<StudioUI*>(sctx->ui);
|
||||
ui->handleKeyEvent(key, down);
|
||||
}
|
||||
|
||||
static ox::Error run(ox::UniquePtr<ox::FileSystem> 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;
|
||||
}
|
||||
|
||||
|
@ -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<studio::ItemMaker> 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<std::size_t>(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<std::size_t>(m_selectedType)]->write(ctx, itemName);
|
||||
if (err) {
|
||||
|
@ -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 <ox/event/signal.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
|
||||
#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<typename T>
|
||||
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<studio::ItemMaker> 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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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<ProjectTreeModel> 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<ProjectTreeModel> model) noexcept;
|
||||
|
||||
|
@ -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 <imgui.h>
|
||||
@ -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)) {
|
||||
|
@ -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 <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
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<ox::UniquePtr<ProjectTreeModel>> children) noexcept;
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#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<ProjectExplorer>(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<FileDialogManager>(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<studio::Project>(m_ctx, path);
|
||||
auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
auto sctx = applicationData<studio::StudioContext>(*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);
|
||||
|
@ -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<studio::Project> m_project;
|
||||
studio::TaskRunner m_taskRunner;
|
||||
core::Context *m_ctx = nullptr;
|
||||
turbine::Context *m_ctx = nullptr;
|
||||
ox::Vector<ox::UniquePtr<studio::BaseEditor>> m_editors;
|
||||
ox::Vector<ox::UniquePtr<studio::Widget>> m_widgets;
|
||||
ox::HashMap<ox::String, studio::EditorMaker::Func> 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 {
|
||||
|
40
src/turbine/CMakeLists.txt
Normal file
40
src/turbine/CMakeLists.txt
Normal file
@ -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
|
||||
)
|
32
src/turbine/clipboard.hpp
Normal file
32
src/turbine/clipboard.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace turbine {
|
||||
|
||||
ox::String getClipboardText(class Context &ctx) noexcept;
|
||||
|
||||
void setClipboardText(class Context &ctx, ox::CRStringView text) noexcept;
|
||||
|
||||
template<typename T>
|
||||
void setClipboardObject([[maybe_unused]] class Context &ctx, [[maybe_unused]] ox::UniquePtr<T> obj) noexcept {
|
||||
ctx.clipboard = std::move(obj);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Result<T*> getClipboardObject([[maybe_unused]] class Context &ctx) noexcept {
|
||||
if (ctx.clipboard && ctx.clipboard->typeMatch(T::TypeName, T::TypeVersion)) {
|
||||
return static_cast<T*>(ctx.clipboard.get());
|
||||
}
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
}
|
25
src/turbine/config.hpp
Normal file
25
src/turbine/config.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<imgui.h>)
|
||||
#endif
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
namespace turbine::config {
|
||||
|
||||
constexpr auto ImGuiEnabled =
|
||||
#if __has_include(<imgui.h>)
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
constexpr auto GbaEventLoopTimerBased = false;
|
||||
constexpr auto GbaTimerBits = 32;
|
||||
constexpr auto GlFpsPrint = false;
|
||||
|
||||
}
|
93
src/turbine/context.hpp
Normal file
93
src/turbine/context.hpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
#include <ox/model/desctypes.hpp>
|
||||
#include <ox/std/buffer.hpp>
|
||||
#include <ox/std/size.hpp>
|
||||
|
||||
#include <keel/context.hpp>
|
||||
|
||||
#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<typename T>
|
||||
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<typename T>
|
||||
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<BaseClipboardObject> 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<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *applicationData(Context &ctx) noexcept {
|
||||
return static_cast<T*>(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;
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user