diff --git a/src/nostalgia/core/assetmanager.hpp b/src/nostalgia/core/assetmanager.hpp index d56ef1f9..8746c79e 100644 --- a/src/nostalgia/core/assetmanager.hpp +++ b/src/nostalgia/core/assetmanager.hpp @@ -28,7 +28,7 @@ struct AssetContainer { public: template - explicit AssetContainer(Args&&... args): m_obj(ox::forward(args)...) { + explicit constexpr AssetContainer(Args&&... args): m_obj(ox::forward(args)...) { } constexpr T *get() noexcept { @@ -60,17 +60,17 @@ class AssetRef { const AssetContainer *m_ctr = nullptr; public: - explicit AssetRef(const AssetContainer *c = nullptr) noexcept: m_ctr(c) { + explicit constexpr AssetRef(const AssetContainer *c = nullptr) noexcept: m_ctr(c) { } - AssetRef(const AssetRef &h) noexcept { + constexpr AssetRef(const AssetRef &h) noexcept { m_ctr = h.m_ctr; if (m_ctr) { m_ctr->incRefs(); } } - AssetRef(AssetRef &&h) noexcept { + constexpr AssetRef(AssetRef &&h) noexcept { m_ctr = h.m_ctr; h.m_ctr = nullptr; } diff --git a/src/nostalgia/core/gfx.hpp b/src/nostalgia/core/gfx.hpp index ab887a99..882d6299 100644 --- a/src/nostalgia/core/gfx.hpp +++ b/src/nostalgia/core/gfx.hpp @@ -73,8 +73,7 @@ struct NostalgiaGraphic { } } - constexpr void setPixel(const geo::Point &pt, uint8_t palIdx) noexcept { - const auto idx = ptToIdx(pt, this->columns); + constexpr void setPixel(uint64_t idx, uint8_t palIdx) noexcept { if (bpp == 4) { if (idx & 1) { pixels[idx / 2] &= 0b0000'1111 | (palIdx << 4); @@ -85,6 +84,11 @@ struct NostalgiaGraphic { this->pixels[idx] = palIdx; } } + + constexpr void setPixel(const geo::Point &pt, uint8_t palIdx) noexcept { + const auto idx = ptToIdx(pt, this->columns); + setPixel(idx, palIdx); + } }; oxModelBegin(NostalgiaPalette) diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp index 8c2f06f7..43174a17 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp @@ -46,7 +46,7 @@ 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", ImVec2(tileSheetParentSize.x, tileSheetParentSize.y), true); + ImGui::BeginChild("child1", tileSheetParentSize, true); drawTileSheet(fbSize); ImGui::EndChild(); ImGui::SameLine(); @@ -75,8 +75,9 @@ void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept { glBindFramebuffer(GL_FRAMEBUFFER, 0); ImGui::Image(reinterpret_cast(m_framebuffer.color.id), static_cast(fbSize), ImVec2(0, 1), ImVec2(1, 0)); // handle input, this must come after drawing + const auto &io = ImGui::GetIO(); + const auto mousePos = geo::Vec2(io.MousePos); if (ImGui::IsItemHovered()) { - const auto &io = ImGui::GetIO(); const auto wheel = io.MouseWheel; const auto wheelh = io.MouseWheelH; if (wheel != 0) { @@ -87,13 +88,16 @@ void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept { m_tileSheetEditor.scrollH(fbSize, wheelh); } if (io.MouseDown[0]) { - auto clickPos = geo::Vec2(io.MousePos); + auto clickPos = mousePos; const auto &winPos = ImGui::GetWindowPos(); clickPos.x -= winPos.x + 10; clickPos.y -= winPos.y + 10; - m_tileSheetEditor.clickPixel(fbSize, clickPos); + m_tileSheetEditor.hitPixel(fbSize, clickPos); } } + if (io.MouseReleased[0]) { + m_tileSheetEditor.releaseMouseButton(); + } } void TileSheetEditorImGui::drawPalettePicker() const noexcept { diff --git a/src/nostalgia/core/studio/tilesheeteditor.cpp b/src/nostalgia/core/studio/tilesheeteditor.cpp index 8dc7d1f0..cd535954 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "tilesheeteditor.hpp" @@ -32,8 +31,8 @@ void TileSheetEditor::draw() noexcept { constexpr Color32 bgColor = 0x717d7e; glClearColor(redf(bgColor), greenf(bgColor), bluef(bgColor), 1.f); glClear(GL_COLOR_BUFFER_BIT); - m_pixelsDrawer.draw(m_updated, m_scrollOffset); - m_pixelGridDrawer.draw(m_updated, m_scrollOffset); + m_pixelsDrawer.draw(updated(), m_scrollOffset); + m_pixelGridDrawer.draw(updated(), m_scrollOffset); } void TileSheetEditor::scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept { @@ -61,7 +60,7 @@ void TileSheetEditor::scrollH(const geo::Vec2 &paneSz, float wheelh) noexcept { m_scrollOffset.x = ox::clamp(m_scrollOffset.x, -(sheetSize.x / 2), 0.f); } -void TileSheetEditor::clickPixel(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept { +void TileSheetEditor::hitPixel(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept { auto [x, y] = clickPos; const auto pixDrawSz = m_pixelsDrawer.pixelSize(paneSize); x /= paneSize.x; @@ -72,8 +71,11 @@ void TileSheetEditor::clickPixel(const geo::Vec2 &paneSize, const geo::Vec2 &cli y /= pixDrawSz.y; const auto pt = geo::Point(static_cast(x * 2), static_cast(y * 2)); const uint8_t palIdx = 0; - m_model.img().setPixel(pt, palIdx); - m_updated = true; + m_model.draw(pt, palIdx); +} + +void TileSheetEditor::releaseMouseButton() noexcept { + m_model.endDraw(); } void TileSheetEditor::resize(const geo::Vec2 &sz) noexcept { @@ -82,11 +84,12 @@ void TileSheetEditor::resize(const geo::Vec2 &sz) noexcept { } bool TileSheetEditor::updated() const noexcept { - return m_updated; + return m_updated || m_model.updated(); } void TileSheetEditor::ackUpdate() noexcept { m_updated = false; + m_model.ackUpdate(); } void TileSheetEditor::saveItem() { diff --git a/src/nostalgia/core/studio/tilesheeteditor.hpp b/src/nostalgia/core/studio/tilesheeteditor.hpp index 3a167dcc..088ce515 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.hpp +++ b/src/nostalgia/core/studio/tilesheeteditor.hpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -24,8 +23,8 @@ class TileSheetEditor { TileSheetEditorModel m_model; TileSheetGrid m_pixelGridDrawer; TileSheetPixels m_pixelsDrawer; - bool m_updated = false; float m_pixelSizeMod = 1; + bool m_updated = false; geo::Vec2 m_scrollOffset; public: @@ -41,7 +40,9 @@ class TileSheetEditor { void draw() noexcept; - void clickPixel(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept; + void hitPixel(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept; + + void releaseMouseButton() noexcept; void scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept; diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.cpp b/src/nostalgia/core/studio/tilesheeteditormodel.cpp index 09f3aa91..5d73d964 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.cpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.cpp @@ -9,12 +9,33 @@ namespace nostalgia::core { TileSheetEditorModel::TileSheetEditorModel(Context *ctx, const ox::String &path) { - // build shaders oxRequireT(img, readObj(ctx, path.c_str())); - m_img = std::move(*img); + m_img = *img; oxThrowError(readObj(ctx, m_img.defaultPalette).moveTo(&m_pal)); } +void TileSheetEditorModel::draw(const geo::Point &pt, std::size_t palIdx) noexcept { + if (!m_ongoingDrawCommand) { + m_ongoingDrawCommand = new DrawCommand(&m_img, ptToIdx(pt, m_img.columns), palIdx); + m_undoStack.push(m_ongoingDrawCommand); + m_updated = true; + } else { + m_updated = m_ongoingDrawCommand->append(ptToIdx(pt, m_img.columns)); + } +} + +void TileSheetEditorModel::endDraw() noexcept { + m_ongoingDrawCommand = nullptr; +} + +bool TileSheetEditorModel::updated() const noexcept { + return m_updated; +} + +void TileSheetEditorModel::ackUpdate() noexcept { + m_updated = false; +} + void TileSheetEditorModel::getFillPixels(bool *pixels, geo::Point pt, int oldColor) const noexcept { const auto tileIdx = [this](const geo::Point &pt) noexcept { return ptToIdx(pt, img().columns) / PixelsPerTile; diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.hpp b/src/nostalgia/core/studio/tilesheeteditormodel.hpp index 141c169c..43dd2788 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.hpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.hpp @@ -6,9 +6,9 @@ #include -#include -#include #include +#include +#include #include #include @@ -49,16 +49,56 @@ struct PixelChunk { int size = 0; }; +struct DrawCommand: public studio::UndoCommand { + private: + struct Change { + uint32_t idx = 0; + uint16_t oldPalIdx = 0; + }; + NostalgiaGraphic *m_img = nullptr; + ox::Vector m_changes; + int m_palIdx = 0; + + public: + constexpr DrawCommand(NostalgiaGraphic *img, std::size_t idx, int palIdx) noexcept { + m_img = img; + m_changes.emplace_back(idx, m_img->pixels[idx]); + m_palIdx = palIdx; + } + + constexpr bool append(std::size_t idx) noexcept { + if (m_changes.back().value.idx != idx) { + m_changes.emplace_back(idx, m_img->pixels[idx]); + m_img->setPixel(idx, m_palIdx); + return true; + } + return false; + } + + void redo() noexcept override { + for (auto c : m_changes) { + m_img->setPixel(c.idx, m_palIdx); + } + } + + void undo() noexcept override { + for (auto c : m_changes) { + m_img->setPixel(c.idx, c.oldPalIdx); + } + } + +}; + oxModelBegin(PixelChunk) oxModelField(pt) oxModelField(size) oxModelEnd() struct TileSheetClipboard { - static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard"; - static constexpr auto TypeVersion = 1; + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard"; + static constexpr auto TypeVersion = 1; - oxModelFriend(TileSheetClipboard); + oxModelFriend(TileSheetClipboard); protected: ox::Vector m_pixels; @@ -76,27 +116,31 @@ struct TileSheetClipboard { void setPoints(const geo::Point &p1, const geo::Point &p2); [[nodiscard]] - geo::Point point1() const; + constexpr geo::Point point1() const noexcept { + return m_p1; + } [[nodiscard]] - geo::Point point2() const; + constexpr geo::Point point2() const noexcept { + return m_p2; + } }; -template -constexpr ox::Error model(T *io, TileSheetClipboard *b) noexcept { - io->template setTypeInfo(); - oxReturnError(io->field("pixels", &b->m_pixels)); - oxReturnError(io->field("p1", &b->m_p1)); - oxReturnError(io->field("p2", &b->m_p2)); - return OxError(0); -} +oxModelBegin(TileSheetClipboard) + oxModelFieldRename(pixels, m_pixels) + oxModelFieldRename(p1, m_p1) + oxModelFieldRename(p2, m_p2) +oxModelEnd() class TileSheetEditorModel { private: NostalgiaGraphic m_img; AssetRef m_pal; + studio::UndoStack m_undoStack; + DrawCommand *m_ongoingDrawCommand = nullptr; + bool m_updated = false; public: TileSheetEditorModel(Context *ctx, const ox::String &path); @@ -118,6 +162,15 @@ class TileSheetEditorModel { [[nodiscard]] constexpr const NostalgiaPalette &pal() const noexcept; + void draw(const geo::Point &pt, std::size_t palIdx) noexcept; + + void endDraw() noexcept; + + [[nodiscard]] + bool updated() const noexcept; + + void ackUpdate() noexcept; + protected: void saveItem(); @@ -136,12 +189,6 @@ class TileSheetEditorModel { [[nodiscard]] ox::String palettePath(const ox::String &palettePath) const; - // slots - public: - ox::Error colorSelected() noexcept; - - ox::Error setColorTable() noexcept; - }; constexpr const NostalgiaGraphic &TileSheetEditorModel::img() const noexcept {