Asset prog
This commit is contained in:
@@ -4,3 +4,7 @@ from args import args
|
||||
def getAssetRelativePath(fullPath):
|
||||
# Get the relative path to the asset
|
||||
return os.path.relpath(fullPath, start=args.assets).replace('\\', '/')
|
||||
|
||||
def getBuiltAssetsRelativePath(fullPath):
|
||||
# Get the relative path to the built asset
|
||||
return os.path.relpath(fullPath, start=args.output_assets).replace('\\', '/')
|
@@ -1,6 +1,8 @@
|
||||
import sys, os
|
||||
from args import inputAssets
|
||||
from args import inputAssets, args
|
||||
from processasset import processAsset
|
||||
from assethelpers import getBuiltAssetsRelativePath
|
||||
import zipfile
|
||||
|
||||
# Setup headers directory.
|
||||
# setOutputDir(args.output)
|
||||
@@ -10,5 +12,29 @@ from processasset import processAsset
|
||||
# if not os.path.exists(args.output):
|
||||
# os.makedirs(args.output)
|
||||
|
||||
files = []
|
||||
|
||||
for asset in inputAssets:
|
||||
processAsset(asset)
|
||||
asset = processAsset(asset)
|
||||
files.extend(asset['files'])
|
||||
|
||||
# Take assets and add to a zip archive.
|
||||
outputFileName = args.output_file
|
||||
print(f"Creating output file: {outputFileName}")
|
||||
with zipfile.ZipFile(outputFileName, 'w') as zipf:
|
||||
for file in files:
|
||||
relativeOutputPath = getBuiltAssetsRelativePath(file)
|
||||
zipf.write(file, arcname=relativeOutputPath)
|
||||
|
||||
# Finalize build
|
||||
if args.build_type == 'header':
|
||||
print("Error: Header build not implemented yet.")
|
||||
sys.exit(1)
|
||||
|
||||
elif args.build_type == 'wad':
|
||||
# Nothing to do, already created above!
|
||||
pass
|
||||
|
||||
else:
|
||||
print("Error: Unknown build type.")
|
||||
sys.exit(1)
|
1
tools/assetstool/constants.py
Normal file
1
tools/assetstool/constants.py
Normal file
@@ -0,0 +1 @@
|
||||
ASSET_FILE_NAME_MAX_LENGTH = 256
|
@@ -1,4 +1,5 @@
|
||||
from processtileset import processTileset
|
||||
from processimage import processPalette, processImage
|
||||
|
||||
processedAssets = []
|
||||
|
||||
@@ -10,5 +11,9 @@ def processAsset(assetPath):
|
||||
|
||||
# Handle tiled tilesets
|
||||
if assetPath.endswith('.tsx'):
|
||||
processTileset(assetPath)
|
||||
return
|
||||
return processTileset(assetPath)
|
||||
elif assetPath.endswith('.png'):
|
||||
if assetPath.endswith('.palette.png'):
|
||||
return processPalette(assetPath)
|
||||
else:
|
||||
return processImage(assetPath)
|
64
tools/assetstool/processimage.py
Normal file
64
tools/assetstool/processimage.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import os
|
||||
from args import args
|
||||
from PIL import Image
|
||||
|
||||
def extractPaletteFromImage(image):
|
||||
# goes through and finds all unique colors in the image
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
pixels = list(image.getdata())
|
||||
uniqueColors = []
|
||||
for color in pixels:
|
||||
if color not in uniqueColors:
|
||||
uniqueColors.append(color)
|
||||
return uniqueColors
|
||||
|
||||
def savePalette(pixels, outputFilePath):
|
||||
# Pixels is a list of (R, G, B, A) tuples
|
||||
data = "DPF" # Header for Dusk Palette Format
|
||||
|
||||
# Count of colors (int32_t)
|
||||
colorCount = len(pixels)
|
||||
data += colorCount.to_bytes(4, byteorder='little').decode('latin1')
|
||||
|
||||
for r, g, b, a in pixels:
|
||||
data += bytes([r, g, b, a]).decode('latin1')
|
||||
|
||||
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
||||
with open(outputFilePath, 'wb') as f:
|
||||
f.write(data.encode('latin1'))
|
||||
|
||||
def processPalette(assetPath):
|
||||
# Process the image file
|
||||
print(f"Processing palette: {assetPath}")
|
||||
|
||||
# Load the image
|
||||
image = Image.open(assetPath)
|
||||
pixels = extractPaletteFromImage(image)
|
||||
|
||||
# Save the processed image to the output directory
|
||||
fileNameWithoutExt = os.path.splitext(os.path.basename(assetPath))[0]
|
||||
outputFilePath = os.path.join(args.output_assets, f"{fileNameWithoutExt}.dpf")
|
||||
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
||||
savePalette(pixels, outputFilePath)
|
||||
|
||||
outputRelative = os.path.relpath(outputFilePath, args.output_assets)
|
||||
|
||||
return {
|
||||
"outputFile": outputRelative,
|
||||
"paletteColors": len(pixels),
|
||||
"files": [ outputFilePath ]
|
||||
}
|
||||
|
||||
def processImage(assetPath):
|
||||
print(f"Processing image: {assetPath}")
|
||||
|
||||
# Load the image
|
||||
image = Image.open(assetPath)
|
||||
pixels = extractPaletteFromImage(image)
|
||||
|
||||
return {
|
||||
# "outputFile": os.path.relpath(assetPath, args.input_assets),
|
||||
# "paletteColors": len(pixels),
|
||||
"files": [ assetPath ]
|
||||
}
|
@@ -1,6 +1,9 @@
|
||||
import sys, os
|
||||
import xml.etree.ElementTree as ET
|
||||
from assethelpers import getAssetRelativePath
|
||||
from args import args
|
||||
from constants import ASSET_FILE_NAME_MAX_LENGTH
|
||||
from processimage import processPalette, processImage
|
||||
|
||||
def processTileset(assetPath):
|
||||
# Process the tileset file
|
||||
@@ -25,18 +28,58 @@ def processTileset(assetPath):
|
||||
return
|
||||
|
||||
# Exactly one image element is required
|
||||
images = root.findall('image')
|
||||
if len(images) != 1:
|
||||
imagesNode = root.findall('image')
|
||||
if len(imagesNode) != 1:
|
||||
print(f"Error: Tileset {assetPath} must have exactly one image element.")
|
||||
return
|
||||
|
||||
image = images[0]
|
||||
if 'source' not in image.attrib:
|
||||
imageNode = imagesNode[0]
|
||||
if 'source' not in imageNode.attrib:
|
||||
print(f"Error: Tileset {assetPath} is missing image source.")
|
||||
return
|
||||
|
||||
imageSource = image.attrib['source']
|
||||
|
||||
imageSource = imageNode.attrib['source']
|
||||
|
||||
directory = os.path.dirname(assetPath)
|
||||
image = processImage(os.path.join(directory, imageSource))
|
||||
|
||||
# Build
|
||||
relative = getAssetRelativePath(assetPath)
|
||||
print(f"Relative path: {relative}")
|
||||
relativeFile = getAssetRelativePath(assetPath)
|
||||
relativeDir = os.path.dirname(relativeFile)
|
||||
|
||||
data = "DTF" # Header for Dusk Tileset Format
|
||||
# Write width (int32_t)
|
||||
data += tilewidth.to_bytes(4, byteorder='little').decode('latin1')
|
||||
# Write height (int32_t)
|
||||
data += tileheight.to_bytes(4, byteorder='little').decode('latin1')
|
||||
# Write tilecount (int32_t)
|
||||
data += tilecount.to_bytes(4, byteorder='little').decode('latin1')
|
||||
# Write column count (int32_t)
|
||||
data += columns.to_bytes(4, byteorder='little').decode('latin1')
|
||||
# Write row count (int32_t)
|
||||
rows = (tilecount + columns - 1) // columns
|
||||
data += rows.to_bytes(4, byteorder='little').decode('latin1')
|
||||
|
||||
# Write image source file name, padd to ASSET_FILE_NAME_MAX_LENGTH
|
||||
imageSourceBytes = image["outputFile"].encode('utf-8')
|
||||
data += len(imageSourceBytes).to_bytes(4, byteorder='little').decode('latin1')
|
||||
data += imageSourceBytes.decode('latin1')
|
||||
paddingLength = max(0, ASSET_FILE_NAME_MAX_LENGTH - len(imageSourceBytes))
|
||||
data += '\x00' * paddingLength
|
||||
|
||||
# Write to output file
|
||||
fileNameWithoutExt = os.path.splitext(os.path.basename(assetPath))[0]
|
||||
outputFilePath = os.path.join(args.output_assets, relativeDir, f"{fileNameWithoutExt}.dtf")
|
||||
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
||||
with open(outputFilePath, 'wb') as f:
|
||||
f.write(data.encode('latin1'))
|
||||
|
||||
return {
|
||||
"outputFile": os.path.relpath(outputFilePath, args.output_assets),
|
||||
"tileWidth": tilewidth,
|
||||
"tileHeight": tileheight,
|
||||
"tileCount": tilecount,
|
||||
"columns": columns,
|
||||
"rows": rows,
|
||||
"image": image
|
||||
}
|
Reference in New Issue
Block a user