Finally merged map asset and map tool
This commit is contained in:
@@ -3,4 +3,4 @@
|
|||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
add_asset(MAP map)
|
add_asset(MAP map.json)
|
||||||
@@ -6,148 +6,83 @@ from assetstool.args import args
|
|||||||
from assetstool.assetcache import assetCache, assetGetCache
|
from assetstool.assetcache import assetCache, assetGetCache
|
||||||
from assetstool.assethelpers import getAssetRelativePath
|
from assetstool.assethelpers import getAssetRelativePath
|
||||||
from dusk.defs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_TILE_COUNT
|
from dusk.defs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_TILE_COUNT
|
||||||
|
from dusk.map import Map
|
||||||
|
from dusk.chunk import Chunk
|
||||||
|
|
||||||
def processTile(tileIndex, x=0, y=0, z=0, chunkX=0, chunkY=0, chunkZ=0):
|
def convertModelData(modelData):
|
||||||
vertices = []
|
# TLDR; Model data stores things efficiently with indices, but we buffer it
|
||||||
indices = []
|
# out to 6 vertex quads for simplicity.
|
||||||
tileType = tileIndex
|
outVertices = []
|
||||||
|
outUVs = []
|
||||||
# Placement X, Y, Z
|
outColors = []
|
||||||
px = (x * TILE_WIDTH) + (chunkX * CHUNK_WIDTH * TILE_WIDTH)
|
for indice in modelData['indices']:
|
||||||
py = (y * TILE_HEIGHT) + (chunkY * CHUNK_HEIGHT * TILE_HEIGHT)
|
vertex = modelData['vertices'][indice]
|
||||||
pz = (z * TILE_DEPTH) + (chunkZ * CHUNK_DEPTH * TILE_DEPTH)
|
uv = modelData['uvs'][indice]
|
||||||
|
color = modelData['colors'][indice]
|
||||||
if tileIndex == 0:
|
outVertices.append(vertex)
|
||||||
# Tile 0, nothing
|
outUVs.append(uv)
|
||||||
return None
|
outColors.append(color)
|
||||||
|
|
||||||
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 {
|
return {
|
||||||
'vertices': vertices,
|
'vertices': outVertices,
|
||||||
'indices': indices,
|
'uvs': outUVs,
|
||||||
'tileType': tileType
|
'colors': outColors
|
||||||
}
|
}
|
||||||
|
|
||||||
def processChunk(path):
|
def processChunk(chunk):
|
||||||
cache = assetGetCache(path)
|
cache = assetGetCache(chunk.getFilename())
|
||||||
if cache:
|
if cache:
|
||||||
return 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 = {
|
baseModel = {
|
||||||
'vertices': [],
|
'vertices': [],
|
||||||
'indices': [],
|
'colors': [],
|
||||||
'vertexCount': 0,
|
'uvs': []
|
||||||
'indexCount': 0
|
|
||||||
}
|
}
|
||||||
|
models = [ baseModel ]
|
||||||
|
|
||||||
# Append the model to chunk.models
|
for tileIndex, tile in chunk.tiles.items():
|
||||||
chunk['models'].append(baseModel)
|
tileBase = tile.getBaseTileModel()
|
||||||
|
|
||||||
for i, tile in enumerate(inData['shapes']):
|
convertedBase = convertModelData(tileBase)
|
||||||
# Set to chunk
|
baseModel['vertices'].extend(convertedBase['vertices'])
|
||||||
|
baseModel['colors'].extend(convertedBase['colors'])
|
||||||
# Calculate x, y, z from i
|
baseModel['uvs'].extend(convertedBase['uvs'])
|
||||||
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
|
# Generate binary buffer for efficient output
|
||||||
buffer = bytearray()
|
buffer = bytearray()
|
||||||
buffer.extend(b'DCF')# Header
|
buffer.extend(b'DCF')# Header
|
||||||
buffer.extend(len(chunk['tiles']).to_bytes(4, 'little')) # Number of tiles
|
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.extend(len(models).to_bytes(1, 'little')) # Number of models
|
||||||
|
|
||||||
# Buffer tile data as array of uint8_t
|
# Buffer tile data as array of uint8_t
|
||||||
for tileIndex in chunk['tiles']:
|
for tileIndex, tile in chunk.tiles.items():
|
||||||
buffer.append(tileIndex.to_bytes(1, 'little')[0])
|
buffer.extend(tile.shape.to_bytes(1, 'little'))
|
||||||
|
|
||||||
# For each model
|
# # For each model
|
||||||
for model in chunk['models']:
|
for model in models:
|
||||||
# Write vertex count and index count
|
vertexCount = len(model['vertices'])
|
||||||
buffer.extend(model['vertexCount'].to_bytes(4, 'little'))
|
buffer.extend(vertexCount.to_bytes(4, 'little'))
|
||||||
|
for i in range(vertexCount):
|
||||||
|
vertex = model['vertices'][i]
|
||||||
|
uv = model['uvs'][i]
|
||||||
|
color = model['colors'][i]
|
||||||
|
|
||||||
# For each vertex
|
buffer.extend(color[0].to_bytes(1, 'little'))
|
||||||
for vertex in model['vertices']:
|
buffer.extend(color[1].to_bytes(1, 'little'))
|
||||||
# This is not tightly packed in memory.
|
buffer.extend(color[2].to_bytes(1, 'little'))
|
||||||
# R G B A U V X Y Z
|
buffer.extend(color[3].to_bytes(1, 'little'))
|
||||||
# Color is 4 bytes (RGBA)
|
|
||||||
# Rest is floats
|
buffer.extend(bytearray(struct.pack('<f', uv[0])))
|
||||||
r, g, b = vertex['color']
|
buffer.extend(bytearray(struct.pack('<f', uv[1])))
|
||||||
a = 255
|
|
||||||
buffer.extend(r.to_bytes(1, 'little'))
|
buffer.extend(bytearray(struct.pack('<f', vertex[0])))
|
||||||
buffer.extend(g.to_bytes(1, 'little'))
|
buffer.extend(bytearray(struct.pack('<f', vertex[1])))
|
||||||
buffer.extend(b.to_bytes(1, 'little'))
|
buffer.extend(bytearray(struct.pack('<f', vertex[2])))
|
||||||
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
|
# Write out map file
|
||||||
relative = getAssetRelativePath(path)
|
relative = getAssetRelativePath(chunk.getFilename())
|
||||||
fileNameWithoutExt = os.path.splitext(os.path.basename(path))[0]
|
fileNameWithoutExt = os.path.splitext(os.path.basename(relative))[0]
|
||||||
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dcf")
|
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dcf")
|
||||||
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
|
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
|
||||||
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
||||||
@@ -158,20 +93,42 @@ def processChunk(path):
|
|||||||
'files': [ outputFilePath ],
|
'files': [ outputFilePath ],
|
||||||
'chunk': chunk
|
'chunk': chunk
|
||||||
}
|
}
|
||||||
|
return assetCache(chunk.getFilename(), outChunk)
|
||||||
return assetCache(path, outChunk)
|
|
||||||
|
|
||||||
|
|
||||||
def processMap(asset):
|
def processMap(asset):
|
||||||
cache = assetGetCache(asset['path'])
|
cache = assetGetCache(asset['path'])
|
||||||
if cache is not None:
|
if cache is not None:
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
# Path provided should be a directory.
|
map = Map(None)
|
||||||
if not os.path.isdir(asset['path']):
|
map.load(asset['path'])
|
||||||
print(f"Error: Asset path {asset['path']} is not a directory.")
|
dir = map.getMapDirectory()
|
||||||
|
|
||||||
|
files = os.listdir(dir)
|
||||||
|
if len(files) == 0:
|
||||||
|
print(f"Error: No chunk files found in map directory {dir}.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
chunkFiles = []
|
||||||
|
for fileName in files:
|
||||||
|
if not fileName.endswith('.json'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
fNameNoExt = os.path.splitext(fileName)[0]
|
||||||
|
fnPieces = fNameNoExt.split('_')
|
||||||
|
if len(fnPieces) != 3:
|
||||||
|
print(f"Error: Chunk filename {fileName} does not contain valid chunk coordinates.")
|
||||||
|
sys.exit(1)
|
||||||
|
chunk = Chunk(map, int(fnPieces[0]), int(fnPieces[1]), int(fnPieces[2]))
|
||||||
|
chunk.load()
|
||||||
|
result = processChunk(chunk)
|
||||||
|
chunkFiles.extend(result['files'])
|
||||||
|
|
||||||
|
outMap = {
|
||||||
|
'files': chunkFiles
|
||||||
|
}
|
||||||
|
return assetCache(asset['path'], outMap)
|
||||||
|
|
||||||
# List files
|
# List files
|
||||||
chunkFiles = []
|
chunkFiles = []
|
||||||
for fileName in os.listdir(asset['path']):
|
for fileName in os.listdir(asset['path']):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
from dusk.event import Event
|
from dusk.event import Event
|
||||||
from dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_VERTEX_COUNT_MAX, TILE_SHAPE_NULL
|
from dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_VERTEX_COUNT_MAX, TILE_SHAPE_NULL
|
||||||
from editortool.map.tile import Tile
|
from dusk.tile import Tile
|
||||||
from editortool.map.vertexbuffer import VertexBuffer
|
from editortool.map.vertexbuffer import VertexBuffer
|
||||||
from OpenGL.GL import *
|
from OpenGL.GL import *
|
||||||
|
|
||||||
@@ -3,11 +3,11 @@ from dusk.event import Event
|
|||||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox
|
from PyQt5.QtWidgets import QFileDialog, QMessageBox
|
||||||
from PyQt5.QtCore import QTimer
|
from PyQt5.QtCore import QTimer
|
||||||
import os
|
import os
|
||||||
from editortool.map.chunk import Chunk
|
from dusk.chunk import Chunk
|
||||||
from dusk.defs import MAP_WIDTH, MAP_HEIGHT, MAP_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH
|
from dusk.defs import MAP_WIDTH, MAP_HEIGHT, MAP_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
MAP_DEFAULT_PATH = os.path.join(os.path.dirname(__file__), '../../../assets/map/')
|
MAP_DEFAULT_PATH = os.path.join(os.path.dirname(__file__), '../../assets/map/')
|
||||||
EDITOR_CONFIG_PATH = os.path.join(os.path.dirname(__file__), '.editor')
|
EDITOR_CONFIG_PATH = os.path.join(os.path.dirname(__file__), '.editor')
|
||||||
|
|
||||||
class Map:
|
class Map:
|
||||||
@@ -28,6 +28,9 @@ class Map:
|
|||||||
for z in range(MAP_DEPTH):
|
for z in range(MAP_DEPTH):
|
||||||
self.chunks[index] = Chunk(self, x, y, z)
|
self.chunks[index] = Chunk(self, x, y, z)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
|
# Only in editor instances:
|
||||||
|
if parent is not None:
|
||||||
QTimer.singleShot(16, self.loadLastFile)
|
QTimer.singleShot(16, self.loadLastFile)
|
||||||
|
|
||||||
def loadLastFile(self):
|
def loadLastFile(self):
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from PyQt5.QtWidgets import QAction, QMenuBar, QFileDialog
|
from PyQt5.QtWidgets import QAction, QMenuBar, QFileDialog
|
||||||
from PyQt5.QtGui import QKeySequence
|
from PyQt5.QtGui import QKeySequence
|
||||||
from editortool.map.map import MAP_DEFAULT_PATH
|
from dusk.map import MAP_DEFAULT_PATH
|
||||||
|
|
||||||
class MapMenubar:
|
class MapMenubar:
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from editortool.map.chunkpanel import ChunkPanel
|
|||||||
from editortool.map.mapinfopanel import MapInfoPanel
|
from editortool.map.mapinfopanel import MapInfoPanel
|
||||||
from editortool.map.menubar import MapMenubar
|
from editortool.map.menubar import MapMenubar
|
||||||
from editortool.map.statusbar import StatusBar
|
from editortool.map.statusbar import StatusBar
|
||||||
from editortool.map.map import Map
|
from dusk.map import Map
|
||||||
from dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_SHAPE_NULL, TILE_SHAPE_FLOOR
|
from dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_SHAPE_NULL, TILE_SHAPE_FLOOR
|
||||||
from editortool.map.selectbox import SelectBox
|
from editortool.map.selectbox import SelectBox
|
||||||
from editortool.map.camera import Camera
|
from editortool.map.camera import Camera
|
||||||
|
|||||||
Reference in New Issue
Block a user