Process tileset.

This commit is contained in:
2025-09-12 12:43:56 -05:00
parent 46a94ecacd
commit 9b98181d28
18 changed files with 205 additions and 47 deletions

View File

@@ -10,8 +10,8 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
if(NOT DEFINED DUSK_TARGET_SYSTEM) if(NOT DEFINED DUSK_TARGET_SYSTEM)
# set(DUSK_TARGET_SYSTEM "linux") set(DUSK_TARGET_SYSTEM "linux")
set(DUSK_TARGET_SYSTEM "psp") # set(DUSK_TARGET_SYSTEM "psp")
endif() endif()
# Prep cache # Prep cache

View File

@@ -4,8 +4,7 @@
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_asset(PALETTE first.palette.png) add_asset(PALETTE first.palette.png)
add_asset(IMAGE font_minogram.png type=ALPHA) add_asset(TILESET font_minogram.png type=ALPHA tileWidth=6 tileHeight=10 columns=16 rows=6)
add_asset(IMAGE entities.png type=PALETTIZED) add_asset(TILESET entities.png type=PALETTIZED tileWidth=16 tileHeight=16)
add_asset(CONFIG init.dcf) add_asset(CONFIG init.dcf)
add_asset(CONFIG init_psp.dcf) add_asset(CONFIG init_psp.dcf)
# add_asset(TILESET entities.tsx)

View File

@@ -8,6 +8,7 @@ target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
display.c display.c
camera.c camera.c
tileset.c
) )
# Subdirectories # Subdirectories

View File

@@ -67,7 +67,7 @@ errorret_t displayInit(void) {
frameBufferInitBackbuffer(); frameBufferInitBackbuffer();
spriteBatchInit(); spriteBatchInit();
errorChain(uiInit()); errorChain(uiInit());
sceneManagerInit(); errorChain(sceneManagerInit());
errorOk(); errorOk();
} }

View File

@@ -14,18 +14,24 @@
#include "asset/assetmanager.h" #include "asset/assetmanager.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "display/tileset/tileset_entities.h"
sceneoverworld_t SCENE_OVERWORLD; sceneoverworld_t SCENE_OVERWORLD;
asset_t *testAsset; asset_t *testAsset;
ref_t testAssetRef; ref_t testAssetRef;
void sceneOverworldInit(void) { errorret_t sceneOverworldInit(void) {
cameraInit(&SCENE_OVERWORLD.camera); cameraInit(&SCENE_OVERWORLD.camera);
glm_vec3_copy((vec3){ 0.0f, 1.0f, 0.0f }, SCENE_OVERWORLD.camera.lookat.up); glm_vec3_copy((vec3){ 0.0f, 1.0f, 0.0f }, SCENE_OVERWORLD.camera.lookat.up);
scene_t *scene = &SCENE_MANAGER_SCENES[SCENE_TYPE_OVERWORLD]; scene_t *scene = &SCENE_MANAGER_SCENES[SCENE_TYPE_OVERWORLD];
scene->flags |= SCENE_FLAG_ACTIVE | SCENE_FLAG_VISIBLE; scene->flags |= SCENE_FLAG_ACTIVE | SCENE_FLAG_VISIBLE;
assetManagerLoadAsset("entities.dpi", &testAsset, &testAssetRef); errorChain(assetManagerLoadAsset(
TILESET_ENTITIES.image, &testAsset, &testAssetRef
));
errorOk();
} }
void sceneOverworldUpdate(void) { void sceneOverworldUpdate(void) {
@@ -79,11 +85,17 @@ void sceneOverworldRenderEntity(const entity_t *entity) {
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type"); assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type"); assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
vec4 uv;
tilesetTileGetUV(&TILESET_ENTITIES, 0, uv);
// For now, just draw a placeholder quad. // For now, just draw a placeholder quad.
spriteBatchPush( spriteBatchPush(
&testAsset->paletteImage.texture, &testAsset->paletteImage.texture,
entity->x, entity->y, entity->x + 32.0f, entity->y + 32.0f, entity->x, entity->y,
COLOR_WHITE, 0.0f, 0.0f, 0.125f, 0.125f entity->x + TILESET_ENTITIES.tileWidth,
entity->y + TILESET_ENTITIES.tileHeight,
COLOR_WHITE,
uv[0], uv[1], uv[2], uv[3]
); );
} }

View File

