Add "Sprite Grid" source option to Export Sprite Sheet (fix #1982)

In this way we can export each grid cell/tile as an individual sprite,
and use the extrude option on each grid cell. We've added the
--split-grid CLI option too.
This commit is contained in:
David Capello 2022-08-19 17:21:56 -03:00
parent a2f61a3378
commit acb246e26b
9 changed files with 77 additions and 23 deletions

View File

@ -540,6 +540,7 @@
<option id="frame_tag" type="std::string" />
<option id="split_layers" type="bool" default="false" />
<option id="split_tags" type="bool" default="false" />
<option id="split_grid" type="bool" default="false" />
<option id="list_layers" type="bool" default="true" />
<option id="list_frame_tags" type="bool" default="true" />
<option id="list_slices" type="bool" default="true" />

View File

@ -49,6 +49,7 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_splitLayers(m_po.add("split-layers").description("Save each visible layer of sprites\nas separated images in the sheet\n"))
, m_splitTags(m_po.add("split-tags").description("Save each tag as a separated file"))
, m_splitSlices(m_po.add("split-slices").description("Save each slice as a separated file"))
, m_splitGrid(m_po.add("split-grid").description("Save each grid tile as a separated file"))
, m_layer(m_po.add("layer").alias("import-layer").requiresValue("<name>").description("Include just the given layer in the sheet\nor save as operation"))
, m_allLayers(m_po.add("all-layers").description("Make all layers visible\nBy default hidden layers will be ignored"))
, m_ignoreLayer(m_po.add("ignore-layer").requiresValue("<name>").description("Exclude the given layer in the sheet\nor save as operation"))

View File

@ -65,6 +65,7 @@ public:
const Option& splitLayers() const { return m_splitLayers; }
const Option& splitTags() const { return m_splitTags; }
const Option& splitSlices() const { return m_splitSlices; }
const Option& splitGrid() const { return m_splitGrid; }
const Option& layer() const { return m_layer; }
const Option& allLayers() const { return m_allLayers; }
const Option& ignoreLayer() const { return m_ignoreLayer; }
@ -132,6 +133,7 @@ private:
Option& m_splitLayers;
Option& m_splitTags;
Option& m_splitSlices;
Option& m_splitGrid;
Option& m_layer;
Option& m_allLayers;
Option& m_ignoreLayer;

View File

@ -33,6 +33,7 @@ namespace app {
bool splitLayers = false;
bool splitTags = false;
bool splitSlices = false;
bool splitGrid = false;
bool allLayers = false;
bool listLayers = false;
bool listTags = false;

View File

@ -283,6 +283,10 @@ int CliProcessor::process(Context* ctx)
else if (opt == &m_options.splitSlices()) {
cof.splitSlices = true;
}
// --split-grid
else if (opt == &m_options.splitGrid()) {
cof.splitGrid = true;
}
// --layer <layer-name>
else if (opt == &m_options.layer()) {
cof.includeLayers.push_back(value.value());
@ -691,6 +695,7 @@ bool CliProcessor::openFile(Context* ctx, CliOpenFile& cof)
doc, tag,
cof.splitLayers,
cof.splitTags,
cof.splitGrid,
(cof.hasLayersFilter() ? &filteredLayers: nullptr),
(!selFrames.empty() ? &selFrames: nullptr));
}

View File

@ -67,6 +67,7 @@ enum Section {
enum Source {
kSource_Sprite,
kSource_SpriteGrid,
kSource_Tilesets,
};
@ -171,6 +172,7 @@ Doc* generate_sprite_sheet_from_params(
const bool mergeDuplicates = params.mergeDuplicates();
const bool splitLayers = params.splitLayers();
const bool splitTags = params.splitTags();
const bool splitGrid = params.splitGrid();
const bool listLayers = params.listLayers();
const bool listTags = params.listTags();
const bool listSlices = params.listSlices();
@ -204,13 +206,17 @@ Doc* generate_sprite_sheet_from_params(
}
exporter.reset();
if (fromTilesets)
// Use each tileset from tilemap layers as a sprite
if (fromTilesets) {
exporter.addTilesetsSamples(
doc,
!selLayers.empty() ? &selLayers: nullptr);
}
// Use the whole canvas as a sprite
else {
exporter.addDocumentSamples(
doc, tag, splitLayers, splitTags,
doc, tag, splitLayers, splitTags, splitGrid,
!selLayers.empty() ? &selLayers: nullptr,
!selFrames.empty() ? &selFrames: nullptr);
}
@ -358,11 +364,15 @@ public:
}
static_assert(kSource_Sprite == 0 &&
kSource_Tilesets == 1,
kSource_SpriteGrid == 1 &&
kSource_Tilesets == 2,
"Source enum has changed");
source()->addItem(new ListItem("Sprite"));
source()->addItem(new ListItem("Sprite Grid"));
source()->addItem(new ListItem("Tilesets"));
if (params.fromTilesets())
if (params.splitGrid())
source()->setSelectedItemIndex(int(kSource_SpriteGrid));
else if (params.fromTilesets())
source()->setSelectedItemIndex(int(kSource_Tilesets));
fill_layers_combobox(
@ -529,6 +539,7 @@ public:
params.listLayers (listLayersValue());
params.listTags (listTagsValue());
params.listSlices (listSlicesValue());
params.splitGrid (source()->getSelectedItemIndex() == int(kSource_SpriteGrid));
params.fromTilesets (source()->getSelectedItemIndex() == int(kSource_Tilesets));
}
@ -721,6 +732,10 @@ private:
return splitTags()->isSelected();
}
bool splitGridValue() const {
return (source()->getSelectedItemIndex() == int(kSource_SpriteGrid));
}
bool listLayersValue() const {
return listLayers()->isSelected();
}
@ -1222,6 +1237,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
if (!params.tag.isSet()) params.tag( defPref.spriteSheet.frameTag());
if (!params.splitLayers.isSet()) params.splitLayers( defPref.spriteSheet.splitLayers());
if (!params.splitTags.isSet()) params.splitTags( defPref.spriteSheet.splitTags());
if (!params.splitGrid.isSet()) params.splitGrid( defPref.spriteSheet.splitGrid());
if (!params.listLayers.isSet()) params.listLayers( defPref.spriteSheet.listLayers());
if (!params.listTags.isSet()) params.listTags( defPref.spriteSheet.listFrameTags());
if (!params.listSlices.isSet()) params.listSlices( defPref.spriteSheet.listSlices());
@ -1268,6 +1284,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
docPref.spriteSheet.frameTag (params.tag());
docPref.spriteSheet.splitLayers (params.splitLayers());
docPref.spriteSheet.splitTags (params.splitTags());
docPref.spriteSheet.splitGrid (params.splitGrid());
docPref.spriteSheet.listLayers (params.listLayers());
docPref.spriteSheet.listFrameTags (params.listTags());
docPref.spriteSheet.listSlices (params.listSlices());

View File

@ -43,6 +43,7 @@ struct ExportSpriteSheetParams : public NewParams {
Param<std::string> tag { this, std::string(), "tag" };
Param<bool> splitLayers { this, false, "splitLayers" };
Param<bool> splitTags { this, false, "splitTags" };
Param<bool> splitGrid { this, false, "splitGrid" };
Param<bool> listLayers { this, true, "listLayers" };
Param<bool> listTags { this, true, "listTags" };
Param<bool> listSlices { this, true, "listSlices" };

View File

@ -96,11 +96,13 @@ typedef std::shared_ptr<gfx::Rect> SharedRectPtr;
DocExporter::Item::Item(Doc* doc,
const doc::Tag* tag,
const doc::SelectedLayers* selLayers,
const doc::SelectedFrames* selFrames)
const doc::SelectedFrames* selFrames,
const bool splitGrid)
: doc(doc)
, tag(tag)
, selLayers(selLayers ? std::make_unique<doc::SelectedLayers>(*selLayers): nullptr)
, selFrames(selFrames ? std::make_unique<doc::SelectedFrames>(*selFrames): nullptr)
, splitGrid(splitGrid)
{
}
@ -146,7 +148,8 @@ doc::SelectedFrames DocExporter::Item::getSelectedFrames() const
class DocExporter::Sample {
public:
Sample(Doc* document,
Sample(const gfx::Size& size,
Doc* document,
Sprite* sprite,
const ImageRef& image,
SelectedLayers* selLayers,
@ -166,10 +169,9 @@ public:
m_extrude(extrude),
m_isLinked(false),
m_isDuplicated(false),
m_originalSize(image ? image->width(): sprite->width(),
image ? image->height(): sprite->height()),
m_trimmedBounds(m_originalSize),
m_inTextureBounds(std::make_shared<gfx::Rect>(m_trimmedBounds)) {
m_originalSize(size),
m_trimmedBounds(size),
m_inTextureBounds(std::make_shared<gfx::Rect>(size)) {
}
Doc* document() const { return m_document; }
@ -732,10 +734,11 @@ void DocExporter::addDocument(
Doc* doc,
const doc::Tag* tag,
const doc::SelectedLayers* selLayers,
const doc::SelectedFrames* selFrames)
const doc::SelectedFrames* selFrames,
const bool splitGrid)
{
DX_TRACE("DX: addDocument doc=", doc, "tag=", tag);
m_documents.push_back(Item(doc, tag, selLayers, selFrames));
m_documents.push_back(Item(doc, tag, selLayers, selFrames, splitGrid));
}
void DocExporter::addImage(
@ -751,6 +754,7 @@ int DocExporter::addDocumentSamples(
const doc::Tag* thisTag,
const bool splitLayers,
const bool splitTags,
const bool splitGrid,
const doc::SelectedLayers* selLayers,
const doc::SelectedFrames* selFrames)
{
@ -819,7 +823,7 @@ int DocExporter::addDocumentSamples(
SelectedLayers oneLayer;
oneLayer.insert(layer);
addDocument(doc, tag, &oneLayer, thisSelFrames);
addDocument(doc, tag, &oneLayer, thisSelFrames, splitGrid);
++items;
}
}
@ -830,13 +834,13 @@ int DocExporter::addDocumentSamples(
SelectedLayers oneLayer;
oneLayer.insert(layer);
addDocument(doc, tag, &oneLayer, thisSelFrames);
addDocument(doc, tag, &oneLayer, thisSelFrames, splitGrid);
++items;
}
}
}
else {
addDocument(doc, tag, selLayers, thisSelFrames);
addDocument(doc, tag, selLayers, thisSelFrames, splitGrid);
++items;
}
}
@ -957,6 +961,9 @@ void DocExporter::captureSamples(Samples& samples,
std::string filename = filename_formatter(format, fnInfo);
Sample sample(
(item.image ? item.image->size():
item.splitGrid ? sprite->gridBounds().size():
sprite->size()),
doc, sprite, item.image, item.selLayers.get(),
frame, innerTag, filename,
m_innerPadding, m_extrude);
@ -1063,7 +1070,23 @@ void DocExporter::captureSamples(Samples& samples,
if (!alreadyTrimmed && m_trimSprite)
sample.setTrimmedBounds(spriteBounds);
samples.addSample(sample);
if (item.splitGrid) {
const gfx::Rect& gridBounds = sprite->gridBounds();
gfx::Point initPos(0, 0), pos;
initPos = pos = snap_to_grid(gridBounds, initPos, PreferSnapTo::BoxOrigin);
for (; pos.y+gridBounds.h <= spriteBounds.h; pos.y+=gridBounds.h) {
for (pos.x=initPos.x; pos.x+gridBounds.w <= spriteBounds.w; pos.x+=gridBounds.w) {
const gfx::Rect cellBounds(pos, gridBounds.size());
sample.setTrimmedBounds(cellBounds);
sample.setSharedBounds(std::make_shared<gfx::Rect>(sample.inTextureBounds()));
samples.addSample(sample);
}
}
}
else {
samples.addSample(sample);
}
DX_TRACE("DX: - Sample:",
sample.document()->filename(),

View File

@ -77,12 +77,6 @@ namespace app {
void setListLayers(bool value) { m_listLayers = value; }
void setListSlices(bool value) { m_listSlices = value; }
void addDocument(
Doc* doc,
const doc::Tag* tag,
const doc::SelectedLayers* selLayers,
const doc::SelectedFrames* selFrames);
void addImage(
Doc* doc,
const doc::ImageRef& image);
@ -92,6 +86,7 @@ namespace app {
const doc::Tag* tag,
const bool splitLayers,
const bool splitTags,
const bool splitGrid,
const doc::SelectedLayers* selLayers,
const doc::SelectedFrames* selFrames);
@ -109,6 +104,12 @@ namespace app {
class SimpleLayoutSamples;
class BestFitLayoutSamples;
void addDocument(
Doc* doc,
const doc::Tag* tag,
const doc::SelectedLayers* selLayers,
const doc::SelectedFrames* selFrames,
const bool splitGrid);
void captureSamples(Samples& samples,
base::task_token& token);
void layoutSamples(Samples& samples,
@ -130,12 +131,14 @@ namespace app {
const doc::Tag* tag = nullptr;
std::unique_ptr<doc::SelectedLayers> selLayers;
std::unique_ptr<doc::SelectedFrames> selFrames;
bool splitGrid = false;
doc::ImageRef image;
Item(Doc* doc,
const doc::Tag* tag,
const doc::SelectedLayers* selLayers,
const doc::SelectedFrames* selFrames);
const doc::SelectedFrames* selFrames,
const bool splitGrid);
Item(Doc* doc,
const doc::ImageRef& image);
Item(Item&& other);