From 0aa71f1dbb8d140fc7172fbd93526943100f122b Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Sun, 13 Feb 2022 03:14:28 -0600 Subject: [PATCH] [nostalgia/core/studio] Decouple core TileSheetEditor from ImGui --- src/nostalgia/core/studio/CMakeLists.txt | 3 +- src/nostalgia/core/studio/module.cpp | 4 +- .../core/studio/tilesheeteditor-imgui.cpp | 115 +++++++++++ .../core/studio/tilesheeteditor-imgui.hpp | 81 ++++++++ src/nostalgia/core/studio/tilesheeteditor.cpp | 182 ++++++------------ src/nostalgia/core/studio/tilesheeteditor.hpp | 44 ++--- 6 files changed, 282 insertions(+), 147 deletions(-) create mode 100644 src/nostalgia/core/studio/tilesheeteditor-imgui.cpp create mode 100644 src/nostalgia/core/studio/tilesheeteditor-imgui.hpp diff --git a/src/nostalgia/core/studio/CMakeLists.txt b/src/nostalgia/core/studio/CMakeLists.txt index a0b4e724..9490feee 100644 --- a/src/nostalgia/core/studio/CMakeLists.txt +++ b/src/nostalgia/core/studio/CMakeLists.txt @@ -5,8 +5,9 @@ add_library( #new_tilesheet_wizard.cpp #newpalettewizard.cpp #paletteeditor.cpp + tilesheeteditor-imgui.cpp tilesheeteditor.cpp - tilesheetpixelgrid.cpp + tilesheetpixelgrid.cpp tilesheetpixels.cpp ) diff --git a/src/nostalgia/core/studio/module.cpp b/src/nostalgia/core/studio/module.cpp index 30e9de0b..096b75e3 100644 --- a/src/nostalgia/core/studio/module.cpp +++ b/src/nostalgia/core/studio/module.cpp @@ -2,7 +2,7 @@ * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. */ -#include "tilesheeteditor.hpp" +#include "tilesheeteditor-imgui.hpp" #include "module.hpp" @@ -13,7 +13,7 @@ ox::Vector Module::editors(core::Context *ctx) { { {"ng"}, [ctx](const ox::String &path) { - return new TileSheetEditor(ctx, path); + return new TileSheetEditorImGui(ctx, path); } } }; diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp new file mode 100644 index 00000000..b2a787fc --- /dev/null +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +#include +#include + +#include "tilesheeteditor-imgui.hpp" + +namespace nostalgia::core { + +TileSheetEditorImGui::TileSheetEditorImGui(Context *ctx, const ox::String &path): m_tileSheetEditor(ctx, path) { + m_itemPath = path; + const auto lastSlash = std::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset(); + m_itemName = m_itemPath.substr(lastSlash + 1); +} + +ox::String TileSheetEditorImGui::itemName() const noexcept { + return m_itemPath; +} + +ox::String TileSheetEditorImGui::itemDisplayName() const noexcept { + return m_itemName; +} + +void TileSheetEditorImGui::exportFile() { +} + +void TileSheetEditorImGui::cut() { + m_tileSheetEditor.cut(); +} + +void TileSheetEditorImGui::copy() { + m_tileSheetEditor.copy(); +} + +void TileSheetEditorImGui::paste() { + m_tileSheetEditor.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 = common::Vec2(tileSheetParentSize.x - 16, tileSheetParentSize.y - 16); + ImGui::BeginChild("child1", ImVec2(tileSheetParentSize.x, tileSheetParentSize.y), true); + drawTileSheet(fbSize); + ImGui::EndChild(); + ImGui::SameLine(); + // draw palette/color picker + ImGui::BeginChild("child2", ImVec2(m_palViewWidth - 8, paneSize.y), true); + drawPalettePicker(); + ImGui::EndChild(); +} + +void TileSheetEditorImGui::saveItem() { +} + +void TileSheetEditorImGui::drawTileSheet(const common::Vec2 &fbSize) noexcept { + const auto fbSizei = common::Size(static_cast(fbSize.x), static_cast(fbSize.y)); + if (m_framebuffer.width != fbSizei.width || m_framebuffer.height != fbSizei.height) { + m_framebuffer = glutils::generateFrameBuffer(fbSizei.width, fbSizei.height); + m_tileSheetEditor.resize(fbSize); + } else if (m_tileSheetEditor.updated()) { + m_tileSheetEditor.resize(fbSize); + m_tileSheetEditor.ackUpdate(); + } + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); + // clear screen and draw + glViewport(0, 0, fbSizei.width, fbSizei.height); + m_tileSheetEditor.draw(); + 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 + if (ImGui::IsItemHovered()) { + const auto &io = ImGui::GetIO(); + const auto wheel = io.MouseWheel; + const auto wheelh = io.MouseWheelH; + if (wheel != 0) { + const auto zoomMod = ox::defines::OS == ox::OS::Darwin ? io.KeySuper : io.KeyCtrl; + m_tileSheetEditor.scrollV(fbSize, wheel, zoomMod); + } + if (wheelh != 0) { + m_tileSheetEditor.scrollH(fbSize, wheelh); + } + if (io.MouseDown[0]) { + auto clickPos = common::Vec2(io.MousePos); + const auto &winPos = ImGui::GetWindowPos(); + clickPos.x -= winPos.x + 10; + clickPos.y -= winPos.y + 10; + m_tileSheetEditor.clickPixel(fbSize, clickPos); + } + } +} + +void TileSheetEditorImGui::drawPalettePicker() noexcept { + // header + ImGui::BeginTable("PaletteTable", 2); + ImGui::TableSetupColumn("No.", 0, 0.35); + ImGui::TableSetupColumn("Color16", 0, 3); + ImGui::TableHeadersRow(); + for (auto i = 1; auto c : m_tileSheetEditor.pal().colors) { + ImGui::TableNextColumn(); + ImGui::Text("%d", i); + ImGui::TableNextColumn(); + ImGui::Text("(%d, %d, %d)", red16(c), green16(c), blue16(c)); + ++i; + } + ImGui::EndTable(); +} + +} diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp new file mode 100644 index 00000000..8a578e45 --- /dev/null +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp @@ -0,0 +1,81 @@ +/* + * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include "tilesheetpixelgrid.hpp" +#include "tilesheetpixels.hpp" +#include "tilesheeteditor.hpp" + +namespace nostalgia::core { + +class TileSheetEditorImGui: public studio::Editor { + + private: + ox::String m_itemPath; + ox::String m_itemName; + glutils::FrameBuffer m_framebuffer; + TileSheetEditor m_tileSheetEditor; + float m_palViewWidth = 200; + + public: + TileSheetEditorImGui(Context *ctx, const ox::String &path); + + ~TileSheetEditorImGui() override = default; + + ox::String itemName() const noexcept override; + + ox::String itemDisplayName() const noexcept override; + + void exportFile() override; + + void cut() override; + + void copy() override; + + void paste() override; + + void draw(core::Context*) noexcept override; + + protected: + void saveItem() override; + + private: + void setPalette(); + + void saveState(); + + void restoreState(); + + [[nodiscard]] + ox::String paletteName(const ox::String &palettePath) const; + + [[nodiscard]] + ox::String palettePath(const ox::String &palettePath) const; + + void drawTileSheet(const common::Vec2 &fbSize) noexcept; + + void drawPalettePicker() noexcept; + + // slots + public: + ox::Error colorSelected() noexcept; + + ox::Error setColorTable() noexcept; + + // slots + private: + ox::Error updateAfterClicked() noexcept; + +}; + +} diff --git a/src/nostalgia/core/studio/tilesheeteditor.cpp b/src/nostalgia/core/studio/tilesheeteditor.cpp index 59f667f6..420d5891 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor.cpp @@ -4,8 +4,6 @@ #include -#include - #include #include #include @@ -16,28 +14,14 @@ namespace nostalgia::core { TileSheetEditor::TileSheetEditor(Context *ctx, const ox::String &path) { - m_itemPath = path; - const auto lastSlash = std::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset(); - m_itemName = m_itemPath.substr(lastSlash + 1); // build shaders oxThrowError(m_pixelsDrawer.buildShader()); oxThrowError(m_pixelGridDrawer.buildShader()); - oxRequireT(img, readObj(ctx, m_itemPath.c_str())); + oxRequireT(img, readObj(ctx, path.c_str())); m_img = std::move(*img); oxThrowError(readObj(ctx, m_img.defaultPalette).moveTo(&m_pal)); } -ox::String TileSheetEditor::itemName() const noexcept { - return m_itemPath; -} - -ox::String TileSheetEditor::itemDisplayName() const noexcept { - return m_itemName; -} - -void TileSheetEditor::exportFile() { -} - void TileSheetEditor::cut() { } @@ -47,29 +31,74 @@ void TileSheetEditor::copy() { void TileSheetEditor::paste() { } -void TileSheetEditor::draw(core::Context*) noexcept { - const auto paneSize = ImGui::GetContentRegionAvail(); - const auto tileSheetParentSize = ImVec2(paneSize.x - m_palViewWidth, paneSize.y); - const auto fbSize = common::Vec2(tileSheetParentSize.x - 16, tileSheetParentSize.y - 16); - ImGui::BeginChild("child1", ImVec2(tileSheetParentSize.x, tileSheetParentSize.y), true); - drawTileSheet(fbSize); - ImGui::EndChild(); - ImGui::SameLine(); - // draw palette/color picker - ImGui::BeginChild("child2", ImVec2(m_palViewWidth - 8, paneSize.y), true); - drawPalettePicker(); - ImGui::EndChild(); -} - -void TileSheetEditor::glDraw() noexcept { - // clear screen +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_updated = false; - //oxAssert(glGetError() == 0, "There was an OpenGL error"); +} + +void TileSheetEditor::scrollV(const common::Vec2 paneSz, float wheel, bool zoomMod) noexcept { + const auto pixelSize = m_pixelsDrawer.pixelSize(paneSz); + const ImVec2 sheetSize(pixelSize.x * static_cast(m_img.columns) * TileWidth, + pixelSize.y * static_cast(m_img.rows) * TileHeight); + if (zoomMod) { + m_pixelSizeMod = ox::clamp(m_pixelSizeMod + wheel * 0.02f, 0.55f, 2.f); + m_pixelsDrawer.setPixelSizeMod(m_pixelSizeMod); + m_pixelGridDrawer.setPixelSizeMod(m_pixelSizeMod); + m_updated = true; + } else { + m_scrollOffset.y -= wheel * 0.1f; + } + // adjust scroll offset in both cases because the image can be zoomed + // or scrolled off screen + m_scrollOffset.y = ox::clamp(m_scrollOffset.y, 0.f, sheetSize.y / 2); +} + +void TileSheetEditor::scrollH(const common::Vec2 paneSz, float wheelh) noexcept { + const auto pixelSize = m_pixelsDrawer.pixelSize(paneSz); + const ImVec2 sheetSize(pixelSize.x * static_cast(m_img.columns) * TileWidth, + pixelSize.y * static_cast(m_img.rows) * TileHeight); + m_scrollOffset.x += wheelh * 0.1f; + m_scrollOffset.x = ox::clamp(m_scrollOffset.x, -(sheetSize.x / 2), 0.f); +} + +void TileSheetEditor::clickPixel(const common::Vec2 &paneSize, const common::Vec2 &clickPos) noexcept { + auto [x, y] = clickPos; + const auto pixDrawSz = m_pixelsDrawer.pixelSize(paneSize); + x /= paneSize.x; + y /= paneSize.y; + x += -m_scrollOffset.x / 2; + y += m_scrollOffset.y / 2; + x /= pixDrawSz.x; + y /= pixDrawSz.y; + const auto pt = common::Point(static_cast(x * 2), static_cast(y * 2)); + const uint8_t palIdx = 0; + m_img.setPixel(pt, palIdx); + m_updated = true; +} + +void TileSheetEditor::resize(const common::Vec2 &sz) noexcept { + m_pixelsDrawer.initBufferSet(sz, m_img, *m_pal); + m_pixelGridDrawer.initBufferSet(sz, m_img); +} + +const NostalgiaGraphic &TileSheetEditor::img() const { + return m_img; +} + +const NostalgiaPalette &TileSheetEditor::pal() const { + return *m_pal; +} + +bool TileSheetEditor::updated() const noexcept { + return m_updated; +} + +void TileSheetEditor::ackUpdate() noexcept { + m_updated = false; } void TileSheetEditor::saveItem() { @@ -107,89 +136,4 @@ void TileSheetEditor::getFillPixels(bool *pixels, common::Point pt, int oldColor } } -void TileSheetEditor::drawTileSheet(const common::Vec2 &fbSize) noexcept { - const auto fbSizei = common::Size(static_cast(fbSize.x), static_cast(fbSize.y)); - if (m_framebuffer.width != fbSizei.width || m_framebuffer.height != fbSizei.height) { - m_framebuffer = glutils::generateFrameBuffer(fbSizei.width, fbSizei.height); - m_pixelsDrawer.initBufferSet(common::Vec2(fbSize), m_img, *m_pal); - m_pixelGridDrawer.initBufferSet(common::Vec2(fbSize), m_img); - } else if (m_updated) { - m_pixelsDrawer.initBufferSet(common::Vec2(fbSize), m_img, *m_pal); - m_pixelGridDrawer.initBufferSet(common::Vec2(fbSize), m_img); - } - glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); - glViewport(0, 0, fbSizei.width, fbSizei.height); - glDraw(); - 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 - if (ImGui::IsItemHovered()) { - const auto &io = ImGui::GetIO(); - const auto wheel = io.MouseWheel; - const auto wheelh = io.MouseWheelH; - if (wheel != 0) { - const auto zoomMod = ox::defines::OS == ox::OS::Darwin ? io.KeySuper : io.KeyCtrl; - const auto pixelSize = m_pixelsDrawer.pixelSize(common::Vec2(fbSize)); - const ImVec2 sheetSize(pixelSize.x * static_cast(m_img.columns) * TileWidth, - pixelSize.y * static_cast(m_img.rows) * TileHeight); - if (zoomMod) { - m_pixelSizeMod = ox::clamp(m_pixelSizeMod + wheel * 0.02f, 0.55f, 2.f); - m_pixelsDrawer.setPixelSizeMod(m_pixelSizeMod); - m_pixelGridDrawer.setPixelSizeMod(m_pixelSizeMod); - m_updated = true; - } else { - m_scrollOffset.y -= wheel * 0.1f; - } - // adjust scroll offset in both cases because the image can be zoomed - // or scrolled off screen - m_scrollOffset.y = ox::clamp(m_scrollOffset.y, 0.f, sheetSize.y / 2); - } - if (wheelh != 0) { - const auto pixelSize = m_pixelsDrawer.pixelSize(common::Vec2(fbSize)); - const ImVec2 sheetSize(pixelSize.x * static_cast(m_img.columns) * TileWidth, - pixelSize.y * static_cast(m_img.rows) * TileHeight); - m_scrollOffset.x += wheelh * 0.1f; - m_scrollOffset.x = ox::clamp(m_scrollOffset.x, -(sheetSize.x / 2), 0.f); - } - if (io.MouseDown[0]) { - auto clickPos = common::Vec2(io.MousePos); - const auto &winPos = ImGui::GetWindowPos(); - clickPos.x -= winPos.x + 10; - clickPos.y -= winPos.y + 10; - clickPixel(fbSize, clickPos); - } - } -} - -void TileSheetEditor::drawPalettePicker() noexcept { - // header - ImGui::BeginTable("PaletteTable", 2); - ImGui::TableSetupColumn("No.", 0, 0.35); - ImGui::TableSetupColumn("Color16", 0, 3); - ImGui::TableHeadersRow(); - for (auto i = 1; auto c : m_pal->colors) { - ImGui::TableNextColumn(); - ImGui::Text("%d", i); - ImGui::TableNextColumn(); - ImGui::Text("(%d, %d, %d)", red16(c), green16(c), blue16(c)); - ++i; - } - ImGui::EndTable(); -} - -void TileSheetEditor::clickPixel(const common::Vec2 &paneSize, const common::Vec2 &clickPos) noexcept { - auto [x, y] = clickPos; - const auto pixDrawSz = m_pixelsDrawer.pixelSize(paneSize); - x /= paneSize.x; - y /= paneSize.y; - x += -m_scrollOffset.x / 2; - y += m_scrollOffset.y / 2; - x /= pixDrawSz.x; - y /= pixDrawSz.y; - const auto pt = common::Point(static_cast(x * 2), static_cast(y * 2)); - const uint8_t palIdx = 0; - m_img.setPixel(pt, palIdx); - m_updated = true; -} - } diff --git a/src/nostalgia/core/studio/tilesheeteditor.hpp b/src/nostalgia/core/studio/tilesheeteditor.hpp index 8b153c9b..6de7790f 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.hpp +++ b/src/nostalgia/core/studio/tilesheeteditor.hpp @@ -95,12 +95,9 @@ constexpr ox::Error model(T *io, TileSheetClipboard *b) noexcept { return OxError(0); } -class TileSheetEditor: public studio::Editor { +class TileSheetEditor { private: - ox::String m_itemPath; - ox::String m_itemName; - glutils::FrameBuffer m_framebuffer; TileSheetGrid m_pixelGridDrawer; TileSheetPixels m_pixelsDrawer; bool m_updated = false; @@ -108,31 +105,38 @@ class TileSheetEditor: public studio::Editor { AssetRef m_pal; float m_pixelSizeMod = 1; common::Vec2 m_scrollOffset; - float m_palViewWidth = 200; public: TileSheetEditor(Context *ctx, const ox::String &path); - ~TileSheetEditor() override = default; + ~TileSheetEditor() = default; - ox::String itemName() const noexcept override; + void cut(); - ox::String itemDisplayName() const noexcept override; + void copy(); - void exportFile() override; + void paste(); - void cut() override; + void draw() noexcept; - void copy() override; + void clickPixel(const common::Vec2 &paneSize, const common::Vec2 &clickPos) noexcept; - void paste() override; + void scrollV(const common::Vec2 paneSz, float wheel, bool zoomMod) noexcept; - void draw(core::Context*) noexcept override; + void scrollH(const common::Vec2 paneSz, float wheel) noexcept; - void glDraw() noexcept; + void resize(const common::Vec2 &sz) noexcept; + + const NostalgiaGraphic &img() const; + + const NostalgiaPalette &pal() const; + + bool updated() const noexcept; + + void ackUpdate() noexcept; protected: - void saveItem() override; + void saveItem(); void getFillPixels(bool *pixels, common::Point pt, int oldColor) const; @@ -149,22 +153,12 @@ class TileSheetEditor: public studio::Editor { [[nodiscard]] ox::String palettePath(const ox::String &palettePath) const; - void drawTileSheet(const common::Vec2 &fbSize) noexcept; - - void drawPalettePicker() noexcept; - - void clickPixel(const common::Vec2 &paneSize, const common::Vec2 &clickPos) noexcept; - // slots public: ox::Error colorSelected() noexcept; ox::Error setColorTable() noexcept; - // slots - private: - ox::Error updateAfterClicked() noexcept; - }; }