[nostalgia] Add start for Scene editor in Studio

This commit is contained in:
Gary Talent 2023-03-11 16:59:26 -06:00
parent 19d5641c6e
commit ae9272841f
37 changed files with 357 additions and 295 deletions

View File

@ -2,8 +2,6 @@
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include <ox/std/array.hpp>
#include <nostalgia/foundation/module.hpp> #include <nostalgia/foundation/module.hpp>
#include <nostalgia/core/module.hpp> #include <nostalgia/core/module.hpp>
@ -16,8 +14,8 @@ void loadModules() noexcept {
if (modulesLoaded) { if (modulesLoaded) {
return; return;
} }
foundation::registerModule(&core::CoreModule::mod); foundation::registerModule(core::module());
foundation::registerModule(&scene::SceneModule::mod); foundation::registerModule(scene::module());
modulesLoaded = true; modulesLoaded = true;
} }

View File

@ -7,12 +7,6 @@ add_library(
typestore.cpp typestore.cpp
) )
add_library(
NostalgiaCore-Headless
headless/core.cpp
headless/gfx.cpp
)
add_library(NostalgiaCore) add_library(NostalgiaCore)
if(NOT NOSTALGIA_BUILD_TYPE STREQUAL "GBA") if(NOT NOSTALGIA_BUILD_TYPE STREQUAL "GBA")
@ -65,11 +59,6 @@ target_link_libraries(
NostalgiaCore-Common NostalgiaCore-Common
) )
target_link_libraries(
NostalgiaCore-Headless PUBLIC
NostalgiaCore-Common
)
if(NOSTALGIA_BUILD_STUDIO) if(NOSTALGIA_BUILD_STUDIO)
add_subdirectory(studio) add_subdirectory(studio)
endif() endif()
@ -84,7 +73,9 @@ install(
core.hpp core.hpp
event.hpp event.hpp
gfx.hpp gfx.hpp
ptidxconv.hpp
input.hpp input.hpp
tilesheet.hpp
typeconv.hpp typeconv.hpp
typestore.hpp typestore.hpp
DESTINATION DESTINATION
@ -94,7 +85,6 @@ install(
install( install(
TARGETS TARGETS
NostalgiaCore NostalgiaCore
NostalgiaCore-Headless
DESTINATION DESTINATION
LIBRARY DESTINATION lib/nostalgia LIBRARY DESTINATION lib/nostalgia
ARCHIVE DESTINATION lib/nostalgia ARCHIVE DESTINATION lib/nostalgia

View File

@ -10,7 +10,7 @@ constexpr auto TileWidth = 8;
constexpr auto TileHeight = 8; constexpr auto TileHeight = 8;
constexpr auto PixelsPerTile = TileWidth * TileHeight; constexpr auto PixelsPerTile = TileWidth * TileHeight;
constexpr auto FileExt_ng = ".ng"; constexpr auto FileExt_ng = "ng";
constexpr auto FileExt_npal = ".npal"; constexpr auto FileExt_npal = "npal";
} }

View File

@ -9,10 +9,22 @@
#include <ox/std/buffer.hpp> #include <ox/std/buffer.hpp>
#include <nostalgia/foundation/context.hpp> #include <nostalgia/foundation/context.hpp>
#include <nostalgia/geo/size.hpp>
#include "event.hpp" #include "event.hpp"
#include "input.hpp" #include "input.hpp"
namespace nostalgia::core::gl {
void drawMainView(core::Context*) noexcept;
void setRenderSize(core::Context*, int width, int height) noexcept;
geo::Size getRenderSize(core::Context*) noexcept;
void clearRenderSize(core::Context *ctx) noexcept;
}
namespace nostalgia::core::renderer {
ox::Error init(Context *ctx) noexcept;
}
namespace nostalgia::geo { namespace nostalgia::geo {
class Size; class Size;
} }
@ -58,6 +70,7 @@ class Context: public foundation::Context {
friend int getScreenHeight(Context *ctx) noexcept; friend int getScreenHeight(Context *ctx) noexcept;
friend int getScreenWidth(Context *ctx) noexcept; friend int getScreenWidth(Context *ctx) noexcept;
friend ox::Error initGfx(Context *ctx) noexcept; friend ox::Error initGfx(Context *ctx) noexcept;
friend ox::Error renderer::init(Context *ctx) noexcept;
friend ox::Error loadBgTileSheet(Context *ctx, friend ox::Error loadBgTileSheet(Context *ctx,
unsigned cbb, unsigned cbb,
const ox::FileAddress &tilesheetPath, const ox::FileAddress &tilesheetPath,
@ -84,14 +97,18 @@ class Context: public foundation::Context {
friend constexpr KeyEventHandler keyEventHandler(Context *ctx) 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 setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept;
friend void setSprite(Context *ctx, friend void setSprite(Context *ctx,
unsigned idx, unsigned idx,
int x, int x,
int y, int y,
unsigned tileIdx, unsigned tileIdx,
unsigned spriteShape, unsigned spriteShape,
unsigned spriteSize, unsigned spriteSize,
unsigned flipX) noexcept; unsigned flipX) noexcept;
friend void hideSprite(Context *ctx, unsigned idx) noexcept; friend void hideSprite(Context *ctx, unsigned idx) noexcept;
friend void gl::drawMainView(core::Context*) noexcept;
friend void gl::setRenderSize(core::Context*, int width, int height) noexcept;
friend geo::Size gl::getRenderSize(core::Context*) noexcept;
friend void gl::clearRenderSize(core::Context *ctx) noexcept;
public: public:
ox::Vector<Drawer*, 5> drawers; ox::Vector<Drawer*, 5> drawers;

View File

@ -18,6 +18,12 @@
namespace nostalgia::core { namespace nostalgia::core {
namespace gl {
void setMainViewEnabled(bool) noexcept;
}
extern ox::Array<char, 128> charMap; extern ox::Array<char, 128> charMap;
class Drawer { class Drawer {

View File

@ -216,9 +216,7 @@ ox::Error initGfx(Context *ctx) noexcept {
ImGui_ImplGlfw_InitForOpenGL(id->window, true); ImGui_ImplGlfw_InitForOpenGL(id->window, true);
} }
themeImgui(); themeImgui();
void *rendererData = nullptr; oxReturnError(renderer::init(ctx));
oxReturnError(renderer::init(ctx, &rendererData));
ctx->setRendererData(rendererData);
return OxError(0); return OxError(0);
} }

View File

@ -1,25 +0,0 @@
/*
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <nostalgia/core/core.hpp>
#include <nostalgia/core/input.hpp>
namespace nostalgia::core {
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem>, ox::CRStringView) noexcept {
return OxError(1);
}
void setUpdateHandler(Context*, UpdateHandler) noexcept {
}
uint64_t ticksMs(Context*) noexcept {
return 0;
}
bool buttonDown(Context*, Key) noexcept {
return false;
}
}

View File

@ -1,101 +0,0 @@
/*
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <nostalgia/core/context.hpp>
#include <nostalgia/core/gfx.hpp>
namespace nostalgia::core {
ox::Error initGfx(Context*) noexcept {
return OxError(0);
}
ox::Error shutdownGfx(Context*) noexcept {
return OxError(0);
}
void setWindowTitle(Context*, ox::CRStringView) noexcept {
}
void focusWindow(Context*) noexcept {
}
int getScreenWidth(Context*) noexcept {
return 0;
}
int getScreenHeight(Context*) noexcept {
return 0;
}
geo::Size getScreenSize(Context*) noexcept {
return {0, 0};
}
uint8_t bgStatus(Context*) noexcept {
return 0;
}
void setBgStatus(Context*, uint32_t) noexcept {
}
bool bgStatus(Context*, unsigned) noexcept {
return false;
}
void setBgStatus(Context*, unsigned, bool) noexcept {
}
// Do NOT rely on Context in the GBA version of this function.
ox::Error initConsole(Context*) noexcept {
return OxError(0);
}
ox::Error loadBgTileSheet(Context*,
int,
const ox::FileAddress&,
const ox::FileAddress&) noexcept {
return OxError(0);
}
ox::Error loadSpriteTileSheet(Context*,
const ox::FileAddress&,
const ox::FileAddress&) noexcept {
return OxError(0);
}
ox::Error loadBgPalette(Context*, int, const ox::FileAddress&) noexcept {
return OxError(0);
}
ox::Error loadSpritePalette(Context*, int, const ox::FileAddress&) noexcept {
return OxError(0);
}
// Do NOT use Context in the GBA version of this function.
void puts(Context*, int, int, ox::CRStringView) noexcept {
}
void setTile(Context*, int, int, int, uint8_t) noexcept {
}
// Do NOT use Context in the GBA version of this function.
void clearTileLayer(Context*, int) noexcept {
}
[[maybe_unused]]
void hideSprite(Context*, unsigned) noexcept {
}
void setSprite(Context*,
unsigned,
int,
int,
unsigned,
unsigned,
unsigned,
unsigned) noexcept {
}
}

View File

@ -14,8 +14,29 @@
namespace nostalgia::core { namespace nostalgia::core {
class CoreModule: public foundation::Module {
private:
NostalgiaPaletteToPaletteConverter nostalgiaPaletteToPaletteConverter;
TileSheetV1ToTileSheetConverter nostalgiaGraphicToTileSheetConverter;
TileSheetToCompactTileSheetConverter tileSheetToCompactTileSheetConverter;
TileSheetV2ToTileSheetConverter tileSheetV2ToTileSheetConverter;
public:
static CoreModule mod;
[[nodiscard]]
ox::Vector<foundation::TypeDescGenerator> types() const noexcept override;
[[nodiscard]]
ox::Vector<const foundation::BaseConverter*> converters() const noexcept override;
[[nodiscard]]
ox::Vector<foundation::PackTransform> packTransforms() const noexcept override;
};
CoreModule CoreModule::mod; CoreModule CoreModule::mod;
const foundation::Module *module() noexcept {
return &CoreModule::mod;
}
ox::Vector<foundation::TypeDescGenerator> CoreModule::types() const noexcept { ox::Vector<foundation::TypeDescGenerator> CoreModule::types() const noexcept {
return { return {
foundation::generateTypeDesc<TileSheetV1>, foundation::generateTypeDesc<TileSheetV1>,

View File

@ -6,25 +6,8 @@
#include <nostalgia/foundation/module.hpp> #include <nostalgia/foundation/module.hpp>
#include "typeconv.hpp"
namespace nostalgia::core { namespace nostalgia::core {
class CoreModule: public foundation::Module { const foundation::Module *module() noexcept;
private:
NostalgiaPaletteToPaletteConverter nostalgiaPaletteToPaletteConverter;
TileSheetV1ToTileSheetConverter nostalgiaGraphicToTileSheetConverter;
TileSheetToCompactTileSheetConverter tileSheetToCompactTileSheetConverter;
TileSheetV2ToTileSheetConverter tileSheetV2ToTileSheetConverter;
public:
static CoreModule mod;
[[nodiscard]]
ox::Vector<foundation::TypeDescGenerator> types() const noexcept override;
[[nodiscard]]
ox::Vector<const foundation::BaseConverter*> converters() const noexcept override;
[[nodiscard]]
ox::Vector<foundation::PackTransform> packTransforms() const noexcept override;
};
} }

View File

@ -18,6 +18,16 @@ namespace nostalgia::core {
void ImGui_Impl_NewFrame() noexcept; void ImGui_Impl_NewFrame() noexcept;
namespace gl {
static bool mainViewEnabled = true;
void setMainViewEnabled(bool enabled) noexcept {
mainViewEnabled = enabled;
}
}
namespace renderer { namespace renderer {
constexpr uint64_t TileRows = 128; constexpr uint64_t TileRows = 128;
@ -67,6 +77,7 @@ struct GlImplData {
SpriteBlockset spriteBlocks; SpriteBlockset spriteBlocks;
ox::Array<Sprite, 128> spriteStates; ox::Array<Sprite, 128> spriteStates;
ox::Array<Background, 4> backgrounds; ox::Array<Background, 4> backgrounds;
ox::Optional<geo::Size> renderSize;
}; };
constexpr ox::StringView bgvshadTmpl = R"( constexpr ox::StringView bgvshadTmpl = R"(
@ -74,9 +85,13 @@ constexpr ox::StringView bgvshadTmpl = R"(
in vec2 vTexCoord; in vec2 vTexCoord;
in vec2 vPosition; in vec2 vPosition;
out vec2 fTexCoord; out vec2 fTexCoord;
uniform float vXScale;
uniform float vTileHeight; uniform float vTileHeight;
void main() { void main() {
gl_Position = vec4(vPosition, 0.0, 1.0); float xScaleInvert = 1.0 - vXScale;
gl_Position = vec4(
vPosition.x * vXScale - xScaleInvert, vPosition.y,
0.0, 1.0);
fTexCoord = vTexCoord * vec2(1, vTileHeight); fTexCoord = vTexCoord * vec2(1, vTileHeight);
})"; })";
@ -98,9 +113,13 @@ constexpr ox::StringView spritevshadTmpl = R"(
in vec2 vTexCoord; in vec2 vTexCoord;
in vec2 vPosition; in vec2 vPosition;
out vec2 fTexCoord; out vec2 fTexCoord;
uniform float vXScale;
uniform float vTileHeight; uniform float vTileHeight;
void main() { void main() {
gl_Position = vec4(vPosition, 0.0, 1.0); 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); fTexCoord = vTexCoord * vec2(1, vTileHeight) * vec2(vEnabled, vEnabled);
})"; })";
@ -111,7 +130,7 @@ static constexpr auto bgVertexRow(unsigned x, unsigned y) noexcept {
return y * TileRows + x; return y * TileRows + x;
} }
static void setSpriteBufferObject(Context *ctx, static void setSpriteBufferObject(Context*,
unsigned vi, unsigned vi,
float enabled, float enabled,
float x, float y, float x, float y,
@ -120,9 +139,8 @@ static void setSpriteBufferObject(Context *ctx,
float *vbo, float *vbo,
GLuint *ebo) noexcept { GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal // don't worry, this memcpy gets optimized to something much more ideal
const auto [sw, sh] = getScreenSize(ctx); constexpr float xmod = 0.1f;
constexpr float ymod = 0.1f; constexpr float ymod = 0.1f;
const auto xmod = ymod * static_cast<float>(sh) / static_cast<float>(sw);
x *= xmod; x *= xmod;
y *= -ymod; y *= -ymod;
x -= 1.f; x -= 1.f;
@ -144,7 +162,7 @@ static void setSpriteBufferObject(Context *ctx,
memcpy(ebo, elms.data(), sizeof(elms)); memcpy(ebo, elms.data(), sizeof(elms));
} }
static void setTileBufferObject(Context *ctx, static void setTileBufferObject(Context*,
unsigned vi, unsigned vi,
float x, float x,
float y, float y,
@ -152,9 +170,8 @@ static void setTileBufferObject(Context *ctx,
float *vbo, float *vbo,
GLuint *ebo) noexcept { GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal // don't worry, this memcpy gets optimized to something much more ideal
const auto [sw, sh] = getScreenSize(ctx); constexpr float ymod = 0.1f;
constexpr float ymod = 2.0f / 20.0f; constexpr float xmod = 0.1f;
const float xmod = ymod * static_cast<float>(sh) / static_cast<float>(sw);
x *= xmod; x *= xmod;
y *= -ymod; y *= -ymod;
x -= 1.0f; x -= 1.0f;
@ -279,11 +296,15 @@ static void drawBackground(CBB *cbb) noexcept {
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(cbb->elements.size()), GL_UNSIGNED_INT, nullptr); glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(cbb->elements.size()), GL_UNSIGNED_INT, nullptr);
} }
static void drawBackgrounds(GlImplData *id) noexcept { static void drawBackgrounds(core::Context *ctx, GlImplData *id) noexcept {
// load background shader and its uniforms // load background shader and its uniforms
glUseProgram(id->bgShader); 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 uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vTileHeight"));
//glUniform3fv(uniformPalette, GlImplData::ColorCnt, id->palette.data()); 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) { for (const auto &bg : id->backgrounds) {
if (bg.enabled) { if (bg.enabled) {
auto &cbb = id->cbbs[bg.cbbIdx]; auto &cbb = id->cbbs[bg.cbbIdx];
@ -294,10 +315,15 @@ static void drawBackgrounds(GlImplData *id) noexcept {
} }
} }
static void drawSprites(GlImplData *id) noexcept { static void drawSprites(core::Context *ctx, GlImplData *id) noexcept {
glUseProgram(id->spriteShader); glUseProgram(id->spriteShader);
auto &sb = id->spriteBlocks; 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 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 // update vbo
glBindVertexArray(sb.vao); glBindVertexArray(sb.vao);
if (sb.updated) { if (sb.updated) {
@ -312,7 +338,7 @@ static void drawSprites(GlImplData *id) noexcept {
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(sb.elements.size()), GL_UNSIGNED_INT, nullptr); glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(sb.elements.size()), GL_UNSIGNED_INT, nullptr);
} }
ox::Error init(Context *ctx, void **rendererData) noexcept { ox::Error init(Context *ctx) noexcept {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
const auto bgVshad = ox::sfmt(bgvshadTmpl, glutils::GlslVersion); const auto bgVshad = ox::sfmt(bgvshadTmpl, glutils::GlslVersion);
@ -320,7 +346,7 @@ ox::Error init(Context *ctx, void **rendererData) noexcept {
const auto spriteVshad = ox::sfmt(spritevshadTmpl, glutils::GlslVersion); const auto spriteVshad = ox::sfmt(spritevshadTmpl, glutils::GlslVersion);
const auto spriteFshad = ox::sfmt(spritefshadTmpl, glutils::GlslVersion); const auto spriteFshad = ox::sfmt(spritefshadTmpl, glutils::GlslVersion);
const auto id = ox::make<GlImplData>(); const auto id = ox::make<GlImplData>();
*rendererData = id; ctx->setRendererData(id);
oxReturnError(glutils::buildShaderProgram(bgVshad.c_str(), bgFshad.c_str()).moveTo(&id->bgShader)); oxReturnError(glutils::buildShaderProgram(bgVshad.c_str(), bgFshad.c_str()).moveTo(&id->bgShader));
oxReturnError(glutils::buildShaderProgram(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&id->spriteShader)); oxReturnError(glutils::buildShaderProgram(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&id->spriteShader));
for (auto &bg : id->cbbs) { for (auto &bg : id->cbbs) {
@ -328,7 +354,7 @@ ox::Error init(Context *ctx, void **rendererData) noexcept {
} }
initSpritesBufferset(ctx, id->spriteShader, &id->spriteBlocks); initSpritesBufferset(ctx, id->spriteShader, &id->spriteBlocks);
ImGui_ImplOpenGL3_Init(glutils::GlslVersion); ImGui_ImplOpenGL3_Init(glutils::GlslVersion);
return OxError(0); return {};
} }
void shutdown(Context*, void *rendererData) noexcept { void shutdown(Context*, void *rendererData) noexcept {
@ -422,9 +448,8 @@ void draw(Context *ctx) noexcept {
glClearColor(0, 0, 0, 1); glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
// render // render
renderer::drawBackgrounds(id); if (gl::mainViewEnabled) {
if (id->spriteBlocks.tex) { gl::drawMainView(ctx);
renderer::drawSprites(id);
} }
for (const auto cd : ctx->drawers) { for (const auto cd : ctx->drawers) {
cd->draw(ctx); cd->draw(ctx);
@ -530,4 +555,35 @@ void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) no
bg.updated = true; bg.updated = true;
} }
namespace gl {
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();
}
geo::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);
}
}
}
} }

View File

@ -11,19 +11,20 @@ ox::Result<ox::UUID> readUuidHeader(const ox::Buffer &buff) noexcept {
} }
ox::Result<ox::UUID> readUuidHeader(const char *buff, std::size_t buffLen) noexcept { ox::Result<ox::UUID> readUuidHeader(const char *buff, std::size_t buffLen) noexcept {
if (buffLen < 40) { if (buffLen < N1HdrSz) {
return OxError(1, "Insufficient data contain complete Nostalgia header"); return OxError(1, "Insufficient data to contain complete Nostalgia header");
} }
if (ox_memcmp(buff, "N1;", 3) != 0) { ox::StringView n1Hdr = "N1;";
return OxError(2, "No Nostalgia header data"); if (n1Hdr == buff) {
return OxError(2, "No Nostalgia asset header data");
} }
return ox::UUID::fromString(ox::StringView(buff + 3, 36)); return ox::UUID::fromString(ox::StringView(buff + n1Hdr.bytes(), 36));
} }
ox::Result<ox::ModelObject> readAsset(ox::TypeStore *ts, const ox::Buffer &buff) noexcept { ox::Result<ox::ModelObject> readAsset(ox::TypeStore *ts, const ox::Buffer &buff) noexcept {
std::size_t offset = 0; std::size_t offset = 0;
if (!readUuidHeader(buff).error) { if (!readUuidHeader(buff).error) {
offset = 40; // the size of N1 headers offset = N1HdrSz;
} }
return ox::readClaw(ts, buff.data() + offset, buff.size() - offset); return ox::readClaw(ts, buff.data() + offset, buff.size() - offset);
} }
@ -31,7 +32,7 @@ ox::Result<ox::ModelObject> readAsset(ox::TypeStore *ts, const ox::Buffer &buff)
ox::Result<AssetHdr> readAssetHeader(const char *buff, std::size_t buffLen) noexcept { ox::Result<AssetHdr> readAssetHeader(const char *buff, std::size_t buffLen) noexcept {
AssetHdr out; AssetHdr out;
const auto err = readUuidHeader(buff, buffLen).moveTo(&out.uuid); const auto err = readUuidHeader(buff, buffLen).moveTo(&out.uuid);
const auto offset = err ? 0 : 40; const auto offset = err ? 0 : N1HdrSz;
buff = buff + offset; buff = buff + offset;
buffLen = buffLen - offset; buffLen = buffLen - offset;
oxReturnError(ox::readClawHeader(buff, buffLen).moveTo(&out.clawHdr)); oxReturnError(ox::readClawHeader(buff, buffLen).moveTo(&out.clawHdr));

View File

@ -9,17 +9,16 @@
#include <ox/claw/claw.hpp> #include <ox/claw/claw.hpp>
#include <ox/fs/fs.hpp> #include <ox/fs/fs.hpp>
#include <nostalgia/foundation/context.hpp>
namespace nostalgia::foundation { namespace nostalgia::foundation {
[[nodiscard]] constexpr auto N1HdrSz = 40;
ox::Result<ox::UUID> readUuidHeader(const ox::Buffer &buff) noexcept; ox::Result<ox::UUID> readUuidHeader(const ox::Buffer &buff) noexcept;
ox::Result<ox::UUID> readUuidHeader(const char *buff, std::size_t buffLen) noexcept; ox::Result<ox::UUID> readUuidHeader(const char *buff, std::size_t buffLen) noexcept;
ox::Error writeUuidHeader(ox::Writer_c auto *writer, const ox::UUID &uuid) noexcept { ox::Error writeUuidHeader(ox::Writer_c auto *writer, const ox::UUID &uuid) noexcept {
const auto hdr = ox::sfmt<ox::BString<40>>("N1;{};", uuid.toString()); const auto hdr = ox::sfmt<ox::BString<N1HdrSz>>("N1;{};", uuid.toString());
return write(writer, hdr); return write(writer, hdr);
} }
@ -28,7 +27,7 @@ ox::Result<T> readAsset(const ox::Buffer &buff) noexcept {
std::size_t offset = 0; std::size_t offset = 0;
const auto err = readUuidHeader(buff).error; const auto err = readUuidHeader(buff).error;
if (!err) { if (!err) {
offset = 40; // the size of N1 headers offset = N1HdrSz; // the size of N1 headers
} }
return ox::readClaw<T>(buff.data() + offset, buff.size() - offset); return ox::readClaw<T>(buff.data() + offset, buff.size() - offset);
} }

View File

@ -41,10 +41,12 @@ class AssetContainer {
AssetContainer& operator=(AssetContainer&) = delete; AssetContainer& operator=(AssetContainer&) = delete;
AssetContainer& operator=(AssetContainer&&) = delete; AssetContainer& operator=(AssetContainer&&) = delete;
[[nodiscard]]
constexpr T *get() noexcept { constexpr T *get() noexcept {
return &m_obj; return &m_obj;
} }
[[nodiscard]]
constexpr const T *get() const noexcept { constexpr const T *get() const noexcept {
return &m_obj; return &m_obj;
} }
@ -66,6 +68,7 @@ class AssetContainer {
--m_references; --m_references;
} }
[[nodiscard]]
constexpr int references() const noexcept { constexpr int references() const noexcept {
return m_references; return m_references;
} }
@ -92,6 +95,7 @@ class AssetRef: public ox::SignalHandler {
} }
} }
[[nodiscard]]
constexpr const T *get() const noexcept { constexpr const T *get() const noexcept {
if (m_ctr) { if (m_ctr) {
return m_ctr->get(); return m_ctr->get();

View File

@ -17,17 +17,15 @@ ox::Result<ox::UPtr<Ctx>> init(ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView a
auto ctx = ox::make_unique<Ctx>(); auto ctx = ox::make_unique<Ctx>();
ctx->appName = appName; ctx->appName = appName;
oxIgnoreError(setRomFs(ctx.get(), std::move(fs))); oxIgnoreError(setRomFs(ctx.get(), std::move(fs)));
auto mods = modules(); const auto &mods = modules();
if (mods) { for (auto &mod : mods) {
for (auto &mod : *mods) { // register type converters
// register type converters for (auto c : mod->converters()) {
for (auto c : mod->converters()) { ctx->converters.emplace_back(c);
ctx->converters.emplace_back(c); }
} // register pack transforms
// register pack transforms for (auto c : mod->packTransforms()) {
for (auto c : mod->packTransforms()) { ctx->packTransforms.emplace_back(c);
ctx->packTransforms.emplace_back(c);
}
} }
} }
return ctx; return ctx;

View File

@ -13,8 +13,8 @@ void registerModule(const Module *mod) noexcept {
} }
[[nodiscard]] [[nodiscard]]
const ox::Vector<const Module*> *modules() noexcept { const ox::Vector<const Module*> &modules() noexcept {
return &mods; return mods;
} }

View File

@ -38,6 +38,6 @@ class Module {
void registerModule(const Module *mod) noexcept; void registerModule(const Module *mod) noexcept;
[[nodiscard]] [[nodiscard]]
const ox::Vector<const foundation::Module*> *modules() noexcept; const ox::Vector<const foundation::Module*> &modules() noexcept;
} }

View File

@ -176,7 +176,7 @@ ox::Result<ox::Buffer> convertBuffToBuff(foundation::Context *ctx, const ox::Buf
} }
template<typename From, typename To, ox::ClawFormat fmt = ox::ClawFormat::Metal> template<typename From, typename To, ox::ClawFormat fmt = ox::ClawFormat::Metal>
auto transformRule(foundation::Context *ctx, ox::Buffer *buff) -> ox::Error { auto transformRule(foundation::Context *ctx, ox::Buffer *buff) noexcept -> ox::Error {
oxRequire(hdr, readAssetHeader(*buff)); oxRequire(hdr, readAssetHeader(*buff));
const auto typeId = ox::buildTypeId( const auto typeId = ox::buildTypeId(
hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams); hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);

View File

@ -3,7 +3,6 @@
*/ */
#include <nostalgia/core/core.hpp> #include <nostalgia/core/core.hpp>
#include <nostalgia/foundation/media.hpp>
#include <nostalgia/scene/scene.hpp> #include <nostalgia/scene/scene.hpp>
using namespace nostalgia; using namespace nostalgia;
@ -37,7 +36,7 @@ ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept {
oxRequire(scn, foundation::readObj<scene::SceneStatic>(ctx.get(), SceneAddr)); oxRequire(scn, foundation::readObj<scene::SceneStatic>(ctx.get(), SceneAddr));
core::setUpdateHandler(ctx.get(), updateHandler); core::setUpdateHandler(ctx.get(), updateHandler);
core::setKeyEventHandler(ctx.get(), keyEventHandler); core::setKeyEventHandler(ctx.get(), keyEventHandler);
s_scene.emplace(scn.get()); s_scene.emplace(*scn);
oxReturnError(s_scene->setupDisplay(ctx.get())); oxReturnError(s_scene->setupDisplay(ctx.get()));
return core::run(ctx.get()); return core::run(ctx.get());
} }

View File

@ -5,7 +5,6 @@
#include <ox/logconn/logconn.hpp> #include <ox/logconn/logconn.hpp>
#include <nostalgia/core/core.hpp> #include <nostalgia/core/core.hpp>
#include <nostalgia/foundation/media.hpp>
#include <nostalgia/appmodules/appmodules.hpp> #include <nostalgia/appmodules/appmodules.hpp>

View File

@ -22,6 +22,6 @@ install(
include/nostalgia/scene include/nostalgia/scene
) )
#if(NOSTALGIA_BUILD_STUDIO) if(NOSTALGIA_BUILD_STUDIO)
# add_subdirectory(studio) add_subdirectory(studio)
#endif() endif()

View File

@ -8,25 +8,25 @@
namespace nostalgia::scene { namespace nostalgia::scene {
Scene::Scene(const SceneStatic *sceneStatic) noexcept: Scene::Scene(const SceneStatic &sceneStatic) noexcept:
m_sceneStatic(sceneStatic) { m_sceneStatic(sceneStatic) {
} }
ox::Error Scene::setupDisplay(core::Context *ctx) noexcept { ox::Error Scene::setupDisplay(core::Context *ctx) noexcept {
if (m_sceneStatic->palettes.empty()) { if (m_sceneStatic.palettes.empty()) {
return OxError(1, "Scene has no palettes"); return OxError(1, "Scene has no palettes");
} }
const auto &palette = m_sceneStatic->palettes[0]; const auto &palette = m_sceneStatic.palettes[0];
oxReturnError(core::loadBgTileSheet( oxReturnError(core::loadBgTileSheet(
ctx, 0, m_sceneStatic->tilesheet, palette)); ctx, 0, m_sceneStatic.tilesheet, palette));
// disable all backgrounds // disable all backgrounds
core::setBgStatus(ctx, 0); core::setBgStatus(ctx, 0);
for (auto layerNo = 0u; const auto &layer : m_sceneStatic->tileMapIdx) { for (auto layerNo = 0u; const auto &layer : m_sceneStatic.tileMapIdx) {
core::setBgStatus(ctx, layerNo, true); core::setBgStatus(ctx, layerNo, true);
core::setBgCbb(ctx, layerNo, 0); core::setBgCbb(ctx, layerNo, 0);
auto x = 0; auto x = 0;
auto y = 0; auto y = 0;
auto width = m_sceneStatic->rows[layerNo]; auto width = m_sceneStatic.rows[layerNo];
for (const auto &tile : layer) { for (const auto &tile : layer) {
core::setTile(ctx, layerNo, x, y, tile); core::setTile(ctx, layerNo, x, y, tile);
core::setTile(ctx, layerNo, x + 1, y, tile + 1); core::setTile(ctx, layerNo, x + 1, y, tile + 1);

View File

@ -10,10 +10,10 @@ namespace nostalgia::scene {
class Scene { class Scene {
private: private:
const SceneStatic *m_sceneStatic = nullptr; const SceneStatic &m_sceneStatic;
public: public:
explicit Scene(const SceneStatic *sceneStatic) noexcept; explicit Scene(const SceneStatic &sceneStatic) noexcept;
ox::Error setupDisplay(core::Context *ctx) noexcept; ox::Error setupDisplay(core::Context *ctx) noexcept;

View File

@ -5,29 +5,44 @@
#include <ox/model/model.hpp> #include <ox/model/model.hpp>
#include "scenestatic.hpp" #include "scenestatic.hpp"
#include "typeconv.hpp"
#include "scenemodule.hpp" #include "scenemodule.hpp"
namespace nostalgia::scene { namespace nostalgia::scene {
SceneModule SceneModule::mod; class SceneModule: public foundation::Module {
private:
SceneDocToSceneStaticConverter sceneDocToSceneStaticConverter;
ox::Vector<foundation::TypeDescGenerator> SceneModule::types() const noexcept { public:
return { [[nodiscard]]
foundation::generateTypeDesc<SceneDoc>, ox::Vector<foundation::TypeDescGenerator> types() const noexcept override {
foundation::generateTypeDesc<SceneStatic>, return {
}; foundation::generateTypeDesc<SceneDoc>,
} foundation::generateTypeDesc<SceneStatic>,
};
}
ox::Vector<const foundation::BaseConverter*> SceneModule::converters() const noexcept { [[nodiscard]]
return { ox::Vector<const foundation::BaseConverter*> converters() const noexcept override {
&sceneDocToSceneStaticConverter, return {
}; &sceneDocToSceneStaticConverter,
} };
}
ox::Vector<foundation::PackTransform> SceneModule::packTransforms() const noexcept { [[nodiscard]]
return { ox::Vector<foundation::PackTransform> packTransforms() const noexcept override {
foundation::transformRule<SceneDoc, SceneStatic>, return {
}; foundation::transformRule<SceneDoc, SceneStatic>,
};
}
};
static SceneModule mod;
const foundation::Module *module() noexcept {
return &mod;
} }
} }

View File

@ -6,22 +6,8 @@
#include <nostalgia/foundation/module.hpp> #include <nostalgia/foundation/module.hpp>
#include "typeconv.hpp"
namespace nostalgia::scene { namespace nostalgia::scene {
class SceneModule: public foundation::Module { const foundation::Module *module() noexcept;
private:
SceneDocToSceneStaticConverter sceneDocToSceneStaticConverter;
public:
static SceneModule mod;
[[nodiscard]]
ox::Vector<foundation::TypeDescGenerator> types() const noexcept override;
[[nodiscard]]
ox::Vector<const foundation::BaseConverter*> converters() const noexcept override;
[[nodiscard]]
ox::Vector<foundation::PackTransform> packTransforms() const noexcept override;
};
} }

View File

@ -14,6 +14,17 @@
namespace nostalgia::scene { namespace nostalgia::scene {
struct SpriteDoc {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SpriteDoc";
constexpr static auto TypeVersion = 1;
constexpr static auto Preloadable = true;
ox::String tilesheetPath;
ox::Vector<core::SubSheetId> subsheetId;
};
struct TileDoc { struct TileDoc {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.TileDoc"; constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.TileDoc";
@ -23,6 +34,7 @@ struct TileDoc {
core::SubSheetId subsheetId = -1; core::SubSheetId subsheetId = -1;
ox::String subsheetPath; ox::String subsheetPath;
uint8_t type = 0; uint8_t type = 0;
ox::Array<uint8_t, 4> layerAttachments;
[[nodiscard]] [[nodiscard]]
constexpr ox::Result<core::SubSheetId> getSubsheetId(const core::TileSheet &ts) const noexcept { constexpr ox::Result<core::SubSheetId> getSubsheetId(const core::TileSheet &ts) const noexcept {
@ -88,6 +100,38 @@ oxModelBegin(SceneDoc)
oxModelField(tiles) oxModelField(tiles)
oxModelEnd() oxModelEnd()
constexpr void setTopEdge(uint8_t &layerAttachments, unsigned val) noexcept {
layerAttachments = (layerAttachments & 0b11111100) | val;
}
constexpr void setBottomEdge(uint8_t &layerAttachments, unsigned val) noexcept {
layerAttachments = (layerAttachments & 0b11110011) | (val << 2);
}
constexpr void setLeftEdge(uint8_t &layerAttachments, unsigned val) noexcept {
layerAttachments = (layerAttachments & 0b11001111) | (val << 4);
}
constexpr void setRightEdge(uint8_t &layerAttachments, unsigned val) noexcept {
layerAttachments = (layerAttachments & 0b00111111) | (val << 6);
}
[[nodiscard]]
constexpr unsigned topEdge(uint8_t layerAttachments) noexcept {
return layerAttachments & 0b11;
}
[[nodiscard]]
constexpr unsigned bottomEdge(uint8_t layerAttachments) noexcept {
return (layerAttachments >> 2) & 0b11;
}
[[nodiscard]]
constexpr unsigned leftEdge(uint8_t layerAttachments) noexcept {
return (layerAttachments >> 4) & 0b11;
}
[[nodiscard]]
constexpr unsigned rightEdge(uint8_t layerAttachments) noexcept {
return (layerAttachments >> 6) & 0b11;
}
struct SceneStatic { struct SceneStatic {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneStatic"; constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneStatic";
@ -97,9 +141,11 @@ struct SceneStatic {
struct Tile { struct Tile {
uint16_t &tileMapIdx; uint16_t &tileMapIdx;
uint8_t &tileType; uint8_t &tileType;
constexpr Tile(uint16_t *pTileMapIdx, uint8_t *pTileType) noexcept: uint8_t &layerAttachments;
tileMapIdx(*pTileMapIdx), constexpr Tile(uint16_t &pTileMapIdx, uint8_t &pTileType, uint8_t &pLayerAttachments) noexcept:
tileType(*pTileType) { tileMapIdx(pTileMapIdx),
tileType(pTileType),
layerAttachments(pLayerAttachments) {
} }
}; };
struct Layer { struct Layer {
@ -107,19 +153,22 @@ struct SceneStatic {
uint16_t &rows; uint16_t &rows;
ox::Vector<uint16_t> &tileMapIdx; ox::Vector<uint16_t> &tileMapIdx;
ox::Vector<uint8_t> &tileType; ox::Vector<uint8_t> &tileType;
ox::Vector<uint8_t> &layerAttachments;
constexpr Layer( constexpr Layer(
uint16_t *pColumns, uint16_t &pColumns,
uint16_t *pRows, uint16_t &pRows,
ox::Vector<uint16_t> *pTileMapIdx, ox::Vector<uint16_t> &pTileMapIdx,
ox::Vector<uint8_t> *pTileType) noexcept: ox::Vector<uint8_t> &pTileType,
columns(*pColumns), ox::Vector<uint8_t> &pLayerAttachments) noexcept:
rows(*pRows), columns(pColumns),
tileMapIdx(*pTileMapIdx), rows(pRows),
tileType(*pTileType) { tileMapIdx(pTileMapIdx),
tileType(pTileType),
layerAttachments(pLayerAttachments) {
} }
[[nodiscard]] [[nodiscard]]
constexpr Tile tile(std::size_t i) noexcept { constexpr Tile tile(std::size_t i) noexcept {
return {&tileMapIdx[i], &tileType[i]}; return {tileMapIdx[i], tileType[i], layerAttachments[i]};
} }
constexpr auto setDimensions(geo::Size dim) noexcept { constexpr auto setDimensions(geo::Size dim) noexcept {
columns = dim.width; columns = dim.width;
@ -127,6 +176,7 @@ struct SceneStatic {
const auto tileCnt = static_cast<unsigned>(columns * rows); const auto tileCnt = static_cast<unsigned>(columns * rows);
tileMapIdx.resize(tileCnt); tileMapIdx.resize(tileCnt);
tileType.resize(tileCnt); tileType.resize(tileCnt);
layerAttachments.resize(tileCnt);
} }
}; };
@ -137,13 +187,21 @@ struct SceneStatic {
ox::Vector<uint16_t> rows; ox::Vector<uint16_t> rows;
ox::Vector<ox::Vector<uint16_t>> tileMapIdx; ox::Vector<ox::Vector<uint16_t>> tileMapIdx;
ox::Vector<ox::Vector<uint8_t>> tileType; ox::Vector<ox::Vector<uint8_t>> tileType;
ox::Vector<ox::Vector<uint8_t>> layerAttachments;
[[nodiscard]] [[nodiscard]]
constexpr Layer layer(std::size_t i) noexcept { constexpr Layer layer(std::size_t i) noexcept {
return {&columns[i], &rows[i], &tileMapIdx[i], &tileType[i]}; return {
columns[i],
rows[i],
tileMapIdx[i],
tileType[i],
layerAttachments[i],
};
} }
constexpr auto setLayerCnt(std::size_t layerCnt) noexcept { constexpr auto setLayerCnt(std::size_t layerCnt) noexcept {
this->layerAttachments.resize(layerCnt);
this->columns.resize(layerCnt); this->columns.resize(layerCnt);
this->rows.resize(layerCnt); this->rows.resize(layerCnt);
this->tileMapIdx.resize(layerCnt); this->tileMapIdx.resize(layerCnt);
@ -157,8 +215,9 @@ oxModelBegin(SceneStatic)
oxModelField(palettes) oxModelField(palettes)
oxModelField(columns) oxModelField(columns)
oxModelField(rows) oxModelField(rows)
oxModelFieldRename(tile_map_idx, tileMapIdx) oxModelField(tileMapIdx)
oxModelFieldRename(tile_type, tileType) oxModelField(tileType)
oxModelField(layerAttachments)
oxModelEnd() oxModelEnd()
} }

View File

@ -9,6 +9,30 @@
namespace nostalgia::scene { namespace nostalgia::scene {
[[nodiscard]]
constexpr unsigned adjustLayerAttachment(unsigned layer, unsigned attachment) noexcept {
if (attachment == 0) {
return layer;
} else {
return attachment - 1;
}
}
constexpr void setLayerAttachments(unsigned layer, const TileDoc &srcTile, SceneStatic::Tile &dstTile) noexcept {
setTopEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[0]));
setBottomEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[1]));
setLeftEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[2]));
setRightEdge(
dstTile.layerAttachments,
adjustLayerAttachment(layer, srcTile.layerAttachments[3]));
}
ox::Error SceneDocToSceneStaticConverter::convert( ox::Error SceneDocToSceneStaticConverter::convert(
foundation::Context *ctx, foundation::Context *ctx,
SceneDoc *src, SceneDoc *src,
@ -32,6 +56,7 @@ ox::Error SceneDocToSceneStaticConverter::convert(
oxRequire(path, srcTile.getSubsheetPath(*ts)); oxRequire(path, srcTile.getSubsheetPath(*ts));
oxRequire(mapIdx, ts->getTileOffset(path)); oxRequire(mapIdx, ts->getTileOffset(path));
dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx); dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx);
setLayerAttachments(layerIdx, srcTile, dstTile);
++tileIdx; ++tileIdx;
} }
} }

View File

@ -19,6 +19,7 @@ target_link_libraries(
NostalgiaAppModules NostalgiaAppModules
NostalgiaStudio NostalgiaStudio
NostalgiaCore-Studio NostalgiaCore-Studio
NostalgiaScene-Studio
) )
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32) if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32)

View File

@ -7,13 +7,17 @@
#include <ox/std/memory.hpp> #include <ox/std/memory.hpp>
#include <nostalgia/core/studio/module.hpp> #include <nostalgia/core/studio/module.hpp>
#include <nostalgia/scene/studio/module.hpp>
namespace nostalgia { namespace nostalgia {
[[maybe_unused]] // GCC warns about the existence of this "unused" constexpr list in a header file... [[maybe_unused]] // GCC warns about the existence of this "unused" inline list in a header file...
constexpr auto BuiltinModules = { inline ox::Vector<std::function<ox::UPtr<studio::Module>()>, 2> BuiltinModules = {
[] { []() -> ox::UPtr<studio::Module> {
return ox::make_unique<core::StudioModule>(); return ox::UPtr<studio::Module>(new core::StudioModule());
},
[]() -> ox::UPtr<studio::Module> {
return ox::UPtr<studio::Module>(new scene::StudioModule());
}, },
}; };

View File

@ -23,7 +23,7 @@ constexpr auto ConfigDir = [] {
} }
}(); }();
ox::String configPath(const core::Context *ctx) noexcept { ox::String configPath(const foundation::Context *ctx) noexcept {
const auto homeDir = std::getenv(ox::defines::OS == ox::OS::Windows ? "USERPROFILE" : "HOME"); const auto homeDir = std::getenv(ox::defines::OS == ox::OS::Windows ? "USERPROFILE" : "HOME");
return ox::sfmt(ConfigDir, homeDir, ctx->appName); return ox::sfmt(ConfigDir, homeDir, ctx->appName);
} }

View File

@ -18,10 +18,10 @@
namespace nostalgia::studio { namespace nostalgia::studio {
[[nodiscard]] [[nodiscard]]
ox::String configPath(const core::Context *ctx) noexcept; ox::String configPath(const foundation::Context *ctx) noexcept;
template<typename T> template<typename T>
ox::Result<T> readConfig(core::Context *ctx, ox::CRStringView name) noexcept { ox::Result<T> readConfig(foundation::Context *ctx, ox::CRStringView name) noexcept {
oxAssert(name != "", "Config type has no TypeName"); oxAssert(name != "", "Config type has no TypeName");
const auto path = ox::sfmt("/{}.json", name); const auto path = ox::sfmt("/{}.json", name);
ox::PassThroughFS fs(configPath(ctx)); ox::PassThroughFS fs(configPath(ctx));
@ -34,13 +34,13 @@ ox::Result<T> readConfig(core::Context *ctx, ox::CRStringView name) noexcept {
} }
template<typename T> template<typename T>
ox::Result<T> readConfig(core::Context *ctx) noexcept { ox::Result<T> readConfig(foundation::Context *ctx) noexcept {
constexpr auto TypeName = ox::requireModelTypeName<T>(); constexpr auto TypeName = ox::requireModelTypeName<T>();
return readConfig<T>(ctx, TypeName); return readConfig<T>(ctx, TypeName);
} }
template<typename T> template<typename T>
ox::Error writeConfig(core::Context *ctx, ox::CRStringView name, T *data) noexcept { ox::Error writeConfig(foundation::Context *ctx, ox::CRStringView name, T *data) noexcept {
oxAssert(name != "", "Config type has no TypeName"); oxAssert(name != "", "Config type has no TypeName");
const auto path = ox::sfmt("/{}.json", name); const auto path = ox::sfmt("/{}.json", name);
ox::PassThroughFS fs(configPath(ctx)); ox::PassThroughFS fs(configPath(ctx));
@ -58,13 +58,13 @@ ox::Error writeConfig(core::Context *ctx, ox::CRStringView name, T *data) noexce
} }
template<typename T> template<typename T>
ox::Error writeConfig(core::Context *ctx, T *data) noexcept { ox::Error writeConfig(foundation::Context *ctx, T *data) noexcept {
constexpr auto TypeName = ox::requireModelTypeName<T>(); constexpr auto TypeName = ox::requireModelTypeName<T>();
return writeConfig(ctx, TypeName, data); return writeConfig(ctx, TypeName, data);
} }
template<typename T, typename Func> template<typename T, typename Func>
void openConfig(core::Context *ctx, const auto &name, Func f) noexcept { void openConfig(foundation::Context *ctx, const auto &name, Func f) noexcept {
oxAssert(name != "", "Config type has no TypeName"); oxAssert(name != "", "Config type has no TypeName");
const auto [c, err] = readConfig<T>(ctx, name); const auto [c, err] = readConfig<T>(ctx, name);
oxLogError(err); oxLogError(err);
@ -72,13 +72,13 @@ void openConfig(core::Context *ctx, const auto &name, Func f) noexcept {
} }
template<typename T, typename Func> template<typename T, typename Func>
void openConfig(core::Context *ctx, Func f) noexcept { void openConfig(foundation::Context *ctx, Func f) noexcept {
constexpr auto TypeName = ox::requireModelTypeName<T>(); constexpr auto TypeName = ox::requireModelTypeName<T>();
openConfig<T>(ctx, TypeName, f); openConfig<T>(ctx, TypeName, f);
} }
template<typename T, typename Func> template<typename T, typename Func>
void editConfig(core::Context *ctx, const auto &name, Func f) noexcept { void editConfig(foundation::Context *ctx, const auto &name, Func f) noexcept {
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName"); oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
auto [c, err] = readConfig<T>(ctx, name); auto [c, err] = readConfig<T>(ctx, name);
oxLogError(err); oxLogError(err);
@ -87,7 +87,7 @@ void editConfig(core::Context *ctx, const auto &name, Func f) noexcept {
} }
template<typename T, typename Func> template<typename T, typename Func>
void editConfig(core::Context *ctx, Func f) noexcept { void editConfig(foundation::Context *ctx, Func f) noexcept {
constexpr auto TypeName = ox::requireModelTypeName<T>(); constexpr auto TypeName = ox::requireModelTypeName<T>();
editConfig<T>(ctx, TypeName, f); editConfig<T>(ctx, TypeName, f);
} }

View File

@ -29,6 +29,13 @@ void BaseEditor::exportFile() {
void BaseEditor::keyStateChanged(core::Key, bool) { void BaseEditor::keyStateChanged(core::Key, bool) {
} }
void BaseEditor::onActivated() noexcept {
}
bool BaseEditor::requiresConstantRefresh() const noexcept {
return m_requiresConstantRefresh;
}
void BaseEditor::close() const { void BaseEditor::close() const {
this->closed.emit(itemName()); this->closed.emit(itemName());
} }
@ -100,6 +107,11 @@ ox::StringView BaseEditor::pathToItemName(ox::CRStringView path) noexcept {
return path.substr(lastSlash + 1); return path.substr(lastSlash + 1);
} }
void BaseEditor::setRequiresConstantRefresh(bool value) noexcept {
m_requiresConstantRefresh = value;
}
Editor::Editor() noexcept { Editor::Editor() noexcept {
m_undoStack.changeTriggered.connect(this, &Editor::markUnsavedChanges); m_undoStack.changeTriggered.connect(this, &Editor::markUnsavedChanges);
} }

View File

@ -27,6 +27,7 @@ class NOSTALGIASTUDIO_EXPORT BaseEditor: public Widget {
bool m_cutEnabled = false; bool m_cutEnabled = false;
bool m_copyEnabled = false; bool m_copyEnabled = false;
bool m_pasteEnabled = false; bool m_pasteEnabled = false;
bool m_requiresConstantRefresh = false;
public: public:
~BaseEditor() override = default; ~BaseEditor() override = default;
@ -50,6 +51,11 @@ class NOSTALGIASTUDIO_EXPORT BaseEditor: public Widget {
virtual void keyStateChanged(core::Key key, bool down); virtual void keyStateChanged(core::Key key, bool down);
virtual void onActivated() noexcept;
[[nodiscard]]
bool requiresConstantRefresh() const noexcept;
void close() const; void close() const;
/** /**
@ -102,6 +108,8 @@ class NOSTALGIASTUDIO_EXPORT BaseEditor: public Widget {
static ox::StringView pathToItemName(ox::CRStringView path) noexcept; static ox::StringView pathToItemName(ox::CRStringView path) noexcept;
void setRequiresConstantRefresh(bool value) noexcept;
// signals // signals
public: public:
ox::Signal<ox::Error(bool)> unsavedChangesChanged; ox::Signal<ox::Error(bool)> unsavedChangesChanged;

View File

@ -14,7 +14,7 @@
namespace nostalgia::studio { namespace nostalgia::studio {
static void generateTypes(ox::TypeStore *ts) noexcept { static void generateTypes(ox::TypeStore *ts) noexcept {
for (const auto mod : *foundation::modules()) { for (const auto mod : foundation::modules()) {
for (auto gen : mod->types()) { for (auto gen : mod->types()) {
oxLogError(gen(ts)); oxLogError(gen(ts));
} }

View File

@ -48,13 +48,16 @@ static ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept {
core::setUpdateHandler(ctx.get(), updateHandler); core::setUpdateHandler(ctx.get(), updateHandler);
core::setKeyEventHandler(ctx.get(), keyEventHandler); core::setKeyEventHandler(ctx.get(), keyEventHandler);
core::setConstantRefresh(ctx.get(), false); core::setConstantRefresh(ctx.get(), false);
core::gl::setMainViewEnabled(false);
studio::StudioContext studioCtx; studio::StudioContext studioCtx;
core::setApplicationData(ctx.get(), &studioCtx); core::setApplicationData(ctx.get(), &studioCtx);
StudioUI ui(ctx.get()); StudioUI ui(ctx.get());
studioCtx.ui = &ui; studioCtx.ui = &ui;
StudioUIDrawer drawer(&ui); StudioUIDrawer drawer(&ui);
ctx->drawers.emplace_back(&drawer); core::addCustomDrawer(ctx.get(), &drawer);
return core::run(ctx.get()); auto err = core::run(ctx.get());
core::removeCustomDrawer(ctx.get(), &drawer);
return err;
} }
static ox::Error run(int, const char**) noexcept { static ox::Error run(int, const char**) noexcept {

View File

@ -217,7 +217,12 @@ void StudioUI::drawTabs() noexcept {
if (m_activeEditorUpdatePending == e.get()) { if (m_activeEditorUpdatePending == e.get()) {
m_activeEditorUpdatePending = nullptr; m_activeEditorUpdatePending = nullptr;
} }
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
m_activeEditor->onActivated();
core::setConstantRefresh(m_ctx, m_activeEditor->requiresConstantRefresh());
}
e->draw(m_ctx); e->draw(m_ctx);
m_activeEditorOnLastDraw = e.get();
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (!open) { if (!open) {
@ -346,7 +351,7 @@ ox::Error StudioUI::openFileActiveTab(ox::CRStringView path, bool makeActiveTab)
} }
// save to config // save to config
studio::editConfig<StudioConfig>(m_ctx, [&](StudioConfig *config) { studio::editConfig<StudioConfig>(m_ctx, [&](StudioConfig *config) {
if (!config->openFiles.contains((path))) { if (!config->openFiles.contains(path)) {
config->openFiles.emplace_back(path); config->openFiles.emplace_back(path);
} }
}); });

View File

@ -31,6 +31,7 @@ class StudioUI: public ox::SignalHandler {
ox::HashMap<ox::String, studio::EditorMaker::Func> m_editorMakers; ox::HashMap<ox::String, studio::EditorMaker::Func> m_editorMakers;
ox::UniquePtr<ProjectExplorer> m_projectExplorer; ox::UniquePtr<ProjectExplorer> m_projectExplorer;
ox::Vector<ox::String> m_openFiles; ox::Vector<ox::String> m_openFiles;
studio::BaseEditor *m_activeEditorOnLastDraw = nullptr;
studio::BaseEditor *m_activeEditor = nullptr; studio::BaseEditor *m_activeEditor = nullptr;
studio::BaseEditor *m_activeEditorUpdatePending = nullptr; studio::BaseEditor *m_activeEditorUpdatePending = nullptr;
NewMenu m_newMenu; NewMenu m_newMenu;