[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.
*/
#include <ox/std/array.hpp>
#include <nostalgia/foundation/module.hpp>
#include <nostalgia/core/module.hpp>
@ -16,8 +14,8 @@ void loadModules() noexcept {
if (modulesLoaded) {
return;
}
foundation::registerModule(&core::CoreModule::mod);
foundation::registerModule(&scene::SceneModule::mod);
foundation::registerModule(core::module());
foundation::registerModule(scene::module());
modulesLoaded = true;
}

View File

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

View File

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

View File

@ -9,10 +9,22 @@
#include <ox/std/buffer.hpp>
#include <nostalgia/foundation/context.hpp>
#include <nostalgia/geo/size.hpp>
#include "event.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 {
class Size;
}
@ -58,6 +70,7 @@ class Context: public foundation::Context {
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 loadBgTileSheet(Context *ctx,
unsigned cbb,
const ox::FileAddress &tilesheetPath,
@ -84,14 +97,18 @@ class Context: public foundation::Context {
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,
int x,
int y,
unsigned tileIdx,
unsigned spriteShape,
unsigned spriteSize,
unsigned flipX) noexcept;
unsigned idx,
int x,
int y,
unsigned tileIdx,
unsigned spriteShape,
unsigned spriteSize,
unsigned flipX) 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:
ox::Vector<Drawer*, 5> drawers;

View File

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

View File

@ -216,9 +216,7 @@ ox::Error initGfx(Context *ctx) noexcept {
ImGui_ImplGlfw_InitForOpenGL(id->window, true);
}
themeImgui();
void *rendererData = nullptr;
oxReturnError(renderer::init(ctx, &rendererData));
ctx->setRendererData(rendererData);
oxReturnError(renderer::init(ctx));
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 {
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;
const foundation::Module *module() noexcept {
return &CoreModule::mod;
}
ox::Vector<foundation::TypeDescGenerator> CoreModule::types() const noexcept {
return {
foundation::generateTypeDesc<TileSheetV1>,

View File

@ -6,25 +6,8 @@
#include <nostalgia/foundation/module.hpp>
#include "typeconv.hpp"
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;
};
const foundation::Module *module() noexcept;
}

View File

@ -18,6 +18,16 @@ namespace nostalgia::core {
void ImGui_Impl_NewFrame() noexcept;
namespace gl {
static bool mainViewEnabled = true;
void setMainViewEnabled(bool enabled) noexcept {
mainViewEnabled = enabled;
}
}
namespace renderer {
constexpr uint64_t TileRows = 128;
@ -67,6 +77,7 @@ struct GlImplData {
SpriteBlockset spriteBlocks;
ox::Array<Sprite, 128> spriteStates;
ox::Array<Background, 4> backgrounds;
ox::Optional<geo::Size> renderSize;
};
constexpr ox::StringView bgvshadTmpl = R"(
@ -74,9 +85,13 @@ constexpr ox::StringView bgvshadTmpl = R"(
in vec2 vTexCoord;
in vec2 vPosition;
out vec2 fTexCoord;
uniform float vXScale;
uniform float vTileHeight;
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);
})";
@ -98,9 +113,13 @@ constexpr ox::StringView spritevshadTmpl = R"(
in vec2 vTexCoord;
in vec2 vPosition;
out vec2 fTexCoord;
uniform float vXScale;
uniform float vTileHeight;
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);
})";
@ -111,7 +130,7 @@ static constexpr auto bgVertexRow(unsigned x, unsigned y) noexcept {
return y * TileRows + x;
}
static void setSpriteBufferObject(Context *ctx,
static void setSpriteBufferObject(Context*,
unsigned vi,
float enabled,
float x, float y,
@ -120,9 +139,8 @@ static void setSpriteBufferObject(Context *ctx,
float *vbo,
GLuint *ebo) noexcept {
// 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;
const auto xmod = ymod * static_cast<float>(sh) / static_cast<float>(sw);
x *= xmod;
y *= -ymod;
x -= 1.f;
@ -144,7 +162,7 @@ static void setSpriteBufferObject(Context *ctx,
memcpy(ebo, elms.data(), sizeof(elms));
}
static void setTileBufferObject(Context *ctx,
static void setTileBufferObject(Context*,
unsigned vi,
float x,
float y,
@ -152,9 +170,8 @@ static void setTileBufferObject(Context *ctx,
float *vbo,
GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal
const auto [sw, sh] = getScreenSize(ctx);
constexpr float ymod = 2.0f / 20.0f;
const float xmod = ymod * static_cast<float>(sh) / static_cast<float>(sw);
constexpr float ymod = 0.1f;
constexpr float xmod = 0.1f;
x *= xmod;
y *= -ymod;
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);
}
static void drawBackgrounds(GlImplData *id) noexcept {
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"));
//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) {
if (bg.enabled) {
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);
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) {
@ -312,7 +338,7 @@ static void drawSprites(GlImplData *id) noexcept {
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);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
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 spriteFshad = ox::sfmt(spritefshadTmpl, glutils::GlslVersion);
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(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&id->spriteShader));
for (auto &bg : id->cbbs) {
@ -328,7 +354,7 @@ ox::Error init(Context *ctx, void **rendererData) noexcept {
}
initSpritesBufferset(ctx, id->spriteShader, &id->spriteBlocks);
ImGui_ImplOpenGL3_Init(glutils::GlslVersion);
return OxError(0);
return {};
}
void shutdown(Context*, void *rendererData) noexcept {
@ -422,9 +448,8 @@ void draw(Context *ctx) noexcept {
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// render
renderer::drawBackgrounds(id);
if (id->spriteBlocks.tex) {
renderer::drawSprites(id);
if (gl::mainViewEnabled) {
gl::drawMainView(ctx);
}
for (const auto cd : ctx->drawers) {
cd->draw(ctx);
@ -530,4 +555,35 @@ void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) no
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 {
if (buffLen < 40) {
return OxError(1, "Insufficient data contain complete Nostalgia header");
if (buffLen < N1HdrSz) {
return OxError(1, "Insufficient data to contain complete Nostalgia header");
}
if (ox_memcmp(buff, "N1;", 3) != 0) {
return OxError(2, "No Nostalgia header data");
ox::StringView n1Hdr = "N1;";
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 {
std::size_t offset = 0;
if (!readUuidHeader(buff).error) {
offset = 40; // the size of N1 headers
offset = N1HdrSz;
}
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 {
AssetHdr out;
const auto err = readUuidHeader(buff, buffLen).moveTo(&out.uuid);
const auto offset = err ? 0 : 40;
const auto offset = err ? 0 : N1HdrSz;
buff = buff + offset;
buffLen = buffLen - offset;
oxReturnError(ox::readClawHeader(buff, buffLen).moveTo(&out.clawHdr));

View File

@ -9,17 +9,16 @@
#include <ox/claw/claw.hpp>
#include <ox/fs/fs.hpp>
#include <nostalgia/foundation/context.hpp>
namespace nostalgia::foundation {
[[nodiscard]]
constexpr auto N1HdrSz = 40;
ox::Result<ox::UUID> readUuidHeader(const ox::Buffer &buff) 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 {
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);
}
@ -28,7 +27,7 @@ ox::Result<T> readAsset(const ox::Buffer &buff) noexcept {
std::size_t offset = 0;
const auto err = readUuidHeader(buff).error;
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);
}

View File

@ -41,10 +41,12 @@ class AssetContainer {
AssetContainer& operator=(AssetContainer&) = delete;
AssetContainer& operator=(AssetContainer&&) = delete;
[[nodiscard]]
constexpr T *get() noexcept {
return &m_obj;
}
[[nodiscard]]
constexpr const T *get() const noexcept {
return &m_obj;
}
@ -66,6 +68,7 @@ class AssetContainer {
--m_references;
}
[[nodiscard]]
constexpr int references() const noexcept {
return m_references;
}
@ -92,6 +95,7 @@ class AssetRef: public ox::SignalHandler {
}
}
[[nodiscard]]
constexpr const T *get() const noexcept {
if (m_ctr) {
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>();
ctx->appName = appName;
oxIgnoreError(setRomFs(ctx.get(), std::move(fs)));
auto mods = modules();
if (mods) {
for (auto &mod : *mods) {
// register type converters
for (auto c : mod->converters()) {
ctx->converters.emplace_back(c);
}
// register pack transforms
for (auto c : mod->packTransforms()) {
ctx->packTransforms.emplace_back(c);
}
const auto &mods = modules();
for (auto &mod : mods) {
// register type converters
for (auto c : mod->converters()) {
ctx->converters.emplace_back(c);
}
// register pack transforms
for (auto c : mod->packTransforms()) {
ctx->packTransforms.emplace_back(c);
}
}
return ctx;

View File

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

View File

@ -38,6 +38,6 @@ class Module {
void registerModule(const Module *mod) noexcept;
[[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>
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));
const auto typeId = ox::buildTypeId(
hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);

View File

@ -3,7 +3,6 @@
*/
#include <nostalgia/core/core.hpp>
#include <nostalgia/foundation/media.hpp>
#include <nostalgia/scene/scene.hpp>
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));
core::setUpdateHandler(ctx.get(), updateHandler);
core::setKeyEventHandler(ctx.get(), keyEventHandler);
s_scene.emplace(scn.get());
s_scene.emplace(*scn);
oxReturnError(s_scene->setupDisplay(ctx.get()));
return core::run(ctx.get());
}

View File

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

View File

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

View File

@ -8,25 +8,25 @@
namespace nostalgia::scene {
Scene::Scene(const SceneStatic *sceneStatic) noexcept:
Scene::Scene(const SceneStatic &sceneStatic) noexcept:
m_sceneStatic(sceneStatic) {
}
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");
}
const auto &palette = m_sceneStatic->palettes[0];
const auto &palette = m_sceneStatic.palettes[0];
oxReturnError(core::loadBgTileSheet(
ctx, 0, m_sceneStatic->tilesheet, palette));
ctx, 0, m_sceneStatic.tilesheet, palette));
// disable all backgrounds
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::setBgCbb(ctx, layerNo, 0);
auto x = 0;
auto y = 0;
auto width = m_sceneStatic->rows[layerNo];
auto width = m_sceneStatic.rows[layerNo];
for (const auto &tile : layer) {
core::setTile(ctx, layerNo, x, y, tile);
core::setTile(ctx, layerNo, x + 1, y, tile + 1);

View File

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

View File

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

View File

@ -14,6 +14,17 @@
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 {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.TileDoc";
@ -23,6 +34,7 @@ struct TileDoc {
core::SubSheetId subsheetId = -1;
ox::String subsheetPath;
uint8_t type = 0;
ox::Array<uint8_t, 4> layerAttachments;
[[nodiscard]]
constexpr ox::Result<core::SubSheetId> getSubsheetId(const core::TileSheet &ts) const noexcept {
@ -88,6 +100,38 @@ oxModelBegin(SceneDoc)
oxModelField(tiles)
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 {
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneStatic";
@ -97,9 +141,11 @@ struct SceneStatic {
struct Tile {
uint16_t &tileMapIdx;
uint8_t &tileType;
constexpr Tile(uint16_t *pTileMapIdx, uint8_t *pTileType) noexcept:
tileMapIdx(*pTileMapIdx),
tileType(*pTileType) {
uint8_t &layerAttachments;
constexpr Tile(uint16_t &pTileMapIdx, uint8_t &pTileType, uint8_t &pLayerAttachments) noexcept:
tileMapIdx(pTileMapIdx),
tileType(pTileType),
layerAttachments(pLayerAttachments) {
}
};
struct Layer {
@ -107,19 +153,22 @@ struct SceneStatic {
uint16_t &rows;
ox::Vector<uint16_t> &tileMapIdx;
ox::Vector<uint8_t> &tileType;
ox::Vector<uint8_t> &layerAttachments;
constexpr Layer(
uint16_t *pColumns,
uint16_t *pRows,
ox::Vector<uint16_t> *pTileMapIdx,
ox::Vector<uint8_t> *pTileType) noexcept:
columns(*pColumns),
rows(*pRows),
tileMapIdx(*pTileMapIdx),
tileType(*pTileType) {
uint16_t &pColumns,
uint16_t &pRows,
ox::Vector<uint16_t> &pTileMapIdx,
ox::Vector<uint8_t> &pTileType,
ox::Vector<uint8_t> &pLayerAttachments) noexcept:
columns(pColumns),
rows(pRows),
tileMapIdx(pTileMapIdx),
tileType(pTileType),
layerAttachments(pLayerAttachments) {
}
[[nodiscard]]
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 {
columns = dim.width;
@ -127,6 +176,7 @@ struct SceneStatic {
const auto tileCnt = static_cast<unsigned>(columns * rows);
tileMapIdx.resize(tileCnt);
tileType.resize(tileCnt);
layerAttachments.resize(tileCnt);
}
};
@ -137,13 +187,21 @@ struct SceneStatic {
ox::Vector<uint16_t> rows;
ox::Vector<ox::Vector<uint16_t>> tileMapIdx;
ox::Vector<ox::Vector<uint8_t>> tileType;
ox::Vector<ox::Vector<uint8_t>> layerAttachments;
[[nodiscard]]
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 {
this->layerAttachments.resize(layerCnt);
this->columns.resize(layerCnt);
this->rows.resize(layerCnt);
this->tileMapIdx.resize(layerCnt);
@ -157,8 +215,9 @@ oxModelBegin(SceneStatic)
oxModelField(palettes)
oxModelField(columns)
oxModelField(rows)
oxModelFieldRename(tile_map_idx, tileMapIdx)
oxModelFieldRename(tile_type, tileType)
oxModelField(tileMapIdx)
oxModelField(tileType)
oxModelField(layerAttachments)
oxModelEnd()
}

View File

@ -9,6 +9,30 @@
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(
foundation::Context *ctx,
SceneDoc *src,
@ -32,6 +56,7 @@ ox::Error SceneDocToSceneStaticConverter::convert(
oxRequire(path, srcTile.getSubsheetPath(*ts));
oxRequire(mapIdx, ts->getTileOffset(path));
dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx);
setLayerAttachments(layerIdx, srcTile, dstTile);
++tileIdx;
}
}

View File

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

View File

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

View File

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

View File

@ -29,6 +29,13 @@ void BaseEditor::exportFile() {
void BaseEditor::keyStateChanged(core::Key, bool) {
}
void BaseEditor::onActivated() noexcept {
}
bool BaseEditor::requiresConstantRefresh() const noexcept {
return m_requiresConstantRefresh;
}
void BaseEditor::close() const {
this->closed.emit(itemName());
}
@ -100,6 +107,11 @@ ox::StringView BaseEditor::pathToItemName(ox::CRStringView path) noexcept {
return path.substr(lastSlash + 1);
}
void BaseEditor::setRequiresConstantRefresh(bool value) noexcept {
m_requiresConstantRefresh = value;
}
Editor::Editor() noexcept {
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_copyEnabled = false;
bool m_pasteEnabled = false;
bool m_requiresConstantRefresh = false;
public:
~BaseEditor() override = default;
@ -50,6 +51,11 @@ class NOSTALGIASTUDIO_EXPORT BaseEditor: public Widget {
virtual void keyStateChanged(core::Key key, bool down);
virtual void onActivated() noexcept;
[[nodiscard]]
bool requiresConstantRefresh() const noexcept;
void close() const;
/**
@ -102,6 +108,8 @@ class NOSTALGIASTUDIO_EXPORT BaseEditor: public Widget {
static ox::StringView pathToItemName(ox::CRStringView path) noexcept;
void setRequiresConstantRefresh(bool value) noexcept;
// signals
public:
ox::Signal<ox::Error(bool)> unsavedChangesChanged;

View File

@ -14,7 +14,7 @@
namespace nostalgia::studio {
static void generateTypes(ox::TypeStore *ts) noexcept {
for (const auto mod : *foundation::modules()) {
for (const auto mod : foundation::modules()) {
for (auto gen : mod->types()) {
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::setKeyEventHandler(ctx.get(), keyEventHandler);
core::setConstantRefresh(ctx.get(), false);
core::gl::setMainViewEnabled(false);
studio::StudioContext studioCtx;
core::setApplicationData(ctx.get(), &studioCtx);
StudioUI ui(ctx.get());
studioCtx.ui = &ui;
StudioUIDrawer drawer(&ui);
ctx->drawers.emplace_back(&drawer);
return core::run(ctx.get());
core::addCustomDrawer(ctx.get(), &drawer);
auto err = core::run(ctx.get());
core::removeCustomDrawer(ctx.get(), &drawer);
return err;
}
static ox::Error run(int, const char**) noexcept {

View File

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