Refactor
This commit is contained in:
177
tools/asset/process/tileset.py
Normal file
177
tools/asset/process/tileset.py
Normal file
@@ -0,0 +1,177 @@
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import datetime
|
||||
from xml.etree import ElementTree
|
||||
from tools.asset.process.image import processImage
|
||||
from tools.asset.path import getAssetRelativePath
|
||||
from tools.asset.args import args
|
||||
from tools.asset.cache import assetGetCache, assetCache
|
||||
|
||||
tilesets = []
|
||||
|
||||
def loadTilesetFromTSX(asset):
|
||||
# Load the TSX file
|
||||
tree = ElementTree.parse(asset['path'])
|
||||
root = tree.getroot()
|
||||
|
||||
# Expect tileheight, tilewidth, columns and tilecount attributes
|
||||
if 'tilewidth' not in root.attrib or 'tileheight' not in root.attrib or 'columns' not in root.attrib or 'tilecount' not in root.attrib:
|
||||
print(f"Error: TSX file {asset['path']} is missing required attributes (tilewidth, tileheight, columns, tilecount)")
|
||||
sys.exit(1)
|
||||
|
||||
tileWidth = int(root.attrib['tilewidth'])
|
||||
tileHeight = int(root.attrib['tileheight'])
|
||||
columns = int(root.attrib['columns'])
|
||||
tileCount = int(root.attrib['tilecount'])
|
||||
rows = (tileCount + columns - 1) // columns # Calculate rows based on tileCount and columns
|
||||
|
||||
# Find the image element
|
||||
imageElement = root.find('image')
|
||||
if imageElement is None or 'source' not in imageElement.attrib:
|
||||
print(f"Error: TSX file {asset['path']} is missing an image element with a source attribute")
|
||||
sys.exit(1)
|
||||
|
||||
imagePath = imageElement.attrib['source']
|
||||
|
||||
# Image is relative to the TSX file
|
||||
imageAssetPath = os.path.join(os.path.dirname(asset['path']), imagePath)
|
||||
|
||||
image = processImage({
|
||||
'path': imageAssetPath,
|
||||
'options': asset['options'],
|
||||
})
|
||||
|
||||
return {
|
||||
"image": image,
|
||||
"tileWidth": tileWidth,
|
||||
"tileHeight": tileHeight,
|
||||
"columns": columns,
|
||||
"rows": rows,
|
||||
"originalWidth": tileWidth * columns,
|
||||
"originalHeight": tileHeight * rows,
|
||||
}
|
||||
|
||||
def loadTilesetFromArgs(asset):
|
||||
# We need to determine how big each tile is. This can either be provided as
|
||||
# an arg of tileWidth/tileHeight or as a count of rows/columns.
|
||||
# Additionally, if the image has been factored, then the user can provide both
|
||||
# tile sizes AND cols/rows to indicate the original size of the image.
|
||||
image = processImage(asset)
|
||||
|
||||
tileWidth, tileHeight = None, None
|
||||
columns, rows = None, None
|
||||
originalWidth, originalHeight = image['width'], image['height']
|
||||
|
||||
if 'tileWidth' in asset['options'] and 'columns' in asset['options']:
|
||||
tileWidth = int(asset['options']['tileWidth'])
|
||||
columns = int(asset['options']['columns'])
|
||||
originalWidth = tileWidth * columns
|
||||
elif 'tileWidth' in asset['options']:
|
||||
tileWidth = int(asset['options']['tileWidth'])
|
||||
columns = image['width'] // tileWidth
|
||||
elif 'columns' in asset['options']:
|
||||
columns = int(asset['options']['columns'])
|
||||
tileWidth = image['width'] // columns
|
||||
else:
|
||||
print(f"Error: Tileset {asset['path']} must specify either tileWidth or columns")
|
||||
sys.exit(1)
|
||||
|
||||
if 'tileHeight' in asset['options'] and 'rows' in asset['options']:
|
||||
tileHeight = int(asset['options']['tileHeight'])
|
||||
rows = int(asset['options']['rows'])
|
||||
originalHeight = tileHeight * rows
|
||||
elif 'tileHeight' in asset['options']:
|
||||
tileHeight = int(asset['options']['tileHeight'])
|
||||
rows = image['height'] // tileHeight
|
||||
elif 'rows' in asset['options']:
|
||||
rows = int(asset['options']['rows'])
|
||||
tileHeight = image['height'] // rows
|
||||
else:
|
||||
print(f"Error: Tileset {asset['path']} must specify either tileHeight or rows")
|
||||
sys.exit(1)
|
||||
|
||||
return {
|
||||
"image": image,
|
||||
"tileWidth": tileWidth,
|
||||
"tileHeight": tileHeight,
|
||||
"columns": columns,
|
||||
"rows": rows,
|
||||
"originalWidth": originalWidth,
|
||||
"originalHeight": originalHeight,
|
||||
}
|
||||
|
||||
def processTileset(asset):
|
||||
cache = assetGetCache(asset['path'])
|
||||
if cache is not None:
|
||||
return cache
|
||||
|
||||
print(f"Processing tileset: {asset['path']}")
|
||||
tilesetData = None
|
||||
if asset['path'].endswith('.tsx'):
|
||||
tilesetData = loadTilesetFromTSX(asset)
|
||||
else:
|
||||
tilesetData = loadTilesetFromArgs(asset)
|
||||
|
||||
fileNameWithoutExtension = os.path.splitext(os.path.basename(asset['path']))[0]
|
||||
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
tilesetName = fileNameWithoutExtension
|
||||
tilesetNameUpper = tilesetName.upper()
|
||||
|
||||
widthScale = tilesetData['originalWidth'] / tilesetData['image']['width']
|
||||
heightScale = tilesetData['originalHeight'] / tilesetData['image']['height']
|
||||
|
||||
# Create header
|
||||
data = f"// Tileset Generated for {asset['path']} at {now}\n"
|
||||
data += f"#pragma once\n"
|
||||
data += f"#include \"display/tileset/tileset.h\"\n\n"
|
||||
data += f"static const tileset_t TILESET_{tilesetNameUpper} = {{\n"
|
||||
data += f" .tileWidth = {tilesetData['tileWidth']},\n"
|
||||
data += f" .tileHeight = {tilesetData['tileHeight']},\n"
|
||||
data += f" .tileCount = {tilesetData['columns'] * tilesetData['rows']},\n"
|
||||
data += f" .columns = {tilesetData['columns']},\n"
|
||||
data += f" .rows = {tilesetData['rows']},\n"
|
||||
data += f" .uv = {{ {widthScale / tilesetData['columns']}f, {heightScale / tilesetData['rows']}f }},\n"
|
||||
data += f" .image = {json.dumps(tilesetData['image']['imagePath'])},\n"
|
||||
data += f"}};\n"
|
||||
|
||||
|
||||
# Write Header
|
||||
outputFile = os.path.join(args.headers_dir, "display", "tileset", f"tileset_{tilesetName}.h")
|
||||
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
|
||||
with open(outputFile, 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
print(f"Write header for tileset: {outputFile}")
|
||||
|
||||
tileset = {
|
||||
"files": [],
|
||||
"image": tilesetData['image'],
|
||||
"headerFile": os.path.relpath(outputFile, args.headers_dir),
|
||||
"tilesetName": tilesetName,
|
||||
"tilesetNameUpper": tilesetNameUpper,
|
||||
"tilesetIndex": len(tilesets),
|
||||
"tilesetData": tilesetData,
|
||||
"files": tilesetData['image']['files'],
|
||||
}
|
||||
|
||||
tilesets.append(tileset)
|
||||
return assetCache(asset['path'], tileset)
|
||||
|
||||
def processTilesetList():
|
||||
data = f"// Tileset List Generated at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
data += f"#pragma once\n"
|
||||
for tileset in tilesets:
|
||||
data += f"#include \"{tileset['headerFile']}\"\n"
|
||||
data += f"\n"
|
||||
data += f"#define TILESET_LIST_COUNT {len(tilesets)}\n\n"
|
||||
data += f"static const tileset_t* TILESET_LIST[TILESET_LIST_COUNT] = {{\n"
|
||||
for tileset in tilesets:
|
||||
data += f" &TILESET_{tileset['tilesetNameUpper']},\n"
|
||||
data += f"}};\n"
|
||||
|
||||
# Write header.
|
||||
outputFile = os.path.join(args.headers_dir, "display", "tileset", f"tilesetlist.h")
|
||||
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
|
||||
with open(outputFile, 'w') as f:
|
||||
f.write(data)
|
||||
Reference in New Issue
Block a user