First texture rendering (if broken)

This commit is contained in:
2026-02-06 12:48:49 -06:00
parent 0d56859d94
commit aa5b41fe31
23 changed files with 226 additions and 179 deletions

12
.ci/dolphin/build-gamecube.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
docker build -t myapp:latest -f .ci/dolphin/Dockerfile .
docker run -it -v ./:/workdir myapp:latest /bin/bash -c ' \
export PATH="$DEVKITPPC/bin:$PATH" && \
cd /workdir && \
rm -rf build-gamecube && \
mkdir -p build-gamecube && \
cmake -S. -Bbuild-gamecube -DDUSK_TARGET_SYSTEM=gamecube -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/GameCube.cmake" && \
cd build-gamecube && \
make VERBOSE=1
'
# docker run -it -v ./:/workdir myapp:latest /bin/bash

13
.ci/dolphin/build-wii.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
docker build -t myapp:latest -f .ci/dolphin/Dockerfile .
docker run -it -v ./:/workdir myapp:latest /bin/bash -c ' \
export PATH="$DEVKITPPC/bin:$PATH" && \
cd /workdir && \
rm -rf build-wii && \
mkdir -p build-wii && \
cmake -S. -Bbuild-wii -DDUSK_TARGET_SYSTEM=wii -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/Wii.cmake" && \
cd build-wii && \
make VERBOSE=1 && \
mv ./Dusk.dol ./boot.dol
'
# docker run -it -v ./:/workdir myapp:latest /bin/bash

View File

@@ -1,14 +0,0 @@
#!/bin/bash
docker build -t myapp:latest -f .ci/dolphin/Dockerfile .
docker run -it -v ./:/workdir myapp:latest /bin/bash -c ' \
export PATH="$DEVKITPPC/bin:$PATH" && \
cd /workdir && \
rm -rf build2 && \
mkdir -p build2 && \
# cmake -S. -Bbuild2 -DDUSK_TARGET_SYSTEM=gamecube -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/GameCube.cmake" && \
cmake -S. -Bbuild2 -DDUSK_TARGET_SYSTEM=wii -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/Wii.cmake" && \
cd build2 && \
make VERBOSE=1 && \
cp ./Dusk.dol ./boot.dol
'
# docker run -it -v ./:/workdir myapp:latest /bin/bash

View File

@@ -71,4 +71,17 @@ jobs:
with:
name: dusk-psp
path: build/gitea/
if-no-files-found: error
if-no-files-found: error
build-dolphin:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Install dependencies
run: |
apt-get update
apt-get install -y docker.io
- name: Docker test
run: |
./.ci/dolphin/build-gamecube.sh

3
.gitignore vendored
View File

@@ -103,4 +103,5 @@ yarn.lock
.editor
.venv
/build2
/build2
/build*

View File

@@ -18,7 +18,7 @@ if PLATFORM == "psp" then
inputBind("lstick_right", INPUT_ACTION_RIGHT)
elseif PLATFORM == "gamecube" then
inputBind("start", INPUT_ACTION_RAGEQUIT)
-- inputBind("start", INPUT_ACTION_RAGEQUIT)
-- inputBind("dpad_up", INPUT_ACTION_UP)
-- inputBind("dpad_down", INPUT_ACTION_DOWN)
-- inputBind("dpad_left", INPUT_ACTION_LEFT)
@@ -52,5 +52,5 @@ else
end
end
localeSet(DUSK_LOCALE_EN_US)
-- localeSet(DUSK_LOCALE_EN_US)
sceneSet('scene/initial.dsf')

View File

@@ -11,14 +11,20 @@ module('glm')
screenSetBackground(colorLime())
mapCamera = cameraCreate()
text = "Hello, Dusk!"
x = -35
y = 0
function sceneDispose()
end
function sceneUpdate()
end
function sceneRender()
mapCamera.position = vec3(4, 4, 4)
mapCamera.position = vec3(50, 50, 50)
cameraPushMatrix(mapCamera)
spriteBatchPush(nil, -1, -1, 1, 1, colorBlue())
textDraw(x, y, text, colorBlue())
spriteBatchFlush()
cameraPopMatrix()
end

View File

@@ -3,4 +3,5 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering
add_asset(TILESET minogram.png type=ALPHA tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering
# add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering

View File

@@ -37,6 +37,7 @@
line,
message
);
debugFlush();
abort();
}

View File

