Palette assets improved.
This commit is contained in:
@@ -86,18 +86,16 @@ target_include_directories(${DUSK_TARGET_NAME} PUBLIC
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Build assets
|
# Build assets
|
||||||
string(JOIN "," DUSK_ASSETS_ARGUMENTS ${DUSK_ASSETS})
|
|
||||||
add_custom_target(DUSK_ASSETS_BUILT ALL
|
add_custom_target(DUSK_ASSETS_BUILT ALL
|
||||||
COMMAND
|
COMMAND
|
||||||
${Python3_EXECUTABLE} ${DUSK_TOOLS_DIR}/assetstool/assets.py
|
${Python3_EXECUTABLE} ${DUSK_TOOLS_DIR}/assetstool/main.py
|
||||||
--assets ${DUSK_ASSETS_DIR}
|
--assets ${DUSK_ASSETS_DIR}
|
||||||
--build-type wad
|
--build-type wad
|
||||||
--output-assets ${DUSK_BUILT_ASSETS_DIR}
|
--output-assets ${DUSK_BUILT_ASSETS_DIR}
|
||||||
--output-file ${DUSK_BUILD_DIR}/dusk.dsk
|
--output-file ${DUSK_BUILD_DIR}/dusk.dsk
|
||||||
--output-headers ${DUSK_GENERATED_HEADERS_DIR}/asset/assetbundle.h
|
--output-headers ${DUSK_GENERATED_HEADERS_DIR}/asset/assetbundle.h
|
||||||
--input ${DUSK_ASSETS_ARGUMENTS}
|
--headers-dir ${DUSK_GENERATED_HEADERS_DIR}
|
||||||
COMMENT
|
--input ${DUSK_ASSETS}
|
||||||
"Creating assets build directory ${DUSK_ASSETS}"
|
|
||||||
)
|
)
|
||||||
add_dependencies(${DUSK_TARGET_NAME} DUSK_ASSETS_BUILT)
|
add_dependencies(${DUSK_TARGET_NAME} DUSK_ASSETS_BUILT)
|
||||||
|
|
||||||
|
@@ -31,7 +31,9 @@ def savePalette(pixels, outputFilePath):
|
|||||||
with open(outputFilePath, "wb") as f:
|
with open(outputFilePath, "wb") as f:
|
||||||
f.write(buf)
|
f.write(buf)
|
||||||
|
|
||||||
def processPalette(assetPath):
|
def processPalette(asset):
|
||||||
|
assetPath = asset['path']
|
||||||
|
|
||||||
# Process the image file
|
# Process the image file
|
||||||
print(f"Processing palette: {assetPath}")
|
print(f"Processing palette: {assetPath}")
|
||||||
|
|
||||||
@@ -58,7 +60,9 @@ def processPalette(assetPath):
|
|||||||
|
|
||||||
return palette
|
return palette
|
||||||
|
|
||||||
def processImage(assetPath):
|
def processImage(asset):
|
||||||
|
assetPath = asset['path']
|
||||||
|
|
||||||
print(f"Processing image: {assetPath}")
|
print(f"Processing image: {assetPath}")
|
||||||
|
|
||||||
# Load the image
|
# Load the image
|
@@ -5,7 +5,9 @@ from args import args
|
|||||||
from constants import ASSET_FILE_NAME_MAX_LENGTH
|
from constants import ASSET_FILE_NAME_MAX_LENGTH
|
||||||
from processimage import processPalette, processImage
|
from processimage import processPalette, processImage
|
||||||
|
|
||||||
def processTileset(assetPath):
|
def processTileset(asset):
|
||||||
|
assetPath = asset['path']
|
||||||
|
|
||||||
# Process the tileset file
|
# Process the tileset file
|
||||||
print(f"Processing tileset: {assetPath}")
|
print(f"Processing tileset: {assetPath}")
|
||||||
|
|
@@ -3,6 +3,7 @@
|
|||||||
# 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(PALETTE first.palette.png test0)
|
add_asset(PALETTE first.palette.png)
|
||||||
add_asset(TILESET entities.tsx test test3)
|
|
||||||
add_asset(IMAGE minogram_6x10.png test2)
|
# add_asset(TILESET entities.tsx)
|
||||||
|
# add_asset(IMAGE minogram_6x10.png)
|
@@ -36,7 +36,6 @@ add_subdirectory(error)
|
|||||||
add_subdirectory(input)
|
add_subdirectory(input)
|
||||||
# add_subdirectory(locale)
|
# add_subdirectory(locale)
|
||||||
add_subdirectory(rpg)
|
add_subdirectory(rpg)
|
||||||
add_subdirectory(scene)
|
|
||||||
add_subdirectory(thread)
|
add_subdirectory(thread)
|
||||||
add_subdirectory(time)
|
add_subdirectory(time)
|
||||||
add_subdirectory(util)
|
add_subdirectory(util)
|
@@ -8,12 +8,12 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
display.c
|
display.c
|
||||||
camera.c
|
camera.c
|
||||||
palette.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirectories
|
# Subdirectories
|
||||||
add_subdirectory(framebuffer)
|
add_subdirectory(framebuffer)
|
||||||
add_subdirectory(mesh)
|
add_subdirectory(mesh)
|
||||||
|
add_subdirectory(palette)
|
||||||
add_subdirectory(texture)
|
add_subdirectory(texture)
|
||||||
add_subdirectory(scene)
|
add_subdirectory(scene)
|
||||||
add_subdirectory(spritebatch)
|
add_subdirectory(spritebatch)
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "palette.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
palette_t PALETTE[PALETTE_COUNT_MAX];
|
|
||||||
uint8_t PALETTE_COUNT = 0;
|
|
||||||
|
|
||||||
void paletteAssetLoadCallback(void *data) {
|
|
||||||
assertNotNull(data, "Data cannot be NULL.");
|
|
||||||
|
|
||||||
uint8_t index = *((uint8_t*)data);
|
|
||||||
assertTrue(index < PALETTE_COUNT_MAX, "Palette index out of bounds.");
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
} palette_t;
|
|
||||||
|
|
||||||
#define PALETTE_COUNT_MAX 8
|
|
||||||
|
|
||||||
extern palette_t PALETTE[PALETTE_COUNT_MAX];
|
|
||||||
extern uint8_t PALETTE_COUNT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Palette asset load callback.
|
|
||||||
*
|
|
||||||
* @param data The data passed to the callback, should be a uint8_t* index
|
|
||||||
* into the PALETTE array.
|
|
||||||
*/
|
|
||||||
void paletteAssetLoadCallback(void *data);
|
|
@@ -6,5 +6,4 @@
|
|||||||
# Sources
|
# Sources
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
scenetest.c
|
|
||||||
)
|
)
|
@@ -6,8 +6,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "display/texture/texture.h"
|
#include "display/color.h"
|
||||||
|
|
||||||
extern texture_t test;
|
typedef struct {
|
||||||
|
const uint8_t colorCount;
|
||||||
void sceneTestAdd(void);
|
const color_t *colors;
|
||||||
|
} palette_t;
|
@@ -13,9 +13,6 @@
|
|||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
#include "rpg/rpg.h"
|
#include "rpg/rpg.h"
|
||||||
|
|
||||||
#include "display/palette.h"
|
|
||||||
#include "scene/test/scenetest.h"
|
|
||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
|
|
||||||
errorret_t engineInit(void) {
|
errorret_t engineInit(void) {
|
||||||
@@ -29,12 +26,6 @@ errorret_t engineInit(void) {
|
|||||||
errorChain(displayInit());
|
errorChain(displayInit());
|
||||||
rpgInit();
|
rpgInit();
|
||||||
|
|
||||||
uint8_t slot = 0;
|
|
||||||
assetLoad("entities.dpi", paletteAssetLoadCallback, &slot);
|
|
||||||
if(ASSET.state == ASSET_STATE_ERROR) errorChain(ASSET.error);
|
|
||||||
|
|
||||||
sceneTestAdd();
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
|
||||||
PRIVATE
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
||||||
add_subdirectory(test)
|
|
@@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "scenetest.h"
|
|
||||||
#include "display/camera.h"
|
|
||||||
#include "display/mesh/quad.h"
|
|
||||||
|
|
||||||
texture_t test;
|
|
||||||
|
|
||||||
void sceneTestAdd(void) {
|
|
||||||
}
|
|
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
# Function that adds an asset to be compiled
|
# Function that adds an asset to be compiled
|
||||||
function(add_asset ASSET_TYPE ASSET_PATH)
|
function(add_asset ASSET_TYPE ASSET_PATH)
|
||||||
message(STATUS "Adding asset: ${ASSET_PATH} (type: ${ASSET_TYPE})")
|
|
||||||
message(STATUS " Options: ${ARGN}")
|
|
||||||
|
|
||||||
set(FULL_ASSET_PATH "${CMAKE_CURRENT_LIST_DIR}/${ASSET_PATH}")
|
set(FULL_ASSET_PATH "${CMAKE_CURRENT_LIST_DIR}/${ASSET_PATH}")
|
||||||
list(APPEND DUSK_ASSETS ${FULL_ASSET_PATH})
|
string(JOIN "%" ASSETS_ARGS ${ARGN})
|
||||||
|
list(APPEND DUSK_ASSETS
|
||||||
|
"${ASSET_TYPE}#${FULL_ASSET_PATH}#${ASSETS_ARGS}$"
|
||||||
|
)
|
||||||
set(DUSK_ASSETS ${DUSK_ASSETS} CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
set(DUSK_ASSETS ${DUSK_ASSETS} CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||||
endfunction()
|
endfunction()
|
@@ -6,15 +6,44 @@ import sys
|
|||||||
parser = argparse.ArgumentParser(description="Generate chunk header files")
|
parser = argparse.ArgumentParser(description="Generate chunk header files")
|
||||||
parser.add_argument('--assets', required=True, help='Dir to output built assets')
|
parser.add_argument('--assets', required=True, help='Dir to output built assets')
|
||||||
parser.add_argument('--build-type', choices=['wad', 'header'], default='raw', help='Type of build to perform')
|
parser.add_argument('--build-type', choices=['wad', 'header'], default='raw', help='Type of build to perform')
|
||||||
parser.add_argument('--output-file', help='Output file for built assets (required for wad build)')
|
parser.add_argument('--output-file', required=True, help='Output file for built assets (required for wad build)')
|
||||||
|
parser.add_argument('--headers-dir', required=True, help='Directory to output individual asset headers (required for header build)')
|
||||||
parser.add_argument('--output-headers', help='Output header file for built assets (required for header build)')
|
parser.add_argument('--output-headers', help='Output header file for built assets (required for header build)')
|
||||||
parser.add_argument('--output-assets', help='Output directory for built assets (required for raw build)')
|
parser.add_argument('--output-assets', required=True, help='Output directory for built assets')
|
||||||
parser.add_argument('--input', required=True, help='Input assets to process', nargs='+')
|
parser.add_argument('--input', required=True, help='Input assets to process', nargs='+')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
inputAssets = []
|
inputAssets = []
|
||||||
for inputArg in args.input:
|
for inputArg in args.input:
|
||||||
inputAssets.extend(inputArg.split(','))
|
files = inputArg.split('$')
|
||||||
|
for file in files:
|
||||||
|
if str(file).strip() == '':
|
||||||
|
continue
|
||||||
|
|
||||||
|
pieces = file.split('#')
|
||||||
|
|
||||||
|
if len(pieces) < 2:
|
||||||
|
print(f"Error: Invalid input asset format '{file}'. Expected format: type#path[#option1%option2...]")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
if len(pieces) > 2:
|
||||||
|
optionParts = pieces[2].split('%')
|
||||||
|
for part in optionParts:
|
||||||
|
partSplit = part.split('=')
|
||||||
|
|
||||||
|
if len(partSplit) < 1:
|
||||||
|
continue
|
||||||
|
if len(partSplit) == 2:
|
||||||
|
options[partSplit[0]] = partSplit[1]
|
||||||
|
else:
|
||||||
|
options[partSplit[0]] = True
|
||||||
|
|
||||||
|
inputAssets.append({
|
||||||
|
'type': pieces[0],
|
||||||
|
'path': pieces[1],
|
||||||
|
'options': options
|
||||||
|
})
|
||||||
|
|
||||||
if not inputAssets:
|
if not inputAssets:
|
||||||
print("Error: No input assets provided.")
|
print("Error: No input assets provided.")
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import sys, os
|
import sys, os
|
||||||
from args import inputAssets, args
|
from args import inputAssets, args
|
||||||
from processasset import processAsset
|
from processasset import processAsset
|
||||||
|
from processpalette import processPaletteList
|
||||||
from assethelpers import getBuiltAssetsRelativePath
|
from assethelpers import getBuiltAssetsRelativePath
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
@@ -26,6 +27,9 @@ with zipfile.ZipFile(outputFileName, 'w') as zipf:
|
|||||||
relativeOutputPath = getBuiltAssetsRelativePath(file)
|
relativeOutputPath = getBuiltAssetsRelativePath(file)
|
||||||
zipf.write(file, arcname=relativeOutputPath)
|
zipf.write(file, arcname=relativeOutputPath)
|
||||||
|
|
||||||
|
# Generate additional headers.
|
||||||
|
processPaletteList()
|
||||||
|
|
||||||
# Finalize build
|
# Finalize build
|
||||||
if args.build_type == 'header':
|
if args.build_type == 'header':
|
||||||
print("Error: Header build not implemented yet.")
|
print("Error: Header build not implemented yet.")
|
@@ -1,19 +1,24 @@
|
|||||||
from processtileset import processTileset
|
import sys
|
||||||
from processimage import processPalette, processImage
|
# from processtileset import processTileset
|
||||||
|
# from processimage import processPalette, processImage
|
||||||
|
from processpalette import processPalette
|
||||||
|
|
||||||
processedAssets = []
|
processedAssets = []
|
||||||
|
|
||||||
def processAsset(assetPath):
|
def processAsset(asset):
|
||||||
if assetPath in processedAssets:
|
if asset['path'] in processedAssets:
|
||||||
return
|
return
|
||||||
|
|
||||||
processedAssets.append(assetPath)
|
processedAssets.append(asset['path'])
|
||||||
|
|
||||||
# Handle tiled tilesets
|
# Handle tiled tilesets
|
||||||
if assetPath.endswith('.tsx'):
|
t = asset['type'].lower()
|
||||||
return processTileset(assetPath)
|
if t == 'palette':
|
||||||
elif assetPath.endswith('.png'):
|
return processPalette(asset)
|
||||||
if assetPath.endswith('.palette.png'):
|
# elif t == 'image':
|
||||||
return processPalette(assetPath)
|
# return processImage(asset)
|
||||||
|
# elif t == 'tileset':
|
||||||
|
# return processTileset(asset)
|
||||||
else:
|
else:
|
||||||
return processImage(assetPath)
|
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
|
||||||
|
sys.exit(1)
|
80
tools/assetstool/processpalette.py
Normal file
80
tools/assetstool/processpalette.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import os
|
||||||
|
from PIL import Image
|
||||||
|
from args import args
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
palettes = []
|
||||||
|
|
||||||
|
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:
|
||||||
|
# We treat alpha 0 as rgba(0,0,0,0) for palette purposes
|
||||||
|
if color[3] == 0:
|
||||||
|
color = (0, 0, 0, 0)
|
||||||
|
if color not in uniqueColors:
|
||||||
|
uniqueColors.append(color)
|
||||||
|
return uniqueColors
|
||||||
|
|
||||||
|
def processPalette(asset):
|
||||||
|
print(f"Processing palette: {asset['path']}")
|
||||||
|
|
||||||
|
paletteIndex = len(palettes)
|
||||||
|
image = Image.open(asset['path'])
|
||||||
|
pixels = extractPaletteFromImage(image)
|
||||||
|
|
||||||
|
fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0]
|
||||||
|
fileNameWithoutPalette = os.path.splitext(fileNameWithoutExt)[0]
|
||||||
|
|
||||||
|
# Header
|
||||||
|
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
data = f"// Palette Generated for {asset['path']} at {now}\n"
|
||||||
|
data += f"#include \"display/palette/palette.h\"\n\n"
|
||||||
|
data += f"#define PALETTE_{paletteIndex}_COLOR_COUNT {len(pixels)}\n\n"
|
||||||
|
data += f"static const color_t PALETTE_{paletteIndex}_COLORS[PALETTE_{paletteIndex}_COLOR_COUNT] = {{\n"
|
||||||
|
for pixel in pixels:
|
||||||
|
data += f" {{ 0x{pixel[0]:02X}, 0x{pixel[1]:02X}, 0x{pixel[2]:02X}, 0x{pixel[3]:02X} }},\n"
|
||||||
|
data += f"}};\n\n"
|
||||||
|
data += f"static const palette_t PALETTE_{paletteIndex} = {{\n"
|
||||||
|
data += f" .colorCount = PALETTE_{paletteIndex}_COLOR_COUNT,\n"
|
||||||
|
data += f" .colors = PALETTE_{paletteIndex}_COLORS,\n"
|
||||||
|
data += f"}};\n"
|
||||||
|
|
||||||
|
# Write Header
|
||||||
|
outputFile = os.path.join(args.headers_dir, "display", "palette", f"palette_{paletteIndex}.h")
|
||||||
|
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
|
||||||
|
with open(outputFile, "w") as f:
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
palette = {
|
||||||
|
"paletteIndex": paletteIndex,
|
||||||
|
"paletteName": fileNameWithoutPalette,
|
||||||
|
"pixels": pixels,
|
||||||
|
"headerFile": os.path.relpath(outputFile, args.headers_dir),
|
||||||
|
"asset": asset,
|
||||||
|
"files": [ ],# No zippable files.
|
||||||
|
}
|
||||||
|
|
||||||
|
palettes.append(palette)
|
||||||
|
return palette
|
||||||
|
|
||||||
|
def processPaletteList():
|
||||||
|
data = f"// Auto-generated palette list\n"
|
||||||
|
for palette in palettes:
|
||||||
|
data += f"#include \"{palette['headerFile']}\"\n"
|
||||||
|
data += f"\n"
|
||||||
|
data += f"#define PALETTE_LIST_COUNT {len(palettes)}\n\n"
|
||||||
|
data += f"static const palette_t* PALETTE_LIST[PALETTE_LIST_COUNT] = {{\n"
|
||||||
|
for palette in palettes:
|
||||||
|
data += f" &PALETTE_{palette['paletteIndex']},\n"
|
||||||
|
data += f"}};\n"
|
||||||
|
|
||||||
|
# Write the palette list to a header file
|
||||||
|
outputFile = os.path.join(args.headers_dir, "display", "palette", "palettelist.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