187 lines
6.0 KiB
Python
187 lines
6.0 KiB
Python
import struct
|
|
import sys
|
|
import os
|
|
import json
|
|
from assetstool.args import args
|
|
from assetstool.assetcache import assetCache, assetGetCache
|
|
from assetstool.assethelpers import getAssetRelativePath
|
|
from editortool.map.mapdefs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_TILE_COUNT
|
|
|
|
def processTile(tileIndex, x=0, y=0, z=0, chunkX=0, chunkY=0, chunkZ=0):
|
|
vertices = []
|
|
indices = []
|
|
tileType = tileIndex
|
|
|
|
# Placement X, Y, Z
|
|
px = (x * TILE_WIDTH) + (chunkX * CHUNK_WIDTH * TILE_WIDTH)
|
|
py = (y * TILE_HEIGHT) + (chunkY * CHUNK_HEIGHT * TILE_HEIGHT)
|
|
pz = (z * TILE_DEPTH) + (chunkZ * CHUNK_DEPTH * TILE_DEPTH)
|
|
|
|
if tileIndex == 0:
|
|
# Tile 0, nothing
|
|
return None
|
|
|
|
elif tileIndex == 5:
|
|
# Tile 2, ramp up
|
|
color = (255,0,0)
|
|
vertices = [
|
|
{'position': (px, py, pz + TILE_DEPTH), 'color': color, 'uv': (0,0)}, # 0,0
|
|
{'position': (px + TILE_WIDTH, py, pz + TILE_DEPTH), 'color': color, 'uv': (1,0)}, # 1,0
|
|
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1
|
|
{'position': (px, py, pz + TILE_DEPTH), 'color': color, 'uv': (0,0)}, # 0,0 (repeat)
|
|
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1 (repeat)
|
|
{'position': (px, py + TILE_HEIGHT, pz), 'color': color, 'uv': (0,1)} # 0,1
|
|
]
|
|
indices = [0, 1, 2, 3, 4, 5]
|
|
|
|
else:
|
|
# Determine color for checkerboard pattern
|
|
if tileIndex == 1:
|
|
color = (255, 255, 255)
|
|
else:
|
|
color = (0, 0, 255)
|
|
|
|
vertices = [
|
|
{'position': (px, py, pz), 'color': color, 'uv': (0,0)}, # 0,0
|
|
{'position': (px + TILE_WIDTH, py, pz), 'color': color, 'uv': (1,0)}, # 1,0
|
|
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1
|
|
{'position': (px, py, pz), 'color': color, 'uv': (0,0)}, # 0,0 (repeat)
|
|
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1 (repeat)
|
|
{'position': (px, py + TILE_HEIGHT, pz), 'color': color, 'uv': (0,1)} # 0,1
|
|
]
|
|
indices = [0, 1, 2, 3, 4, 5]
|
|
|
|
return {
|
|
'vertices': vertices,
|
|
'indices': indices,
|
|
'tileType': tileType
|
|
}
|
|
|
|
def processChunk(path):
|
|
cache = assetGetCache(path)
|
|
if cache:
|
|
return cache
|
|
|
|
# Read input file as JSON
|
|
with open(path, 'r') as f:
|
|
inData = json.load(f)
|
|
|
|
# Filename must contain chunk coordinates as X_Y_Z
|
|
fileName = os.path.basename(path)
|
|
nameParts = os.path.splitext(fileName)[0].split('_')
|
|
if len(nameParts) != 3:
|
|
print(f"Error: Chunk filename {fileName} does not contain valid chunk coordinates.")
|
|
sys.exit(1)
|
|
|
|
chunk = {
|
|
'chunkX': int(nameParts[0]),
|
|
'chunkY': int(nameParts[1]),
|
|
'chunkZ': int(nameParts[2]),
|
|
'tiles': [0] * CHUNK_TILE_COUNT,
|
|
'models': []
|
|
}
|
|
|
|
baseModel = {
|
|
'vertices': [],
|
|
'indices': [],
|
|
'vertexCount': 0,
|
|
'indexCount': 0
|
|
}
|
|
|
|
# Append the model to chunk.models
|
|
chunk['models'].append(baseModel)
|
|
|
|
for i, tile in enumerate(inData['shapes']):
|
|
# Set to chunk
|
|
|
|
# Calculate x, y, z from i
|
|
x = i % CHUNK_WIDTH
|
|
y = (i // CHUNK_WIDTH) % CHUNK_HEIGHT
|
|
z = i // (CHUNK_WIDTH * CHUNK_HEIGHT)
|
|
|
|
# Add tile 3D model
|
|
result = processTile(tile, x, y, z, chunk['chunkX'], chunk['chunkY'], chunk['chunkZ'])
|
|
if result is not None and len(result['vertices']) > 0:
|
|
base = len(baseModel['vertices'])
|
|
quad_indices = [base + idx for idx in result['indices']]
|
|
baseModel['vertices'].extend(result['vertices'])
|
|
baseModel['indices'].extend(quad_indices)
|
|
baseModel['vertexCount'] = len(baseModel['vertices'])
|
|
baseModel['indexCount'] = len(baseModel['indices'])
|
|
chunk['tiles'][i] = result['tileType']
|
|
|
|
# Generate binary buffer for efficient output
|
|
buffer = bytearray()
|
|
buffer.extend(b'DCF')# Header
|
|
buffer.extend(len(chunk['tiles']).to_bytes(4, 'little')) # Number of tiles
|
|
buffer.extend(len(chunk['models']).to_bytes(1, 'little')) # Number of models
|
|
|
|
# Buffer tile data as array of uint8_t
|
|
for tileIndex in chunk['tiles']:
|
|
buffer.append(tileIndex.to_bytes(1, 'little')[0])
|
|
|
|
# For each model
|
|
for model in chunk['models']:
|
|
# Write vertex count and index count
|
|
buffer.extend(model['vertexCount'].to_bytes(4, 'little'))
|
|
|
|
# For each vertex
|
|
for vertex in model['vertices']:
|
|
# This is not tightly packed in memory.
|
|
# R G B A U V X Y Z
|
|
# Color is 4 bytes (RGBA)
|
|
# Rest is floats
|
|
r, g, b = vertex['color']
|
|
a = 255
|
|
buffer.extend(r.to_bytes(1, 'little'))
|
|
buffer.extend(g.to_bytes(1, 'little'))
|
|
buffer.extend(b.to_bytes(1, 'little'))
|
|
buffer.extend(a.to_bytes(1, 'little'))
|
|
u, v = vertex['uv']
|
|
buffer.extend(bytearray(struct.pack('<f', u)))
|
|
buffer.extend(bytearray(struct.pack('<f', v)))
|
|
x, y, z = vertex['position']
|
|
buffer.extend(bytearray(struct.pack('<f', x)))
|
|
buffer.extend(bytearray(struct.pack('<f', y)))
|
|
buffer.extend(bytearray(struct.pack('<f', z)))
|
|
|
|
# Write out map file
|
|
relative = getAssetRelativePath(path)
|
|
fileNameWithoutExt = os.path.splitext(os.path.basename(path))[0]
|
|
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dcf")
|
|
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
|
|
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
|
with open(outputFilePath, "wb") as f:
|
|
f.write(buffer)
|
|
|
|
outChunk = {
|
|
'files': [ outputFilePath ],
|
|
'chunk': chunk
|
|
}
|
|
|
|
return assetCache(path, outChunk)
|
|
|
|
|
|
def processMap(asset):
|
|
cache = assetGetCache(asset['path'])
|
|
if cache is not None:
|
|
return cache
|
|
|
|
# Path provided should be a directory.
|
|
if not os.path.isdir(asset['path']):
|
|
print(f"Error: Asset path {asset['path']} is not a directory.")
|
|
sys.exit(1)
|
|
|
|
# List files
|
|
chunkFiles = []
|
|
for fileName in os.listdir(asset['path']):
|
|
if not fileName.endswith('.json'):
|
|
continue
|
|
result = processChunk(os.path.join(asset['path'], fileName))
|
|
chunkFiles.extend(result['files'])
|
|
|
|
outMap = {
|
|
'files': chunkFiles
|
|
}
|
|
|
|
return assetCache(asset['path'], outMap) |