gai-ca2/project/assets/tilemap/export.lua

303 lines
7.9 KiB
Lua

-- export.lua
-- Copyright (C) 2020 David Capello
--
-- This file is released under the terms of the MIT license.
local spr = app.sprite
if not spr then spr = app.activeSprite end -- just to support older versions of Aseprite
if not spr then return print "No active sprite" end
if ColorMode.TILEMAP == nil then ColorMode.TILEMAP = 4 end
assert(ColorMode.TILEMAP == 4)
local fs = app.fs
local pc = app.pixelColor
local output_folder = fs.joinPath(app.fs.filePath(spr.filename), fs.fileTitle(spr.filename))
local image_n = 0
local tileset_n = 0
local function write_json_data(filename, data)
local json = dofile('./json.lua')
local file = io.open(filename, "w")
file:write(json.encode(data))
file:close()
end
local function fill_user_data(t, obj)
if obj.color.alpha > 0 then
if obj.color.alpha == 255 then
t.color = string.format("#%02x%02x%02x",
obj.color.red,
obj.color.green,
obj.color.blue)
else
t.color = string.format("#%02x%02x%02x%02x",
obj.color.red,
obj.color.green,
obj.color.blue,
obj.color.alpha)
end
end
if pcall(function() return obj.data end) then -- a tag doesn't have the data field pre-v1.3
if obj.data and obj.data ~= "" then
t.data = obj.data
end
end
end
local function export_tileset(layer, tileset)
print("Exporting tileset for layer:", layer.name)
local t = {}
local grid = tileset.grid
local size = grid.tileSize
t.grid = { tileSize = { width = grid.tileSize.width, height = grid.tileSize.height } }
if #tileset > 0 then
print("Tileset contains", #tileset, "tiles")
local spec = spr.spec
spec.width = size.width
spec.height = size.height * #tileset
local image = Image(spec)
image:clear()
for i = 0, #tileset - 1 do
local tile = tileset:getTile(i)
image:drawImage(tile, 0, i * size.height)
end
tileset_n = tileset_n + 1
local imageFn = fs.joinPath(output_folder, "tileset_" .. layer.name .. "_" .. tileset_n .. ".png")
image:saveAs(imageFn)
print("Saved tileset image to:", imageFn)
t.image = imageFn
else
print("Tileset is empty for layer:", layer.name)
end
return t
end
local function export_tilesets(tilesets)
print("Exporting", #tilesets, "tilesets")
local t = {}
for _,tileset in ipairs(tilesets) do
table.insert(t, export_tileset(tileset))
end
return t
end
local function export_frames(frames)
local t = {}
for _,frame in ipairs(frames) do
table.insert(t, { duration=frame.duration })
end
return t
end
local function export_cel(cel)
local t = {
frame=cel.frameNumber-1,
bounds={ x=cel.bounds.x,
y=cel.bounds.y,
width=cel.bounds.width,
height=cel.bounds.height }
}
if cel.image.colorMode == ColorMode.TILEMAP then
local tilemap = cel.image
-- save tilemap
t.tilemap = { width=tilemap.width,
height=tilemap.height,
tiles={} }
for it in tilemap:pixels() do
table.insert(t.tilemap.tiles, pc.tileI(it()))
end
else
-- save regular cel
image_n = image_n + 1
local imageFn = fs.joinPath(output_folder, "image" .. image_n .. ".png")
cel.image:saveAs(imageFn)
t.image = imageFn
end
fill_user_data(t, cel)
return t
end
local function export_cels(cels)
local t = {}
for _,cel in ipairs(cels) do
table.insert(t, export_cel(cel))
end
return t
end
local function get_tileset_index(layer)
for i,tileset in ipairs(layer.sprite.tilesets) do
if layer.tileset == tileset then
return i-1
end
end
return -1
end
local function export_tilemap(layer, cel)
print("Exporting tilemap for layer:", layer.name)
local tilemap = cel.image
if not tilemap then
print("Error: Tilemap image is nil for layer:", layer.name)
return nil
end
local grid = layer.tileset.grid
local tileWidth = grid.tileSize.width
local tileHeight = grid.tileSize.height
local mapWidth = tilemap.width
local mapHeight = tilemap.height
-- Create a new output image based on tilemap dimensions
local spec = ImageSpec{
width = mapWidth * tileWidth,
height = mapHeight * tileHeight,
colorMode = ColorMode.RGB
}
local outputImage = Image(spec)
outputImage:clear()
-- Iterate through tilemap pixels and render individual tiles
for it in tilemap:pixels() do
local tileIndex = app.pixelColor.tileI(it())
if tileIndex >= 0 then
local tileImage = layer.tileset:getTile(tileIndex)
if tileImage then
local destX = it.x * tileWidth
local destY = it.y * tileHeight
outputImage:drawImage(tileImage, destX, destY)
else
print(string.format("Warning: Tile index %d has no image in layer %s", tileIndex, layer.name))
end
end
end
-- Save the rendered image to a unique file
local imageFn = fs.joinPath(output_folder, "tilemap_" .. layer.name .. ".png")
outputImage:saveAs(imageFn)
print("Saved tilemap image to:", imageFn)
return imageFn
end
local function export_layer(layer, export_layers)
print("Exporting layer:", layer.name)
local t = { name = layer.name }
if layer.isImage then
if layer.opacity < 255 then
t.opacity = layer.opacity
end
if layer.blendMode ~= BlendMode.NORMAL then
t.blendMode = layer.blendMode
end
if #layer.cels >= 1 then
print("Layer has", #layer.cels, "cels")
t.cels = {}
for _, cel in ipairs(layer.cels) do
if layer.isTilemap and layer.tileset then
local tilemapImage = export_tilemap(layer, cel)
table.insert(t.cels, { frame = cel.frameNumber - 1, image = tilemapImage })
else
table.insert(t.cels, export_cel(cel))
end
end
end
elseif layer.isGroup then
print("Layer is a group with", #layer.layers, "sublayers")
t.layers = export_layers(layer.layers)
end
fill_user_data(t, layer)
return t
end
local function export_layers(layers)
print("Exporting", #layers, "layers")
local t = {}
for _, layer in ipairs(layers) do
table.insert(t, export_layer(layer, export_layers))
end
return t
end
local function ani_dir(d)
local values = { "forward", "reverse", "pingpong" }
return values[d+1]
end
local function export_tag(tag)
local t = {
name=tag.name,
from=tag.fromFrame.frameNumber-1,
to=tag.toFrame.frameNumber-1,
aniDir=ani_dir(tag.aniDir)
}
fill_user_data(t, tag)
return t
end
local function export_tags(tags)
local t = {}
for _,tag in ipairs(tags) do
table.insert(t, export_tag(tag, export_tags))
end
return t
end
local function export_slice(slice)
local t = {
name=slice.name,
bounds={ x=slice.bounds.x,
y=slice.bounds.y,
width=slice.bounds.width,
height=slice.bounds.height }
}
if slice.center then
t.center={ x=slice.center.x,
y=slice.center.y,
width=slice.center.width,
height=slice.center.height }
end
if slice.pivot then
t.pivot={ x=slice.pivot.x,
y=slice.pivot.y }
end
fill_user_data(t, slice)
return t
end
local function export_slices(slices)
local t = {}
for _,slice in ipairs(slices) do
table.insert(t, export_slice(slice, export_slices))
end
return t
end
----------------------------------------------------------------------
-- Creates output folder
fs.makeDirectory(output_folder)
----------------------------------------------------------------------
-- Write /sprite.json file in the output folder
local jsonFn = fs.joinPath(output_folder, "sprite.json")
local data = {
filename = spr.filename,
width = spr.width,
height = spr.height,
frames = export_frames(spr.frames),
layers = export_layers(spr.layers)
}
if #spr.tags > 0 then
data.tags = export_tags(spr.tags)
end
if #spr.slices > 0 then
data.slices = export_slices(spr.slices)
end
write_json_data(jsonFn, data)