[nostalgia/core/studio] Put TileSheetEditor draw command in UndoCommand
This commit is contained in:
parent
4f69f027ef
commit
d792e4c515
@ -28,7 +28,7 @@ struct AssetContainer {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
explicit AssetContainer(Args&&... args): m_obj(ox::forward<Args>(args)...) {
|
explicit constexpr AssetContainer(Args&&... args): m_obj(ox::forward<Args>(args)...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T *get() noexcept {
|
constexpr T *get() noexcept {
|
||||||
@ -60,17 +60,17 @@ class AssetRef {
|
|||||||
const AssetContainer<T> *m_ctr = nullptr;
|
const AssetContainer<T> *m_ctr = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AssetRef(const AssetContainer<T> *c = nullptr) noexcept: m_ctr(c) {
|
explicit constexpr AssetRef(const AssetContainer<T> *c = nullptr) noexcept: m_ctr(c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetRef(const AssetRef &h) noexcept {
|
constexpr AssetRef(const AssetRef &h) noexcept {
|
||||||
m_ctr = h.m_ctr;
|
m_ctr = h.m_ctr;
|
||||||
if (m_ctr) {
|
if (m_ctr) {
|
||||||
m_ctr->incRefs();
|
m_ctr->incRefs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetRef(AssetRef &&h) noexcept {
|
constexpr AssetRef(AssetRef &&h) noexcept {
|
||||||
m_ctr = h.m_ctr;
|
m_ctr = h.m_ctr;
|
||||||
h.m_ctr = nullptr;
|
h.m_ctr = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -73,8 +73,7 @@ struct NostalgiaGraphic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void setPixel(const geo::Point &pt, uint8_t palIdx) noexcept {
|
constexpr void setPixel(uint64_t idx, uint8_t palIdx) noexcept {
|
||||||
const auto idx = ptToIdx(pt, this->columns);
|
|
||||||
if (bpp == 4) {
|
if (bpp == 4) {
|
||||||
if (idx & 1) {
|
if (idx & 1) {
|
||||||
pixels[idx / 2] &= 0b0000'1111 | (palIdx << 4);
|
pixels[idx / 2] &= 0b0000'1111 | (palIdx << 4);
|
||||||
@ -85,6 +84,11 @@ struct NostalgiaGraphic {
|
|||||||
this->pixels[idx] = palIdx;
|
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)
|
oxModelBegin(NostalgiaPalette)
|
||||||
|
@ -46,7 +46,7 @@ void TileSheetEditorImGui::draw(core::Context*) noexcept {
|
|||||||
const auto paneSize = ImGui::GetContentRegionAvail();
|
const auto paneSize = ImGui::GetContentRegionAvail();
|
||||||
const auto tileSheetParentSize = ImVec2(paneSize.x - m_palViewWidth, paneSize.y);
|
const auto tileSheetParentSize = ImVec2(paneSize.x - m_palViewWidth, paneSize.y);
|
||||||
const auto fbSize = geo::Vec2(tileSheetParentSize.x - 16, tileSheetParentSize.y - 16);
|
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);
|
drawTileSheet(fbSize);
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@ -75,8 +75,9 @@ void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept {
|
|||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
ImGui::Image(reinterpret_cast<void*>(m_framebuffer.color.id), static_cast<ImVec2>(fbSize), ImVec2(0, 1), ImVec2(1, 0));
|
ImGui::Image(reinterpret_cast<void*>(m_framebuffer.color.id), static_cast<ImVec2>(fbSize), ImVec2(0, 1), ImVec2(1, 0));
|
||||||
// handle input, this must come after drawing
|
// handle input, this must come after drawing
|
||||||
|
const auto &io = ImGui::GetIO();
|
||||||
|
const auto mousePos = geo::Vec2(io.MousePos);
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
const auto &io = ImGui::GetIO();
|
|
||||||
const auto wheel = io.MouseWheel;
|
const auto wheel = io.MouseWheel;
|
||||||
const auto wheelh = io.MouseWheelH;
|
const auto wheelh = io.MouseWheelH;
|
||||||
if (wheel != 0) {
|
if (wheel != 0) {
|
||||||
@ -87,13 +88,16 @@ void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept {
|
|||||||
m_tileSheetEditor.scrollH(fbSize, wheelh);
|
m_tileSheetEditor.scrollH(fbSize, wheelh);
|
||||||
}
|
}
|
||||||
if (io.MouseDown[0]) {
|
if (io.MouseDown[0]) {
|
||||||
auto clickPos = geo::Vec2(io.MousePos);
|
auto clickPos = mousePos;
|
||||||
const auto &winPos = ImGui::GetWindowPos();
|
const auto &winPos = ImGui::GetWindowPos();
|
||||||
clickPos.x -= winPos.x + 10;
|
clickPos.x -= winPos.x + 10;
|
||||||
clickPos.y -= winPos.y + 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 {
|
void TileSheetEditorImGui::drawPalettePicker() const noexcept {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include <nostalgia/core/consts.hpp>
|
#include <nostalgia/core/consts.hpp>
|
||||||
#include <nostalgia/core/media.hpp>
|
#include <nostalgia/core/media.hpp>
|
||||||
#include <nostalgia/core/ptidxconv.hpp>
|
|
||||||
#include <nostalgia/geo/point.hpp>
|
#include <nostalgia/geo/point.hpp>
|
||||||
|
|
||||||
#include "tilesheeteditor.hpp"
|
#include "tilesheeteditor.hpp"
|
||||||
@ -32,8 +31,8 @@ void TileSheetEditor::draw() noexcept {
|
|||||||
constexpr Color32 bgColor = 0x717d7e;
|
constexpr Color32 bgColor = 0x717d7e;
|
||||||
glClearColor(redf(bgColor), greenf(bgColor), bluef(bgColor), 1.f);
|
glClearColor(redf(bgColor), greenf(bgColor), bluef(bgColor), 1.f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
m_pixelsDrawer.draw(m_updated, m_scrollOffset);
|
m_pixelsDrawer.draw(updated(), m_scrollOffset);
|
||||||
m_pixelGridDrawer.draw(m_updated, m_scrollOffset);
|
m_pixelGridDrawer.draw(updated(), m_scrollOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditor::scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept {
|
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);
|
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;
|
auto [x, y] = clickPos;
|
||||||
const auto pixDrawSz = m_pixelsDrawer.pixelSize(paneSize);
|
const auto pixDrawSz = m_pixelsDrawer.pixelSize(paneSize);
|
||||||
x /= paneSize.x;
|
x /= paneSize.x;
|
||||||
@ -72,8 +71,11 @@ void TileSheetEditor::clickPixel(const geo::Vec2 &paneSize, const geo::Vec2 &cli
|
|||||||
y /= pixDrawSz.y;
|
y /= pixDrawSz.y;
|
||||||
const auto pt = geo::Point(static_cast<int>(x * 2), static_cast<int>(y * 2));
|
const auto pt = geo::Point(static_cast<int>(x * 2), static_cast<int>(y * 2));
|
||||||
const uint8_t palIdx = 0;
|
const uint8_t palIdx = 0;
|
||||||
m_model.img().setPixel(pt, palIdx);
|
m_model.draw(pt, palIdx);
|
||||||
m_updated = true;
|
}
|
||||||
|
|
||||||
|
void TileSheetEditor::releaseMouseButton() noexcept {
|
||||||
|
m_model.endDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditor::resize(const geo::Vec2 &sz) noexcept {
|
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 {
|
bool TileSheetEditor::updated() const noexcept {
|
||||||
return m_updated;
|
return m_updated || m_model.updated();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditor::ackUpdate() noexcept {
|
void TileSheetEditor::ackUpdate() noexcept {
|
||||||
m_updated = false;
|
m_updated = false;
|
||||||
|
m_model.ackUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditor::saveItem() {
|
void TileSheetEditor::saveItem() {
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <ox/model/def.hpp>
|
#include <ox/model/def.hpp>
|
||||||
|
|
||||||
#include <nostalgia/core/gfx.hpp>
|
#include <nostalgia/core/gfx.hpp>
|
||||||
#include <nostalgia/geo/bounds.hpp>
|
|
||||||
#include <nostalgia/geo/vec.hpp>
|
#include <nostalgia/geo/vec.hpp>
|
||||||
#include <nostalgia/glutils/glutils.hpp>
|
#include <nostalgia/glutils/glutils.hpp>
|
||||||
#include <nostalgia/studio/studio.hpp>
|
#include <nostalgia/studio/studio.hpp>
|
||||||
@ -24,8 +23,8 @@ class TileSheetEditor {
|
|||||||
TileSheetEditorModel m_model;
|
TileSheetEditorModel m_model;
|
||||||
TileSheetGrid m_pixelGridDrawer;
|
TileSheetGrid m_pixelGridDrawer;
|
||||||
TileSheetPixels m_pixelsDrawer;
|
TileSheetPixels m_pixelsDrawer;
|
||||||
bool m_updated = false;
|
|
||||||
float m_pixelSizeMod = 1;
|
float m_pixelSizeMod = 1;
|
||||||
|
bool m_updated = false;
|
||||||
geo::Vec2 m_scrollOffset;
|
geo::Vec2 m_scrollOffset;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -41,7 +40,9 @@ class TileSheetEditor {
|
|||||||
|
|
||||||
void draw() noexcept;
|
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;
|
void scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept;
|
||||||
|
|
||||||
|
@ -9,12 +9,33 @@
|
|||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
TileSheetEditorModel::TileSheetEditorModel(Context *ctx, const ox::String &path) {
|
TileSheetEditorModel::TileSheetEditorModel(Context *ctx, const ox::String &path) {
|
||||||
// build shaders
|
|
||||||
oxRequireT(img, readObj<NostalgiaGraphic>(ctx, path.c_str()));
|
oxRequireT(img, readObj<NostalgiaGraphic>(ctx, path.c_str()));
|
||||||
m_img = std::move(*img);
|
m_img = *img;
|
||||||
oxThrowError(readObj<NostalgiaPalette>(ctx, m_img.defaultPalette).moveTo(&m_pal));
|
oxThrowError(readObj<NostalgiaPalette>(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 {
|
void TileSheetEditorModel::getFillPixels(bool *pixels, geo::Point pt, int oldColor) const noexcept {
|
||||||
const auto tileIdx = [this](const geo::Point &pt) noexcept {
|
const auto tileIdx = [this](const geo::Point &pt) noexcept {
|
||||||
return ptToIdx(pt, img().columns) / PixelsPerTile;
|
return ptToIdx(pt, img().columns) / PixelsPerTile;
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
#include <ox/std/string.hpp>
|
#include <ox/std/string.hpp>
|
||||||
|
|
||||||
#include <nostalgia/geo/bounds.hpp>
|
|
||||||
#include <nostalgia/geo/vec.hpp>
|
|
||||||
#include <nostalgia/core/gfx.hpp>
|
#include <nostalgia/core/gfx.hpp>
|
||||||
|
#include <nostalgia/geo/point.hpp>
|
||||||
|
#include <nostalgia/geo/vec.hpp>
|
||||||
#include <nostalgia/glutils/glutils.hpp>
|
#include <nostalgia/glutils/glutils.hpp>
|
||||||
#include <nostalgia/studio/studio.hpp>
|
#include <nostalgia/studio/studio.hpp>
|
||||||
|
|
||||||
@ -49,16 +49,56 @@ struct PixelChunk {
|
|||||||
int size = 0;
|
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<Change, 8> 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)
|
oxModelBegin(PixelChunk)
|
||||||
oxModelField(pt)
|
oxModelField(pt)
|
||||||
oxModelField(size)
|
oxModelField(size)
|
||||||
oxModelEnd()
|
oxModelEnd()
|
||||||
|
|
||||||
struct TileSheetClipboard {
|
struct TileSheetClipboard {
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard";
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard";
|
||||||
static constexpr auto TypeVersion = 1;
|
static constexpr auto TypeVersion = 1;
|
||||||
|
|
||||||
oxModelFriend(TileSheetClipboard);
|
oxModelFriend(TileSheetClipboard);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ox::Vector<int> m_pixels;
|
ox::Vector<int> m_pixels;
|
||||||
@ -76,27 +116,31 @@ struct TileSheetClipboard {
|
|||||||
void setPoints(const geo::Point &p1, const geo::Point &p2);
|
void setPoints(const geo::Point &p1, const geo::Point &p2);
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
geo::Point point1() const;
|
constexpr geo::Point point1() const noexcept {
|
||||||
|
return m_p1;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
geo::Point point2() const;
|
constexpr geo::Point point2() const noexcept {
|
||||||
|
return m_p2;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
oxModelBegin(TileSheetClipboard)
|
||||||
constexpr ox::Error model(T *io, TileSheetClipboard *b) noexcept {
|
oxModelFieldRename(pixels, m_pixels)
|
||||||
io->template setTypeInfo<TileSheetClipboard>();
|
oxModelFieldRename(p1, m_p1)
|
||||||
oxReturnError(io->field("pixels", &b->m_pixels));
|
oxModelFieldRename(p2, m_p2)
|
||||||
oxReturnError(io->field("p1", &b->m_p1));
|
oxModelEnd()
|
||||||
oxReturnError(io->field("p2", &b->m_p2));
|
|
||||||
return OxError(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
class TileSheetEditorModel {
|
class TileSheetEditorModel {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NostalgiaGraphic m_img;
|
NostalgiaGraphic m_img;
|
||||||
AssetRef<NostalgiaPalette> m_pal;
|
AssetRef<NostalgiaPalette> m_pal;
|
||||||
|
studio::UndoStack m_undoStack;
|
||||||
|
DrawCommand *m_ongoingDrawCommand = nullptr;
|
||||||
|
bool m_updated = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TileSheetEditorModel(Context *ctx, const ox::String &path);
|
TileSheetEditorModel(Context *ctx, const ox::String &path);
|
||||||
@ -118,6 +162,15 @@ class TileSheetEditorModel {
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr const NostalgiaPalette &pal() const noexcept;
|
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:
|
protected:
|
||||||
void saveItem();
|
void saveItem();
|
||||||
|
|
||||||
@ -136,12 +189,6 @@ class TileSheetEditorModel {
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::String palettePath(const ox::String &palettePath) const;
|
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 {
|
constexpr const NostalgiaGraphic &TileSheetEditorModel::img() const noexcept {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user