@@ -8,6 +8,7 @@
#pragma once #pragma once
#include "display/camera.h" #include "display/camera.h"
#include "rpg/world/map.h" #include "rpg/world/map.h"
#include "error/error.h"
typedef struct { typedef struct {
camera_t camera; camera_t camera;
@@ -18,7 +19,7 @@ extern sceneoverworld_t SCENE_OVERWORLD;
/** /**
* Initialize the overworld scene. * Initialize the overworld scene.
*/ */
void sceneOverworldInit(void); errorret_t sceneOverworldInit(void);
/** /**
* Update the overworld scene. * Update the overworld scene.

View File

@@ -7,12 +7,13 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include "error/error.h"
#define SCENE_FLAG_VISIBLE (1 << 0) #define SCENE_FLAG_VISIBLE (1 << 0)
#define SCENE_FLAG_ACTIVE (1 << 1) #define SCENE_FLAG_ACTIVE (1 << 1)
typedef struct { typedef struct {
void (*init)(void); errorret_t (*init)(void);
void (*update)(void); void (*update)(void);
void (*render)(void); void (*render)(void);
void (*dispose)(void); void (*dispose)(void);

View File

@@ -22,9 +22,10 @@ scene_t SCENE_MANAGER_SCENES[SCENE_TYPE_COUNT] = {
} }
}; };
void sceneManagerInit(void) { errorret_t sceneManagerInit(void) {
scene_t *initial = &SCENE_MANAGER_SCENES[SCENE_TYPE_INITIAL]; scene_t *initial = &SCENE_MANAGER_SCENES[SCENE_TYPE_INITIAL];
if(initial->init != NULL) initial->init(); if(initial->init != NULL) errorChain(initial->init());
errorOk();
} }
void sceneManagerUpdate(void) { void sceneManagerUpdate(void) {

View File

@@ -15,7 +15,7 @@ typedef struct {
extern scenemanager_t SCENE_MANAGER; extern scenemanager_t SCENE_MANAGER;
extern scene_t SCENE_MANAGER_SCENES[SCENE_TYPE_COUNT]; extern scene_t SCENE_MANAGER_SCENES[SCENE_TYPE_COUNT];
void sceneManagerInit(void); errorret_t sceneManagerInit(void);
void sceneManagerUpdate(void); void sceneManagerUpdate(void);
void sceneManagerRender(void); void sceneManagerRender(void);
void sceneManagerDispose(void); void sceneManagerDispose(void);

30
src/display/tileset.c Normal file
View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "tileset.h"
void tilesetTileGetUV(
const tileset_t *tileset,
const uint16_t tileIndex,
vec4 outUV
) {
const uint16_t column = tileIndex % tileset->columns;
const uint16_t row = tileIndex / tileset->columns;
tilesetPositionGetUV(tileset, column, row, outUV);
}
void tilesetPositionGetUV(
const tileset_t *tileset,
const uint16_t column,
const uint16_t row,
vec4 outUV
) {
outUV[0] = column * tileset->uv[0];
outUV[1] = row * tileset->uv[1];
outUV[2] = outUV[0] + tileset->uv[0];
outUV[3] = outUV[1] + tileset->uv[1];
}

32
src/display/tileset.h Normal file
View File

@@ -0,0 +1,32 @@
/**
* 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 tileset_s {
const uint16_t tileWidth;
const uint16_t tileHeight;
const uint16_t tileCount;
const uint16_t columns;
const uint16_t rows;
const vec2 uv;
const char_t *image;
} tileset_t;
void tilesetTileGetUV(
const tileset_t *tileset,
const uint16_t tileIndex,
vec4 outUV
);
void tilesetPositionGetUV(
const tileset_t *tileset,
const uint16_t column,
const uint16_t row,
vec4 outUV
);

View File

@@ -32,7 +32,7 @@ errorret_t uiInit(void) {
UI.camera.viewType = CAMERA_VIEW_TYPE_MATRIX; UI.camera.viewType = CAMERA_VIEW_TYPE_MATRIX;
glm_mat4_identity(UI.camera.view); glm_mat4_identity(UI.camera.view);
UI.scale = 2.0f; UI.scale = 1.0f;
uiFPSInit(); uiFPSInit();
@@ -54,7 +54,7 @@ void uiRender(void) {
cameraPushMatrix(&UI.camera); cameraPushMatrix(&UI.camera);
uiConsoleRender(); uiConsoleRender();
// uiFPSRender(); uiFPSRender();
spriteBatchFlush(); spriteBatchFlush();
cameraPopMatrix(); cameraPopMatrix();

View File

@@ -21,7 +21,7 @@ void uiConsoleRender(void) {
i--; i--;
continue; continue;
} }
uiTextDraw(0, i * UI_TEXT_TILE_HEIGHT, line, COLOR_WHITE); uiTextDraw(0, i * TILESET_FONT_MINOGRAM.tileHeight, line, COLOR_WHITE);
i--; i--;
} while(i > 0); } while(i > 0);
} }

View File

@@ -17,7 +17,7 @@ errorret_t uiTextInit(void) {
memoryZero(&UI_TEXT, sizeof(uitext_t)); memoryZero(&UI_TEXT, sizeof(uitext_t));
errorChain(assetManagerLoadAsset( errorChain(assetManagerLoadAsset(
"font_minogram.dai", &UI_TEXT.asset, &UI_TEXT.assetRef TILESET_FONT_MINOGRAM.image, &UI_TEXT.asset, &UI_TEXT.assetRef
)); ));
errorOk(); errorOk();
} }
@@ -35,29 +35,25 @@ void uiTextDrawChar(
const color_t color const color_t color
) { ) {
int32_t tileIndex = (int32_t)(c) - UI_TEXT_CHAR_START; int32_t tileIndex = (int32_t)(c) - UI_TEXT_CHAR_START;
if(tileIndex < 0 || tileIndex >= UI_TEXT_TILE_COUNT) { if(tileIndex < 0 || tileIndex >= TILESET_FONT_MINOGRAM.tileCount) {
tileIndex = ((int32_t)'@') - UI_TEXT_CHAR_START; tileIndex = ((int32_t)'@') - UI_TEXT_CHAR_START;
} }
assertTrue( assertTrue(
tileIndex >= 0 && tileIndex <= UI_TEXT_TILE_COUNT, tileIndex >= 0 && tileIndex <= TILESET_FONT_MINOGRAM.tileCount,
"Character is out of bounds for font tiles" "Character is out of bounds for font tiles"
); );
const float_t w = (float)UI_TEXT.asset->alphaImage.texture.width; vec4 uv;
const float_t h = (float)UI_TEXT.asset->alphaImage.texture.height; tilesetTileGetUV(&TILESET_FONT_MINOGRAM, tileIndex, uv);
const int32_t tileX = (tileIndex % UI_TEXT_COLUMN_COUNT);
const int32_t tileY = (tileIndex / UI_TEXT_COLUMN_COUNT);
spriteBatchPush( spriteBatchPush(
&UI_TEXT.asset->alphaImage.texture, &UI_TEXT.asset->alphaImage.texture,
x, y, x, y,
x + UI_TEXT_TILE_WIDTH, y + UI_TEXT_TILE_HEIGHT, x + TILESET_FONT_MINOGRAM.tileWidth,
y + TILESET_FONT_MINOGRAM.tileHeight,
color, color,
(tileX * UI_TEXT_TILE_WIDTH) / w, uv[0], uv[1], uv[2], uv[3]
(tileY * UI_TEXT_TILE_HEIGHT) / h,
((tileX + 1) * UI_TEXT_TILE_WIDTH) / w,
((tileY + 1) * UI_TEXT_TILE_HEIGHT) / h
); );
} }
@@ -77,17 +73,17 @@ void uiTextDraw(
while((c = text[i++]) != '\0') { while((c = text[i++]) != '\0') {
if(c == '\n') { if(c == '\n') {
posX = x; posX = x;
posY += UI_TEXT_TILE_HEIGHT; posY += TILESET_FONT_MINOGRAM.tileHeight;
continue; continue;
} }
if(c == ' ') { if(c == ' ') {
posX += UI_TEXT_TILE_WIDTH; posX += TILESET_FONT_MINOGRAM.tileWidth;
continue; continue;
} }
uiTextDrawChar(posX, posY, c, color); uiTextDrawChar(posX, posY, c, color);
posX += UI_TEXT_TILE_WIDTH; posX += TILESET_FONT_MINOGRAM.tileWidth;
} }
} }
@@ -101,7 +97,7 @@ void uiTextMeasure(
assertNotNull(outHeight, "Output height pointer cannot be NULL"); assertNotNull(outHeight, "Output height pointer cannot be NULL");
int32_t width = 0; int32_t width = 0;
int32_t height = UI_TEXT_TILE_HEIGHT; int32_t height = TILESET_FONT_MINOGRAM.tileHeight;
int32_t lineWidth = 0; int32_t lineWidth = 0;
char_t c; char_t c;
@@ -112,11 +108,11 @@ void uiTextMeasure(
width = lineWidth; width = lineWidth;
} }
lineWidth = 0; lineWidth = 0;
height += UI_TEXT_TILE_HEIGHT; height += TILESET_FONT_MINOGRAM.tileHeight;
continue; continue;
} }
lineWidth += UI_TEXT_TILE_WIDTH; lineWidth += TILESET_FONT_MINOGRAM.tileWidth;
} }
if(lineWidth > width) { if(lineWidth > width) {

View File

@@ -7,16 +7,10 @@
#pragma once #pragma once
#include "asset/assetmanager.h" #include "asset/assetmanager.h"
#include "display/tileset/tileset_font_minogram.h"
#define UI_TEXT_CHAR_START '!' #define UI_TEXT_CHAR_START '!'
#define UI_TEXT_COLUMN_COUNT 16
#define UI_TEXT_ROW_COUNT 6
#define UI_TEXT_TILE_COUNT (UI_TEXT_COLUMN_COUNT*UI_TEXT_ROW_COUNT)
#define UI_TEXT_TILE_WIDTH 6.0f
#define UI_TEXT_TILE_HEIGHT 10.0f
typedef struct { typedef struct {
ref_t assetRef; ref_t assetRef;
asset_t *asset; asset_t *asset;

View File

@@ -3,6 +3,7 @@ import sys
from processimage import processImage from processimage import processImage
from processpalette import processPalette from processpalette import processPalette
from processconfig import processConfig from processconfig import processConfig
from processtileset import processTileset
processedAssets = [] processedAssets = []
@@ -20,8 +21,8 @@ def processAsset(asset):
return processImage(asset) return processImage(asset)
elif t == 'config': elif t == 'config':
return processConfig(asset) return processConfig(asset)
# elif t == 'tileset': elif t == 'tileset':
# return processTileset(asset) return processTileset(asset)
else: else:
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'") print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
sys.exit(1) sys.exit(1)

View File

@@ -64,7 +64,10 @@ def processPalettizedImage(asset):
f.write(data) f.write(data)
outImage = { outImage = {
"imagePath": outputFileRelative,
"files": [ outputFilePath ], "files": [ outputFilePath ],
'width': image.width,
'height': image.height,
} }
return outImage return outImage
@@ -90,6 +93,9 @@ def processAlphaImage(asset):
f.write(data) f.write(data)
outImage = { outImage = {
"files": [ outputFilePath ] "imagePath": outputFileRelative,
"files": [ outputFilePath ],
'width': image.width,
'height': image.height,
} }
return outImage return outImage

View File

@@ -0,0 +1,84 @@
import json
from processimage import processImage
import sys
from assethelpers import getAssetRelativePath
import os
import datetime
from args import args
def processTileset(asset):
print(f"Processing tileset: {asset['path']}")
# We need to determine how big each tile is. This can either be provided as
# an arg of tileWidth/tileHeight or as a count of rows/columns.
# Additionally, if the image has been factored, then the user can provide both
# tile sizes AND cols/rows to indicate the original size of the image.
image = processImage(asset)
tileWidth, tileHeight = None, None
columns, rows = None, None
originalWidth, originalHeight = image['width'], image['height']
if 'tileWidth' in asset['options'] and 'columns' in asset['options']:
tileWidth = int(asset['options']['tileWidth'])
columns = int(asset['options']['columns'])
originalWidth = tileWidth * columns
elif 'tileWidth' in asset['options']:
tileWidth = int(asset['options']['tileWidth'])
columns = image['width'] // tileWidth
elif 'columns' in asset['options']:
columns = int(asset['options']['columns'])
tileWidth = image['width'] // columns
else:
print(f"Error: Tileset {asset['path']} must specify either tileWidth or columns")
sys.exit(1)
if 'tileHeight' in asset['options'] and 'rows' in asset['options']:
tileHeight = int(asset['options']['tileHeight'])
rows = int(asset['options']['rows'])
originalHeight = tileHeight * rows
elif 'tileHeight' in asset['options']:
tileHeight = int(asset['options']['tileHeight'])
rows = image['height'] // tileHeight
elif 'rows' in asset['options']:
rows = int(asset['options']['rows'])
tileHeight = image['height'] // rows
else:
print(f"Error: Tileset {asset['path']} must specify either tileHeight or rows")
sys.exit(1)
fileNameWithoutExtension = os.path.splitext(os.path.basename(asset['path']))[0]
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
tilesetName = fileNameWithoutExtension
tilesetNameUpper = tilesetName.upper()
widthScale = originalWidth / image['width']
heightScale = originalHeight / image['height']
# Create header
data = f"// Tileset Generated for {asset['path']} at {now}\n"
data += f"#pragma once\n"
data += f"#include \"display/tileset.h\"\n\n"
data += f"static const tileset_t TILESET_{tilesetNameUpper} = {{\n"
data += f" .tileWidth = {tileWidth},\n"
data += f" .tileHeight = {tileHeight},\n"
data += f" .tileCount = {columns * rows},\n"
data += f" .columns = {columns},\n"
data += f" .rows = {rows},\n"
data += f" .uv = {{ {widthScale / columns}f, {heightScale / rows}f }},\n"
data += f" .image = {json.dumps(image['imagePath'])},\n"
data += f"}};\n"
# Write Header
outputFile = os.path.join(args.headers_dir, "display", "tileset", f"tileset_{tilesetName}.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, 'w') as f:
f.write(data)
outTileset = {
"image": image,
"headerFile": outputFile,
"files": image['files'],
}
return outTileset