Squashed 'deps/nostalgia/' changes from 791b7746..a3d6a58c
a3d6a58c [nostalgia/core/studio] Fix library cpp file ownership e598e7fe [nostalgia,keel] Add ability to types Obj to Obj ba9e720f [ox/model] Fix ModelTypeName_v to use requireModelTypeName 8e816a26 [nostalgia/core/studio] Cleanup, fix possible TileSheet fill tool failure 5b9929ab [keel] Add detail to preload logging ceb54b3f [nostalgia/core/opengl] Cleanup 87644447 [nostalgia/core] Add clearCbb functions ce9a0b1f [nostalgia/core/opengl] Cleanup memcpys f7a468ea [ox/std] Add spancpy 861d177a [studio] Cleanup 3936756b [nostalgia/developer-handbook] Update error handling to reflect the enablement of exceptions for GBA build 3e78ec3f [studio] Cleanup 3c3d53b4 [studio] Ensure Editor tabs do first draw immediately, fix shift key being missed with tab shortcuts 151d7c57 [nostalgia/core/gba] Fix partial tilesheet loading overrun 4e4d8d2c [nostalgia/core/gba] Make panic use standard abort call 03d1fd28 [ox/std] Add and integrate standard abort call 6701decc [gbabuildcore] Enable exceptions 6cff5266 [teagba] Add symbols needed for enabling exceptions dd50bd02 [studio] Remap toggle explorer keyboard shortcut, add Ctrl+1-0 mappings for jumping between tabs 55a16602 [nostalgia/core] Fix TileSheet validation/repair to ensure pixels gets cleared if there are subsheets ed365dfe [studio] Fix new project menu to return an appropriately sized string for name 23a09e4a [nostalgia/core/studio] Fix SubSheet editor to return an appropriately sized string b69e7ebb [nostalgia/core/studio/tilesheeteditor] Fix select all not to go beyond end 418d6e3f [nostalgia/core/studio] Fix crash that occurs when a non-leaf node subsheet is selected c44d8678 [nostalgia/core/studio] Fix tile insert to correct input when inserting past the last tile eb4cd710 [nostalgia/core/studio] Fix tile insert to work on last tile d259770f Merge commit '4ea4a61d542777a270c4e2c283e0e986fc9eec9c' 80bad608 [keel] Fix reloadAsset 2bce9a2b [ox/std] Add non-const SmallMap::pairs git-subtree-dir: deps/nostalgia git-subtree-split: a3d6a58cc898f88434e8901aacb579c819fac3e6
This commit is contained in:
@ -142,6 +142,10 @@ ox::Error loadBgTileSheet(
|
||||
unsigned cbb,
|
||||
TileSheetSet const&set) noexcept;
|
||||
|
||||
void clearCbb(Context &ctx, unsigned cbb) noexcept;
|
||||
|
||||
void clearCbbs(Context &ctx) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
|
11
src/nostalgia/modules/core/include/nostalgia/core/studio.hpp
Normal file
11
src/nostalgia/modules/core/include/nostalgia/core/studio.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
}
|
@ -18,6 +18,23 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
struct SubSheetTemplate {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.SubSheetTemplate";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String name;
|
||||
int32_t width{};
|
||||
int32_t height{};
|
||||
ox::Vector<SubSheetTemplate> subsheets;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(SubSheetTemplate)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_FIELD(width)
|
||||
OX_MODEL_FIELD(height)
|
||||
OX_MODEL_FIELD(subsheets)
|
||||
OX_MODEL_END()
|
||||
|
||||
|
||||
// Predecessor to TileSheet, kept for backward compatibility
|
||||
struct TileSheetV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
|
||||
@ -237,10 +254,12 @@ struct TileSheetV4 {
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV4::SubSheet const&ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
return ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
|
||||
[bpp, bytes](TileSheetV4::SubSheet const&s) {
|
||||
return bytes == s.pixels.size() && valid(s, bpp);
|
||||
});
|
||||
return
|
||||
(ss.pixels.empty() || ss.subsheets.empty()) &&
|
||||
ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
|
||||
[bpp, bytes](TileSheetV4::SubSheet const&s) {
|
||||
return bytes == s.pixels.size() && valid(s, bpp);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
@ -249,8 +268,14 @@ constexpr bool valid(TileSheetV4 const&ts) noexcept {
|
||||
}
|
||||
|
||||
constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ss.pixels.resize(bytes);
|
||||
if (ss.subsheets.empty()) {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ss.pixels.resize(bytes);
|
||||
} else {
|
||||
ss.pixels.clear();
|
||||
ss.columns = -1;
|
||||
ss.rows = -1;
|
||||
}
|
||||
for (auto &s : ss.subsheets) {
|
||||
repair(s, bpp);
|
||||
}
|
||||
|
@ -63,6 +63,19 @@ ox::Error loadSpritePalette(
|
||||
return {};
|
||||
}
|
||||
|
||||
void clearCbb(Context&, unsigned const cbb) noexcept {
|
||||
for (auto &v : MEM_BG_TILES[cbb]) {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void clearCbbs(Context &ctx) noexcept {
|
||||
clearCbb(ctx, 0);
|
||||
clearCbb(ctx, 1);
|
||||
clearCbb(ctx, 2);
|
||||
clearCbb(ctx, 3);
|
||||
}
|
||||
|
||||
static ox::Error loadTileSheetSet(
|
||||
Context &ctx,
|
||||
ox::Span<uint16_t> tileMapTargetMem,
|
||||
@ -99,10 +112,10 @@ ox::Error loadBgTileSheet(
|
||||
size_t const tileCnt) noexcept {
|
||||
size_t const bppMod = ts.bpp == 4;
|
||||
size_t const bytesPerTile = PixelsPerTile >> bppMod;
|
||||
auto const pixCnt = tileCnt * bytesPerTile;
|
||||
auto const cnt = (tileCnt * bytesPerTile) / 2;
|
||||
auto const srcPxIdx = srcTileIdx * bytesPerTile;
|
||||
auto const dstPxIdx = (dstTileIdx * bytesPerTile) / 2;
|
||||
for (size_t i = 0; i < pixCnt; ++i) {
|
||||
for (size_t i = 0; i < cnt; ++i) {
|
||||
auto const srcIdx = srcPxIdx + i * 2;
|
||||
auto const p1 = static_cast<uint16_t>(ts.pixels[srcIdx]);
|
||||
auto const p2 = static_cast<uint16_t>(ts.pixels[srcIdx + 1]);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
|
||||
#include <ox/std/def.hpp>
|
||||
#include <ox/std/realstd.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
@ -51,9 +52,7 @@ OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
if (err.src.file_name() != nullptr) {
|
||||
oxErrf("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line());
|
||||
}
|
||||
// disable all interrupt handling and IntrWait on no interrupts
|
||||
REG_IE = 0;
|
||||
teagba::intrwait(0, 0);
|
||||
abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ static class: public keel::Module {
|
||||
ox::Vector<keel::PackTransform> packTransforms() const noexcept final {
|
||||
return {
|
||||
// convert tilesheets to CompactTileSheets
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> {
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) -> ox::Result<bool> {
|
||||
if (typeId == ox::ModelTypeId_v<TileSheetV1> ||
|
||||
typeId == ox::ModelTypeId_v<TileSheetV2> ||
|
||||
typeId == ox::ModelTypeId_v<TileSheetV3> ||
|
||||
@ -78,7 +78,7 @@ static class: public keel::Module {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> {
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) -> ox::Result<bool> {
|
||||
if (typeId == ox::ModelTypeId_v<NostalgiaPalette> ||
|
||||
typeId == ox::ModelTypeId_v<PaletteV1> ||
|
||||
typeId == ox::ModelTypeId_v<PaletteV2> ||
|
||||
|
@ -111,15 +111,15 @@ static constexpr auto bgVertexRow(uint_t x, uint_t y) noexcept {
|
||||
}
|
||||
|
||||
static void setSpriteBufferObject(
|
||||
uint_t vi,
|
||||
float enabled,
|
||||
uint_t const vi,
|
||||
float const enabled,
|
||||
float x,
|
||||
float y,
|
||||
uint_t textureRow,
|
||||
uint_t flipX,
|
||||
uint_t priority,
|
||||
float *vbo,
|
||||
GLuint *ebo) noexcept {
|
||||
uint_t const textureRow,
|
||||
uint_t const flipX,
|
||||
uint_t const priority,
|
||||
ox::Span<float> const vbo,
|
||||
ox::Span<GLuint> const ebo) noexcept {
|
||||
// don't worry, this memcpy gets optimized to something much more ideal
|
||||
constexpr float xmod = 0.1f;
|
||||
constexpr float ymod = 0.1f;
|
||||
@ -138,25 +138,25 @@ static void setSpriteBufferObject(
|
||||
enabled, x + xmod, y + ymod, prif, R, textureRowf + 0, // top right
|
||||
enabled, x, y + ymod, prif, L, textureRowf + 0, // top left
|
||||
};
|
||||
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||
ox::spancpy<float>(vbo, vertices);
|
||||
ox::Array<GLuint, SpriteVertexEboLength> const elms {
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
};
|
||||
memcpy(ebo, elms.data(), sizeof(elms));
|
||||
ox::spancpy<GLuint>(ebo, elms);
|
||||
}
|
||||
|
||||
static void setTileBufferObject(
|
||||
uint_t vi,
|
||||
uint_t const vi,
|
||||
float x,
|
||||
float y,
|
||||
float textureTileIdx,
|
||||
float priority,
|
||||
float palOffset,
|
||||
bool flipX,
|
||||
bool flipY,
|
||||
float *vbo,
|
||||
GLuint *ebo) noexcept {
|
||||
float const textureTileIdx,
|
||||
float const priority,
|
||||
float const palOffset,
|
||||
bool const flipX,
|
||||
bool const flipY,
|
||||
ox::Span<float> const vbo,
|
||||
ox::Span<GLuint> const ebo) noexcept {
|
||||
// don't worry, this memcpy gets optimized to something much more ideal
|
||||
constexpr float ymod = 0.1f;
|
||||
constexpr float xmod = 0.1f;
|
||||
@ -170,24 +170,35 @@ static void setTileBufferObject(
|
||||
float const T = flipY ? 1 : 0;
|
||||
float const B = flipY ? 0 : 1;
|
||||
ox::Array<float, BgVertexVboLength> const vertices {
|
||||
x, y, prif, L, B, textureTileIdx, palOffset, // bottom left
|
||||
x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right
|
||||
x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right
|
||||
x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left
|
||||
x, y, prif, L, B, textureTileIdx, palOffset, // bottom left
|
||||
x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right
|
||||
x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right
|
||||
x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left
|
||||
};
|
||||
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||
ox::spancpy<float>(vbo, vertices);
|
||||
ox::Array<GLuint, BgVertexEboLength> const elms {
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
};
|
||||
memcpy(ebo, elms.data(), sizeof(elms));
|
||||
ox::spancpy<GLuint>(ebo, elms);
|
||||
}
|
||||
|
||||
static void initSpriteBufferObjects(Context &ctx, glutils::BufferSet &bs) noexcept {
|
||||
for (auto i = 0u; i < ctx.spriteCount; ++i) {
|
||||
auto vbo = &bs.vertices[i * static_cast<std::size_t>(SpriteVertexVboLength)];
|
||||
auto ebo = &bs.elements[i * static_cast<std::size_t>(SpriteVertexEboLength)];
|
||||
setSpriteBufferObject(i * static_cast<uint_t>(SpriteVertexVboRows) * ctx.blocksPerSprite, 0, 0, 0, 0, false, 0, vbo, ebo);
|
||||
auto const vbo = ox::Span{bs.vertices}
|
||||
+ i * static_cast<std::size_t>(SpriteVertexVboLength);
|
||||
auto const ebo = ox::Span{bs.elements}
|
||||
+ i * static_cast<std::size_t>(SpriteVertexEboLength);
|
||||
setSpriteBufferObject(
|
||||
i * static_cast<uint_t>(SpriteVertexVboRows) * ctx.blocksPerSprite,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
0,
|
||||
vbo,
|
||||
ebo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,8 +206,10 @@ static void initBackgroundBufferObjects(glutils::BufferSet &bs) noexcept {
|
||||
for (auto x = 0u; x < TileColumns; ++x) {
|
||||
for (auto y = 0u; y < TileRows; ++y) {
|
||||
const auto i = bgVertexRow(x, y);
|
||||
auto vbo = &bs.vertices[i * static_cast<std::size_t>(BgVertexVboLength)];
|
||||
auto ebo = &bs.elements[i * static_cast<std::size_t>(BgVertexEboLength)];
|
||||
auto const vbo = ox::Span{bs.vertices}
|
||||
+ i * static_cast<std::size_t>(BgVertexVboLength);
|
||||
auto const ebo = ox::Span{bs.elements}
|
||||
+ i * static_cast<std::size_t>(BgVertexEboLength);
|
||||
setTileBufferObject(
|
||||
static_cast<uint_t>(i * BgVertexVboRows),
|
||||
static_cast<float>(x),
|
||||
@ -421,8 +434,8 @@ static void setSprite(
|
||||
auto const eboIdx = eboBase + renderer::SpriteVertexEboLength * i;
|
||||
oxAssert(vboIdx < ctx.spriteBlocks.vertices.size(), "vbo overflow");
|
||||
oxAssert(eboIdx < ctx.spriteBlocks.elements.size(), "ebo overflow");
|
||||
auto const vbo = &ctx.spriteBlocks.vertices[vboIdx];
|
||||
auto const ebo = &ctx.spriteBlocks.elements[eboIdx];
|
||||
auto const vbo = ox::Span{ctx.spriteBlocks.vertices} + vboIdx;
|
||||
auto const ebo = ox::Span{ctx.spriteBlocks.elements} + eboIdx;
|
||||
renderer::setSpriteBufferObject(
|
||||
static_cast<uint_t>(vboIdx),
|
||||
enabled,
|
||||
@ -556,7 +569,7 @@ static void copyPixels(
|
||||
CompactTileSheet const&ts,
|
||||
ox::Span<uint32_t> dst,
|
||||
size_t const srcPxIdx,
|
||||
size_t pxlCnt) noexcept {
|
||||
size_t const pxlCnt) noexcept {
|
||||
size_t idx{};
|
||||
if (ts.bpp == 4) {
|
||||
for (size_t i = 0; i < pxlCnt; i += 2) {
|
||||
@ -573,6 +586,18 @@ static void copyPixels(
|
||||
}
|
||||
}
|
||||
|
||||
void clearCbb(Context &ctx, unsigned const cbb) noexcept {
|
||||
for (auto &v : ctx.cbbs[cbb].pixels) {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void clearCbbs(Context &ctx) noexcept {
|
||||
for (unsigned i = 0 ; i < ctx.cbbs.size(); ++i) {
|
||||
clearCbb(ctx, i);
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned const cbb,
|
||||
@ -656,8 +681,8 @@ void setBgTile(
|
||||
const auto x = static_cast<uint_t>(column);
|
||||
const auto i = renderer::bgVertexRow(x, y);
|
||||
auto &cbb = ctx.cbbs[z];
|
||||
const auto vbo = &cbb.vertices[i * renderer::BgVertexVboLength];
|
||||
const auto ebo = &cbb.elements[i * renderer::BgVertexEboLength];
|
||||
const auto vbo = ox::Span{cbb.vertices} + i * renderer::BgVertexVboLength;
|
||||
const auto ebo = ox::Span{cbb.elements} + i * renderer::BgVertexEboLength;
|
||||
auto &bg = ctx.backgrounds[bgIdx];
|
||||
renderer::setTileBufferObject(
|
||||
static_cast<uint_t>(i * renderer::BgVertexVboRows),
|
||||
|
@ -3,7 +3,6 @@ add_library(NostalgiaCore-Studio)
|
||||
add_library(
|
||||
NostalgiaCore-Studio-ImGui
|
||||
studiomodule.cpp
|
||||
tilesheeteditor/tilesheeteditor-imgui.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
@ -15,7 +14,6 @@ target_link_libraries(
|
||||
target_link_libraries(
|
||||
NostalgiaCore-Studio-ImGui PUBLIC
|
||||
NostalgiaCore-Studio
|
||||
Studio
|
||||
)
|
||||
|
||||
install(
|
||||
|
@ -9,5 +9,9 @@ target_sources(
|
||||
commands/renamepagecommand.cpp
|
||||
commands/updatecolorcommand.cpp
|
||||
commands/updatecolorinfocommand.cpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
NostalgiaCore-Studio-ImGui PRIVATE
|
||||
paletteeditor-imgui.cpp
|
||||
)
|
||||
|
@ -6,6 +6,11 @@ target_sources(
|
||||
tilesheetpixels.cpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
NostalgiaCore-Studio-ImGui PRIVATE
|
||||
tilesheeteditor-imgui.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
NostalgiaCore-Studio-ImGui PUBLIC
|
||||
lodepng
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
core::InsertTilesCommand::InsertTilesCommand(
|
||||
InsertTilesCommand::InsertTilesCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx idx,
|
||||
std::size_t tileIdx,
|
||||
@ -31,9 +31,11 @@ ox::Error InsertTilesCommand::redo() noexcept {
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto dstPos = m_insertPos + m_insertCnt;
|
||||
auto const dst = &p[dstPos];
|
||||
auto const src = &p[m_insertPos];
|
||||
ox::memmove(dst, src, p.size() - dstPos);
|
||||
if (dstPos < p.size()) {
|
||||
auto const dst = &p[dstPos];
|
||||
ox::memmove(dst, src, p.size() - dstPos);
|
||||
}
|
||||
ox::memset(src, 0, m_insertCnt * sizeof(decltype(p[0])));
|
||||
return {};
|
||||
}
|
||||
@ -41,12 +43,14 @@ ox::Error InsertTilesCommand::redo() noexcept {
|
||||
ox::Error InsertTilesCommand::undo() noexcept {
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto const srcIdx = m_insertPos + m_insertCnt;
|
||||
auto const src = &p[srcIdx];
|
||||
auto const dst1 = &p[m_insertPos];
|
||||
auto const dst2 = &p[p.size() - m_insertCnt];
|
||||
auto const sz = p.size() - srcIdx;
|
||||
ox::memmove(dst1, src, sz);
|
||||
auto const srcIdx = m_insertPos + m_insertCnt;
|
||||
if (srcIdx < p.size()) {
|
||||
auto const sz = p.size() - srcIdx;
|
||||
auto const src = &p[srcIdx];
|
||||
ox::memmove(dst1, src, sz);
|
||||
}
|
||||
ox::memcpy(dst2, m_deletedPixels.data(), m_deletedPixels.size());
|
||||
return {};
|
||||
}
|
||||
|
@ -9,27 +9,24 @@ namespace nostalgia::core {
|
||||
core::UpdateSubSheetCommand::UpdateSubSheetCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx idx,
|
||||
ox::String name,
|
||||
int cols,
|
||||
int rows) noexcept:
|
||||
m_img(img),
|
||||
m_idx(std::move(idx)),
|
||||
m_sheet(getSubSheet(m_img, m_idx)),
|
||||
m_newName(std::move(name)),
|
||||
m_newCols(cols),
|
||||
m_newRows(rows) {
|
||||
ox::StringParam name,
|
||||
int const cols,
|
||||
int const rows):
|
||||
m_img{img},
|
||||
m_idx{std::move(idx)},
|
||||
m_sheet{getSubSheet(m_img, m_idx)} {
|
||||
m_sheet = getSubSheet(m_img, m_idx);
|
||||
m_sheet.name = std::move(name);
|
||||
OX_THROW_ERROR(resizeSubsheet(m_sheet, m_img.bpp, {cols, rows}));
|
||||
}
|
||||
|
||||
ox::Error UpdateSubSheetCommand::redo() noexcept {
|
||||
auto &sheet = getSubSheet(m_img, m_idx);
|
||||
sheet.name = m_newName;
|
||||
oxLogError(resizeSubsheet(sheet, m_img.bpp, {m_newCols, m_newRows}));
|
||||
std::swap(m_sheet, getSubSheet(m_img, m_idx));
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error UpdateSubSheetCommand::undo() noexcept {
|
||||
auto &sheet = getSubSheet(m_img, m_idx);
|
||||
sheet = m_sheet;
|
||||
std::swap(m_sheet, getSubSheet(m_img, m_idx));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -13,17 +13,14 @@ class UpdateSubSheetCommand: public TileSheetCommand {
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_idx;
|
||||
TileSheet::SubSheet m_sheet;
|
||||
ox::String m_newName;
|
||||
int m_newCols = 0;
|
||||
int m_newRows = 0;
|
||||
|
||||
public:
|
||||
UpdateSubSheetCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx idx,
|
||||
ox::String name,
|
||||
ox::StringParam name,
|
||||
int cols,
|
||||
int rows) noexcept;
|
||||
int rows);
|
||||
|
||||
ox::Error redo() noexcept final;
|
||||
|
||||
|
@ -188,7 +188,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
||||
if (ImGui::IsKeyDown(ImGuiKey_ModCtrl)) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
||||
auto const&img = m_model.activeSubSheet();
|
||||
m_model.setSelection({{}, {img.columns * TileWidth, img.rows * TileHeight}});
|
||||
m_model.setSelection({{}, {img.columns * TileWidth - 1, img.rows * TileHeight - 1}});
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_G)) {
|
||||
m_model.clearSelection();
|
||||
}
|
||||
@ -544,7 +544,7 @@ void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept
|
||||
auto const popupHeight = modSize ? 130.f : 85.f;
|
||||
auto const popupSz = ImVec2{popupWidth, popupHeight};
|
||||
if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
|
||||
ImGui::InputText("Name", m_name.data(), m_name.cap());
|
||||
ig::InputText("Name", m_name);
|
||||
if (modSize) {
|
||||
ImGui::InputInt("Columns", &m_cols);
|
||||
ImGui::InputInt("Rows", &m_rows);
|
||||
|
@ -26,11 +26,6 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
Palette const TileSheetEditorModel::s_defaultPalette = {
|
||||
.colorNames = {ox::Vector<ox::String>{{}}},
|
||||
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
|
||||
};
|
||||
|
||||
// delete pixels of all non-leaf nodes
|
||||
static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
|
||||
if (ss.subsheets.empty()) {
|
||||
@ -42,7 +37,14 @@ static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
TileSheetEditorModel::TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack):
|
||||
|
||||
Palette const TileSheetEditorModel::s_defaultPalette = {
|
||||
.colorNames = {ox::Vector<ox::String>{{}}},
|
||||
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
|
||||
};
|
||||
|
||||
TileSheetEditorModel::TileSheetEditorModel(
|
||||
studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack):
|
||||
m_sctx(sctx),
|
||||
m_tctx(m_sctx.tctx),
|
||||
m_path(path),
|
||||
@ -62,7 +64,7 @@ void TileSheetEditorModel::cut() {
|
||||
TileSheetClipboard blankCb;
|
||||
auto cb = ox::make_unique<TileSheetClipboard>();
|
||||
auto const&s = activeSubSheet();
|
||||
iterateSelectionRows(*m_selection, [&](int x, int y) {
|
||||
iterateSelectionRows(*m_selection, [&](int const x, int const y) {
|
||||
auto pt = ox::Point{x, y};
|
||||
auto const idx = core::idx(s, pt);
|
||||
auto const c = getPixel(s, m_img.bpp, idx);
|
||||
@ -73,7 +75,8 @@ void TileSheetEditorModel::cut() {
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
turbine::setClipboardObject(m_tctx, std::move(cb));
|
||||
pushCommand(ox::make<CutPasteCommand>(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
|
||||
pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::copy() {
|
||||
@ -81,7 +84,7 @@ void TileSheetEditorModel::copy() {
|
||||
return;
|
||||
}
|
||||
auto cb = ox::make_unique<TileSheetClipboard>();
|
||||
iterateSelectionRows(*m_selection, [&](int x, int y) {
|
||||
iterateSelectionRows(*m_selection, [&](int const x, int const y) {
|
||||
auto pt = ox::Point{x, y};
|
||||
const auto&s = activeSubSheet();
|
||||
const auto idx = core::idx(s, pt);
|
||||
@ -105,7 +108,8 @@ void TileSheetEditorModel::paste() {
|
||||
auto const&s = activeSubSheet();
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
pushCommand(ox::make<CutPasteCommand>(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
|
||||
pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept {
|
||||
@ -120,8 +124,8 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
}
|
||||
constexpr ox::StringView uuidPrefix = "uuid://";
|
||||
if (ox::beginsWith(path, uuidPrefix)) {
|
||||
auto uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes());
|
||||
auto out = keelCtx(m_tctx).uuidToPath.at(uuid);
|
||||
auto const uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes());
|
||||
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid);
|
||||
if (out.error) {
|
||||
return {};
|
||||
}
|
||||
@ -131,13 +135,14 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept {
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept {
|
||||
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
|
||||
pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
pushCommand(ox::make<PaletteChangeCommand>(
|
||||
activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
return {};
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::setPalettePage(size_t pg) noexcept {
|
||||
void TileSheetEditorModel::setPalettePage(size_t const pg) noexcept {
|
||||
m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1);
|
||||
m_updated = true;
|
||||
}
|
||||
@ -146,7 +151,7 @@ size_t TileSheetEditorModel::palettePage() const noexcept {
|
||||
return m_palettePage;
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept {
|
||||
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const palIdx) noexcept {
|
||||
const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
|
||||
return;
|
||||
@ -155,7 +160,8 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) n
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
||||
} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
pushCommand(ox::make<DrawCommand>(
|
||||
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,16 +177,20 @@ void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept
|
||||
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
|
||||
void TileSheetEditorModel::insertTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
|
||||
void TileSheetEditorModel::deleteTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept {
|
||||
pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows));
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(
|
||||
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept {
|
||||
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
|
||||
pushCommand(cmd);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -189,7 +199,7 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n
|
||||
this->activeSubsheetChanged.emit(m_activeSubsSheetIdx);
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
|
||||
void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
|
||||
auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
// build idx list
|
||||
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
|
||||
@ -197,10 +207,10 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
|
||||
}
|
||||
ox::Array<bool, PixelsPerTile> updateMap = {};
|
||||
auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt);
|
||||
getFillPixels(updateMap, pt, oldColor);
|
||||
getFillPixels(activeSubSheet, updateMap, pt, oldColor);
|
||||
ox::Vector<std::size_t> idxList;
|
||||
auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile;
|
||||
for (auto u : updateMap) {
|
||||
for (auto const u : updateMap) {
|
||||
if (u) {
|
||||
idxList.emplace_back(i);
|
||||
}
|
||||
@ -230,7 +240,7 @@ void TileSheetEditorModel::completeSelection() noexcept {
|
||||
m_selTracker.finishSelection();
|
||||
m_selection.emplace(m_selTracker.selection());
|
||||
auto&pt = m_selection->b;
|
||||
auto&s = activeSubSheet();
|
||||
auto const&s = activeSubSheet();
|
||||
pt.x = ox::min(s.columns * TileWidth - 1, pt.x);
|
||||
pt.y = ox::min(s.rows * TileHeight - 1, pt.y);
|
||||
}
|
||||
@ -275,47 +285,44 @@ ox::Error TileSheetEditorModel::saveFile() noexcept {
|
||||
return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal);
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept {
|
||||
bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept {
|
||||
auto const&s = activeSubSheet();
|
||||
auto const pt = idxToPt(static_cast<int>(idx), s.columns);
|
||||
return m_selection && m_selection->contains(pt);
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept {
|
||||
const auto &activeSubSheet = this->activeSubSheet();
|
||||
const auto tileIdx = [activeSubSheet](const ox::Point &pt) noexcept {
|
||||
return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile;
|
||||
};
|
||||
// get points
|
||||
const auto leftPt = pt + ox::Point(-1, 0);
|
||||
const auto rightPt = pt + ox::Point(1, 0);
|
||||
const auto topPt = pt + ox::Point(0, -1);
|
||||
const auto bottomPt = pt + ox::Point(0, 1);
|
||||
// calculate indices
|
||||
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);
|
||||
void TileSheetEditorModel::getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
ox::Point const&pt,
|
||||
int const oldColor) const noexcept {
|
||||
auto const idx = ptToIdx(pt, activeSubSheet.columns);
|
||||
auto const relIdx = idx % PixelsPerTile;
|
||||
if (pixels[relIdx] || getPixel(activeSubSheet, m_img.bpp, idx) != oldColor) {
|
||||
return;
|
||||
}
|
||||
// mark pixels to update
|
||||
pixels[idx % PixelsPerTile] = true;
|
||||
if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) {
|
||||
getFillPixels(pixels, leftPt, oldColor);
|
||||
pixels[relIdx] = true;
|
||||
if (pt.x % TileWidth != 0) {
|
||||
auto const leftPt = pt + ox::Point{-1, 0};
|
||||
getFillPixels(activeSubSheet, pixels, leftPt, oldColor);
|
||||
}
|
||||
if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) {
|
||||
getFillPixels(pixels, rightPt, oldColor);
|
||||
if (pt.x % TileWidth != TileWidth - 1) {
|
||||
auto const rightPt = pt + ox::Point{1, 0};
|
||||
getFillPixels(activeSubSheet, pixels, rightPt, oldColor);
|
||||
}
|
||||
if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) {
|
||||
getFillPixels(pixels, topPt, oldColor);
|
||||
if (pt.y % TileHeight != 0) {
|
||||
auto const topPt = pt + ox::Point{0, -1};
|
||||
getFillPixels(activeSubSheet, pixels, topPt, oldColor);
|
||||
}
|
||||
if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) {
|
||||
getFillPixels(pixels, bottomPt, oldColor);
|
||||
if (pt.y % TileHeight != TileHeight - 1) {
|
||||
auto const bottomPt = pt + ox::Point{0, 1};
|
||||
getFillPixels(activeSubSheet, pixels, bottomPt, oldColor);
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
|
||||
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>(cmd));
|
||||
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd});
|
||||
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
|
||||
m_updated = true;
|
||||
}
|
||||
|
@ -4,9 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/bounds.hpp>
|
||||
#include <ox/std/point.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
@ -38,7 +36,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
bool m_updated = false;
|
||||
|
||||
public:
|
||||
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack);
|
||||
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack);
|
||||
|
||||
~TileSheetEditorModel() override = default;
|
||||
|
||||
@ -63,7 +61,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
[[nodiscard]]
|
||||
ox::StringView palPath() const noexcept;
|
||||
|
||||
ox::Error setPalette(ox::StringView path) noexcept;
|
||||
ox::Error setPalette(ox::StringViewCR path) noexcept;
|
||||
|
||||
void setPalettePage(size_t pg) noexcept;
|
||||
|
||||
@ -128,7 +126,11 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
bool pixelSelected(std::size_t idx) const noexcept;
|
||||
|
||||
private:
|
||||
void getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept;
|
||||
void getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
ox::Point const&pt,
|
||||
int oldColor) const noexcept;
|
||||
|
||||
void pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
|
||||
|
@ -56,8 +56,10 @@ void TileSheetEditorView::scrollH(ox::Vec2 const&paneSz, float wheelh) noexcept
|
||||
}
|
||||
|
||||
void TileSheetEditorView::insertTile(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept {
|
||||
auto const pt = clickPoint(paneSize, clickPos);
|
||||
auto pt = clickPoint(paneSize, clickPos);
|
||||
auto const&s = m_model.activeSubSheet();
|
||||
pt.x = ox::min(pt.x, s.columns * TileWidth - 1);
|
||||
pt.y = ox::min(pt.y, s.rows * TileHeight - 1);
|
||||
auto const tileIdx = ptToIdx(pt, s.columns) / PixelsPerTile;
|
||||
m_model.insertTiles(m_model.activeSubSheetIdx(), tileIdx, 1);
|
||||
}
|
||||
|
@ -80,6 +80,11 @@ void TileSheetGrid::setBufferObject(
|
||||
}
|
||||
|
||||
void TileSheetGrid::setBufferObjects(ox::Vec2 const&paneSize, TileSheet::SubSheet const&subsheet) noexcept {
|
||||
if (subsheet.columns < 1 || subsheet.rows < 1) {
|
||||
m_bufferSet.elements.clear();
|
||||
m_bufferSet.vertices.clear();
|
||||
return;
|
||||
}
|
||||
auto const pixSize = pixelSize(paneSize);
|
||||
auto const set = [&](std::size_t i, ox::Point pt1, ox::Point pt2, Color32 c) {
|
||||
auto const vbo = &m_bufferSet.vertices[i * VertexVboLength];
|
||||
|
@ -51,7 +51,7 @@ ox::Error TileSheetPixels::buildShader() noexcept {
|
||||
return glutils::buildShaderProgram(s_programSrc).moveTo(m_shader);
|
||||
}
|
||||
|
||||
void TileSheetPixels::draw(bool update, ox::Vec2 const&scroll) noexcept {
|
||||
void TileSheetPixels::draw(bool const update, ox::Vec2 const&scroll) noexcept {
|
||||
glUseProgram(m_shader);
|
||||
glBindVertexArray(m_bufferSet.vao);
|
||||
if (update) {
|
||||
@ -117,6 +117,11 @@ void TileSheetPixels::setPixelBufferObject(
|
||||
void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
|
||||
// set buffer lengths
|
||||
auto const&subSheet = m_model.activeSubSheet();
|
||||
if (subSheet.columns < 1 || subSheet.rows < 1) {
|
||||
m_bufferSet.vertices.clear();
|
||||
m_bufferSet.elements.clear();
|
||||
return;
|
||||
}
|
||||
auto const&pal = m_model.pal();
|
||||
auto const width = subSheet.columns * TileWidth;
|
||||
auto const height = subSheet.rows * TileHeight;
|
||||
|
Reference in New Issue
Block a user