From e8a046c2dcfced7cf8d733761259bd7965291386 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Sat, 26 Feb 2022 22:48:18 -0600 Subject: [PATCH] [nostalgia] Add basic support for subsheets --- src/nostalgia/core/CMakeLists.txt | 18 +- src/nostalgia/core/gfx.hpp | 296 +++++++++++++----- src/nostalgia/core/studio/CMakeLists.txt | 2 +- .../core/studio/tilesheeteditor-imgui.cpp | 102 +++++- .../core/studio/tilesheeteditor-imgui.hpp | 20 +- .../core/studio/tilesheeteditormodel.cpp | 57 +++- .../core/studio/tilesheeteditormodel.hpp | 172 +++++++++- ...heeteditor.cpp => tilesheeteditorview.cpp} | 56 ++-- ...heeteditor.hpp => tilesheeteditorview.hpp} | 35 +-- .../core/studio/tilesheetpixelgrid.cpp | 32 +- .../core/studio/tilesheetpixelgrid.hpp | 4 +- src/nostalgia/core/studio/tilesheetpixels.cpp | 24 +- src/nostalgia/core/studio/tilesheetpixels.hpp | 4 +- src/nostalgia/core/typeconv.cpp | 4 +- src/nostalgia/core/userland/gfx.cpp | 2 +- src/nostalgia/studio/lib/project.hpp | 6 +- src/nostalgia/studio/lib/task.hpp | 2 +- src/nostalgia/studio/lib/undostack.cpp | 21 +- src/nostalgia/studio/lib/undostack.hpp | 8 +- src/nostalgia/studio/lib/widget.hpp | 3 +- 20 files changed, 630 insertions(+), 238 deletions(-) rename src/nostalgia/core/studio/{tilesheeteditor.cpp => tilesheeteditorview.cpp} (52%) rename src/nostalgia/core/studio/{tilesheeteditor.hpp => tilesheeteditorview.hpp} (79%) diff --git a/src/nostalgia/core/CMakeLists.txt b/src/nostalgia/core/CMakeLists.txt index 4b115daa..a6a89b12 100644 --- a/src/nostalgia/core/CMakeLists.txt +++ b/src/nostalgia/core/CMakeLists.txt @@ -45,8 +45,8 @@ else() ) endif() -set( - NOSTALGIA_CORE_GENERAL_SRC +add_library( + NostalgiaCore-Common OBJECT gfx.cpp media.cpp typeconv.cpp @@ -54,33 +54,35 @@ set( add_library( NostalgiaCore - ${NOSTALGIA_CORE_GENERAL_SRC} ${NOSTALGIA_CORE_IMPL_SRC} ) add_library( NostalgiaCore-Headless - ${NOSTALGIA_CORE_GENERAL_SRC} headless/core.cpp headless/gfx.cpp headless/media.cpp ) if(NOT MSVC) - target_compile_options(NostalgiaCore PUBLIC -Wsign-conversion) + target_compile_options(NostalgiaCore-Common PUBLIC -Wsign-conversion) endif() target_link_libraries( - NostalgiaCore PUBLIC + NostalgiaCore-Common PUBLIC OxClaw OxFS +) + +target_link_libraries( + NostalgiaCore PUBLIC + NostalgiaCore-Common ${NOSTALGIA_CORE_IMPL_LIBS} ) target_link_libraries( NostalgiaCore-Headless PUBLIC - OxClaw - OxFS + NostalgiaCore-Common ) if(NOSTALGIA_BUILD_STUDIO) diff --git a/src/nostalgia/core/gfx.hpp b/src/nostalgia/core/gfx.hpp index 31c13920..b171de9d 100644 --- a/src/nostalgia/core/gfx.hpp +++ b/src/nostalgia/core/gfx.hpp @@ -60,30 +60,221 @@ struct TileSheet { struct SubSheet { static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet"; static constexpr auto TypeVersion = 1; - ox::BString<32> name; - uint64_t begin = 0; + ox::String name; int columns = 1; int rows = 1; ox::Vector subsheets; + ox::Vector pixels; constexpr SubSheet() noexcept = default; - constexpr SubSheet(const char *pName, int pColumns, int pRows) noexcept: name(pName), columns(pColumns), rows(pRows) { + constexpr SubSheet(const SubSheet &other) noexcept { + name = other.name; + columns = other.columns; + rows = other.rows; + subsheets = other.subsheets; + pixels = other.pixels; + } + constexpr SubSheet(SubSheet &&other) noexcept { + name = std::move(other.name); + columns = other.columns; + rows = other.rows; + subsheets = std::move(other.subsheets); + pixels = std::move(other.pixels); + other.name = ""; + other.columns = 0; + other.rows = 0; + } + constexpr SubSheet(const char *pName, int pColumns, int pRows) noexcept: + name(pName), columns(pColumns), rows(pRows), pixels(static_cast(columns * rows * PixelsPerTile)) { + } + constexpr SubSheet(const char *pName, int pColumns, int pRows, ox::Vector pPixels) noexcept: + name(pName), columns(pColumns), rows(pRows), pixels(std::move(pPixels)) { + } + + constexpr SubSheet &operator=(const SubSheet &other) noexcept { + name = other.name; + columns = other.columns; + rows = other.rows; + subsheets = other.subsheets; + pixels = other.pixels; + return *this; + } + + constexpr SubSheet &operator=(SubSheet &&other) noexcept { + name = std::move(other.name); + columns = other.columns; + rows = other.rows; + subsheets = std::move(other.subsheets); + pixels = std::move(other.pixels); + return *this; + } + + /** + * Reads all pixels of this sheet or its children into the given pixel list + * @param pixels + */ + void readPixelsTo(ox::Vector *pPixels) const noexcept { + if (subsheets.size()) { + for (auto &s: subsheets) { + s.readPixelsTo(pPixels); + } + } else { + for (auto p : this->pixels) { + pPixels->emplace_back(p); + } + } + } + + [[nodiscard]] + constexpr std::size_t size() const noexcept { + return static_cast(columns) * static_cast(rows); + } + + [[nodiscard]] + constexpr std::size_t unusedPixels() const noexcept { + std::size_t childrenSize = 0; + for (auto &c : subsheets) { + childrenSize += c.size(); + } + return size() - childrenSize; + } + + [[nodiscard]] + constexpr uint8_t getPixel4Bpp(std::size_t idx) const noexcept { + if (idx & 1) { + return this->pixels[idx / 2] >> 4; + } else { + return this->pixels[idx / 2] & 0b0000'1111; + } + } + + [[nodiscard]] + constexpr uint8_t getPixel8Bpp(std::size_t idx) const noexcept { + return this->pixels[idx]; + } + + [[nodiscard]] + constexpr auto getPixel(int8_t bpp, std::size_t idx) const noexcept { + if (bpp == 4) { + return getPixel4Bpp(idx); + } else { + return getPixel8Bpp(idx); + } + } + + [[nodiscard]] + constexpr auto getPixel4Bpp(const geo::Point &pt) const noexcept { + const auto idx = ptToIdx(pt, columns); + return getPixel4Bpp(idx); + } + + [[nodiscard]] + constexpr auto getPixel8Bpp(const geo::Point &pt) const noexcept { + const auto idx = ptToIdx(pt, columns); + return getPixel8Bpp(idx); + } + + [[nodiscard]] + constexpr auto getPixel(int8_t bpp, const geo::Point &pt) const noexcept { + const auto idx = ptToIdx(pt, columns); + return getPixel(bpp, idx); + } + + constexpr void setPixel(int8_t bpp, uint64_t idx, uint8_t palIdx) noexcept { + auto &pixel = this->pixels[idx / 2]; + if (bpp == 4) { + if (idx & 1) { + pixel = (pixel & 0b0000'1111) | (palIdx << 4); + } else { + pixel = (pixel & 0b1111'0000) | (palIdx); + } + } else { + pixel = palIdx; + } + } + + constexpr void setPixel(int8_t bpp, const geo::Point &pt, uint8_t palIdx) noexcept { + const auto idx = ptToIdx(pt, columns); + setPixel(bpp, idx, palIdx); + } + + /** + * Gets a count of the pixels in this sheet, and not that of its children. + * @param bpp bits per pixel, need for knowing how to count the pixels + * @return a count of the pixels in this sheet + */ + constexpr auto pixelCnt(int8_t bpp) const noexcept { + return bpp == 4 ? pixels.size() * 2 : pixels.size(); } }; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet"; - static constexpr auto TypeVersion = 1; + static constexpr auto TypeVersion = 2; int8_t bpp = 0; ox::FileAddress defaultPalette; - ox::Vector pixels; SubSheet subsheet; + constexpr TileSheet() noexcept = default; + inline TileSheet(const TileSheet &other) noexcept { + bpp = other.bpp; + defaultPalette = other.defaultPalette; + subsheet = other.subsheet; + } + inline TileSheet(TileSheet &&other) noexcept { + bpp = other.bpp; + defaultPalette = std::move(other.defaultPalette); + subsheet = std::move(other.subsheet); + } + + inline auto &operator=(const TileSheet &other) noexcept { + bpp = other.bpp; + defaultPalette = other.defaultPalette; + subsheet = other.subsheet; + return *this; + } + inline auto &operator=(TileSheet &&other) noexcept { + bpp = other.bpp; + defaultPalette = std::move(other.defaultPalette); + subsheet = std::move(other.subsheet); + return *this; + } + + [[nodiscard]] + constexpr auto validateSubSheetIdx(const SubSheetIdx &pIdx, std::size_t pIdxIt, const SubSheet *pSubsheet) noexcept { + if (pIdxIt == pIdx.size()) { + return pIdx; + } + const auto currentIdx = pIdx[pIdxIt]; + if (pSubsheet->subsheets.size() <= currentIdx) { + auto out = pIdx; + out.back().value = pSubsheet->subsheets.size() - 1; + return out; + } + return validateSubSheetIdx(pIdx, pIdxIt + 1, &pSubsheet->subsheets[pIdx[pIdxIt]]); + } + + /** + * validateSubSheetIdx takes a SubSheetIdx and moves the index to the + * preceding or parent sheet if the current corresponding sheet does + * not exist. + * @param idx SubSheetIdx to validate and correct + * @return a valid version of idx + */ + [[nodiscard]] + constexpr auto validateSubSheetIdx(const SubSheetIdx &idx) noexcept { + return validateSubSheetIdx(idx, 0, &subsheet); + } + [[nodiscard]] constexpr static const auto &getSubSheet(const SubSheetIdx &idx, std::size_t idxIt, const SubSheet *pSubsheet) noexcept { if (idxIt == idx.size()) { return *pSubsheet; } - return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]); + const auto currentIdx = idx[idxIt]; + if (pSubsheet->subsheets.size() < currentIdx) { + return *pSubsheet; + } + return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[currentIdx]); } [[nodiscard]] @@ -104,6 +295,17 @@ struct TileSheet { return getSubSheet(idx, 0, &subsheet); } + constexpr ox::Error addSubSheet(const SubSheetIdx &idx) noexcept { + auto &parent = getSubSheet(idx); + if (parent.subsheets.size() < 2) { + parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", parent.subsheets.size()).c_str(), 1, 1); + } else { + parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows); + parent.subsheets.emplace_back("Subsheet 1", 1, 1); + } + return OxError(0); + } + [[nodiscard]] constexpr static auto rmSubSheet(const SubSheetIdx &idx, std::size_t idxIt, SubSheet *pSubsheet) noexcept { if (idxIt == idx.size() - 1) { @@ -117,88 +319,29 @@ struct TileSheet { return rmSubSheet(idx, 0, &subsheet); } - [[nodiscard]] - constexpr const auto &columns(const SubSheetIdx &idx = {}) const noexcept { - return getSubSheet(idx).columns; - } - - [[nodiscard]] - constexpr const auto &rows(const SubSheetIdx &idx = {}) const noexcept { - return getSubSheet(idx).rows; - } - - [[nodiscard]] - constexpr auto &columns(const SubSheetIdx &idx = {}) noexcept { - return getSubSheet(idx).columns; - } - - [[nodiscard]] - constexpr auto &rows(const SubSheetIdx &idx = {}) noexcept { - return getSubSheet(idx).rows; - } - - [[nodiscard]] - constexpr uint8_t getPixel4Bpp(std::size_t idx) const noexcept { - oxAssert(bpp == 4, "TileSheetV1::getPixel4Bpp: wrong bpp"); - if (idx & 1) { - return this->pixels[idx / 2] >> 4; - } else { - return this->pixels[idx / 2] & 0b0000'1111; - } - } - - [[nodiscard]] - constexpr uint8_t getPixel8Bpp(std::size_t idx) const noexcept { - oxAssert(bpp == 8, "TileSheetV1::getPixel8Bpp: wrong bpp"); - return this->pixels[idx]; - } - - [[nodiscard]] - constexpr auto getPixel(std::size_t idx) const noexcept { - if (this->bpp == 4) { - return getPixel4Bpp(idx); - } else { - return getPixel8Bpp(idx); - } - } - [[nodiscard]] constexpr auto getPixel4Bpp(const geo::Point &pt, const SubSheetIdx &subsheetIdx) const noexcept { oxAssert(bpp == 4, "TileSheetV1::getPixel4Bpp: wrong bpp"); - const auto idx = ptToIdx(pt, this->getSubSheet(subsheetIdx).columns); - return getPixel4Bpp(idx); + auto &s = this->getSubSheet(subsheetIdx); + const auto idx = ptToIdx(pt, s.columns); + return s.getPixel4Bpp(idx); } [[nodiscard]] constexpr auto getPixel8Bpp(const geo::Point &pt, const SubSheetIdx &subsheetIdx) const noexcept { oxAssert(bpp == 8, "TileSheetV1::getPixel8Bpp: wrong bpp"); - const auto idx = ptToIdx(pt, this->getSubSheet(subsheetIdx).columns); - return getPixel8Bpp(idx); + auto &s = this->getSubSheet(subsheetIdx); + const auto idx = ptToIdx(pt, s.columns); + return s.getPixel8Bpp(idx); } [[nodiscard]] - constexpr auto getPixel(const geo::Point &pt, const SubSheetIdx &subsheetIdx) const noexcept { - const auto idx = ptToIdx(pt, this->getSubSheet(subsheetIdx).columns); - return getPixel(idx); + auto pixels() const noexcept { + ox::Vector out; + subsheet.readPixelsTo(&out); + return out; } - constexpr void setPixel(uint64_t idx, uint8_t palIdx) noexcept { - auto &pixel = this->pixels[idx / 2]; - if (bpp == 4) { - if (idx & 1) { - pixel = (pixel & 0b0000'1111) | (palIdx << 4); - } else { - pixel = (pixel & 0b1111'0000) | (palIdx); - } - } else { - pixel = palIdx; - } - } - - constexpr void setPixel(const SubSheetIdx &subsheetIdx, const geo::Point &pt, uint8_t palIdx) noexcept { - const auto idx = ptToIdx(pt, this->getSubSheet(subsheetIdx).columns); - setPixel(idx, palIdx); - } }; struct CompactTileSheet { @@ -228,16 +371,15 @@ oxModelEnd() oxModelBegin(TileSheet::SubSheet) oxModelField(name); - oxModelField(begin); oxModelField(rows); oxModelField(columns); oxModelField(subsheets) + oxModelField(pixels) oxModelEnd() oxModelBegin(TileSheet) oxModelField(bpp) oxModelField(defaultPalette) - oxModelField(pixels) oxModelField(subsheet) oxModelEnd() diff --git a/src/nostalgia/core/studio/CMakeLists.txt b/src/nostalgia/core/studio/CMakeLists.txt index c0a36a7f..b0ae7fbf 100644 --- a/src/nostalgia/core/studio/CMakeLists.txt +++ b/src/nostalgia/core/studio/CMakeLists.txt @@ -6,7 +6,7 @@ add_library( #newpalettewizard.cpp #paletteeditor.cpp tilesheeteditor-imgui.cpp - tilesheeteditor.cpp + tilesheeteditorview.cpp tilesheeteditormodel.cpp tilesheetpixelgrid.cpp tilesheetpixels.cpp diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp index 12aeaf7d..cb3e6af0 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp @@ -2,8 +2,6 @@ * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include - #include #include @@ -32,37 +30,114 @@ void TileSheetEditorImGui::exportFile() { } void TileSheetEditorImGui::cut() { - m_tileSheetEditor.cut(); + model()->cut(); } void TileSheetEditorImGui::copy() { - m_tileSheetEditor.copy(); + model()->copy(); } void TileSheetEditorImGui::paste() { - m_tileSheetEditor.paste(); + model()->paste(); } void TileSheetEditorImGui::draw(core::Context*) noexcept { const auto paneSize = ImGui::GetContentRegionAvail(); const auto tileSheetParentSize = ImVec2(paneSize.x - m_palViewWidth, paneSize.y); const auto fbSize = geo::Vec2(tileSheetParentSize.x - 16, tileSheetParentSize.y - 16); - ImGui::BeginChild("child1", tileSheetParentSize, true); - drawTileSheet(fbSize); + ImGui::BeginChild("TileSheetView", tileSheetParentSize, true); + { + drawTileSheet(fbSize); + } ImGui::EndChild(); ImGui::SameLine(); - // draw palette/color picker - ImGui::BeginChild("child2", ImVec2(m_palViewWidth - 8, paneSize.y), true); - drawPalettePicker(); + ImGui::BeginChild("Controls", ImVec2(m_palViewWidth - 8, paneSize.y), true); + { + // draw palette/color picker + ImGui::BeginChild("child1", ImVec2(m_palViewWidth - 24, paneSize.y / 2.07f), true); + { + drawPalettePicker(); + } + ImGui::EndChild(); + ImGui::BeginChild("child2", ImVec2(m_palViewWidth - 24, paneSize.y / 2.07f), true); + { + const auto btnSize = ImVec2(18, 18); + if (ImGui::Button("+", btnSize)) { + auto insertOnIdx = model()->activeSubSheetIdx(); + const auto &parent = *model()->activeSubSheet(); + model()->addSubsheet(insertOnIdx); + insertOnIdx.emplace_back(parent.subsheets.size() - 1); + model()->setActiveSubsheet(insertOnIdx); + } + ImGui::SameLine(); + if (ImGui::Button("-", btnSize)) { + const auto &activeSubsheetIdx = model()->activeSubSheetIdx(); + if (activeSubsheetIdx.size() > 0) { + model()->rmSubsheet(activeSubsheetIdx); + } + } + TileSheet::SubSheetIdx path; + constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; + if (ImGui::BeginTable("Subsheets", 3, flags)) { + ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50); + ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50); + ImGui::TableHeadersRow(); + drawSubsheetSelector(&m_tileSheetEditor.img().subsheet, &path); + ImGui::EndTable(); + } + } + ImGui::EndChild(); + } ImGui::EndChild(); } +void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, TileSheet::SubSheetIdx *path) noexcept { + ImGui::TableNextRow(0, 5); + using Str = ox::BasicString<100>; + auto pathStr = ox::join("##", *path).value; + auto lbl = ox::sfmt("{}##{}", subsheet->name, pathStr); + const auto rowSelected = *path == model()->activeSubSheetIdx(); + const auto flags = ImGuiTreeNodeFlags_SpanFullWidth + | ImGuiTreeNodeFlags_OpenOnArrow + | ImGuiTreeNodeFlags_DefaultOpen + | (subsheet->subsheets.size() ? 0 : ImGuiTreeNodeFlags_Leaf) + | (rowSelected ? ImGuiTreeNodeFlags_Selected : 0); + ImGui::TableNextColumn(); + const auto open = ImGui::TreeNodeEx(lbl.c_str(), flags); + if (ImGui::IsItemClicked()) { + model()->setActiveSubsheet(*path); + } + if (subsheet->subsheets.size()) { + ImGui::TableNextColumn(); + ImGui::Text("--"); + ImGui::TableNextColumn(); + ImGui::Text("--"); + } else { + ImGui::TableNextColumn(); + ImGui::Text("%d", subsheet->columns); + ImGui::TableNextColumn(); + ImGui::Text("%d", subsheet->rows); + } + if (open) { + for (auto i = 0ul; auto &child : subsheet->subsheets) { + path->push_back(i); + ImGui::PushID(static_cast(i)); + drawSubsheetSelector(&child, path); + ImGui::PopID(); + path->pop_back(); + ++i; + } + ImGui::TreePop(); + } +} + studio::UndoStack *TileSheetEditorImGui::undoStack() noexcept { - return m_tileSheetEditor.undoStack(); + return model()->undoStack(); } void TileSheetEditorImGui::saveItem() { - const auto err = m_tileSheetEditor.model()->saveFile(); + const auto err = model()->saveFile(); if (!err) { this->setUnsavedChanges(false); } else { @@ -143,8 +218,7 @@ void TileSheetEditorImGui::drawPalettePicker() noexcept { } } -ox::Error TileSheetEditorImGui::markUnsavedChanges() noexcept { - oxDebug("markUnsavedChanges"); +ox::Error TileSheetEditorImGui::markUnsavedChanges(int) noexcept { setUnsavedChanges(true); return OxError(0); } diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp index 2d62ab55..7946dd66 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp @@ -14,7 +14,7 @@ #include "tilesheetpixelgrid.hpp" #include "tilesheetpixels.hpp" -#include "tilesheeteditor.hpp" +#include "tilesheeteditorview.hpp" namespace nostalgia::core { @@ -24,8 +24,8 @@ class TileSheetEditorImGui: public studio::Editor { ox::String m_itemPath; ox::String m_itemName; glutils::FrameBuffer m_framebuffer; - TileSheetEditor m_tileSheetEditor; - float m_palViewWidth = 200; + TileSheetEditorView m_tileSheetEditor; + float m_palViewWidth = 300; geo::Vec2 m_prevMouseDownPos; public: @@ -47,12 +47,24 @@ class TileSheetEditorImGui: public studio::Editor { void draw(core::Context*) noexcept override; + void drawSubsheetSelector(TileSheet::SubSheet*, TileSheet::SubSheetIdx *path) noexcept; + studio::UndoStack *undoStack() noexcept final; protected: void saveItem() override; private: + [[nodiscard]] + constexpr auto model() const noexcept { + return m_tileSheetEditor.model(); + } + + [[nodiscard]] + constexpr auto model() noexcept { + return m_tileSheetEditor.model(); + } + void setPalette(); void saveState(); @@ -73,7 +85,7 @@ class TileSheetEditorImGui: public studio::Editor { private: ox::Error updateAfterClicked() noexcept; - ox::Error markUnsavedChanges() noexcept; + ox::Error markUnsavedChanges(int) noexcept; }; diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.cpp b/src/nostalgia/core/studio/tilesheeteditormodel.cpp index 85c8e32d..9c865795 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.cpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.cpp @@ -29,12 +29,16 @@ void TileSheetEditorModel::paste() { } void TileSheetEditorModel::drawCommand(const geo::Point &pt, std::size_t palIdx) noexcept { + auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); + const auto idx = ptToIdx(pt, activeSubSheet.columns); + if (idx >= activeSubSheet.pixelCnt(m_img.bpp)) { + return; + } if (m_ongoingDrawCommand) { - m_updated = m_updated || m_ongoingDrawCommand->append(ptToIdx(pt, m_img.columns())); + m_updated = m_updated || m_ongoingDrawCommand->append(idx); } else { - const auto idx = ptToIdx(pt, m_img.columns()); - if (m_img.getPixel(idx) != palIdx) { - pushCommand(new DrawCommand(&m_img, idx, palIdx)); + if (activeSubSheet.getPixel(m_img.bpp, idx) != palIdx) { + pushCommand(new DrawCommand(&m_img, m_activeSubsSheetIdx, idx, palIdx)); } } } @@ -43,12 +47,32 @@ void TileSheetEditorModel::endDrawCommand() noexcept { m_ongoingDrawCommand = nullptr; } +void TileSheetEditorModel::addSubsheet(const TileSheet::SubSheetIdx &parentIdx) noexcept { + pushCommand(new AddSubSheetCommand(&m_img, parentIdx)); +} + +void TileSheetEditorModel::rmSubsheet(const TileSheet::SubSheetIdx &idx) noexcept { + pushCommand(new RmSubSheetCommand(&m_img, idx)); +} + +void TileSheetEditorModel::setActiveSubsheet(const TileSheet::SubSheetIdx &idx) noexcept { + m_activeSubsSheetIdx = idx; + this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); +} + bool TileSheetEditorModel::updated() const noexcept { return m_updated; } -ox::Error TileSheetEditorModel::markUpdated() noexcept { +ox::Error TileSheetEditorModel::markUpdated(int cmdId) noexcept { m_updated = true; + if (cmdId == CommandId::AddSubSheet || cmdId == CommandId::RmSubSheet) { + // make sure the current active SubSheet is still valid + auto idx = m_img.validateSubSheetIdx(m_activeSubsSheetIdx); + if (idx != m_activeSubsSheetIdx) { + setActiveSubsheet(idx); + } + } return OxError(0); } @@ -63,8 +87,9 @@ ox::Error TileSheetEditorModel::saveFile() noexcept { } void TileSheetEditorModel::getFillPixels(bool *pixels, const geo::Point &pt, int oldColor) const noexcept { - const auto tileIdx = [this](const geo::Point &pt) noexcept { - return ptToIdx(pt, img().columns()) / PixelsPerTile; + auto &activeSubSheet = *this->activeSubSheet(); + const auto tileIdx = [activeSubSheet](const geo::Point &pt) noexcept { + return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile; }; // get points const auto leftPt = pt + geo::Point(-1, 0); @@ -72,24 +97,24 @@ void TileSheetEditorModel::getFillPixels(bool *pixels, const geo::Point &pt, int const auto topPt = pt + geo::Point(0, -1); const auto bottomPt = pt + geo::Point(0, 1); // calculate indices - const auto idx = ptToIdx(pt, m_img.columns()); - const auto leftIdx = ptToIdx(leftPt, m_img.columns()); - const auto rightIdx = ptToIdx(rightPt, m_img.columns()); - const auto topIdx = ptToIdx(topPt, m_img.columns()); - const auto bottomIdx = ptToIdx(bottomPt, m_img.columns()); + const auto idx = ptToIdx(pt, activeSubSheet.columns); + const auto leftIdx = ptToIdx(leftPt, activeSubSheet.columns); + const auto rightIdx = ptToIdx(rightPt, activeSubSheet.columns); + const auto topIdx = ptToIdx(topPt, activeSubSheet.columns); + const auto bottomIdx = ptToIdx(bottomPt, activeSubSheet.columns); const auto tile = tileIdx(pt); // mark pixels to update pixels[idx % PixelsPerTile] = true; - if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && m_img.pixels[leftIdx] == oldColor) { + if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && activeSubSheet.pixels[leftIdx] == oldColor) { getFillPixels(pixels, leftPt, oldColor); } - if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && m_img.pixels[rightIdx] == oldColor) { + if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && activeSubSheet.pixels[rightIdx] == oldColor) { getFillPixels(pixels, rightPt, oldColor); } - if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && m_img.pixels[topIdx] == oldColor) { + if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && activeSubSheet.pixels[topIdx] == oldColor) { getFillPixels(pixels, topPt, oldColor); } - if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && m_img.pixels[bottomIdx] == oldColor) { + if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && activeSubSheet.pixels[bottomIdx] == oldColor) { getFillPixels(pixels, bottomPt, oldColor); } } diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.hpp b/src/nostalgia/core/studio/tilesheeteditormodel.hpp index 28d1458d..ac8cfcb8 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.hpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -15,13 +16,19 @@ namespace nostalgia::core { // Command IDs to use with QUndoCommand::id() enum class CommandId { - UpdatePixel = 1, - ModPixel = 2, - UpdateDimension = 3, - InsertTile = 4, - ClipboardPaste = 5, + Draw = 1, + AddSubSheet = 2, + RmSubSheet = 3, }; +constexpr bool operator==(CommandId c, int i) noexcept { + return static_cast(c) == i; +} + +constexpr bool operator==(int i, CommandId c) noexcept { + return static_cast(c) == i; +} + struct DrawCommand: public studio::UndoCommand { private: struct Change { @@ -29,25 +36,29 @@ struct DrawCommand: public studio::UndoCommand { uint16_t oldPalIdx = 0; }; TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_subSheetIdx; ox::Vector m_changes; int m_palIdx = 0; public: - constexpr DrawCommand(TileSheet *img, std::size_t idx, int palIdx) noexcept { + constexpr DrawCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, std::size_t idx, int palIdx) noexcept { m_img = img; - m_changes.emplace_back(idx, m_img->getPixel(idx)); + auto &subsheet = m_img->getSubSheet(subSheetIdx); + m_subSheetIdx = subSheetIdx; + m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx)); m_palIdx = palIdx; } constexpr auto append(std::size_t idx) noexcept { - if (m_changes.back().value.idx != idx && m_img->getPixel(idx) != m_palIdx) { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); + if (m_changes.back().value.idx != idx && subsheet.getPixel(m_img->bpp, idx) != m_palIdx) { // duplicate entries are bad auto existing = std::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) { return c.idx == idx; }); if (existing == m_changes.cend()) { - m_changes.emplace_back(idx, m_img->getPixel(idx)); - m_img->setPixel(idx, m_palIdx); + m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx)); + subsheet.setPixel(m_img->bpp, idx, m_palIdx); return true; } } @@ -55,17 +66,118 @@ struct DrawCommand: public studio::UndoCommand { } void redo() noexcept final { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); for (const auto &c : m_changes) { - m_img->setPixel(c.idx, m_palIdx); + subsheet.setPixel(m_img->bpp, c.idx, m_palIdx); } } void undo() noexcept final { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); for (const auto &c : m_changes) { - m_img->setPixel(c.idx, c.oldPalIdx); + subsheet.setPixel(m_img->bpp, c.idx, c.oldPalIdx); } } + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::Draw); + } + +}; + +struct AddSubSheetCommand: public studio::UndoCommand { + private: + TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_parentIdx; + ox::Vector m_addedSheets; + + public: + constexpr AddSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &parentIdx) noexcept { + m_img = img; + m_parentIdx = parentIdx; + auto &parent = m_img->getSubSheet(m_parentIdx); + if (parent.subsheets.size()) { + auto idx = m_parentIdx; + idx.emplace_back(parent.subsheets.size()); + m_addedSheets.push_back(idx); + } else { + auto idx = m_parentIdx; + idx.emplace_back(0); + m_addedSheets.push_back(idx); + idx.back().value = 1; + m_addedSheets.push_back(idx); + } + } + + void redo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + if (m_addedSheets.size() < 2) { + auto i = parent.subsheets.size(); + parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", i).c_str(), 1, 1); + } else { + parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels)); + parent.rows = 0; + parent.columns = 0; + parent.subsheets.emplace_back("Subsheet 1", 1, 1); + } + } + + void undo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + if (parent.subsheets.size() == 2) { + auto s = parent.subsheets[0]; + parent.rows = s.rows; + parent.columns = s.columns; + parent.pixels = std::move(s.pixels); + parent.subsheets.clear(); + } else { + for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) { + oxLogError(m_img->rmSubSheet(*idx)); + } + } + } + + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::AddSubSheet); + } + +}; + +struct RmSubSheetCommand: public studio::UndoCommand { + private: + TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_idx; + TileSheet::SubSheetIdx m_parentIdx; + TileSheet::SubSheet m_sheet; + + public: + constexpr RmSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &idx) noexcept { + m_img = img; + m_idx = idx; + m_parentIdx = idx; + m_parentIdx.pop_back(); + auto &parent = m_img->getSubSheet(m_parentIdx); + m_sheet = parent.subsheets[idx.back().value]; + } + + void redo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + oxLogError(parent.subsheets.erase(m_idx.back().value).error); + } + + void undo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + auto i = m_idx.back().value; + parent.subsheets.insert(i, m_sheet); + } + + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::RmSubSheet); + } + }; struct TileSheetClipboard { @@ -109,8 +221,12 @@ oxModelEnd() class TileSheetEditorModel: public ox::SignalHandler { + public: + ox::Signal activeSubsheetChanged; + private: TileSheet m_img; + TileSheet::SubSheetIdx m_activeSubsSheetIdx; AssetRef m_pal; studio::UndoStack m_undoStack; DrawCommand *m_ongoingDrawCommand = nullptr; @@ -142,18 +258,36 @@ class TileSheetEditorModel: public ox::SignalHandler { void endDrawCommand() noexcept; + void addSubsheet(const TileSheet::SubSheetIdx &parentIdx) noexcept; + + void rmSubsheet(const TileSheet::SubSheetIdx &idx) noexcept; + + void setActiveSubsheet(const TileSheet::SubSheetIdx&) noexcept; + + constexpr const TileSheet::SubSheet *activeSubSheet() const noexcept { + auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); + return &activeSubSheet; + } + + constexpr TileSheet::SubSheet *activeSubSheet() noexcept { + auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); + return &activeSubSheet; + } + + constexpr const TileSheet::SubSheetIdx &activeSubSheetIdx() const noexcept { + return m_activeSubsSheetIdx; + } + [[nodiscard]] bool updated() const noexcept; - ox::Error markUpdated() noexcept; + ox::Error markUpdated(int) noexcept; void ackUpdate() noexcept; ox::Error saveFile() noexcept; - constexpr studio::UndoStack *undoStack() noexcept { - return &m_undoStack; - } + constexpr studio::UndoStack *undoStack() noexcept; protected: void saveItem(); @@ -189,4 +323,8 @@ constexpr const Palette &TileSheetEditorModel::pal() const noexcept { return *m_pal; } -} \ No newline at end of file +constexpr studio::UndoStack *TileSheetEditorModel::undoStack() noexcept { + return &m_undoStack; +} + +} diff --git a/src/nostalgia/core/studio/tilesheeteditor.cpp b/src/nostalgia/core/studio/tilesheeteditorview.cpp similarity index 52% rename from src/nostalgia/core/studio/tilesheeteditor.cpp rename to src/nostalgia/core/studio/tilesheeteditorview.cpp index 428b2757..08981198 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.cpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.cpp @@ -2,35 +2,22 @@ * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include - #include #include #include -#include "tilesheeteditor.hpp" +#include "tilesheeteditorview.hpp" namespace nostalgia::core { -TileSheetEditor::TileSheetEditor(Context *ctx, const ox::String &path): m_model(ctx, path) { +TileSheetEditorView::TileSheetEditorView(Context *ctx, const ox::String &path): m_model(ctx, path) { // build shaders oxThrowError(m_pixelsDrawer.buildShader()); oxThrowError(m_pixelGridDrawer.buildShader()); + m_model.activeSubsheetChanged.connect(this, &TileSheetEditorView::setActiveSubsheet); } -void TileSheetEditor::cut() { - m_model.cut(); -} - -void TileSheetEditor::copy() { - m_model.copy(); -} - -void TileSheetEditor::paste() { - m_model.paste(); -} - -void TileSheetEditor::draw() noexcept { +void TileSheetEditorView::draw() noexcept { constexpr Color32 bgColor = 0x717d7e; glClearColor(redf(bgColor), greenf(bgColor), bluef(bgColor), 1.f); glClear(GL_COLOR_BUFFER_BIT); @@ -38,10 +25,10 @@ void TileSheetEditor::draw() noexcept { m_pixelGridDrawer.draw(updated(), m_scrollOffset); } -void TileSheetEditor::scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept { +void TileSheetEditorView::scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept { const auto pixelSize = m_pixelsDrawer.pixelSize(paneSz); - const ImVec2 sheetSize(pixelSize.x * static_cast(img().columns()) * TileWidth, - pixelSize.y * static_cast(img().rows()) * TileHeight); + const ImVec2 sheetSize(pixelSize.x * static_cast(m_model.activeSubSheet()->columns) * TileWidth, + pixelSize.y * static_cast(m_model.activeSubSheet()->rows) * TileHeight); if (zoomMod) { m_pixelSizeMod = ox::clamp(m_pixelSizeMod + wheel * 0.02f, 0.55f, 2.f); m_pixelsDrawer.setPixelSizeMod(m_pixelSizeMod); @@ -55,15 +42,15 @@ void TileSheetEditor::scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod m_scrollOffset.y = ox::clamp(m_scrollOffset.y, 0.f, sheetSize.y / 2); } -void TileSheetEditor::scrollH(const geo::Vec2 &paneSz, float wheelh) noexcept { +void TileSheetEditorView::scrollH(const geo::Vec2 &paneSz, float wheelh) noexcept { const auto pixelSize = m_pixelsDrawer.pixelSize(paneSz); - const ImVec2 sheetSize(pixelSize.x * static_cast(img().columns()) * TileWidth, - pixelSize.y * static_cast(img().rows()) * TileHeight); + const ImVec2 sheetSize(pixelSize.x * static_cast(m_model.activeSubSheet()->columns) * TileWidth, + pixelSize.y * static_cast(m_model.activeSubSheet()->rows) * TileHeight); m_scrollOffset.x += wheelh * 0.1f; m_scrollOffset.x = ox::clamp(m_scrollOffset.x, -(sheetSize.x / 2), 0.f); } -void TileSheetEditor::click(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept { +void TileSheetEditorView::click(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept { auto [x, y] = clickPos; const auto pixDrawSz = m_pixelsDrawer.pixelSize(paneSize); x /= paneSize.x; @@ -76,25 +63,32 @@ void TileSheetEditor::click(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos m_model.drawCommand(pt, m_palIdx); } -void TileSheetEditor::releaseMouseButton() noexcept { +void TileSheetEditorView::releaseMouseButton() noexcept { m_model.endDrawCommand(); } -void TileSheetEditor::resizeView(const geo::Vec2 &sz) noexcept { - m_pixelsDrawer.initBufferSet(sz, img(), pal()); - m_pixelGridDrawer.initBufferSet(sz, img()); +void TileSheetEditorView::resizeView(const geo::Vec2 &sz) noexcept { + m_viewSize = sz; + initView(); } -bool TileSheetEditor::updated() const noexcept { +bool TileSheetEditorView::updated() const noexcept { return m_updated || m_model.updated(); } -void TileSheetEditor::ackUpdate() noexcept { +void TileSheetEditorView::ackUpdate() noexcept { m_updated = false; m_model.ackUpdate(); } -void TileSheetEditor::saveItem() { +void TileSheetEditorView::initView() noexcept { + m_pixelsDrawer.initBufferSet(m_viewSize, img(), *m_model.activeSubSheet(), pal()); + m_pixelGridDrawer.initBufferSet(m_viewSize, *m_model.activeSubSheet()); +} + +ox::Error TileSheetEditorView::setActiveSubsheet(const TileSheet::SubSheetIdx&) noexcept { + initView(); + return OxError(0); } } diff --git a/src/nostalgia/core/studio/tilesheeteditor.hpp b/src/nostalgia/core/studio/tilesheeteditorview.hpp similarity index 79% rename from src/nostalgia/core/studio/tilesheeteditor.hpp rename to src/nostalgia/core/studio/tilesheeteditorview.hpp index a2b8e304..a96dd227 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.hpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.hpp @@ -36,27 +36,22 @@ constexpr auto toString(TileSheetTool t) noexcept { return ""; } -class TileSheetEditor { +class TileSheetEditorView: public ox::SignalHandler { private: TileSheetEditorModel m_model; TileSheetGrid m_pixelGridDrawer; TileSheetPixels m_pixelsDrawer; + geo::Vec2 m_viewSize; float m_pixelSizeMod = 1; bool m_updated = false; geo::Vec2 m_scrollOffset; std::size_t m_palIdx = 0; public: - TileSheetEditor(Context *ctx, const ox::String &path); + TileSheetEditorView(Context *ctx, const ox::String &path); - ~TileSheetEditor() = default; - - void cut(); - - void copy(); - - void paste(); + ~TileSheetEditorView() override = default; void draw() noexcept; @@ -73,6 +68,9 @@ class TileSheetEditor { [[nodiscard]] constexpr const TileSheet &img() const noexcept; + [[nodiscard]] + constexpr TileSheet &img() noexcept; + [[nodiscard]] constexpr const Palette &pal() const noexcept; @@ -100,14 +98,9 @@ class TileSheetEditor { void ackUpdate() noexcept; - constexpr studio::UndoStack *undoStack() noexcept { - return m_model.undoStack(); - } - - protected: - void saveItem(); - private: + void initView() noexcept; + void setPalette(); void saveState(); @@ -121,6 +114,8 @@ class TileSheetEditor { ox::String palettePath(const ox::String &palettePath) const; // slots + ox::Error setActiveSubsheet(const TileSheet::SubSheetIdx &idx) noexcept; + public: ox::Error colorSelected() noexcept; @@ -128,11 +123,15 @@ class TileSheetEditor { }; -constexpr const TileSheet &TileSheetEditor::img() const noexcept { +constexpr const TileSheet &TileSheetEditorView::img() const noexcept { return m_model.img(); } -constexpr const Palette &TileSheetEditor::pal() const noexcept { +constexpr TileSheet &TileSheetEditorView::img() noexcept { + return m_model.img(); +} + +constexpr const Palette &TileSheetEditorView::pal() const noexcept { return m_model.pal(); } diff --git a/src/nostalgia/core/studio/tilesheetpixelgrid.cpp b/src/nostalgia/core/studio/tilesheetpixelgrid.cpp index 208ebd25..57f9a52c 100644 --- a/src/nostalgia/core/studio/tilesheetpixelgrid.cpp +++ b/src/nostalgia/core/studio/tilesheetpixelgrid.cpp @@ -30,13 +30,13 @@ void TileSheetGrid::draw(bool update, const geo::Vec2 &scroll) noexcept { glDrawArrays(GL_POINTS, 0, static_cast(m_bufferSet.vertices.size() / VertexVboRowLength)); } -void TileSheetGrid::initBufferSet(const geo::Vec2 &paneSize, const TileSheet &img) noexcept { +void TileSheetGrid::initBufferSet(const geo::Vec2 &paneSize, const TileSheet::SubSheet &subsheet) noexcept { // vao m_bufferSet.vao = glutils::generateVertexArrayObject(); glBindVertexArray(m_bufferSet.vao); // vbo m_bufferSet.vbo = glutils::generateBuffer(); - setBufferObjects(paneSize, img, &m_bufferSet); + setBufferObjects(paneSize, subsheet, &m_bufferSet); glutils::sendVbo(m_bufferSet); // vbo layout const auto pt1Attr = static_cast(glGetAttribLocation(m_shader, "vPt1")); @@ -62,38 +62,38 @@ void TileSheetGrid::setBufferObject(geo::Point pt1, geo::Point pt2, Color32 c, f memcpy(vbo, vertices, sizeof(vertices)); } -void TileSheetGrid::setBufferObjects(const geo::Vec2 &paneSize, const TileSheet &img, glutils::BufferSet *bg) noexcept { +void TileSheetGrid::setBufferObjects(const geo::Vec2 &paneSize, const TileSheet::SubSheet &subsheet, glutils::BufferSet *bg) noexcept { const auto pixSize = pixelSize(paneSize); const auto set = [bg, pixSize](unsigned i, geo::Point pt1, geo::Point pt2, Color32 c) { const auto vbo = &bg->vertices[i * VertexVboLength]; setBufferObject(pt1, pt2, c, vbo, pixSize); }; // set buffer length - const auto width = img.columns() * TileWidth; - const auto height = img.rows() * TileHeight; - const auto pixelCnt = static_cast(width * height); - const auto tileCnt = static_cast(img.columns() * img.rows()); - m_bufferSet.vertices.resize((tileCnt + pixelCnt) * VertexVboLength); + const auto width = subsheet.columns * TileWidth; + const auto height = subsheet.rows * TileHeight; + const auto tileCnt = static_cast(subsheet.columns + subsheet.rows); + const auto pixelCnt = static_cast(width + height); + m_bufferSet.vertices.resize((tileCnt + pixelCnt + 4) * VertexVboLength); // set buffer auto i = 0ull; // pixel outlines constexpr auto pixOutlineColor = color32(0.4431f, 0.4901f, 0.4941f); - for (auto x = 0; x < img.columns() * TileWidth + 1; ++x) { - set(i, {x, 0}, {x, img.rows() * TileHeight}, pixOutlineColor); + for (auto x = 0; x < subsheet.columns * TileWidth + 1; ++x) { + set(i, {x, 0}, {x, subsheet.rows * TileHeight}, pixOutlineColor); ++i; } - for (auto y = 0; y < img.rows() * TileHeight + 1; ++y) { - set(i, {0, y}, {img.columns() * TileWidth, y}, pixOutlineColor); + for (auto y = 0; y < subsheet.rows * TileHeight + 1; ++y) { + set(i, {0, y}, {subsheet.columns * TileWidth, y}, pixOutlineColor); ++i; } // tile outlines constexpr auto tileOutlineColor = color32(0.f, 0.f, 0.f); - for (auto x = 0; x < img.columns() * TileWidth + 1; x += TileWidth) { - set(i, {x, 0}, {x, img.rows() * TileHeight}, tileOutlineColor); + for (auto x = 0; x < subsheet.columns * TileWidth + 1; x += TileWidth) { + set(i, {x, 0}, {x, subsheet.rows * TileHeight}, tileOutlineColor); ++i; } - for (auto y = 0; y < img.rows() * TileHeight + 1; y += TileHeight) { - set(i, {0, y}, {img.columns() * TileWidth, y}, tileOutlineColor); + for (auto y = 0; y < subsheet.rows * TileHeight + 1; y += TileHeight) { + set(i, {0, y}, {subsheet.columns * TileWidth, y}, tileOutlineColor); ++i; } } diff --git a/src/nostalgia/core/studio/tilesheetpixelgrid.hpp b/src/nostalgia/core/studio/tilesheetpixelgrid.hpp index 7fc44085..f3c37c4a 100644 --- a/src/nostalgia/core/studio/tilesheetpixelgrid.hpp +++ b/src/nostalgia/core/studio/tilesheetpixelgrid.hpp @@ -69,12 +69,12 @@ class TileSheetGrid { void draw(bool update, const geo::Vec2 &scroll) noexcept; - void initBufferSet(const geo::Vec2 &paneSize, const TileSheet &img) noexcept; + void initBufferSet(const geo::Vec2 &paneSize, const TileSheet::SubSheet &subsheet) noexcept; private: static void setBufferObject(geo::Point pt1, geo::Point pt2, Color32 c, float *vbo, const geo::Vec2 &pixSize) noexcept; - void setBufferObjects(const geo::Vec2 &paneSize, const TileSheet &img, glutils::BufferSet *bg) noexcept; + void setBufferObjects(const geo::Vec2 &paneSize, const TileSheet::SubSheet &subsheet, glutils::BufferSet *bg) noexcept; [[nodiscard]] geo::Vec2 pixelSize(const geo::Vec2 &paneSize) const noexcept; diff --git a/src/nostalgia/core/studio/tilesheetpixels.cpp b/src/nostalgia/core/studio/tilesheetpixels.cpp index b080c8df..6a704a87 100644 --- a/src/nostalgia/core/studio/tilesheetpixels.cpp +++ b/src/nostalgia/core/studio/tilesheetpixels.cpp @@ -29,14 +29,14 @@ void TileSheetPixels::draw(bool update, const geo::Vec2 &scroll) noexcept { glDrawElements(GL_TRIANGLES, static_cast(m_bufferSet.elements.size()), GL_UNSIGNED_INT, nullptr); } -void TileSheetPixels::initBufferSet(const geo::Vec2 &paneSize, const TileSheet &img, const Palette &pal) noexcept { +void TileSheetPixels::initBufferSet(const geo::Vec2 &paneSize, const TileSheet &img, const TileSheet::SubSheet &subSheet, const Palette &pal) noexcept { // vao m_bufferSet.vao = glutils::generateVertexArrayObject(); glBindVertexArray(m_bufferSet.vao); // vbo & ebo m_bufferSet.vbo = glutils::generateBuffer(); m_bufferSet.ebo = glutils::generateBuffer(); - setBufferObjects(paneSize, img, pal, &m_bufferSet); + setBufferObjects(paneSize, img, subSheet, pal, &m_bufferSet); glutils::sendVbo(m_bufferSet); glutils::sendEbo(m_bufferSet); // vbo layout @@ -78,10 +78,10 @@ void TileSheetPixels::setPixelBufferObject(const geo::Vec2 &paneSize, unsigned v memcpy(ebo, elms, sizeof(elms)); } -void TileSheetPixels::setBufferObjects(const geo::Vec2 &paneSize, const TileSheet &img, const Palette &pal, glutils::BufferSet *bg) noexcept { - const auto setPixel = [this, paneSize, bg, img, pal](std::size_t i, uint8_t p) { +void TileSheetPixels::setBufferObjects(const geo::Vec2 &paneSize, const TileSheet &img, const TileSheet::SubSheet &subSheet, const Palette &pal, glutils::BufferSet *bg) noexcept { + const auto setPixel = [this, paneSize, bg, subSheet, pal](std::size_t i, uint8_t p) { const auto color = pal.colors[p]; - const auto pt = idxToPt(static_cast(i), img.columns()); + const auto pt = idxToPt(static_cast(i), subSheet.columns); const auto fx = static_cast(pt.x); const auto fy = static_cast(pt.y); const auto vbo = &bg->vertices[i * VertexVboLength]; @@ -89,22 +89,22 @@ void TileSheetPixels::setBufferObjects(const geo::Vec2 &paneSize, const TileShee setPixelBufferObject(paneSize, i * VertexVboRows, fx, fy, color, vbo, ebo); }; // set buffer lengths - const auto width = img.columns() * TileWidth; - const auto height = img.rows() * TileHeight; + const auto width = subSheet.columns * TileWidth; + const auto height = subSheet.rows * TileHeight; const auto tiles = static_cast(width * height); m_bufferSet.vertices.resize(tiles * VertexVboLength); m_bufferSet.elements.resize(tiles * VertexEboLength); // set pixels if (img.bpp == 4) { - for (std::size_t i = 0; i < img.pixels.size(); ++i) { - const auto colorIdx1 = img.pixels[i] & 0xF; - const auto colorIdx2 = img.pixels[i] >> 4; + for (std::size_t i = 0; i < subSheet.pixels.size(); ++i) { + const auto colorIdx1 = subSheet.pixels[i] & 0xF; + const auto colorIdx2 = subSheet.pixels[i] >> 4; setPixel(i * 2 + 0, colorIdx1); setPixel(i * 2 + 1, colorIdx2); } } else { - for (std::size_t i = 0; i < img.pixels.size(); ++i) { - const auto p = img.pixels[i]; + for (std::size_t i = 0; i < subSheet.pixels.size(); ++i) { + const auto p = subSheet.pixels[i]; setPixel(i, p); } } diff --git a/src/nostalgia/core/studio/tilesheetpixels.hpp b/src/nostalgia/core/studio/tilesheetpixels.hpp index 42d622ab..185d5fca 100644 --- a/src/nostalgia/core/studio/tilesheetpixels.hpp +++ b/src/nostalgia/core/studio/tilesheetpixels.hpp @@ -48,7 +48,7 @@ class TileSheetPixels { void draw(bool update, const geo::Vec2 &scroll) noexcept; - void initBufferSet(const geo::Vec2 &paneSize, const TileSheet &img, const Palette &pal) noexcept; + void initBufferSet(const geo::Vec2 &paneSize, const TileSheet &img, const TileSheet::SubSheet &subSheet, const Palette &pal) noexcept; [[nodiscard]] geo::Vec2 pixelSize(const geo::Vec2 &paneSize) const noexcept; @@ -56,7 +56,7 @@ class TileSheetPixels { private: void setPixelBufferObject(const geo::Vec2 &paneS, unsigned vertexRow, float x, float y, Color16 color, float *vbo, GLuint *ebo) const noexcept; - void setBufferObjects(const geo::Vec2 &paneS, const TileSheet &img, const Palette &pal, glutils::BufferSet *bg) noexcept; + void setBufferObjects(const geo::Vec2 &paneS, const TileSheet &img, const TileSheet::SubSheet &subSheet, const Palette &pal, glutils::BufferSet *bg) noexcept; }; diff --git a/src/nostalgia/core/typeconv.cpp b/src/nostalgia/core/typeconv.cpp index 7deec7c4..b99d27aa 100644 --- a/src/nostalgia/core/typeconv.cpp +++ b/src/nostalgia/core/typeconv.cpp @@ -15,7 +15,7 @@ struct NostalgiaGraphicToTileSheetConverter: public Convertersubsheet.rows = src->rows; dst->subsheet.columns = src->columns; dst->defaultPalette = std::move(src->defaultPalette); - dst->pixels = std::move(src->pixels); + dst->subsheet.pixels = std::move(src->pixels); return OxError(0); } }; @@ -31,7 +31,7 @@ struct TileSheetToCompactTileSheetConverter: public Converterbpp = src->bpp; dst->defaultPalette = std::move(src->defaultPalette); - dst->pixels = std::move(src->pixels); + dst->pixels = src->pixels(); return OxError(0); } }; diff --git a/src/nostalgia/core/userland/gfx.cpp b/src/nostalgia/core/userland/gfx.cpp index bf6623f0..9dada4c5 100644 --- a/src/nostalgia/core/userland/gfx.cpp +++ b/src/nostalgia/core/userland/gfx.cpp @@ -27,7 +27,7 @@ ox::Error loadBgTileSheet(Context *ctx, int section, const ox::FileAddress &tilesheetPath, const ox::FileAddress &palettePath) noexcept { - oxRequire(tilesheet, readObj(ctx, tilesheetPath)); + oxRequire(tilesheet, readObj(ctx, tilesheetPath)); oxRequire(palette, readObj(ctx, palettePath ? palettePath : tilesheet->defaultPalette)); const unsigned bytesPerTile = tilesheet->bpp == 8 ? 64 : 32; const auto tiles = tilesheet->pixels.size() / bytesPerTile; diff --git a/src/nostalgia/studio/lib/project.hpp b/src/nostalgia/studio/lib/project.hpp index 4bc7f7c0..cb94df60 100644 --- a/src/nostalgia/studio/lib/project.hpp +++ b/src/nostalgia/studio/lib/project.hpp @@ -50,8 +50,7 @@ class NOSTALGIASTUDIO_EXPORT Project { /** * Writes a MetalClaw object to the project at the given path. */ - template - ox::Error writeObj(const ox::String &path, T *obj) const noexcept; + ox::Error writeObj(const ox::String &path, auto *obj) const noexcept; template ox::Result> loadObj(const ox::String &path) const noexcept; @@ -86,8 +85,7 @@ class NOSTALGIASTUDIO_EXPORT Project { }; -template -ox::Error Project::writeObj(const ox::String &path, T *obj) const noexcept { +ox::Error Project::writeObj(const ox::String &path, auto *obj) const noexcept { // write MetalClaw oxRequireM(buff, ox::writeClaw(obj, ox::ClawFormat::Metal)); // write to FS diff --git a/src/nostalgia/studio/lib/task.hpp b/src/nostalgia/studio/lib/task.hpp index ed1e1b18..cd77fc03 100644 --- a/src/nostalgia/studio/lib/task.hpp +++ b/src/nostalgia/studio/lib/task.hpp @@ -17,7 +17,7 @@ enum class TaskState { class Task: public ox::SignalHandler { public: - virtual ~Task() noexcept = default; + ~Task() noexcept override = default; virtual TaskState update(nostalgia::core::Context *ctx) noexcept = 0; }; diff --git a/src/nostalgia/studio/lib/undostack.cpp b/src/nostalgia/studio/lib/undostack.cpp index 5aa03bac..b15f7a4f 100644 --- a/src/nostalgia/studio/lib/undostack.cpp +++ b/src/nostalgia/studio/lib/undostack.cpp @@ -12,25 +12,30 @@ void UndoStack::push(UndoCommand *cmd) noexcept { } m_stack.emplace_back(cmd); ++m_stackIdx; + auto cmdId = cmd->commandId(); cmd->redo(); - redoTriggered.emit(); - changeTriggered.emit(); + redoTriggered.emit(cmdId); + changeTriggered.emit(cmdId); } void UndoStack::redo() noexcept { if (m_stackIdx < m_stack.size()) { - m_stack[m_stackIdx++]->redo(); + auto &c = m_stack[m_stackIdx++]; + auto cmdId = c->commandId(); + c->redo(); + redoTriggered.emit(cmdId); + changeTriggered.emit(cmdId); } - redoTriggered.emit(); - changeTriggered.emit(); } void UndoStack::undo() noexcept { if (m_stackIdx) { - m_stack[--m_stackIdx]->undo(); + auto &c = m_stack[--m_stackIdx]; + auto cmdId = c->commandId(); + c->undo(); + undoTriggered.emit(cmdId); + changeTriggered.emit(cmdId); } - undoTriggered.emit(); - changeTriggered.emit(); } } diff --git a/src/nostalgia/studio/lib/undostack.hpp b/src/nostalgia/studio/lib/undostack.hpp index e7e021d5..0e52035a 100644 --- a/src/nostalgia/studio/lib/undostack.hpp +++ b/src/nostalgia/studio/lib/undostack.hpp @@ -16,6 +16,8 @@ class UndoCommand { virtual ~UndoCommand() noexcept = default; virtual void redo() noexcept = 0; virtual void undo() noexcept = 0; + [[nodiscard]] + virtual int commandId() const noexcept = 0; }; class UndoStack { @@ -40,9 +42,9 @@ class UndoStack { return m_stackIdx; } - ox::Signal redoTriggered; - ox::Signal undoTriggered; - ox::Signal changeTriggered; + ox::Signal redoTriggered; + ox::Signal undoTriggered; + ox::Signal changeTriggered; }; } diff --git a/src/nostalgia/studio/lib/widget.hpp b/src/nostalgia/studio/lib/widget.hpp index 0f3867ad..542078e0 100644 --- a/src/nostalgia/studio/lib/widget.hpp +++ b/src/nostalgia/studio/lib/widget.hpp @@ -12,7 +12,8 @@ namespace nostalgia::studio { class Widget: public ox::SignalHandler { public: + ~Widget() noexcept override = default; virtual void draw(core::Context*) noexcept = 0; }; -} \ No newline at end of file +}