diff --git a/assets/main_palette.dpf b/assets/main_palette.dpf new file mode 100644 index 0000000..bae3908 Binary files /dev/null and b/assets/main_palette.dpf differ diff --git a/assets/scene/minesweeper.lua b/assets/scene/minesweeper.lua index 2df6492..9c85b4d 100644 --- a/assets/scene/minesweeper.lua +++ b/assets/scene/minesweeper.lua @@ -198,6 +198,8 @@ function sceneRender() 0, 0, 1, 1 ) + + textDraw(10, 10, "Minesweeper") -- centerX = math.floor(screenGetWidth() / 2) -- centerY = math.floor(screenGetHeight() / 2) diff --git a/assets/ui/minogram.dpt b/assets/ui/minogram.dpt index 223156e..9fb8c56 100644 Binary files a/assets/ui/minogram.dpt and b/assets/ui/minogram.dpt differ diff --git a/src/asset/asset.c b/src/asset/asset.c index bf99078..06f8b38 100644 --- a/src/asset/asset.c +++ b/src/asset/asset.c @@ -304,7 +304,11 @@ errorret_t assetLoad(const char_t *filename, void *output) { zip_fclose(file); // Pass to the asset type loader - errorret_t ret = def->entire(data, output); + assetentire_t entire = { + .data = data, + .output = output + }; + errorret_t ret = def->entire(entire); memoryFree(data); errorChain(ret); diff --git a/src/asset/assettype.h b/src/asset/assettype.h index 91de6d2..7802a98 100644 --- a/src/asset/assettype.h +++ b/src/asset/assettype.h @@ -7,6 +7,8 @@ #pragma once #include "type/assettexture.h" +#include "type/assetpalette.h" +#include "type/assettileset.h" #include "type/assetlanguage.h" #include "type/assetscript.h" #include "type/assetmap.h" @@ -17,6 +19,8 @@ typedef enum { ASSET_TYPE_NULL, ASSET_TYPE_TEXTURE, + ASSET_TYPE_PALETTE, + ASSET_TYPE_TILESET, ASSET_TYPE_LANGUAGE, ASSET_TYPE_SCRIPT, ASSET_TYPE_MAP, @@ -30,6 +34,11 @@ typedef enum { ASSET_LOAD_STRAT_CUSTOM } assetloadstrat_t; +typedef struct assetentire_s { + void *data; + void *output; +} assetentire_t; + typedef struct assetcustom_s { zip_file_t *zipFile; void *output; @@ -40,7 +49,7 @@ typedef struct { const size_t dataSize; const assetloadstrat_t loadStrategy; union { - errorret_t (*entire)(void *data, void *output); + errorret_t (*entire)(assetentire_t entire); errorret_t (*custom)(assetcustom_t custom); }; } assettypedef_t; @@ -57,6 +66,20 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = { .entire = assetTextureLoad }, + [ASSET_TYPE_PALETTE] = { + .extension = "dpf", + .loadStrategy = ASSET_LOAD_STRAT_ENTIRE, + .dataSize = sizeof(palette_t), + .entire = assetPaletteLoad + }, + + [ASSET_TYPE_TILESET] = { + .extension = "dtf", + .loadStrategy = ASSET_LOAD_STRAT_ENTIRE, + .dataSize = sizeof(assettileset_t), + .entire = assetTilesetLoad + }, + [ASSET_TYPE_LANGUAGE] = { .extension = "DLF", .loadStrategy = ASSET_LOAD_STRAT_CUSTOM, diff --git a/src/asset/type/CMakeLists.txt b/src/asset/type/CMakeLists.txt index b43aa18..81a18ad 100644 --- a/src/asset/type/CMakeLists.txt +++ b/src/asset/type/CMakeLists.txt @@ -7,6 +7,8 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC assettexture.c + assetpalette.c + assettileset.c assetlanguage.c assetscript.c assetmap.c diff --git a/src/asset/type/assetpalette.c b/src/asset/type/assetpalette.c new file mode 100644 index 0000000..9951885 --- /dev/null +++ b/src/asset/type/assetpalette.c @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "assetpalette.h" +#include "asset/assettype.h" +#include "assert/assert.h" + +errorret_t assetPaletteLoad(assetentire_t entire) { + assertNotNull(entire.data, "Data pointer cannot be NULL."); + assertNotNull(entire.output, "Output pointer cannot be NULL."); + + assetpalette_t *assetData = (assetpalette_t *)entire.data; + palette_t *palette = (palette_t *)entire.output; + + // Read header and version (first 4 bytes) + if( + assetData->header[0] != 'D' || + assetData->header[1] != 'P' || + assetData->header[2] != 'F' + ) { + errorThrow("Invalid palette header"); + } + + // Version (can only be 1 atm) + if(assetData->version != 0x01) { + errorThrow("Unsupported palette version"); + } + + // Check color count. + if( + assetData->colorCount == 0 || + assetData->colorCount > PALETTE_COLOR_COUNT_MAX + ) { + errorThrow("Invalid palette color count"); + } + + paletteInit( + palette, + assetData->colorCount, + assetData->colors + ); + errorOk(); +} \ No newline at end of file diff --git a/src/asset/type/assetpalette.h b/src/asset/type/assetpalette.h new file mode 100644 index 0000000..82ed2e0 --- /dev/null +++ b/src/asset/type/assetpalette.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" +#include "display/palette/palette.h" + +typedef struct assetentire_s assetentire_t; + +#pragma pack(push, 1) +typedef struct { + char_t header[3]; + uint8_t version; + + uint8_t colorCount; + color_t colors[PALETTE_COLOR_COUNT_MAX]; +} assetpalette_t; +#pragma pack(pop) + +/** + * Loads a palette from the given data pointer into the output palette. + * + * @param entire Data received from the asset loader system. + * @return An error code. + */ +errorret_t assetPaletteLoad(assetentire_t entire); \ No newline at end of file diff --git a/src/asset/type/assettexture.c b/src/asset/type/assettexture.c index 3f6f138..c2775b5 100644 --- a/src/asset/type/assettexture.c +++ b/src/asset/type/assettexture.c @@ -6,15 +6,16 @@ */ #include "assettexture.h" +#include "asset/assettype.h" #include "assert/assert.h" #include "display/texture.h" -errorret_t assetTextureLoad(void *data, void *output) { - assertNotNull(data, "Data pointer cannot be NULL."); - assertNotNull(output, "Output pointer cannot be NULL."); +errorret_t assetTextureLoad(assetentire_t entire) { + assertNotNull(entire.data, "Data pointer cannot be NULL."); + assertNotNull(entire.output, "Output pointer cannot be NULL."); - assettexture_t *assetData = (assettexture_t *)data; - texture_t *texture = (texture_t *)output; + assettexture_t *assetData = (assettexture_t *)entire.data; + texture_t *texture = (texture_t *)entire.output; // Read header and version (first 4 bytes) if( diff --git a/src/asset/type/assettexture.h b/src/asset/type/assettexture.h index 1acbf4e..258101a 100644 --- a/src/asset/type/assettexture.h +++ b/src/asset/type/assettexture.h @@ -14,6 +14,8 @@ ASSET_TEXTURE_WIDTH_MAX * ASSET_TEXTURE_HEIGHT_MAX \ ) +typedef struct assetentire_s assetentire_t; + #pragma pack(push, 1) typedef struct { char_t header[3]; @@ -34,4 +36,4 @@ typedef struct { * @param output Pointer to the texture_t to load the image into. * @return An error code. */ -errorret_t assetTextureLoad(void *data, void *output); \ No newline at end of file +errorret_t assetTextureLoad(assetentire_t entire); \ No newline at end of file diff --git a/src/asset/type/assettileset.c b/src/asset/type/assettileset.c new file mode 100644 index 0000000..04469cb --- /dev/null +++ b/src/asset/type/assettileset.c @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "asset/asset.h" +#include "assert/assert.h" +#include "display/tileset/tileset.h" + +errorret_t assetTilesetLoad(assetentire_t entire) { + assertNotNull(entire.data, "Asset data cannot be null"); + assertNotNull(entire.output, "Asset output cannot be null"); + + assettileset_t *tilesetData = (assettileset_t *)entire.data; + tileset_t *tileset = (tileset_t *)entire.output; + + if( + tilesetData->header[0] != 'D' || + tilesetData->header[1] != 'T' || + tilesetData->header[2] != 'F' + ) { + errorThrow("Invalid tileset header"); + } + + if(tilesetData->version != 0x00) { + errorThrow("Unsupported tileset version"); + } + + errorThrow("unfinished"); +} \ No newline at end of file diff --git a/src/asset/type/assettileset.h b/src/asset/type/assettileset.h new file mode 100644 index 0000000..0bee407 --- /dev/null +++ b/src/asset/type/assettileset.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +#pragma pack(push, 1) +typedef struct { + char_t header[3]; + uint8_t version; +} assettileset_t; +#pragma pack(pop) + +/** + * Loads a tileset from the given data pointer into the output tileset. + * + * @param entire Data received from the asset loader system. + * @return An error code. + */ +errorret_t assetTilesetLoad(assetentire_t entire); \ No newline at end of file diff --git a/src/display/display.c b/src/display/display.c index 624e6c8..0520af2 100644 --- a/src/display/display.c +++ b/src/display/display.c @@ -17,11 +17,8 @@ #include "display/text.h" #include "assert/assert.h" #include "util/memory.h" - -#include -#include -#include -#include +#include "util/string.h" +#include "asset/asset.h" display_t DISPLAY = { 0 }; @@ -79,6 +76,36 @@ errorret_t displayInit(void) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); + // Get and validate GL state. + GLenum err = glGetError(); + if(err != GL_NO_ERROR) { + assertUnreachable("GL Error before checking support"); + } + + // Check if paletted textures are supported. + #if PSP + DISPLAY.usingShaderedPalettes = false; + #else + GLint mask = 0; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + if(mask & GL_CONTEXT_CORE_PROFILE_BIT) { + GLint numExtens = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtens); + for(GLint i = 0; i < numExtens; ++i) { + const char* e = (const char*)glGetStringi(GL_EXTENSIONS, i); + if(!e) continue; + if(stringCompare(e, "GL_EXT_paletted_texture") != 0) continue; + DISPLAY.usingShaderedPalettes = false; + break; + } + } else { + const char* ext = (const char*)glGetString(GL_EXTENSIONS); + DISPLAY.usingShaderedPalettes = ( + ext && strstr(ext, "GL_EXT_paletted_texture") + ); + } + #endif + #elif DOLPHIN VIDEO_Init(); DISPLAY.screenMode = VIDEO_GetPreferredMode(NULL); @@ -155,6 +182,7 @@ errorret_t displayInit(void) { frameBufferInitBackbuffer(); spriteBatchInit(); errorChain(textInit()); + errorChain(assetLoad("main_palette.dpf", &PALETTES[0])); screenInit(); errorOk(); diff --git a/src/display/display.h b/src/display/display.h index 4c40be1..f62b7c8 100644 --- a/src/display/display.h +++ b/src/display/display.h @@ -15,6 +15,7 @@ typedef struct { #if DISPLAY_SDL2 SDL_Window *window; SDL_GLContext glContext; + bool_t usingShaderedPalettes; #elif DOLPHIN void *frameBuffer[2];// Double-Bufferred diff --git a/src/display/palette/palette.c b/src/display/palette/palette.c index 288a243..c556551 100644 --- a/src/display/palette/palette.c +++ b/src/display/palette/palette.c @@ -5,4 +5,39 @@ * https://opensource.org/licenses/MIT */ -#include "palette.h" \ No newline at end of file +#include "palette.h" +#include "assert/assert.h" +#include "util/memory.h" + +palette_t PALETTES[PALETTE_COUNT_MAX] = { 0 }; + +void paletteInit( + palette_t *palette, + const uint8_t colorCount, + const color_t *colors +) { + assertNotNull(palette, "Palette cannot be NULL"); + assertTrue(colorCount > 0, "Color count must be greater than 0"); + assertNotNull(colors, "Colors array cannot be NULL"); + assertTrue(colorCount <= PALETTE_COLOR_COUNT_MAX, "Color count too big"); + assertTrue( + palette - PALETTES < PALETTE_COUNT_MAX, + "Palette index out of range" + ); + + memoryZero(palette, sizeof(palette_t)); + + palette->colorCount = colorCount; + memoryCopy(colors, palette->colors, colorCount * sizeof(color_t)); +} + +void paletteBind(palette_t *palette, const uint8_t slot) { + assertNotNull(palette, "Palette cannot be NULL"); + assertTrue(slot < PALETTE_COUNT_MAX, "Palette slot out of range"); + + // Nothing yet. +} + +void paletteDispose(palette_t *palette) { + assertNotNull(palette, "Palette cannot be NULL"); +} \ No newline at end of file diff --git a/src/display/palette/palette.h b/src/display/palette/palette.h index fe8a594..3b085b8 100644 --- a/src/display/palette/palette.h +++ b/src/display/palette/palette.h @@ -8,11 +8,40 @@ #pragma once #include "display/color.h" -#define PALETTE_COUNT 4 +#define PALETTE_COUNT_MAX 4 +#define PALETTE_COLOR_COUNT_MAX 0xFF typedef struct { - const uint8_t colorCount; - const color_t *colors; + uint8_t colorCount; + color_t colors[PALETTE_COLOR_COUNT_MAX]; } palette_t; -extern palette_t PALETTES[PALETTE_COUNT]; \ No newline at end of file +extern palette_t PALETTES[PALETTE_COUNT_MAX]; + +/** + * Initializes a palette with the given colors. + * + * @param palette The palette to initialize. + * @param colorCount The number of colors in the palette. + * @param colors An array of colors for the palette. + */ +void paletteInit( + palette_t *palette, + const uint8_t colorCount, + const color_t *colors +); + +/** + * Binds a palette for use in rendering. + * + * @param palette The palette to bind. + * @param slot The slot to bind the palette to. + */ +void paletteBind(palette_t *palette, const uint8_t slot); + +/** + * Disposes of a palette, freeing any associated resources. + * + * @param palette The palette to dispose. + */ +void paletteDispose(palette_t *palette); \ No newline at end of file diff --git a/src/display/text.c b/src/display/text.c index c8969b8..8453e2e 100644 --- a/src/display/text.c +++ b/src/display/text.c @@ -16,6 +16,7 @@ tileset_t DEFAULT_FONT_TILESET; errorret_t textInit(void) { errorChain(assetLoad("ui/minogram.dpt", &DEFAULT_FONT_TEXTURE)); + errorChain(assetLoad("ui/minogram.dtf", &DEFAULT_FONT_TILESET)); errorOk(); } diff --git a/src/display/texture.c b/src/display/texture.c index e198f14..d2ac69e 100644 --- a/src/display/texture.c +++ b/src/display/texture.c @@ -9,8 +9,7 @@ #include "assert/assert.h" #include "util/memory.h" #include "util/math.h" -#include "util/string.h" -#include "debug/debug.h" +#include "display/display.h" const texture_t *TEXTURE_BOUND = NULL; @@ -44,98 +43,21 @@ void textureInit( ); break; - // case TEXTURE_FORMAT_ALPHA: { - // assertNotNull(data.alpha.data, "Alpha texture data cannot be NULL"); - // #if PSP - // // PSP kinda broken with alpha textures, so we convert it to paletted - // // texture here. We also crunch the amount of alpha values. - // #define PSP_ALPHA_PALETTE_CRUNCH 4 - // #define PSP_ALPHA_PALETTE_COUNT (256 / PSP_ALPHA_PALETTE_CRUNCH) - - // color_t paletteColors[PSP_ALPHA_PALETTE_COUNT]; - // for(uint8_t i = 0; i < PSP_ALPHA_PALETTE_COUNT; i++) { - // paletteColors[i].r = 0xFF; - // paletteColors[i].g = 0xFF; - // paletteColors[i].b = 0xFF; - // paletteColors[i].a = i * PSP_ALPHA_PALETTE_CRUNCH; - // } - - // // Generate indexes, crunching the alpha values to fit. - // uint8_t indexes[width * height]; - // for(int32_t i = 0; i < width * height; i++) { - // uint8_t alpha = data.alpha.data[i] / PSP_ALPHA_PALETTE_CRUNCH;; - // indexes[i] = alpha; - // } - - // palette_t palette = { - // .colorCount = PSP_ALPHA_PALETTE_COUNT, - // .colors = paletteColors - // }; - - // return textureInit( - // texture, width, height, TEXTURE_FORMAT_PALETTE, - // (texturedata_t){ - // .palette = { .palette = &palette, .data = indexes } - // } - // ); - // #else - // glTexImage2D( - // GL_TEXTURE_2D, 0, format, width, height, 0, - // format, GL_UNSIGNED_BYTE, (void*)data.alpha.data - // ); - // #endif - // break; - // } - case TEXTURE_FORMAT_PALETTE: assertNotNull(data.paletteData, "Palette texture data cannot be NULL"); - // Get and validate the palette. - GLenum err = glGetError(); - if(err != GL_NO_ERROR) { - assertUnreachable("GL Error before setting color table"); - } - - // Do we support paletted textures? - bool_t havePalTex = false; - - #if PSP - havePalTex = true; - #else - GLint mask = 0; - glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); - if(mask & GL_CONTEXT_CORE_PROFILE_BIT) { - GLint numExtens = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExtens); - for(GLint i = 0; i < numExtens; ++i) { - const char* e = (const char*)glGetStringi(GL_EXTENSIONS, i); - if(!e) continue; - if(stringCompare(e, "GL_EXT_paletted_texture") != 0) continue; - havePalTex = true; - break; - } - } else { - const char* ext = (const char*)glGetString(GL_EXTENSIONS); - havePalTex = ext && strstr(ext, "GL_EXT_paletted_texture"); + if(DISPLAY.usingShaderedPalettes) { + // Palette textures not supported, convert to GL_RED style texture + // so shader can perform the lookup. + uint8_t formatted[width * height]; + for(int32_t i = 0; i < width * height; i++) { + uint8_t index = data.paletteData[i]; + formatted[i] = index; } - #endif - - if(!havePalTex) { - assertUnreachable("Paletted textures not supported on this platform"); - // Not supported, convert to RGBA using lookup - // color_t formatted[width * height]; - // for(int32_t i = 0; i < width * height; i++) { - // uint8_t index = data..data[i]; - // assertTrue( - // index < data.palette.palette->colorCount, - // "Palette index out of range" - // ); - // formatted[i] = data.palette.palette->colors[index]; - // } - // glTexImage2D( - // GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - // GL_RGBA, GL_UNSIGNED_BYTE, (void*)formatted - // ); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_R, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, (void*)formatted + ); } else { glTexImage2D( GL_TEXTURE_2D, diff --git a/tools/palette-indexer.html b/tools/palette-indexer.html index f54ba48..4d8d901 100644 --- a/tools/palette-indexer.html +++ b/tools/palette-indexer.html @@ -396,6 +396,8 @@ // Width and Height elError.style.display = 'none'; + + // Convert from little endian imageWidth = dataView.getUint32(3); imageHeight = dataView.getUint32(7); @@ -631,12 +633,9 @@ // Create Dusk Palette File (.dpf) const header = new Uint8Array([0x44, 0x50, 0x46, 0x01]); // 'DPF' + version 1 + number of colors (4 bytes) - const colorCount = palette.length; - const colorCountBytes = new Uint8Array(4); - colorCountBytes[0] = (colorCount >> 24) & 0xFF; - colorCountBytes[1] = (colorCount >> 16) & 0xFF; - colorCountBytes[2] = (colorCount >> 8) & 0xFF; - colorCountBytes[3] = colorCount & 0xFF; + // Color count (1 byte) + const colorCountBytes = new Uint8Array(1); + colorCountBytes[0] = palette.length; // add color data (palette.length * 4 bytes) const colorData = new Uint8Array(palette.length * 4); @@ -661,19 +660,9 @@ btnDownloadImage.addEventListener('click', () => { const header = new Uint8Array([0x44, 0x50, 0x54, 0x01]); // 'DPT' + version 1 - // Width - const widthBytes = new Uint8Array(4); - widthBytes[3] = (imageWidth >> 24) & 0xFF; - widthBytes[2] = (imageWidth >> 16) & 0xFF; - widthBytes[1] = (imageWidth >> 8) & 0xFF; - widthBytes[0] = imageWidth & 0xFF; - - // Height - const heightBytes = new Uint8Array(4); - heightBytes[3] = (imageHeight >> 24) & 0xFF; - heightBytes[2] = (imageHeight >> 16) & 0xFF; - heightBytes[1] = (imageHeight >> 8) & 0xFF; - heightBytes[0] = imageHeight & 0xFF; + // Dimensions + const widthBytes = new Uint8Array([ imageWidth ]); + const heightBytes = new Uint32Array([ imageHeight ]); // add indexed image data (imageWidth * imageHeight bytes) const imageData = new Uint8Array(indexedImage.length); diff --git a/tools/tileset-creator.html b/tools/tileset-creator.html new file mode 100644 index 0000000..ce8ba12 --- /dev/null +++ b/tools/tileset-creator.html @@ -0,0 +1,335 @@ + + + + + + Dusk Tools / Tileset Creator + + + + + +

Dusk Tileset Creator

+

+ Tool to create tilesets for textures. Currently only supports well sliced + tilesets (those with fixed dimensions essentially). In the future, may + support more freeform tilesets. +

+ +
+

Tileset Settings

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

Preview

+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + + \ No newline at end of file