[jasper/world] Rework WorldEditor to use incremental loading
All checks were successful
Build / build (push) Successful in 3m43s

This commit is contained in:
Gary Talent 2025-01-11 22:20:29 -06:00
parent c774b5da70
commit 590b2524e5
10 changed files with 87 additions and 50 deletions

View File

@ -33,6 +33,10 @@ class World {
ox::Result<int> editorModeUpdate() noexcept; ox::Result<int> editorModeUpdate() noexcept;
#endif #endif
void setupTiles() noexcept;
void setupTile(uint32_t lyr, uint32_t x, uint32_t y) const noexcept;
private: private:
void setupLayer(uint_t lyr, uint_t cbb) const noexcept; void setupLayer(uint_t lyr, uint_t cbb) const noexcept;

View File

@ -166,6 +166,9 @@ auto &cbb(ox::CommonRefWith<WorldStatic> auto&ws, size_t const lyr) noexcept {
class WorldStaticLoader { class WorldStaticLoader {
public:
ox::Signal<ox::Error(uint32_t layer, uint32_t x, uint32_t y)> tileUpdated;
ox::Signal<ox::Error()> resourcesUpdated;
private: private:
keel::Context &m_kctx; keel::Context &m_kctx;
WorldStatic &m_worldStatic; WorldStatic &m_worldStatic;
@ -177,7 +180,8 @@ class WorldStaticLoader {
ox::SmallMap<DocObjRef, CacheEntry> m_cache; ox::SmallMap<DocObjRef, CacheEntry> m_cache;
ox::SmallMap<uint64_t, WorldObjectSet> m_objSets; ox::SmallMap<uint64_t, WorldObjectSet> m_objSets;
uint16_t m_cbbIt{4}; uint16_t m_cbbIt{4};
bool m_loaded = false; bool m_resourcesChanged{};
bool m_loaded{};
public: public:
WorldStaticLoader(keel::Context &kctx, WorldStatic &worldStatic, WorldDoc const&doc); WorldStaticLoader(keel::Context &kctx, WorldStatic &worldStatic, WorldDoc const&doc);

View File

@ -23,10 +23,6 @@ class AddObjectSet: public studio::UndoCommand {
ox::Error undo() noexcept override; ox::Error undo() noexcept override;
[[nodiscard]] [[nodiscard]]
int commandId() const noexcept override; int commandId() const noexcept override;
[[nodiscard]]
inline size_t insertIdx() const noexcept {
return m_insertIdx;
}
}; };
class RmObjectSet: public studio::UndoCommand { class RmObjectSet: public studio::UndoCommand {

View File

@ -8,22 +8,24 @@
namespace jasper::world { namespace jasper::world {
EditWorldSizeCommand::EditWorldSizeCommand( EditWorldSizeCommand::EditWorldSizeCommand(
WorldStaticLoader &loader,
WorldDoc &doc, WorldDoc &doc,
ox::Size const&size): ox::Size const&size):
m_doc(doc), m_loader{loader},
m_oldSize(m_doc.columns, m_doc.rows), m_doc{doc},
m_newSize(size) {} m_oldSize{m_doc.columns, m_doc.rows},
m_newSize{size} {}
ox::Error EditWorldSizeCommand::redo() noexcept { ox::Error EditWorldSizeCommand::redo() noexcept {
m_oldMap = m_doc.tiles; m_oldMap = m_doc.tiles;
resize(m_doc, m_newSize); resize(m_doc, m_newSize);
return {}; return m_loader.loadWorldStatic();
} }
ox::Error EditWorldSizeCommand::undo() noexcept { ox::Error EditWorldSizeCommand::undo() noexcept {
resize(m_doc, m_oldSize); resize(m_doc, m_oldSize);
m_doc.tiles = std::move(m_oldMap); m_doc.tiles = std::move(m_oldMap);
return {}; return m_loader.loadWorldStatic();
} }
int EditWorldSizeCommand::commandId() const noexcept { int EditWorldSizeCommand::commandId() const noexcept {

View File

@ -4,8 +4,6 @@
#pragma once #pragma once
#include <ox/fs/fs.hpp>
#include <studio/undostack.hpp> #include <studio/undostack.hpp>
#include <jasper/world/world.hpp> #include <jasper/world/world.hpp>
@ -14,23 +12,20 @@ namespace jasper::world {
class EditWorldSizeCommand: public studio::UndoCommand { class EditWorldSizeCommand: public studio::UndoCommand {
private: private:
WorldStaticLoader &m_loader;
WorldDoc &m_doc; WorldDoc &m_doc;
size_t m_insertIdx{};
ox::Size const m_oldSize; ox::Size const m_oldSize;
WorldDoc::TileMap m_oldMap; WorldDoc::TileMap m_oldMap;
ox::Size m_newSize; ox::Size const m_newSize;
public: public:
EditWorldSizeCommand( EditWorldSizeCommand(
WorldStaticLoader &loader,
WorldDoc &doc, WorldDoc &doc,
ox::Size const&size); ox::Size const&size);
ox::Error redo() noexcept override; ox::Error redo() noexcept override;
ox::Error undo() noexcept override; ox::Error undo() noexcept override;
[[nodiscard]] [[nodiscard]]
int commandId() const noexcept override; int commandId() const noexcept override;
[[nodiscard]]
inline size_t insertIdx() const noexcept {
return m_insertIdx;
}
private: private:
enum class Dimension { enum class Dimension {
X, Y, Both, Neither, X, Y, Both, Neither,

View File

@ -77,10 +77,13 @@ WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringParam
m_sctx{sctx}, m_sctx{sctx},
m_doc{*keel::readObj<WorldDoc>(keelCtx(m_sctx), itemPath()).unwrapThrow()} { m_doc{*keel::readObj<WorldDoc>(keelCtx(m_sctx), itemPath()).unwrapThrow()} {
OX_THROW_ERROR(m_loader.loadWorldStatic()); OX_THROW_ERROR(m_loader.loadWorldStatic());
OX_THROW_ERROR(m_view.setupWorld());
OX_THROW_ERROR(loadObjectSets()); OX_THROW_ERROR(loadObjectSets());
m_objSetPicker.filePicked.connect(this, &WorldEditorImGui::addObjSet); m_objSetPicker.filePicked.connect(this, &WorldEditorImGui::addObjSet);
m_sctx.project->fileUpdated.connect(this, &WorldEditorImGui::handleDepUpdate); m_sctx.project->fileUpdated.connect(this, &WorldEditorImGui::handleDepUpdate);
studio::Editor::undoStack()->changeTriggered.connect(this, &WorldEditorImGui::undoStackChanged); m_loader.resourcesUpdated.connect(&m_view, &WorldEditorView::setupWorld);
m_loader.tileUpdated.connect(&m_view, &WorldEditorView::setupTile);
Editor::undoStack()->changeTriggered.connect(this, &WorldEditorImGui::undoStackChanged);
studio::openConfig<WorldEditorConfig>( studio::openConfig<WorldEditorConfig>(
keelCtx(m_sctx), itemPath(), [this](WorldEditorConfig const&config) { keelCtx(m_sctx), itemPath(), [this](WorldEditorConfig const&config) {
m_view.setAnimate(config.animateBg); m_view.setAnimate(config.animateBg);
@ -106,7 +109,6 @@ void WorldEditorImGui::draw(studio::StudioContext&) noexcept {
} }
void WorldEditorImGui::onActivated() noexcept { void WorldEditorImGui::onActivated() noexcept {
oxLogError(m_view.setupWorld());
} }
void WorldEditorImGui::copy() { void WorldEditorImGui::copy() {
@ -240,7 +242,7 @@ void WorldEditorImGui::drawPropEditor() noexcept {
if (ig::PopupControlsOkCancel(popupSz.x, m_sizeEditor.show) == ig::PopupResponse::OK) { if (ig::PopupControlsOkCancel(popupSz.x, m_sizeEditor.show) == ig::PopupResponse::OK) {
if (changed) { if (changed) {
std::ignore = pushCommand<EditWorldSizeCommand>( std::ignore = pushCommand<EditWorldSizeCommand>(
m_doc, ox::Size{m_sizeEditor.columns, m_sizeEditor.rows}); m_loader, m_doc, ox::Size{m_sizeEditor.columns, m_sizeEditor.rows});
} }
} }
ImGui::EndPopup(); ImGui::EndPopup();
@ -379,7 +381,6 @@ ox::Error WorldEditorImGui::handleDrop(float const fbPaneScale) noexcept {
m_doc, m_doc,
std::move(mods)); std::move(mods));
} }
OX_RETURN_ERROR(m_loader.loadWorldStatic());
return {}; return {};
} }
@ -430,7 +431,6 @@ ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*cmd) {
if (dynamic_cast<EditWorldSizeCommand const*>(cmd)) { if (dynamic_cast<EditWorldSizeCommand const*>(cmd)) {
OX_RETURN_ERROR(m_loader.loadWorldStatic()); OX_RETURN_ERROR(m_loader.loadWorldStatic());
} }
OX_RETURN_ERROR(m_view.setupWorld());
return {}; return {};
} }

View File

@ -9,10 +9,21 @@
namespace jasper::world { namespace jasper::world {
WorldEditorView::WorldEditorView(studio::StudioContext &sctx, WorldStatic const&worldStatic): WorldEditorView::WorldEditorView(studio::StudioContext &sctx, WorldStatic const&worldStatic):
m_sctx(sctx), m_sctx{sctx},
m_cctx(ncore::init(m_sctx.tctx, {.glInstallDrawer = false}).unwrapThrow()), m_cctx{ncore::init(m_sctx.tctx, {.glInstallDrawer = false}).unwrapThrow()},
m_worldStatic(worldStatic), m_worldStatic{worldStatic},
m_world(*m_cctx, m_worldStatic) { m_world{*m_cctx, m_worldStatic} {}
ox::Error WorldEditorView::setupTile(
uint32_t const lyr,
uint32_t const x,
uint32_t const y) const noexcept {
m_world.setupTile(lyr, x, y);
return {};
}
void WorldEditorView::setupTiles() noexcept {
m_world.setupTiles();
} }
ox::Error WorldEditorView::setupWorld() noexcept { ox::Error WorldEditorView::setupWorld() noexcept {

View File

@ -19,7 +19,7 @@ namespace jasper::world {
namespace ncore = nostalgia::core; namespace ncore = nostalgia::core;
class WorldEditorView { class WorldEditorView: public ox::SignalHandler {
private: private:
studio::StudioContext &m_sctx; studio::StudioContext &m_sctx;
@ -37,6 +37,10 @@ class WorldEditorView {
public: public:
WorldEditorView(studio::StudioContext &ctx, WorldStatic const&worldStatic); WorldEditorView(studio::StudioContext &ctx, WorldStatic const&worldStatic);
ox::Error setupTile(uint32_t lyr, uint32_t x, uint32_t y) const noexcept;
void setupTiles() noexcept;
ox::Error setupWorld() noexcept; ox::Error setupWorld() noexcept;
void draw(ox::Size const&targetSz) noexcept; void draw(ox::Size const&targetSz) noexcept;

View File

@ -42,27 +42,25 @@ ox::Error World::setupDisplay() noexcept {
rs.tilesheetIdx, rs.tilesheetIdx,
rs.tileCnt)); rs.tileCnt));
} }
setupTiles();
return {};
}
void World::setupTiles() noexcept {
ncore::setBgStatus(m_nctx, 0); // disable all backgrounds ncore::setBgStatus(m_nctx, 0); // disable all backgrounds
for (auto layerNo = 0u; auto const&layer : m_worldStatic.map) { for (auto layerNo = 0u; auto const&layer : m_worldStatic.map) {
setupLayer(layerNo, layer.cbb); setupLayer(layerNo, layer.cbb);
++layerNo; ++layerNo;
} }
return {};
} }
void World::setupLayer( void World::setupTile(
uint_t const lyr, uint32_t const lyr,
uint_t const cbb) const noexcept { uint32_t const x,
ncore::clearBg(m_nctx, lyr); uint32_t const y) const noexcept {
ncore::setBgStatus(m_nctx, lyr, true);
ncore::setBgCbb(m_nctx, lyr, cbb);
auto const rows = ox::min<int>(m_worldStatic.rows, ncore::tileRows(m_nctx) / 2);
auto const columns = ox::min<int>(m_worldStatic.columns, ncore::tileColumns(m_nctx) / 2);
for (auto y = 0; y < rows; ++y) {
for (auto x = 0; x < columns; ++x) {
auto &t = tile(m_worldStatic, lyr, x, y); auto &t = tile(m_worldStatic, lyr, x, y);
auto const tx = x * 2; auto const tx = static_cast<int>(x) * 2;
auto const ty = y * 2; auto const ty = static_cast<int>(y) * 2;
auto const &obj = m_worldStatic.objTileRefSets[t.objIdxRefSet];; auto const &obj = m_worldStatic.objTileRefSets[t.objIdxRefSet];;
ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 0, { ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 0, {
.tileIdx = static_cast<uint_t>(obj.cbbIdx + 0), .tileIdx = static_cast<uint_t>(obj.cbbIdx + 0),
@ -80,6 +78,19 @@ void World::setupLayer(
.tileIdx = static_cast<uint_t>(obj.cbbIdx + 3), .tileIdx = static_cast<uint_t>(obj.cbbIdx + 3),
.palBank = obj.palBank, .palBank = obj.palBank,
}); });
}
void World::setupLayer(
uint_t const lyr,
uint_t const cbb) const noexcept {
ncore::clearBg(m_nctx, lyr);
ncore::setBgStatus(m_nctx, lyr, true);
ncore::setBgCbb(m_nctx, lyr, cbb);
auto const rows = ox::min<int>(m_worldStatic.rows, ncore::tileRows(m_nctx) / 2);
auto const columns = ox::min<int>(m_worldStatic.columns, ncore::tileColumns(m_nctx) / 2);
for (auto y = 0; y < rows; ++y) {
for (auto x = 0; x < columns; ++x) {
setupTile(lyr, static_cast<uint32_t>(x), static_cast<uint32_t>(y));
} }
} }
} }

View File

@ -56,7 +56,9 @@ ox::Error WorldStaticLoader::loadWorldStatic() noexcept {
} }
} }
m_loaded = true; m_loaded = true;
return keel::ensureValid(m_worldStatic); OX_RETURN_ERROR(keel::ensureValid(m_worldStatic));
resourcesUpdated.emit();
return {};
} }
ox::Error WorldStaticLoader::updateTile( ox::Error WorldStaticLoader::updateTile(
@ -79,6 +81,12 @@ ox::Error WorldStaticLoader::updateTile(
} }
} }
loadTile(dst, newSrc); loadTile(dst, newSrc);
if (m_resourcesChanged) {
m_resourcesChanged = false;
resourcesUpdated.emit();
} else {
tileUpdated.emit(layer, col, row);
}
return {}; return {};
} }
@ -150,6 +158,7 @@ ox::Result<uint8_t> WorldStaticLoader::setupTileResrc(DocObjRef const&docObjRef)
.tileCnt = static_cast<uint8_t>(subsheet->size()), .tileCnt = static_cast<uint8_t>(subsheet->size()),
}); });
m_cbbIt += refSet.tileCnt; m_cbbIt += refSet.tileCnt;
m_resourcesChanged = true;
if (!docObjRef.unique) { if (!docObjRef.unique) {
m_cache[docObjRef] = {.value = out, .refCnt = 1}; m_cache[docObjRef] = {.value = out, .refCnt = 1};
} }
@ -180,6 +189,7 @@ void WorldStaticLoader::deleteRefSet(uint8_t const refIdx, DocObjRef const&oldOb
} }
std::ignore = m_worldStatic.objTileRefSets.erase(refIdx); std::ignore = m_worldStatic.objTileRefSets.erase(refIdx);
m_cache.erase(oldObjRef); m_cache.erase(oldObjRef);
m_resourcesChanged = true;
} }
} }