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)
if(NOT DEFINED DUSK_TARGET_SYSTEM)
# set(DUSK_TARGET_SYSTEM "linux")
set(DUSK_TARGET_SYSTEM "psp")
set(DUSK_TARGET_SYSTEM "linux")
# set(DUSK_TARGET_SYSTEM "psp")
endif()
# Prep cache

View File

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

View File

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

View File

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

View File

@@ -14,18 +14,24 @@
#include "asset/assetmanager.h"
#include "assert/assert.h"
#include "display/tileset/tileset_entities.h"
sceneoverworld_t SCENE_OVERWORLD;
asset_t *testAsset;
ref_t testAssetRef;
void sceneOverworldInit(void) {
errorret_t sceneOverworldInit(void) {
cameraInit(&SCENE_OVERWORLD.camera);
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->flags |= SCENE_FLAG_ACTIVE | SCENE_FLAG_VISIBLE;
assetManagerLoadAsset("entities.dpi", &testAsset, &testAssetRef);
errorChain(assetManagerLoadAsset(
TILESET_ENTITIES.image, &testAsset, &testAssetRef
));
errorOk();
}
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_NULL, "Cannot have NULL entity type");
vec4 uv;
tilesetTileGetUV(&TILESET_ENTITIES, 0, uv);
// For now, just draw a placeholder quad.
spriteBatchPush(
&testAsset->paletteImage.texture,
entity->x, entity->y, entity->x + 32.0f, entity->y + 32.0f,
COLOR_WHITE, 0.0f, 0.0f, 0.125f, 0.125f
entity->x, entity->y,
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
#include "display/camera.h"
#include "rpg/world/map.h"
#include "error/error.h"
typedef struct {
camera_t camera;
@@ -18,7 +19,7 @@ extern sceneoverworld_t SCENE_OVERWORLD;
/**
* Initialize the overworld scene.
*/
void sceneOverworldInit(void);
errorret_t sceneOverworldInit(void);
/**
* Update the overworld scene.

View File

@@ -7,12 +7,13 @@
#pragma once
#include "dusk.h"
#include "error/error.h"
#define SCENE_FLAG_VISIBLE (1 << 0)
#define SCENE_FLAG_ACTIVE (1 << 1)
typedef struct {
void (*init)(void);
errorret_t (*init)(void);
void (*update)(void);
void (*render)(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];
if(initial->init != NULL) initial->init();
if(initial->init != NULL) errorChain(initial->init());
errorOk();
}
void sceneManagerUpdate(void) {

View File

@@ -15,7 +15,7 @@ typedef struct {
extern scenemanager_t SCENE_MANAGER;
extern scene_t SCENE_MANAGER_SCENES[SCENE_TYPE_COUNT];
void sceneManagerInit(void);
errorret_t sceneManagerInit(void);
void sceneManagerUpdate(void);
void sceneManagerRender(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;
glm_mat4_identity(UI.camera.view);
UI.scale = 2.0f;
UI.scale = 1.0f;
uiFPSInit();
@@ -54,7 +54,7 @@ void uiRender(void) {
cameraPushMatrix(&UI.camera);
uiConsoleRender();
// uiFPSRender();
uiFPSRender();
spriteBatchFlush();
cameraPopMatrix();

View File

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

View File

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

View File

@@ -7,16 +7,10 @@
#pragma once
#include "asset/assetmanager.h"
#include "display/tileset/tileset_font_minogram.h"
#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 {
ref_t assetRef;
asset_t *asset;

View File

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

View File

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