Going to redo assets.

This commit is contained in:
2025-08-24 13:57:12 -05:00
parent 329925ea54
commit 479aad2f06
36 changed files with 285 additions and 128 deletions

View File

@@ -29,13 +29,13 @@ set(DUSK_TARGET_NAME "Dusk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_BUILD_BINARY ${DUSK_BUILD_DIR}/Dusk CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_ASSETS "" CACHE INTERNAL ${DUSK_CACHE_TARGET})
# Toolchain
# Create directories
file(MAKE_DIRECTORY ${DUSK_GENERATED_HEADERS_DIR})
file(MAKE_DIRECTORY ${DUSK_ASSETS_BUILD_DIR})
# Compilers
# Find packages
find_package(Python3 COMPONENTS Interpreter REQUIRED)
# Toolchains
if(DUSK_TARGET_SYSTEM STREQUAL "psp")
find_package(pspsdk REQUIRED)
endif()
@@ -52,6 +52,9 @@ add_executable(${DUSK_TARGET_NAME})
# Add tools
add_subdirectory(tools)
# Assets
add_subdirectory(assets)
# Add libraries
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
find_package(SDL2 REQUIRED)
@@ -64,14 +67,11 @@ if(DUSK_TARGET_SYSTEM STREQUAL "linux")
elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
find_package(SDL2 REQUIRED)
target_link_libraries(${DUSK_TARGET_NAME}
PRIVATE
# pspsdk
${SDL2_LIBRARIES}
target_link_libraries(${DUSK_TARGET_NAME} PRIVATE
${SDL2_LIBRARIES}
)
target_include_directories(${DUSK_TARGET_NAME}
PRIVATE
${SDL2_INCLUDE_DIRS}
target_include_directories(${DUSK_TARGET_NAME} PRIVATE
${SDL2_INCLUDE_DIRS}
)
endif()
@@ -83,6 +83,18 @@ target_include_directories(${DUSK_TARGET_NAME} PUBLIC
${DUSK_GENERATED_HEADERS_DIR}
)
# Build assets
string(JOIN "," DUSK_ASSETS_ARGUMENTS ${DUSK_ASSETS})
add_custom_target(DUSK_ASSETS_BUILT ALL
COMMAND
${Python3_EXECUTABLE} ${DUSK_TOOLS_DIR}/assetstool/assets.py
--output ${DUSK_GENERATED_HEADERS_DIR}/assets
--input ${DUSK_ASSETS_ARGUMENTS}
COMMENT
"Creating assets build directory ${DUSK_ASSETS}"
)
add_dependencies(${DUSK_TARGET_NAME} DUSK_ASSETS_BUILT)
# Postbuild, create PBP file for PSP.
if(DUSK_TARGET_SYSTEM STREQUAL "psp")
create_pbp_file(

6
assets/CMakeLists.txt Normal file
View File

@@ -0,0 +1,6 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_asset(entities.tsx)

BIN
assets/entities.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

4
assets/entities.tsx Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.1" name="entities" tilewidth="16" tileheight="16" tilecount="64" columns="8">
<image source="entities.png" width="128" height="128"/>
</tileset>

View File

@@ -26,13 +26,14 @@ target_sources(${DUSK_TARGET_NAME}
# Subdirs
add_subdirectory(assert)
add_subdirectory(asset)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(ecs)
add_subdirectory(engine)
add_subdirectory(error)
add_subdirectory(input)
add_subdirectory(locale)
# add_subdirectory(locale)
add_subdirectory(scene)
add_subdirectory(thread)
add_subdirectory(time)

View File

@@ -0,0 +1,12 @@
# 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
asset.c
assetraw.c
assetsystem.c
)

View File

@@ -7,56 +7,12 @@
#include "asset.h"
assetcallbacks_t ASSET_CALLBACKS[ASSET_TYPE_COUNT] = {
assetcallbacks_t ASSET_CALLBACKS[] = {
{
.init = assetRawInit,
.loadAsync = assetRawLoadAsync,
.loadSync = assetRawLoadSync,
.dispose = assetRawDispose
},
{
.init = NULL,
.loadAsync = NULL,
.loadSync = NULL,
.dispose = NULL
},
{
.init = NULL,
.loadAsync = NULL,
.loadSync = NULL,
.dispose = NULL
},
{
.init = NULL,
.loadAsync = NULL,
.loadSync = NULL,
.dispose = NULL
},
{
.init = NULL,
.loadAsync = NULL,
.loadSync = NULL,
.dispose = NULL
},
{
.init = NULL,
.loadAsync = NULL,
.loadSync = NULL,
.dispose = NULL
},
{
.init = NULL,
.loadAsync = NULL,
.loadSync = NULL,
.dispose = NULL
},
{
.init = NULL,
.loadAsync = NULL,
.loadSync = NULL,
.dispose = NULL
}
};

View File

@@ -6,31 +6,12 @@
*/
#pragma once
#include "assetraw.h"
typedef enum {
ASSET_TYPE_RAW,
ASSET_TYPE_TEXTURE,
ASSET_TYPE_JSON,
ASSET_TYPE_MODEL,
ASSET_TYPE_AUDIO,
ASSET_TYPE_COUNT
} assettype_t;
typedef struct {
void (*init)(asset_t *asset);
void (*loadAsync)(asset_t *asset);
void (*loadSync)(asset_t *asset);
void (*dispose)(asset_t *asset);
} assetcallbacks_t;
#include "dusk.h"
typedef struct asset_s {
void *arg;
int32_t nothing;
} asset_t;
extern assetcallbacks_t ASSET_CALLBACKS[];
void assetInit(void);
void assetDispose(void);

14
src/asset/assetimage.h Normal file
View File

@@ -0,0 +1,14 @@
/**
* 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 {
const int32_t width;
const int32_t height;
} assetimage_t;

View File

@@ -1,24 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetraw.h"
void assetRawInit(asset_t *asset) {
}
void assetRawLoadAsync(asset_t *asset) {
}
void assetRawLoadSync(asset_t *asset) {
}
void assetRawDispose(asset_t *asset) {
}

View File

@@ -1,16 +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 asset_s asset_t;
void assetRawInit(asset_t *asset);
void assetRawLoadAsync(asset_t *asset);
void assetRawLoadSync(asset_t *asset);
void assetRawDispose(asset_t *asset);

20
src/asset/assetsystem.c Normal file
View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetsystem.h"
#include "util/memory.h"
assetsystem_t ASSET_SYSTEM;
void assetSystemInit(void) {
memoryZero(&ASSET_SYSTEM, sizeof(assetsystem_t));
// threadInit(&ASSET_SYSTEM.thread, NULL);
}
void assetSystemDispose(void) {
// threadDispose(&ASSET_SYSTEM.thread);
}

View File

@@ -7,11 +7,9 @@
#pragma once
#include "asset.h"
#include "thread/thread.h"
typedef struct {
void *args;
thread_t loadThread;
int32_t nothing;
} assetsystem_t;
extern assetsystem_t ASSET_SYSTEM;

19
src/asset/assettileset.h Normal file
View File

@@ -0,0 +1,19 @@
/**
* 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 assetimage_s assetimage_t;
typedef struct {
const int32_t tileWidth;
const int32_t tileHeight;
const int32_t tileCount;
const int32_t columns;
const assetimage_t *image;
} assettileset_t;

View File

@@ -12,6 +12,7 @@
#include "display/display.h"
#include "ecs/ecssystem.h"
#include "scene/node.h"
#include "asset/assetsystem.h"
#include "scene/test/scenetest.h"
@@ -25,6 +26,7 @@ errorret_t engineInit(void) {
timeInit();
consoleInit();
ecsSystemInit();
assetSystemInit();
errorChain(displayInit());
sceneTestAdd();
@@ -43,6 +45,7 @@ errorret_t engineUpdate(void) {
errorret_t engineDispose(void) {
ecsSystemDispose();
errorChain(displayDispose());
assetSystemDispose();
consoleDispose();
errorOk();

View File

@@ -1,11 +1,11 @@
# Copyright (c) 2023 Dominic Msters
# Copyright (c) 2025 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Tools
add_subdirectory(eventcompile)
add_subdirectory(fontcompile)
add_subdirectory(languagecompile)
add_subdirectory(mapcompile)
add_subdirectory(tilecompile)
# Function that adds an asset to be compiled
function(add_asset ASSET_PATH)
set(FULL_ASSET_PATH "${CMAKE_CURRENT_LIST_DIR}/${ASSET_PATH}")
list(APPEND DUSK_ASSETS ${FULL_ASSET_PATH})
set(DUSK_ASSETS ${DUSK_ASSETS} CACHE INTERNAL ${DUSK_CACHE_TARGET})
endfunction()

View File

@@ -0,0 +1,17 @@
import os
import sys
from tilesetparser import parseTileset
from imageparser import parseImage
def parseAsset(assetPath):
if not os.path.isfile(assetPath):
print(f"Error: Input asset '{assetPath}' does not exist.")
sys.exit(1)
if assetPath.endswith(".tsx"):
return parseTileset(assetPath)
elif assetPath.endswith(".png"):
return parseImage(assetPath)
else:
print(f"Warning: Unsupported asset type for '{assetPath}'. Skipping.")
return []

View File

@@ -0,0 +1,31 @@
import sys, os
import argparse
from assetparser import parseAsset
from header import setOutputDir
# Check if the script is run with the correct arguments
parser = argparse.ArgumentParser(description="Generate chunk header files")
parser.add_argument('--output', required=True, help='Dir to output headers')
parser.add_argument('--input', required=True, help='Input assets to process', nargs='+')
args = parser.parse_args()
# Setup headers directory.
setOutputDir(args.output)
outputHeaders = []
# Create output directory if it doesn't exist
if not os.path.exists(args.output):
os.makedirs(args.output)
# Split input assets by comma
inputAssets = []
for inputArg in args.input:
inputAssets.extend(inputArg.split(','))
# Begin processing assets
if not inputAssets:
print("Error: No input assets provided.")
sys.exit(1)
for asset in inputAssets:
outputHeaders.extend(parseAsset(asset))

View File

@@ -0,0 +1,15 @@
import os
def setOutputDir(outputDir):
global OUTPUT_DIR
OUTPUT_DIR = outputDir
def getOutputDir():
return OUTPUT_DIR
def getHeaderInclude(headerPath):
outputDir = getOutputDir()
relPath = os.path.relpath(headerPath, outputDir)
path = relPath.replace('\\', '/') # Use forward slashes for includes
print(f" Including header: {path}")
return f'#include "{path}"'

View File

@@ -0,0 +1,34 @@
import os
from os import abort
from header import getOutputDir
from PIL import Image
def parseImage(imagePath):
print(f"Parsing image: {imagePath}")
if not os.path.isfile(imagePath):
abort(f"Error: Image file {imagePath} does not exist")
outputFile = os.path.join(getOutputDir(), f"image_{os.path.basename(imagePath)}.h")
dataOut = ""
dataOut += f"// Auto-generated image header for {os.path.basename(imagePath)}\n"
dataOut += f"#pragma once\n"
dataOut += f"#include \"asset/assetimage.h\"\n\n"
name = os.path.splitext(os.path.basename(imagePath))[0]
name = name.upper().replace(' ', '_')
dataOut += f"static const assetimage_t IMAGE_{name} = {{\n"
try:
with Image.open(imagePath) as img:
width, height = img.size
dataOut += f" .width = {width},\n"
dataOut += f" .height = {height},\n"
except Exception as e:
abort(f"Error: Unable to open image {imagePath}: {e}")
dataOut += f"}};\n"
with open(outputFile, 'w') as f:
f.write(dataOut)
return [ outputFile ]

View File

@@ -0,0 +1,74 @@
from os import abort
import os
import xml.etree.ElementTree as ET
from imageparser import parseImage
from header import getOutputDir, getHeaderInclude
def parseTileset(assetPath):
tree = ET.parse(assetPath)
root = tree.getroot()
# Should have tilewidth, tileheight, tilecount and columns attributes
if not all(attr in root.attrib for attr in ['tilewidth', 'tileheight', 'tilecount', 'columns']):
print(f"Error: Missing required attributes in tileset {assetPath}")
return []
tileWidth = int(root.attrib['tilewidth'])
tileHeight = int(root.attrib['tileheight'])
tileCount = int(root.attrib['tilecount'])
columns = int(root.attrib['columns'])
# Find image elements
images = root.findall('image')
if not images:
abort(f"Error: No image elements found in tileset {assetPath}")
imageSources = []
for image in images:
imageSource = image.attrib.get('source')
if not imageSource:
abort(f"Error: Image element missing 'source' attribute in tileset {assetPath}")
# Get relative dir from this assetPath
assetDir = os.path.dirname(assetPath)
imageSource = os.path.normpath(os.path.join(assetDir, imageSource))
imageSources.extend(parseImage(imageSource))
# Now do our own header.
headers = []
print(f"Generating tileset header for {assetPath}")
name = os.path.splitext(os.path.basename(assetPath))[0]
name = name.upper().replace(' ', '_')
imageNameWithoutExtension = os.path.splitext(os.path.splitext(os.path.basename(imageSources[0]))[0])[0]
imageNameWithoutExtension = imageNameWithoutExtension.upper().replace(' ', '_')
dataOut = ""
dataOut += f"// Auto-generated tileset header for {os.path.basename(assetPath)}\n"
dataOut += f"#pragma once\n"
dataOut += f"#include \"asset/assettileset.h\"\n"
for imgHeader in imageSources:
dataOut += getHeaderInclude(imgHeader) + "\n"
dataOut += f"\n"
dataOut += f"static const assettileset_t TILESET_{name} = {{\n"
dataOut += f" .tileCount = {tileCount},\n"
dataOut += f" .columns = {columns},\n"
dataOut += f" .tileHeight = {tileHeight},\n"
dataOut += f" .tileWidth = {tileWidth},\n"
dataOut += f" .image = &{imageNameWithoutExtension},\n"
dataOut += f"}};\n"
# Write out to output dir
outputDir = getOutputDir()
if not os.path.isdir(outputDir):
os.makedirs(outputDir)
outputFile = os.path.join(outputDir, f"tileset_{os.path.basename(assetPath)}.h")
with open(outputFile, 'w') as f:
f.write(dataOut)
headers.append(outputFile)
headers.extend(imageSources)
return headers