From 2103a03a154effd30dd70f0b5e871962883130c3 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Sat, 11 Jan 2025 03:38:05 -0600 Subject: [PATCH] [jasper/world] Add support for incremental updating of WorldStatic --- .../include/jasper/world/objectcache.hpp | 54 ------ .../world/include/jasper/world/worlddoc.hpp | 2 +- .../include/jasper/world/worldstatic.hpp | 46 ++++- src/jasper/modules/world/src/CMakeLists.txt | 1 - .../modules/world/src/keel/typeconv.cpp | 4 +- src/jasper/modules/world/src/objectcache.cpp | 98 ---------- .../worldeditor/commands/modifytiles.cpp | 25 +-- .../worldeditor/commands/modifytiles.hpp | 6 +- .../studio/worldeditor/worldeditor-imgui.cpp | 40 ++-- .../studio/worldeditor/worldeditor-imgui.hpp | 9 +- .../src/studio/worldeditor/worldeditor.cpp | 20 -- .../src/studio/worldeditor/worldeditor.hpp | 34 ---- .../studio/worldeditor/worldeditorview.cpp | 4 +- src/jasper/modules/world/src/world.cpp | 8 +- src/jasper/modules/world/src/worldstatic.cpp | 183 +++++++++++------- 15 files changed, 195 insertions(+), 339 deletions(-) delete mode 100644 src/jasper/modules/world/include/jasper/world/objectcache.hpp delete mode 100644 src/jasper/modules/world/src/objectcache.cpp delete mode 100644 src/jasper/modules/world/src/studio/worldeditor/worldeditor.cpp delete mode 100644 src/jasper/modules/world/src/studio/worldeditor/worldeditor.hpp diff --git a/src/jasper/modules/world/include/jasper/world/objectcache.hpp b/src/jasper/modules/world/include/jasper/world/objectcache.hpp deleted file mode 100644 index 641bf4b..0000000 --- a/src/jasper/modules/world/include/jasper/world/objectcache.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2023 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -#include - -#include - -#include -#include - -namespace jasper::world { - -class ObjectCache { - public: - struct Obj { - ObjectId id{}; - uint16_t palBank{}; - size_t tilesheetIdx{}; - }; - private: - struct ObjSet { - uint64_t setId{}; - ox::Vector objects; - }; - ox::Vector m_tilesheets; - ox::Vector m_palettes; - size_t m_tileIdx{}; - size_t m_palBank{}; - ox::Vector m_objSets; - ncore::TileSheetSet m_tilesheetSet; - public: - void clear() noexcept; - ox::Error indexSet( - keel::Context &kctx, - uint64_t setId, - WorldObjectSet const&objSet) noexcept; - [[nodiscard]] - ox::Optional obj(uint64_t setId, ObjectId objId) const noexcept; - [[nodiscard]] - ncore::TileSheetSet const&tilesheetSet() const noexcept; - [[nodiscard]] - ox::Vector const&tilesheets() const noexcept; - [[nodiscard]] - ox::Vector const&palettes() const noexcept; - private: - void addTileSheetSetEntry(ox::FileAddress path, int32_t tiles) noexcept; -}; - -ox::Result buildObjCache(keel::Context &kctx, WorldDoc const&doc) noexcept; - -} diff --git a/src/jasper/modules/world/include/jasper/world/worlddoc.hpp b/src/jasper/modules/world/include/jasper/world/worlddoc.hpp index f2c9826..f6f7213 100644 --- a/src/jasper/modules/world/include/jasper/world/worlddoc.hpp +++ b/src/jasper/modules/world/include/jasper/world/worlddoc.hpp @@ -166,7 +166,7 @@ void resize(WorldDoc &doc, ox::Size const&sz) noexcept; } template<> -struct std::hash { +struct ox::hash { [[nodiscard]] constexpr size_t operator()(jasper::world::DocObjRef const&v) const noexcept { return static_cast(v.worldObjectSetId) diff --git a/src/jasper/modules/world/include/jasper/world/worldstatic.hpp b/src/jasper/modules/world/include/jasper/world/worldstatic.hpp index 8eed617..28052f0 100644 --- a/src/jasper/modules/world/include/jasper/world/worldstatic.hpp +++ b/src/jasper/modules/world/include/jasper/world/worldstatic.hpp @@ -10,8 +10,6 @@ #include #include -#include - #include "worlddoc.hpp" namespace jasper::world { @@ -144,10 +142,6 @@ constexpr bool valid(WorldStatic const&ws) noexcept { }); } -void loadTile(TileStatic &dst, TileDoc const&src) noexcept; - -ox::Result loadWorldStatic(keel::Context &kctx, WorldDoc const&doc) noexcept; - [[nodiscard]] constexpr size_t layers(WorldStatic const&ws) noexcept { return ws.map.size(); @@ -164,8 +158,46 @@ auto &tile( } [[nodiscard]] -auto &cbb(ox::CommonRefWith auto&ws, size_t lyr) noexcept { +auto &cbb(ox::CommonRefWith auto&ws, size_t const lyr) noexcept { return ws.map[lyr].cbb; } + +class WorldStaticLoader { + private: + keel::Context &m_kctx; + WorldStatic &m_worldStatic; + WorldDoc const&m_doc; + struct CacheEntry { + uint8_t value{}; + int refCnt{}; + }; + ox::SmallMap m_cache; + ox::SmallMap m_objSets; + uint16_t m_cbbIt{4}; + bool m_loaded = false; + + public: + WorldStaticLoader(keel::Context &kctx, WorldStatic &worldStatic, WorldDoc const&doc); + + ox::Error loadWorldStatic() noexcept; + + ox::Error updateTile( + uint32_t layer, + uint32_t col, + uint32_t row, + TileDoc const&oldSrc) noexcept; + + void loadTile( + TileStatic &dst, + TileDoc const&src) noexcept; + + void reset(); + + private: + ox::Result setupTileResrc(DocObjRef const&docObjRef) noexcept; + + void deleteRefSet(uint8_t refIdx, DocObjRef const&oldObjRef) noexcept; +}; + } diff --git a/src/jasper/modules/world/src/CMakeLists.txt b/src/jasper/modules/world/src/CMakeLists.txt index 73bdbe1..7cf5cce 100644 --- a/src/jasper/modules/world/src/CMakeLists.txt +++ b/src/jasper/modules/world/src/CMakeLists.txt @@ -1,7 +1,6 @@ add_library( JasperWorld - objectcache.cpp world.cpp worlddoc.cpp worldobject.cpp diff --git a/src/jasper/modules/world/src/keel/typeconv.cpp b/src/jasper/modules/world/src/keel/typeconv.cpp index eb3d63f..c4aff2f 100644 --- a/src/jasper/modules/world/src/keel/typeconv.cpp +++ b/src/jasper/modules/world/src/keel/typeconv.cpp @@ -5,8 +5,6 @@ #include #include -#include - #include "typeconv.hpp" namespace jasper::world { @@ -15,7 +13,7 @@ ox::Error WorldDocToWorldStaticConverter::convert( keel::Context &kctx, WorldDoc &src, WorldStatic &dst) const noexcept { - return loadWorldStatic(kctx, src).moveTo(dst); + return WorldStaticLoader{kctx, dst, src}.loadWorldStatic(); } } diff --git a/src/jasper/modules/world/src/objectcache.cpp b/src/jasper/modules/world/src/objectcache.cpp deleted file mode 100644 index d7ebeb8..0000000 --- a/src/jasper/modules/world/src/objectcache.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2023 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include - -#include - -namespace jasper::world { - -void ObjectCache::clear() noexcept { - m_tileIdx = 0; - m_palBank = 0; - m_tilesheets.clear(); - m_palettes.clear(); - m_objSets.clear(); -} - -ox::Error ObjectCache::indexSet( - keel::Context &kctx, - uint64_t const setId, - WorldObjectSet const&objSet) noexcept { - auto &set = m_objSets.emplace_back(ObjSet{ - .setId = setId, - .objects = {}, - }); - OX_REQUIRE(ts, readObj(kctx, objSet.tilesheet)); - auto tsId = ox::find(m_tilesheets.begin(), m_tilesheets.end(), objSet.tilesheet); - if (tsId == m_tilesheets.end()) { - m_tilesheets.emplace_back(objSet.tilesheet); - } - for (auto const&obj : objSet.objects) { - set.objects.emplace_back(Obj{ - .id = obj.id, - .palBank = static_cast(obj.palBank + m_palBank), - .tilesheetIdx = getTileIdx(*ts, obj.subsheetId).or_value(0) + m_tileIdx, - }); - } - auto const tileCnt = getTileCnt(*ts); - m_tileIdx += tileCnt; - m_tilesheetSet.bpp = 4; - addTileSheetSetEntry(objSet.tilesheet, static_cast(tileCnt)); - for (auto const&pal : objSet.palettes) { - m_palettes.emplace_back(pal); - OX_REQUIRE(p, readObj(kctx, pal)); - m_palBank += largestPage(*p); - } - return {}; -} - -ox::Optional ObjectCache::obj(uint64_t setId, ObjectId objId) const noexcept { - for (auto &set : m_objSets) { - if (set.setId == setId) { - for (auto &obj : set.objects) { - if (obj.id == objId) { - return ox::Optional{ox::in_place, obj}; - } - } - break; - } - } - return {}; -} - -ncore::TileSheetSet const&ObjectCache::tilesheetSet() const noexcept { - return m_tilesheetSet; -} - -ox::Vector const&ObjectCache::tilesheets() const noexcept { - return m_tilesheets; -} - -ox::Vector const&ObjectCache::palettes() const noexcept { - return m_palettes; -} - -void ObjectCache::addTileSheetSetEntry(ox::FileAddress path, int32_t tiles) noexcept { - ncore::TileSheetSetEntry entry; - entry.tilesheet = std::move(path); - entry.sections.push_back({ - .begin = 0, - .tiles = tiles, - }); - m_tilesheetSet.entries.emplace_back(std::move(entry)); -} - -ox::Result buildObjCache(keel::Context &kctx, WorldDoc const&doc) noexcept { - ObjectCache cache; - for (auto &set : doc.objSets) { - OX_REQUIRE(s, readObj(kctx, set.path)); - OX_RETURN_ERROR(cache.indexSet(kctx, set.id, *s)); - } - return cache; -} - -} diff --git a/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp b/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp index cd04a4c..963e1d7 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp +++ b/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp @@ -10,12 +10,12 @@ namespace jasper::world { ModifyTilesCommand::ModifyTilesCommand( + WorldStaticLoader &loader, WorldDoc &doc, - WorldStatic &worldStatic, ox::Vector mods): - m_doc(doc), - m_worldStatic(worldStatic), - m_mods(std::move(mods)) { + m_loader{loader}, + m_doc{doc}, + m_mods{std::move(mods)} { auto const unchanged = [this](Mod const&mod) { auto const&docTile = tile(m_doc, mod.layer, mod.tileAddr.x, mod.tileAddr.y); return docTile.obj.worldObjectId == mod.objId && docTile.obj.worldObjectSetId == mod.setId; @@ -26,27 +26,30 @@ ModifyTilesCommand::ModifyTilesCommand( } ox::Error ModifyTilesCommand::redo() noexcept { - swap(); - return {}; + return swap(); } ox::Error ModifyTilesCommand::undo() noexcept { - swap(); - return {}; + return swap(); } int ModifyTilesCommand::commandId() const noexcept { return static_cast(WorldEditorCommand::ModifyTiles); } -void ModifyTilesCommand::swap() noexcept { +ox::Error ModifyTilesCommand::swap() noexcept { for (auto &mod : m_mods) { auto &docTile = tile(m_doc, mod.layer, mod.tileAddr.x, mod.tileAddr.y); - auto &activeTile = tile(m_worldStatic, mod.layer, mod.tileAddr.x, mod.tileAddr.y); + auto const oldDocTileVal = docTile; std::swap(docTile.obj.worldObjectId, mod.objId); std::swap(docTile.obj.worldObjectSetId, mod.setId); - loadTile(activeTile, docTile); + OX_RETURN_ERROR(m_loader.updateTile( + mod.layer, + static_cast(mod.tileAddr.x), + static_cast(mod.tileAddr.y), + oldDocTileVal)); } + return {}; } } diff --git a/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.hpp b/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.hpp index 90915b4..07d9e3e 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.hpp +++ b/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.hpp @@ -19,20 +19,20 @@ class ModifyTilesCommand: public studio::UndoCommand { uint64_t setId{}; }; private: + WorldStaticLoader &m_loader; WorldDoc &m_doc; - WorldStatic &m_worldStatic; ox::Vector m_mods; public: ModifyTilesCommand( + WorldStaticLoader &loader, WorldDoc &doc, - WorldStatic &worldStatic, ox::Vector mods); ox::Error redo() noexcept override; ox::Error undo() noexcept override; [[nodiscard]] int commandId() const noexcept override; private: - void swap() noexcept; + ox::Error swap() noexcept; }; } diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp index e80e70c..2b1600a 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp @@ -48,11 +48,6 @@ OX_MODEL_BEGIN(WorldTileDragDrop) OX_MODEL_END() -static WorldDoc makeValid(WorldDoc doc) noexcept { - std::ignore = repair(doc); - return doc; -} - [[nodiscard]] static ox::Vec2 fbPos(ox::Vec2 fbPos) noexcept { auto const winPos = ImGui::GetWindowPos(); @@ -79,11 +74,9 @@ constexpr ox::Point fbPtToTileAddr( WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringParam path): Editor(std::move(path)), - m_sctx(sctx), - m_doc(makeValid(*readObj(keelCtx(m_sctx), itemPath()).unwrapThrow())), - m_objCache(buildObjCache(keelCtx(m_sctx), m_doc).unwrapThrow()), - m_worldStatic(loadWorldStatic(keelCtx(m_sctx), m_doc)), - m_view(m_sctx, m_worldStatic) { + m_sctx{sctx}, + m_doc{*keel::readObj(keelCtx(m_sctx), itemPath()).unwrapThrow()} { + OX_THROW_ERROR(m_loader.loadWorldStatic()); OX_THROW_ERROR(loadObjectSets()); m_objSetPicker.filePicked.connect(this, &WorldEditorImGui::addObjSet); m_sctx.project->fileUpdated.connect(this, &WorldEditorImGui::handleDepUpdate); @@ -148,8 +141,8 @@ void WorldEditorImGui::cut() noexcept { }); }); std::ignore = pushCommand( + m_loader, m_doc, - m_worldStatic, std::move(mods)); } } @@ -172,8 +165,8 @@ void WorldEditorImGui::paste() { }); } std::ignore = pushCommand( + m_loader, m_doc, - m_worldStatic, std::move(mods)); } } @@ -331,7 +324,7 @@ void WorldEditorImGui::drawWorldView() noexcept { } } -void WorldEditorImGui::handleMouseSelection(float fbPaneScale) noexcept { +void WorldEditorImGui::handleMouseSelection(float const fbPaneScale) noexcept { auto const&io = ImGui::GetIO(); if (io.MouseDown[0]) { auto const fbPos = world::fbPos(ox::Vec2{io.MousePos}); @@ -350,7 +343,7 @@ void WorldEditorImGui::handleMouseSelection(float fbPaneScale) noexcept { } } -ox::Error WorldEditorImGui::handleDrop(float fbPaneScale) noexcept { +ox::Error WorldEditorImGui::handleDrop(float const fbPaneScale) noexcept { OX_REQUIRE(objId, ig::getDragDropPayload("WorldTile")); auto const&io = ImGui::GetIO(); auto const fbPos = world::fbPos(ox::Vec2{io.MousePos}); @@ -382,15 +375,15 @@ ox::Error WorldEditorImGui::handleDrop(float fbPaneScale) noexcept { }; } std::ignore = pushCommand( + m_loader, m_doc, - m_worldStatic, std::move(mods)); } - OX_RETURN_ERROR(loadWorldStatic(keelCtx(m_sctx), m_doc).moveTo(m_worldStatic)); + OX_RETURN_ERROR(m_loader.loadWorldStatic()); return {}; } -ox::Error WorldEditorImGui::addObjSet(ox::StringView path) noexcept { +ox::Error WorldEditorImGui::addObjSet(ox::StringViewCR path) noexcept { OX_REQUIRE(uuid, getUuid(keelCtx(m_sctx), path)); std::ignore = pushCommand(m_doc, uuid); return {}; @@ -400,7 +393,7 @@ void WorldEditorImGui::rmObjSet() noexcept { std::ignore = pushCommand(m_doc, m_palMgr.selectedIdx); } -ox::Error WorldEditorImGui::handleDepUpdate(ox::StringView, ox::UUID const&uuid) noexcept { +ox::Error WorldEditorImGui::handleDepUpdate(ox::StringViewCR, ox::UUID const&uuid) noexcept { auto &kctx = keelCtx(m_sctx); auto const objSetMatches = [&uuid, &kctx](ObjectSetEntry const&set) { auto const [setUuid, err] = getUuid(kctx, set.path); @@ -410,10 +403,10 @@ ox::Error WorldEditorImGui::handleDepUpdate(ox::StringView, ox::UUID const&uuid) return set.key == uuid; }; auto const depUpdated = ox::any_of(m_doc.objSets.begin(), m_doc.objSets.end(), objSetMatches) - || ox::any_of(m_dependencies.pairs().begin(), m_dependencies.pairs().end(), depMatches); + || ox::any_of(m_dependencies.pairs().begin(), m_dependencies.pairs().end(), depMatches); if (depUpdated) { - OX_RETURN_ERROR(buildObjCache(kctx, m_doc).moveTo(m_objCache)); - OX_RETURN_ERROR(loadWorldStatic(keelCtx(m_sctx), m_doc).moveTo(m_worldStatic)); + m_loader.reset(); + OX_RETURN_ERROR(m_loader.loadWorldStatic()); OX_RETURN_ERROR(loadObjectSets()); } return {}; @@ -435,10 +428,11 @@ ox::Error WorldEditorImGui::loadObjectSets() noexcept { } ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*cmd) { - OX_RETURN_ERROR(m_view.setupWorld()); if (dynamic_cast(cmd)) { - OX_RETURN_ERROR(loadWorldStatic(keelCtx(m_sctx), m_doc).moveTo(m_worldStatic)); + m_loader.reset(); + OX_RETURN_ERROR(m_loader.loadWorldStatic()); } + OX_RETURN_ERROR(m_view.setupWorld()); return {}; } diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp index 072ab73..75c719c 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp @@ -9,7 +9,6 @@ #include #include -#include #include "worldeditorview.hpp" @@ -24,7 +23,6 @@ class WorldEditorImGui: public studio::Editor { studio::StudioContext &m_sctx; studio::ig::FilePicker m_objSetPicker{m_sctx, "Choose Object Set", FileExt_jwob}; WorldDoc m_doc; - ObjectCache m_objCache; struct ObjSetRef { ox::String name; uint64_t id{}; @@ -38,7 +36,8 @@ class WorldEditorImGui: public studio::Editor { ox::Vector m_objSets; ox::SmallMap m_dependencies; WorldStatic m_worldStatic; - WorldEditorView m_view; + WorldStaticLoader m_loader{keelCtx(m_sctx), m_worldStatic, m_doc}; + WorldEditorView m_view{m_sctx, m_worldStatic}; struct { size_t selectedIdx{}; } m_palMgr; @@ -83,12 +82,12 @@ class WorldEditorImGui: public studio::Editor { ox::Error handleDrop(float fbPaneScale) noexcept; - ox::Error addObjSet(ox::StringView path) noexcept; + ox::Error addObjSet(ox::StringViewCR path) noexcept; void rmObjSet() noexcept; // handles the updating of an object set in case it is one used by this world - ox::Error handleDepUpdate(ox::StringView path, ox::UUID const&) noexcept; + ox::Error handleDepUpdate(ox::StringViewCR path, ox::UUID const&) noexcept; ox::Error loadObjectSets() noexcept; diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor.cpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor.cpp deleted file mode 100644 index f3c799f..0000000 --- a/src/jasper/modules/world/src/studio/worldeditor/worldeditor.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2023 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#include - -#include "worldeditor.hpp" - -namespace jasper::world { - -WorldEditor::WorldEditor(studio::StudioContext &ctx, ox::StringView path): - m_ctx(ctx), - m_world(*readObj(keelCtx(m_ctx.tctx), path).unwrapThrow()) { -} - -ox::Error WorldEditor::saveItem() noexcept { - return m_ctx.project->writeObj(m_itemPath, m_world); -} - -} diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor.hpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor.hpp deleted file mode 100644 index b64ca4d..0000000 --- a/src/jasper/modules/world/src/studio/worldeditor/worldeditor.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2023 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. - */ - -#pragma once - -#include -#include - -#include - -namespace jasper::world { - -class WorldEditor { - - private: - studio::StudioContext &m_ctx; - ox::String m_itemPath; - WorldStatic m_world; - - public: - WorldEditor(studio::StudioContext &ctx, ox::StringView path); - - [[nodiscard]] - WorldStatic const&world() const noexcept { - return m_world; - } - - protected: - ox::Error saveItem() noexcept; - -}; - -} diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.cpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.cpp index 240983a..bff7ce5 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.cpp +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.cpp @@ -83,10 +83,10 @@ void WorldEditorView::setPixelBufferObject( auto const selection = 1.f * static_cast(selected); // don't worry, these memcpys gets optimized to something much more ideal std::array const vertices{ - x, y, selection, // bottom left + x, y, selection, // bottom left x + xmod, y, selection, // bottom right x + xmod, y + ymod, selection, // top right - x, y + ymod, selection, // top left + x, y + ymod, selection, // top left }; memcpy(vbo, vertices.data(), sizeof(vertices)); ox::Array const elms{ diff --git a/src/jasper/modules/world/src/world.cpp b/src/jasper/modules/world/src/world.cpp index e2f9abe..6dbe523 100644 --- a/src/jasper/modules/world/src/world.cpp +++ b/src/jasper/modules/world/src/world.cpp @@ -15,6 +15,9 @@ World::World(ncore::Context &nctx, WorldStatic const&worldStatic) noexcept: m_worldStatic(worldStatic) {} ox::Error World::setupDisplay() noexcept { + if (m_worldStatic.objTileRefSets.empty()) { + return ox::Error{1, "WorldStatic must have at least one ref set"}; + } auto &kctx = keelCtx(m_nctx); if (m_tilesheets.empty()) { for (auto const&tsAddr : m_worldStatic.tilesheets) { @@ -23,11 +26,10 @@ ox::Error World::setupDisplay() noexcept { } } if (m_worldStatic.palettes.empty()) { - return ox::Error(1, "World has no palettes"); + return ox::Error{1, "World has no palettes"}; } for (auto i = 0u; auto const&palAddr : m_worldStatic.palettes) { - OX_REQUIRE(pal, readObj(keelCtx(m_nctx), palAddr)); - OX_RETURN_ERROR(ncore::loadBgPalette(m_nctx, i, *pal)); + OX_RETURN_ERROR(ncore::loadBgPalette(m_nctx, i, palAddr)); ++i; } for (auto const&rs : m_worldStatic.objTileRefSets) { diff --git a/src/jasper/modules/world/src/worldstatic.cpp b/src/jasper/modules/world/src/worldstatic.cpp index 2d7b4f4..61295cf 100644 --- a/src/jasper/modules/world/src/worldstatic.cpp +++ b/src/jasper/modules/world/src/worldstatic.cpp @@ -11,40 +11,106 @@ namespace jasper::world { -static void addToVec(ox::Vector &vec, ox::FileAddress const&val) { +inline void ensureInVec(ox::Vector &vec, ox::FileAddress const&val) { if (!vec.contains(val)) { vec.emplace_back(val); } } -void loadTile( - TileStatic &dst, - TileDoc const&src) noexcept { + +WorldStaticLoader::WorldStaticLoader(keel::Context &kctx, WorldStatic &worldStatic, WorldDoc const&doc): + m_kctx{kctx}, + m_worldStatic{worldStatic}, + m_doc{doc} {} + +ox::Error WorldStaticLoader::loadWorldStatic() noexcept { + reset(); + auto const tilesPerLayer = + static_cast(m_doc.columns) * static_cast(m_doc.rows); + for (auto const&setRef : m_doc.objSets) { + auto const [set, err] = + keel::readObj(m_kctx, setRef.path); + if (!err) { + m_objSets[setRef.id] = *set; + ensureInVec(m_worldStatic.tilesheets, set->tilesheet); + for (auto const&pal : set->palettes) { + ensureInVec(m_worldStatic.palettes, pal); + } + } + } + m_worldStatic.columns = static_cast(m_doc.columns), + m_worldStatic.rows = static_cast(m_doc.rows), + m_worldStatic.map = { + {.tiles = ox::Vector{tilesPerLayer},}, + {.tiles = ox::Vector{tilesPerLayer},}, + {.tiles = ox::Vector{tilesPerLayer},}, + }; + // tiles + for (auto lyr = 0u; lyr < layers(m_worldStatic); ++lyr) { + for (auto x = 0u; x < static_cast(m_worldStatic.columns); ++x) { + for (auto y = 0u; y < static_cast(m_worldStatic.rows); ++y) { + auto &dst = tile(m_worldStatic, lyr, x, y); + auto &src = tile(m_doc, lyr, x, y);; + loadTile(dst, src); + } + } + } + m_loaded = true; + return {}; +} + +ox::Error WorldStaticLoader::updateTile( + uint32_t const layer, + uint32_t const col, + uint32_t const row, + TileDoc const&oldSrc) noexcept { + auto &dst = tile(m_worldStatic, layer, col, row); + auto &newSrc = tile(m_doc, layer, col, row); + if (oldSrc.obj.unique) { + deleteRefSet(dst.objIdxRefSet, oldSrc.obj); + } + auto const current = m_cache.at(oldSrc.obj); + if (current.ok()) { + --current.value->refCnt; + if (current.value->refCnt == 0) { + auto const rs = dst.objIdxRefSet; + dst.objIdxRefSet = 0; + deleteRefSet(rs, oldSrc.obj); + } + } + loadTile(dst, newSrc); + return {}; +} + +void WorldStaticLoader::loadTile(TileStatic &dst, TileDoc const&src) noexcept { dst.tileType = src.type; setTopEdge(dst.layerAttachments, src.topLayerAttachment); setBottomEdge(dst.layerAttachments, src.bottomLayerAttachment); setLeftEdge(dst.layerAttachments, src.leftLayerAttachment); setRightEdge(dst.layerAttachments, src.rightLayerAttachment); + dst.objIdxRefSet = setupTileResrc(src.obj).or_value(0);; +} + +void WorldStaticLoader::reset() { + m_worldStatic = {}; + m_cache.clear(); + m_objSets.clear(); + m_cbbIt = 4; + m_loaded = false; } /** * Returns the index in rsrcs for the given docObjRef. */ -static ox::Result setupTileResrc( - ox::SmallMap &cache, - keel::Context &kctx, - ox::Vector const&tilesheets, - ox::SmallMap const&objSets, - ox::Vector &rsrcs, - uint16_t &cbbIt, - DocObjRef const&docObjRef) noexcept { +ox::Result WorldStaticLoader::setupTileResrc(DocObjRef const&docObjRef) noexcept { if (!docObjRef.unique) { - auto const [out, err] = cache.at(docObjRef); + auto const [out, err] = m_cache.at(docObjRef); if (!err) { - return *out; + ++out->refCnt; + return out->value; } } - OX_REQUIRE(objSet, objSets.at(docObjRef.worldObjectSetId)); + OX_REQUIRE(objSet, m_objSets.at(docObjRef.worldObjectSetId)); auto const obj = ox::find_if( objSet->objects.begin(), objSet->objects.end(), [&docObjRef](WorldObject const&o) { @@ -53,9 +119,9 @@ static ox::Result setupTileResrc( if (obj == objSet->objects.end()) { return ox::Error{obj == objSet->objects.end(), "could not find WorldObject in WorldObjectSet"}; } - OX_REQUIRE(ts, keel::readObj(kctx, objSet->tilesheet)); + OX_REQUIRE(ts, keel::readObj(m_kctx, objSet->tilesheet)); OX_REQUIRE(tsIdx, ox::findIdx( - tilesheets.begin(), tilesheets.end(), objSet->tilesheet).to()); + m_worldStatic.tilesheets.begin(), m_worldStatic.tilesheets.end(), objSet->tilesheet).to()); auto const subsheetOffset = ncore::getTileIdx(*ts, obj->subsheetId); if (!subsheetOffset) { return ox::Error{1, "invalid subsheet idx"}; @@ -64,77 +130,46 @@ static ox::Result setupTileResrc( if (!subsheet) { return ox::Error{1, "could not find subsheet"}; } - auto const out = static_cast(rsrcs.size()); - auto const&refSet = rsrcs.emplace_back(ObjTileRefSet{ + auto const out = static_cast(m_worldStatic.objTileRefSets.size()); + auto const&refSet = m_worldStatic.objTileRefSets.emplace_back(ObjTileRefSet{ .palBank = obj->palBank, .tilesheetIdx = static_cast(*subsheetOffset), - .cbbIdx = cbbIt, + .cbbIdx = m_cbbIt, .tilesheetId = tsIdx, .tileCnt = static_cast(subsheet->size()), }); - cbbIt += refSet.tileCnt; + m_cbbIt += refSet.tileCnt; if (!docObjRef.unique) { - cache[docObjRef] = out; + m_cache[docObjRef] = {.value = out, .refCnt = 1}; } return out; } -ox::Result loadWorldStatic( - keel::Context &kctx, - WorldDoc const&doc) noexcept { - auto const tilesPerLayer = - static_cast(doc.columns) * static_cast(doc.rows); - ox::Vector tilesheets; - ox::Vector palettes; - ox::SmallMap objSets; - for (auto const&setRef : doc.objSets) { - auto const [set, err] = - keel::readObj(kctx, setRef.path); - if (!err) { - objSets[setRef.id] = *set; - addToVec(tilesheets, set->tilesheet); - for (auto const&pal : set->palettes) { - addToVec(palettes, pal); - } - } - } - ox::Result result = WorldStatic{ - .objTileRefSets = {}, - .tilesheets = tilesheets, - .palettes = palettes, - .columns = static_cast(doc.columns), - .rows = static_cast(doc.rows), - .map = { - {.tiles = ox::Vector{tilesPerLayer},}, - {.tiles = ox::Vector{tilesPerLayer},}, - {.tiles = ox::Vector{tilesPerLayer},}, - }, - }; - auto &out = result.value; - ox::SmallMap refSetCache; - uint16_t cbbIt = 4; - // tiles - for (auto lyr = 0u; lyr < layers(out); ++lyr) { - for (auto x = 0u; x < static_cast(out.columns); ++x) { - for (auto y = 0u; y < static_cast(out.rows); ++y) { - auto &dst = tile(out, lyr, x, y); - auto &src = tile(doc, lyr, x, y);; - loadTile(dst, src); - auto const refSetIdx = setupTileResrc( - refSetCache, - kctx, - out.tilesheets, - objSets, - out.objTileRefSets, - cbbIt, - src.obj); - if (refSetIdx.ok()) { - dst.objIdxRefSet = refSetIdx.value; +void WorldStaticLoader::deleteRefSet(uint8_t const refIdx, DocObjRef const&oldObjRef) noexcept { + for (auto lyr = 0u; lyr < layers(m_worldStatic); ++lyr) { + for (auto x = 0u; x < static_cast(m_worldStatic.columns); ++x) { + for (auto y = 0u; y < static_cast(m_worldStatic.rows); ++y) { + auto &dst = tile(m_worldStatic, lyr, x, y); + auto oldVal = dst.objIdxRefSet; + if (dst.objIdxRefSet > refIdx) { + --dst.objIdxRefSet; } + oxAssert(dst.objIdxRefSet < m_worldStatic.objTileRefSets.size() - 1, "Invalid objIdxRefSet"); } } } - return result; + for (auto &p : m_cache.pairs()) { + if (p.value.value > refIdx) { + --p.value.value; + } + } + auto const&removedRefSet = m_worldStatic.objTileRefSets[refIdx]; + for (size_t i = refIdx + 1; i < m_worldStatic.objTileRefSets.size(); ++i) { + auto &refSet = m_worldStatic.objTileRefSets[i]; + refSet.cbbIdx -= removedRefSet.frames; + } + std::ignore = m_worldStatic.objTileRefSets.erase(refIdx); + m_cache.erase(oldObjRef); } }