@@ -29,11 +29,12 @@ errorret_t assetInit(void) {
DIR *pdir = opendir(*dolphinSearchPath);
if(pdir == NULL) continue;
// Scan if file is present
while(true) {
struct dirent* pent = readdir(pdir);
if(pent == NULL) break;
if(stringCompareInsensitive(pent->d_name, ASSET_FILE) != 0) {
continue;
}
@@ -42,13 +43,13 @@ errorret_t assetInit(void) {
snprintf(
foundPath,
FILENAME_MAX,
"%s%s",
"%s/%s",
*dolphinSearchPath,
ASSET_FILE
);
break;
}
// Close dir.
closedir(pdir);
@@ -56,6 +57,9 @@ errorret_t assetInit(void) {
if(foundPath[0] != '\0') break;
} while(*(++dolphinSearchPath) != NULL);
if(foundPath[0] != '\0') {
}
// Did we find the asset file?
if(foundPath[0] == '\0') {
errorThrow("Failed to find asset file on FAT filesystem.");
@@ -226,27 +230,45 @@ bool_t assetFileExists(const char_t *filename) {
}
errorret_t assetLoad(const char_t *filename, void *output) {
#if DOLPHIN
errorOk();
#endif
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
assertNotNull(output, "Output pointer cannot be NULL.");
// Get file size of the asset.
zip_stat_t st;
zip_stat_init(&st);
if(!zip_stat(ASSET.zip, filename, 0, &st) == 0) {
errorThrow("Failed to stat asset file: %s", filename);
}
// Minimum file size.
zip_int64_t fileSize = (zip_int64_t)st.size;
if(fileSize < sizeof(assetheader_t)) {
errorThrow("Asset file too small to contain header: %s", filename);
}
// Try to open the file
zip_file_t *file = zip_fopen(ASSET.zip, filename, 0);
if(file == NULL) {
errorThrow("Failed to open asset file: %s", filename);
}
// Read the header.
zip_int64_t bytesRemaining = fileSize;
assetheader_t header;
memoryZero(&header, sizeof(assetheader_t));
zip_int64_t bytesRead = zip_fread(file, &header, sizeof(assetheader_t));
if(bytesRead != sizeof(assetheader_t)) {
zip_int64_t bytesRead = zip_fread(
file,
&header,
(zip_uint64_t)sizeof(assetheader_t)
);
if((size_t)bytesRead != sizeof(assetheader_t)) {
zip_fclose(file);
errorThrow("Failed to read asset header for: %s", filename);
}
bytesRemaining -= (zip_uint64_t)bytesRead;
assertTrue(sizeof(assetheader_t) == ASSET_HEADER_SIZE, "Asset header size mismatch.");
assertTrue(bytesRead == ASSET_HEADER_SIZE, "Asset header read size mismatch.");
// Find the asset type based on the header
const assettypedef_t *def = NULL;
@@ -276,20 +298,44 @@ errorret_t assetLoad(const char_t *filename, void *output) {
switch(def->loadStrategy) {
case ASSET_LOAD_STRAT_ENTIRE:
assertNotNull(def->entire, "Asset load function cannot be NULL.");
void *data = memoryAllocate(def->dataSize);
bytesRead = zip_fread(file, data, def->dataSize);
if(bytesRead == 0 || bytesRead > def->dataSize) {
// Must have more to read
if(bytesRemaining <= 0) {
zip_fclose(file);
errorThrow("No data remaining to read for asset: %s", filename);
}
if(bytesRemaining > def->dataSize) {
zip_fclose(file);
errorThrow(
"Asset file has too much data remaining after header: %s",
filename
);
}
// Create space to read the entire asset data
void *data = memoryAllocate(bytesRemaining);
if(!data) {
zip_fclose(file);
errorThrow("Failed to allocate memory for asset data of file: %s", filename);
}
// Read in the asset data.
bytesRead = zip_fread(file, data, bytesRemaining);
if(bytesRead == 0 || bytesRead > bytesRemaining) {
memoryFree(data);
zip_fclose(file);
errorThrow("Failed to read asset data for file: %s", filename);
}
bytesRemaining -= bytesRead;
// Close the file now we have the data
zip_fclose(file);
// Pass to the asset type loader
errorret_t ret = def->entire(data, output);
memoryFree(data);
errorChain(ret);
break;

View File

@@ -40,15 +40,23 @@
static const char_t *ASSET_DOLPHIN_PATHS[] = {
"/",
"/Dusk",
"%s/dusk",
"%s/DUSK",
"%s/apps",
"%s/apps/Dusk",
"%s/apps/dusk",
"%s/apps/DUSK",
"/dusk",
"/DUSK",
"/apps",
"/apps/Dusk",
"/apps/dusk",
"/apps/DUSK",
".",
"./",
"./Dusk",
"./dusk",
"./DUSK",
"./apps",
"./apps/Dusk",
"./apps/dusk",
"./apps/DUSK",
NULL
};
#endif
#define ASSET_FILE "dusk.dsk"

View File

@@ -9,13 +9,21 @@
#include "assert/assert.h"
#include "display/texture.h"
#include "debug/debug.h"
errorret_t assetAlphaImageLoad(void *data, void *output) {
assertNotNull(data, "Data pointer cannot be NULL.");
assertNotNull(output, "Output pointer cannot be NULL.");
debugPrint("Loading ASSET_TYPE_ALPHA_IMAGE asset.\n");
assetalphaimage_t *dataPtr = (assetalphaimage_t *)data;
texture_t *outputPtr = (texture_t *)output;
// Fix endian
dataPtr->width = le32toh(dataPtr->width);
dataPtr->height = le32toh(dataPtr->height);
textureInit(
outputPtr,
dataPtr->width,

View File

@@ -43,6 +43,13 @@ errorret_t assetLanguageInit(
errorThrow("Failed to read language asset header.");
}
// Fix the endianness of the header data.
for(uint32_t i = 0; i < LANG_KEY_COUNT; i++) {
lang->header.strings[i].chunk = le32toh(lang->header.strings[i].chunk);
lang->header.strings[i].offset = le32toh(lang->header.strings[i].offset);
lang->header.strings[i].length = le32toh(lang->header.strings[i].length);
}
lang->chunksOffset = zip_ftell(lang->zip);
if(lang->chunksOffset <= 0) {
zip_fclose(lang->zip);

View File

@@ -55,6 +55,9 @@ errorret_t assetMapChunkHandler(assetcustom_t custom) {
errorThrow("Failed to read chunk asset header.");
}
// Fix endianess if necessary
header.tileCount = le32toh(header.tileCount);
if(header.tileCount != CHUNK_TILE_COUNT) {
zip_fclose(custom.zipFile);
errorThrow(
@@ -107,6 +110,9 @@ errorret_t assetMapChunkHandler(assetcustom_t custom) {
errorThrow("Failed to read chunk model header.");
}
// Fix endianess if necessary
modelHeader.vertexCount = le32toh(modelHeader.vertexCount);
if(
vertexIndex + modelHeader.vertexCount >
CHUNK_VERTEX_COUNT_MAX

View File

@@ -15,6 +15,10 @@ errorret_t assetPaletteImageLoad(void *data, void *output) {
assetpaletteimage_t *assetData = (assetpaletteimage_t *)data;
texture_t *texture = (texture_t *)output;
// Fix endian
assetData->width = le32toh(assetData->width);
assetData->height = le32toh(assetData->height);
textureInit(
texture,

View File

@@ -8,6 +8,8 @@
#include "debug.h"
#if DOLPHIN
#include "display/display.h"
static char_t DEBUG_ERROR_BUFFER[16*1024] = {0};
#endif
void debugPrint(const char_t *message, ...) {
@@ -15,7 +17,6 @@ void debugPrint(const char_t *message, ...) {
va_start(args, message);
vprintf(message, args);
va_end(args);
fflush(stdout);
#if PSP
FILE *file = fopen("ms0:/PSP/GAME/Dusk/debug.log", "a");
@@ -27,36 +28,68 @@ void debugPrint(const char_t *message, ...) {
}
#elif DOLPHIN
if(!DISPLAY.frameBuffer) {
errorret_t ret = displayInit();
if(ret.code != ERROR_OK) {
abort();
}
// append to error buffer
size_t start = strlen(DEBUG_ERROR_BUFFER);
va_start(args, message);
vsnprintf(
DEBUG_ERROR_BUFFER + start,
sizeof(DEBUG_ERROR_BUFFER) - start,
message,
args
);
va_end(args);
#endif
}
void debugFlush() {
#if PSP
// No buffering, so nothing to flush
#elif DOLPHIN
// Either create graphics, or hijack the displays' graphics.
void *xfb = NULL;
GXRModeObj *rmode = NULL;
void *framebuffer;
if(DISPLAY.frameBuffer) {
console_init(
DISPLAY.frameBuffer,
20,
20,
DISPLAY.screenMode->fbWidth,
DISPLAY.screenMode->xfbHeight,
DISPLAY.screenMode->fbWidth * VI_DISPLAY_PIX_SZ
);
} else {
VIDEO_Init();
rmode = VIDEO_GetPreferredMode(NULL);
framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
console_init(
framebuffer,
20,
20,
rmode->fbWidth,
rmode->xfbHeight,
rmode->fbWidth*VI_DISPLAY_PIX_SZ
);
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(framebuffer);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
}
console_init(
DISPLAY.frameBuffer,
20,
20,
DISPLAY.screenMode->fbWidth,
DISPLAY.screenMode->xfbHeight,
DISPLAY.screenMode->fbWidth * VI_DISPLAY_PIX_SZ
);
// Printf
va_start(args, message);
vprintf(message, args);
va_end(args);
printf("\nPress START to exit...");
printf("SOB\n");
printf(DEBUG_ERROR_BUFFER);
printf("\nEOB.");
while(SYS_MainLoop()) {
VIDEO_WaitVSync();
PAD_ScanPads();
int buttonsDown = PAD_ButtonsDown(0);
if (buttonsDown & PAD_BUTTON_START) {
exit(0);
}
}
#else
fflush(stdout);
#endif
}

View File

@@ -14,4 +14,9 @@
* @param message The message format string.
* @param ... Additional arguments for the format string.
*/
void debugPrint(const char_t *message, ...);
void debugPrint(const char_t *message, ...);
/**
* Flushes the debug output buffer.
*/
void debugFlush();

View File

@@ -139,7 +139,7 @@ errorret_t displayInit(void) {
quadInit();
frameBufferInitBackbuffer();
spriteBatchInit();
// errorChain(textInit());
errorChain(textInit());
screenInit();
errorOk();
@@ -193,46 +193,6 @@ errorret_t displayUpdate(void) {
);
errorChain(sceneRender());
texture_t texture;
// color_t colors[4 * 4] = {
// COLOR_RED_4B, COLOR_GREEN_4B, COLOR_BLUE_4B, COLOR_YELLOW_4B,
// COLOR_CYAN_4B, COLOR_MAGENTA_4B, COLOR_WHITE_4B, COLOR_BLACK_4B,
// COLOR_ORANGE_4B, COLOR_PURPLE_4B, COLOR_GRAY_4B, COLOR_BROWN_4B,
// COLOR_PINK_4B, COLOR_LIME_4B, COLOR_NAVY_4B, COLOR_TEAL_4B
// };
// texturedata_t data = {
// .rgba = { .colors = colors }
// };
// textureInit(&texture, 4, 4, TEXTURE_FORMAT_RGBA, data);
uint8_t alphaData[8 * 4] = {
255, 255, 255, 255, 255, 255, 255, 255,
0, 0, 255, 0, 0, 255, 0, 0,
255, 255, 255, 255, 255, 255, 255, 255,
0, 0, 0, 255, 255, 0, 0, 0,
};
texturedata_t data = {
.alpha = { .data = alphaData }
};
textureInit(&texture, 8, 4, TEXTURE_FORMAT_ALPHA, data);
camera_t camera;
cameraInit(&camera);
cameraPushMatrix(&camera);
spriteBatchClear();
spriteBatchPush(
&texture,
-1, -1,
1, 1,
COLOR_WHITE_4B,
0, 0,
1, 1
);
spriteBatchFlush();
cameraPopMatrix();
// Render UI
// uiRender();
@@ -255,8 +215,6 @@ errorret_t displayUpdate(void) {
// if(DISPLAY.screenMode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
#endif
textureDispose(&texture);
// For now, we just return an OK error.
errorOk();
}

View File

@@ -33,6 +33,9 @@
#include <ogcsys.h>
#include <gccore.h>
#include <malloc.h>
#include <sys/endian.h>
#else
#include <endian.h>
#endif
typedef bool bool_t;

View File

@@ -31,7 +31,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
// Init systems. Order is important.
timeInit();
inputInit();
// errorChain(assetInit());
errorChain(assetInit());
errorChain(localeManagerInit());
errorChain(scriptManagerInit());
errorChain(displayInit());
@@ -43,47 +43,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
// Run the initial script.
scriptcontext_t ctx;
errorChain(scriptContextInit(&ctx));
// errorChain(scriptContextExecFile(&ctx, "init.dsf"));
errorChain(scriptContextExec(&ctx,
"module('platform')\n"
"module('input')\n"
"module('scene')\n"
"module('locale')\n"
"if PLATFORM == \"psp\" then\n"
" inputBind(\"up\", INPUT_ACTION_UP)\n"
" inputBind(\"down\", INPUT_ACTION_DOWN)\n"
" inputBind(\"left\", INPUT_ACTION_LEFT)\n"
" inputBind(\"right\", INPUT_ACTION_RIGHT)\n"
" inputBind(\"circle\", INPUT_ACTION_CANCEL)\n"
" inputBind(\"cross\", INPUT_ACTION_ACCEPT)\n"
" inputBind(\"select\", INPUT_ACTION_RAGEQUIT)\n"
" inputBind(\"lstick_up\", INPUT_ACTION_UP)\n"
" inputBind(\"lstick_down\", INPUT_ACTION_DOWN)\n"
" inputBind(\"lstick_left\", INPUT_ACTION_LEFT)\n"
" inputBind(\"lstick_right\", INPUT_ACTION_RIGHT)\n"
// "elseif PLATFORM == \"gamecube\" then\n"
// " inputBind(\"start\", INPUT_ACTION_RAGEQUIT)\n"
"else\n"
" if INPUT_KEYBOARD then\n"
" inputBind(\"w\", INPUT_ACTION_UP)\n"
" inputBind(\"s\", INPUT_ACTION_DOWN)\n"
" inputBind(\"a\", INPUT_ACTION_LEFT)\n"
" inputBind(\"d\", INPUT_ACTION_RIGHT)\n"
" inputBind(\"left\", INPUT_ACTION_LEFT)\n"
" inputBind(\"right\", INPUT_ACTION_RIGHT)\n"
" inputBind(\"up\", INPUT_ACTION_UP)\n"
" inputBind(\"down\", INPUT_ACTION_DOWN)\n"
" inputBind(\"enter\", INPUT_ACTION_ACCEPT)\n"
" inputBind(\"e\", INPUT_ACTION_ACCEPT)\n"
" inputBind(\"q\", INPUT_ACTION_CANCEL)\n"
" inputBind(\"escape\", INPUT_ACTION_RAGEQUIT)\n"
" end \n"
"end\n"
// "localeSet(DUSK_LOCALE_EN_US)\n"
// "sceneSet('scene/initial.dsf')\n"
));
errorChain(scriptContextExecFile(&ctx, "init.dsf"));
scriptContextDispose(&ctx);
errorOk();

View File

@@ -14,7 +14,6 @@ localemanager_t LOCALE;
errorret_t localeManagerInit() {
memoryZero(&LOCALE, sizeof(localemanager_t));
// errorChain(localeManagerSetLocale(DUSK_LOCALE_EN_US));
errorOk();
}

View File

@@ -75,32 +75,7 @@ errorret_t sceneSet(const char_t *script) {
// Create a new script context.
errorChain(scriptContextInit(&SCENE.scriptContext));
// errorChain(scriptContextExecFile(&SCENE.scriptContext, script));
errorChain(scriptContextExec(&SCENE.scriptContext,
"module('spritebatch')\n"
"module('camera')\n"
"module('color')\n"
"module('text')\n"
"module('screen')\n"
"module('time')\n"
"module('map')\n"
"module('glm')\n"
"screenSetBackground(colorLime())\n"
"mapCamera = cameraCreate()\n"
"text = 'Hello World'\n"
"function sceneDispose()\n"
"end\n"
"function sceneUpdate()\n"
"end\n"
"function sceneRender()\n"
" mapCamera.position = vec3(32, 32, 32)\n"
" cameraPushMatrix(mapCamera)\n"
" spriteBatchPush(nil, -10, -10, 10, 10, colorBlue())\n"
" spriteBatchFlush()\n"
" cameraPopMatrix()\n"
"end\n"
));
errorChain(scriptContextExecFile(&SCENE.scriptContext, script));
errorOk();
}
@@ -111,6 +86,7 @@ void sceneDispose(void) {
const char_t *strErr = lua_tostring(SCENE.scriptContext.luaState, -1);
lua_pop(SCENE.scriptContext.luaState, 1);
debugPrint("Failed to call function '%s': %s\n", "sceneDispose", strErr);
debugFlush();
}
} else {
lua_pop(SCENE.scriptContext.luaState, 1);

View File

@@ -37,6 +37,7 @@ int moduleSysPrint(lua_State *L) {
luaL_pushresult(&b);
const char *msg = lua_tostring(L, -1);
debugPrint("%s\n", msg);
debugFlush();
return 0; // no values returned to Lua
}