DEbug not working so moving pcs
This commit is contained in:
BIN
assets/main_palette.dpf
Normal file
BIN
assets/main_palette.dpf
Normal file
Binary file not shown.
@@ -199,6 +199,8 @@ function sceneRender()
|
|||||||
1, 1
|
1, 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
textDraw(10, 10, "Minesweeper")
|
||||||
|
|
||||||
-- centerX = math.floor(screenGetWidth() / 2)
|
-- centerX = math.floor(screenGetWidth() / 2)
|
||||||
-- centerY = math.floor(screenGetHeight() / 2)
|
-- centerY = math.floor(screenGetHeight() / 2)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -304,7 +304,11 @@ errorret_t assetLoad(const char_t *filename, void *output) {
|
|||||||
zip_fclose(file);
|
zip_fclose(file);
|
||||||
|
|
||||||
// Pass to the asset type loader
|
// 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);
|
memoryFree(data);
|
||||||
|
|
||||||
errorChain(ret);
|
errorChain(ret);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "type/assettexture.h"
|
#include "type/assettexture.h"
|
||||||
|
#include "type/assetpalette.h"
|
||||||
|
#include "type/assettileset.h"
|
||||||
#include "type/assetlanguage.h"
|
#include "type/assetlanguage.h"
|
||||||
#include "type/assetscript.h"
|
#include "type/assetscript.h"
|
||||||
#include "type/assetmap.h"
|
#include "type/assetmap.h"
|
||||||
@@ -17,6 +19,8 @@ typedef enum {
|
|||||||
ASSET_TYPE_NULL,
|
ASSET_TYPE_NULL,
|
||||||
|
|
||||||
ASSET_TYPE_TEXTURE,
|
ASSET_TYPE_TEXTURE,
|
||||||
|
ASSET_TYPE_PALETTE,
|
||||||
|
ASSET_TYPE_TILESET,
|
||||||
ASSET_TYPE_LANGUAGE,
|
ASSET_TYPE_LANGUAGE,
|
||||||
ASSET_TYPE_SCRIPT,
|
ASSET_TYPE_SCRIPT,
|
||||||
ASSET_TYPE_MAP,
|
ASSET_TYPE_MAP,
|
||||||
@@ -30,6 +34,11 @@ typedef enum {
|
|||||||
ASSET_LOAD_STRAT_CUSTOM
|
ASSET_LOAD_STRAT_CUSTOM
|
||||||
} assetloadstrat_t;
|
} assetloadstrat_t;
|
||||||
|
|
||||||
|
typedef struct assetentire_s {
|
||||||
|
void *data;
|
||||||
|
void *output;
|
||||||
|
} assetentire_t;
|
||||||
|
|
||||||
typedef struct assetcustom_s {
|
typedef struct assetcustom_s {
|
||||||
zip_file_t *zipFile;
|
zip_file_t *zipFile;
|
||||||
void *output;
|
void *output;
|
||||||
@@ -40,7 +49,7 @@ typedef struct {
|
|||||||
const size_t dataSize;
|
const size_t dataSize;
|
||||||
const assetloadstrat_t loadStrategy;
|
const assetloadstrat_t loadStrategy;
|
||||||
union {
|
union {
|
||||||
errorret_t (*entire)(void *data, void *output);
|
errorret_t (*entire)(assetentire_t entire);
|
||||||
errorret_t (*custom)(assetcustom_t custom);
|
errorret_t (*custom)(assetcustom_t custom);
|
||||||
};
|
};
|
||||||
} assettypedef_t;
|
} assettypedef_t;
|
||||||
@@ -57,6 +66,20 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
|
|||||||
.entire = assetTextureLoad
|
.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] = {
|
[ASSET_TYPE_LANGUAGE] = {
|
||||||
.extension = "DLF",
|
.extension = "DLF",
|
||||||
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
|
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
assettexture.c
|
assettexture.c
|
||||||
|
assetpalette.c
|
||||||
|
assettileset.c
|
||||||
assetlanguage.c
|
assetlanguage.c
|
||||||
assetscript.c
|
assetscript.c
|
||||||
assetmap.c
|
assetmap.c
|
||||||
|
|||||||
47
src/asset/type/assetpalette.c
Normal file
47
src/asset/type/assetpalette.c
Normal file
@@ -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();
|
||||||
|
}
|
||||||
30
src/asset/type/assetpalette.h
Normal file
30
src/asset/type/assetpalette.h
Normal file
@@ -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);
|
||||||
@@ -6,15 +6,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "assettexture.h"
|
#include "assettexture.h"
|
||||||
|
#include "asset/assettype.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "display/texture.h"
|
#include "display/texture.h"
|
||||||
|
|
||||||
errorret_t assetTextureLoad(void *data, void *output) {
|
errorret_t assetTextureLoad(assetentire_t entire) {
|
||||||
assertNotNull(data, "Data pointer cannot be NULL.");
|
assertNotNull(entire.data, "Data pointer cannot be NULL.");
|
||||||
assertNotNull(output, "Output pointer cannot be NULL.");
|
assertNotNull(entire.output, "Output pointer cannot be NULL.");
|
||||||
|
|
||||||
assettexture_t *assetData = (assettexture_t *)data;
|
assettexture_t *assetData = (assettexture_t *)entire.data;
|
||||||
texture_t *texture = (texture_t *)output;
|
texture_t *texture = (texture_t *)entire.output;
|
||||||
|
|
||||||
// Read header and version (first 4 bytes)
|
// Read header and version (first 4 bytes)
|
||||||
if(
|
if(
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
ASSET_TEXTURE_WIDTH_MAX * ASSET_TEXTURE_HEIGHT_MAX \
|
ASSET_TEXTURE_WIDTH_MAX * ASSET_TEXTURE_HEIGHT_MAX \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
typedef struct assetentire_s assetentire_t;
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char_t header[3];
|
char_t header[3];
|
||||||
@@ -34,4 +36,4 @@ typedef struct {
|
|||||||
* @param output Pointer to the texture_t to load the image into.
|
* @param output Pointer to the texture_t to load the image into.
|
||||||
* @return An error code.
|
* @return An error code.
|
||||||
*/
|
*/
|
||||||
errorret_t assetTextureLoad(void *data, void *output);
|
errorret_t assetTextureLoad(assetentire_t entire);
|
||||||
32
src/asset/type/assettileset.c
Normal file
32
src/asset/type/assettileset.c
Normal file
@@ -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");
|
||||||
|
}
|
||||||
24
src/asset/type/assettileset.h
Normal file
24
src/asset/type/assettileset.h
Normal file
@@ -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);
|
||||||
@@ -17,11 +17,8 @@
|
|||||||
#include "display/text.h"
|
#include "display/text.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
#include <stdlib.h>
|
#include "asset/asset.h"
|
||||||
#include <string.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
display_t DISPLAY = { 0 };
|
display_t DISPLAY = { 0 };
|
||||||
|
|
||||||
@@ -79,6 +76,36 @@ errorret_t displayInit(void) {
|
|||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
glEnableClientState(GL_VERTEX_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
|
#elif DOLPHIN
|
||||||
VIDEO_Init();
|
VIDEO_Init();
|
||||||
DISPLAY.screenMode = VIDEO_GetPreferredMode(NULL);
|
DISPLAY.screenMode = VIDEO_GetPreferredMode(NULL);
|
||||||
@@ -155,6 +182,7 @@ errorret_t displayInit(void) {
|
|||||||
frameBufferInitBackbuffer();
|
frameBufferInitBackbuffer();
|
||||||
spriteBatchInit();
|
spriteBatchInit();
|
||||||
errorChain(textInit());
|
errorChain(textInit());
|
||||||
|
errorChain(assetLoad("main_palette.dpf", &PALETTES[0]));
|
||||||
screenInit();
|
screenInit();
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ typedef struct {
|
|||||||
#if DISPLAY_SDL2
|
#if DISPLAY_SDL2
|
||||||
SDL_Window *window;
|
SDL_Window *window;
|
||||||
SDL_GLContext glContext;
|
SDL_GLContext glContext;
|
||||||
|
bool_t usingShaderedPalettes;
|
||||||
|
|
||||||
#elif DOLPHIN
|
#elif DOLPHIN
|
||||||
void *frameBuffer[2];// Double-Bufferred
|
void *frameBuffer[2];// Double-Bufferred
|
||||||
|
|||||||
@@ -6,3 +6,38 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "palette.h"
|
#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");
|
||||||
|
}
|
||||||
@@ -8,11 +8,40 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
|
|
||||||
#define PALETTE_COUNT 4
|
#define PALETTE_COUNT_MAX 4
|
||||||
|
#define PALETTE_COLOR_COUNT_MAX 0xFF
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const uint8_t colorCount;
|
uint8_t colorCount;
|
||||||
const color_t *colors;
|
color_t colors[PALETTE_COLOR_COUNT_MAX];
|
||||||
} palette_t;
|
} palette_t;
|
||||||
|
|
||||||
extern palette_t PALETTES[PALETTE_COUNT];
|
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);
|
||||||
@@ -16,6 +16,7 @@ tileset_t DEFAULT_FONT_TILESET;
|
|||||||
|
|
||||||
errorret_t textInit(void) {
|
errorret_t textInit(void) {
|
||||||
errorChain(assetLoad("ui/minogram.dpt", &DEFAULT_FONT_TEXTURE));
|
errorChain(assetLoad("ui/minogram.dpt", &DEFAULT_FONT_TEXTURE));
|
||||||
|
errorChain(assetLoad("ui/minogram.dtf", &DEFAULT_FONT_TILESET));
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/math.h"
|
#include "util/math.h"
|
||||||
#include "util/string.h"
|
#include "display/display.h"
|
||||||
#include "debug/debug.h"
|
|
||||||
|
|
||||||
const texture_t *TEXTURE_BOUND = NULL;
|
const texture_t *TEXTURE_BOUND = NULL;
|
||||||
|
|
||||||
@@ -44,98 +43,21 @@ void textureInit(
|
|||||||
);
|
);
|
||||||
break;
|
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:
|
case TEXTURE_FORMAT_PALETTE:
|
||||||
assertNotNull(data.paletteData, "Palette texture data cannot be NULL");
|
assertNotNull(data.paletteData, "Palette texture data cannot be NULL");
|
||||||
|
|
||||||
// Get and validate the palette.
|
if(DISPLAY.usingShaderedPalettes) {
|
||||||
GLenum err = glGetError();
|
// Palette textures not supported, convert to GL_RED style texture
|
||||||
if(err != GL_NO_ERROR) {
|
// so shader can perform the lookup.
|
||||||
assertUnreachable("GL Error before setting color table");
|
uint8_t formatted[width * height];
|
||||||
|
for(int32_t i = 0; i < width * height; i++) {
|
||||||
|
uint8_t index = data.paletteData[i];
|
||||||
|
formatted[i] = index;
|
||||||
}
|
}
|
||||||
|
glTexImage2D(
|
||||||
// Do we support paletted textures?
|
GL_TEXTURE_2D, 0, GL_R, width, height, 0,
|
||||||
bool_t havePalTex = false;
|
GL_RGBA, GL_UNSIGNED_BYTE, (void*)formatted
|
||||||
|
);
|
||||||
#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");
|
|
||||||
}
|
|
||||||
#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
|
|
||||||
// );
|
|
||||||
} else {
|
} else {
|
||||||
glTexImage2D(
|
glTexImage2D(
|
||||||
GL_TEXTURE_2D,
|
GL_TEXTURE_2D,
|
||||||
|
|||||||
@@ -396,6 +396,8 @@
|
|||||||
|
|
||||||
// Width and Height
|
// Width and Height
|
||||||
elError.style.display = 'none';
|
elError.style.display = 'none';
|
||||||
|
|
||||||
|
// Convert from little endian
|
||||||
imageWidth = dataView.getUint32(3);
|
imageWidth = dataView.getUint32(3);
|
||||||
imageHeight = dataView.getUint32(7);
|
imageHeight = dataView.getUint32(7);
|
||||||
|
|
||||||
@@ -631,12 +633,9 @@
|
|||||||
// Create Dusk Palette File (.dpf)
|
// Create Dusk Palette File (.dpf)
|
||||||
const header = new Uint8Array([0x44, 0x50, 0x46, 0x01]); // 'DPF' + version 1 + number of colors (4 bytes)
|
const header = new Uint8Array([0x44, 0x50, 0x46, 0x01]); // 'DPF' + version 1 + number of colors (4 bytes)
|
||||||
|
|
||||||
const colorCount = palette.length;
|
// Color count (1 byte)
|
||||||
const colorCountBytes = new Uint8Array(4);
|
const colorCountBytes = new Uint8Array(1);
|
||||||
colorCountBytes[0] = (colorCount >> 24) & 0xFF;
|
colorCountBytes[0] = palette.length;
|
||||||
colorCountBytes[1] = (colorCount >> 16) & 0xFF;
|
|
||||||
colorCountBytes[2] = (colorCount >> 8) & 0xFF;
|
|
||||||
colorCountBytes[3] = colorCount & 0xFF;
|
|
||||||
|
|
||||||
// add color data (palette.length * 4 bytes)
|
// add color data (palette.length * 4 bytes)
|
||||||
const colorData = new Uint8Array(palette.length * 4);
|
const colorData = new Uint8Array(palette.length * 4);
|
||||||
@@ -661,19 +660,9 @@
|
|||||||
btnDownloadImage.addEventListener('click', () => {
|
btnDownloadImage.addEventListener('click', () => {
|
||||||
const header = new Uint8Array([0x44, 0x50, 0x54, 0x01]); // 'DPT' + version 1
|
const header = new Uint8Array([0x44, 0x50, 0x54, 0x01]); // 'DPT' + version 1
|
||||||
|
|
||||||
// Width
|
// Dimensions
|
||||||
const widthBytes = new Uint8Array(4);
|
const widthBytes = new Uint8Array([ imageWidth ]);
|
||||||
widthBytes[3] = (imageWidth >> 24) & 0xFF;
|
const heightBytes = new Uint32Array([ imageHeight ]);
|
||||||
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;
|
|
||||||
|
|
||||||
// add indexed image data (imageWidth * imageHeight bytes)
|
// add indexed image data (imageWidth * imageHeight bytes)
|
||||||
const imageData = new Uint8Array(indexedImage.length);
|
const imageData = new Uint8Array(indexedImage.length);
|
||||||
|
|||||||
335
tools/tileset-creator.html
Normal file
335
tools/tileset-creator.html
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Dusk Tools / Tileset Creator</title>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Dusk Tileset Creator</h1>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Tileset Settings</h2>
|
||||||
|
<div>
|
||||||
|
<label>Tile Width:</label>
|
||||||
|
<input type="number" value="8" data-tile-width min="1" step="1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Tile Height:</label>
|
||||||
|
<input type="number" value="8" data-tile-height min="1" step="1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Column Count:</label>
|
||||||
|
<input type="number" value="10" data-column-count min="1" step="1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Row Count:</label>
|
||||||
|
<input type="number" value="10" data-row-count min="1" step="1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Preview</h2>
|
||||||
|
<div>
|
||||||
|
<input type="file" data-texture-input />
|
||||||
|
</div>
|
||||||
|
<div data-output-error style="color:red;display:none;"></div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
Preview Scale:
|
||||||
|
<input type="number" value="4" data-indexed-preview-scale min="1" step="1" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
Preview Background:
|
||||||
|
<button data-page-bg-white>White</button>
|
||||||
|
<button data-page-bg-transparent>Black</button>
|
||||||
|
<button data-page-bg-checkerboard>Checkerboard</button>
|
||||||
|
<button data-page-bg-magenta>Magenta</button>
|
||||||
|
<button data-page-bg-blue>Blue</button>
|
||||||
|
<button data-page-bg-green>Green</button>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<canvas data-output-preview style="border:1px solid black;"></canvas>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<textarea data-output-information rows="15" style="width: 500px;"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button data-tileset-download>Download Tileset</button>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
const elTileWidth = document.querySelector('[data-tile-width]');
|
||||||
|
const elTileHeight = document.querySelector('[data-tile-height]');
|
||||||
|
const elColumnCount = document.querySelector('[data-column-count]');
|
||||||
|
const elRowCount = document.querySelector('[data-row-count]');
|
||||||
|
const elFileInput = document.querySelector('[data-texture-input]');
|
||||||
|
const elOutputError = document.querySelector('[data-output-error]');
|
||||||
|
const elOutputInformation = document.querySelector('[data-output-information]');
|
||||||
|
const elOutputPreview = document.querySelector('[data-output-preview]');
|
||||||
|
const elScale = document.querySelector('[data-indexed-preview-scale]');
|
||||||
|
const btnDownloadTileset = document.querySelector('[data-tileset-download]');
|
||||||
|
const btnBackgroundWhite = document.querySelector('[data-page-bg-white]');
|
||||||
|
const btnBackgroundTransparent = document.querySelector('[data-page-bg-transparent]');
|
||||||
|
const btnBackgroundCheckerboard = document.querySelector('[data-page-bg-checkerboard]');
|
||||||
|
const btnBackgroundMagenta = document.querySelector('[data-page-bg-magenta]');
|
||||||
|
const btnBackgroundBlue = document.querySelector('[data-page-bg-blue]');
|
||||||
|
const btnBackgroundGreen = document.querySelector('[data-page-bg-green]');
|
||||||
|
const btnDownloadPalette = document.querySelector('[data-palette-download]');
|
||||||
|
const btnDownloadImage = document.querySelector('[data-indexed-download]');
|
||||||
|
|
||||||
|
let imageWidth = 0;
|
||||||
|
let imageHeight = 0;
|
||||||
|
let image = null;
|
||||||
|
let hoveredX = -1;
|
||||||
|
let hoveredY = -1;
|
||||||
|
|
||||||
|
const updatePreview = () => {
|
||||||
|
if(!image) return;
|
||||||
|
|
||||||
|
const scale = parseInt(elScale.value) || 1;
|
||||||
|
elOutputPreview.width = imageWidth * scale;
|
||||||
|
elOutputPreview.height = imageHeight * scale;
|
||||||
|
const ctx = elOutputPreview.getContext('2d');
|
||||||
|
ctx.clearRect(0, 0, elOutputPreview.width, elOutputPreview.height);
|
||||||
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
ctx.drawImage(image, 0, 0, elOutputPreview.width, elOutputPreview.height);
|
||||||
|
|
||||||
|
// Draw grid lines
|
||||||
|
const tileWidth = parseInt(elTileWidth.value) || 0;
|
||||||
|
const tileHeight = parseInt(elTileHeight.value) || 0;
|
||||||
|
const scaledTileWidth = tileWidth * scale;
|
||||||
|
const scaledTileHeight = tileHeight * scale;
|
||||||
|
const columnCount = parseInt(elColumnCount.value) || 0;
|
||||||
|
const rowCount = parseInt(elRowCount.value) || 0;
|
||||||
|
ctx.strokeStyle = 'rgba(255,0,0,1)';
|
||||||
|
for(let x = scaledTileWidth; x < elOutputPreview.width; x += scaledTileWidth) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, 0);
|
||||||
|
ctx.lineTo(x, elOutputPreview.height);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
for(let y = scaledTileHeight; y < elOutputPreview.height; y += scaledTileHeight) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, y);
|
||||||
|
ctx.lineTo(elOutputPreview.width, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
const u0 = tileWidth / imageWidth;
|
||||||
|
const v0 = tileHeight / imageHeight;
|
||||||
|
const hoveredU0 = hoveredX != -1 && hoveredY != -1 ? (hoveredX * tileWidth / imageWidth) : 0;
|
||||||
|
const hoveredV0 = hoveredX != -1 && hoveredY != -1 ? (hoveredY * tileHeight / imageHeight) : 0;
|
||||||
|
const hoveredU1 = hoveredU0 + u0;
|
||||||
|
const hoveredV1 = hoveredV0 + v0;
|
||||||
|
|
||||||
|
elOutputInformation.value = [
|
||||||
|
hoveredX != -1 ? `Hovered Tile: ${hoveredX}, ${hoveredY} (${hoveredY * columnCount + hoveredX})` : 'Hovered Tile: None',
|
||||||
|
hoveredX != -1 ? `Hovered UV: ${(hoveredU0).toFixed(4)}, ${(hoveredV0).toFixed(4)} -> ${(hoveredU1).toFixed(4)}, ${(hoveredV1).toFixed(4)}` : 'Hovered UV: None',
|
||||||
|
`Image Width: ${imageWidth}`,
|
||||||
|
`Image Height: ${imageHeight}`,
|
||||||
|
`Tile Width: ${elTileWidth.value}`,
|
||||||
|
`Tile Height: ${elTileHeight.value}`,
|
||||||
|
`Column Count: ${columnCount}`,
|
||||||
|
`uv: ${u0.toFixed(4)}, ${v0.toFixed(4)}`,
|
||||||
|
`Row Count: ${rowCount}`,
|
||||||
|
`Tile count: ${columnCount * rowCount}`,
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
elTileWidth.addEventListener('input', () => {
|
||||||
|
if(imageWidth) {
|
||||||
|
const tileWidth = parseInt(elTileWidth.value);
|
||||||
|
const columnCount = Math.floor(imageWidth / tileWidth);
|
||||||
|
elColumnCount.value = columnCount;
|
||||||
|
}
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
elTileHeight.addEventListener('input', () => {
|
||||||
|
if(imageHeight) {
|
||||||
|
const tileHeight = parseInt(elTileHeight.value);
|
||||||
|
const rowCount = Math.floor(imageHeight / tileHeight);
|
||||||
|
elRowCount.value = rowCount;
|
||||||
|
}
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
elColumnCount.addEventListener('input', () => {
|
||||||
|
if(!imageWidth) {
|
||||||
|
alert('Set an image first to calculate tile width from column count');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnCount = parseInt(elColumnCount.value);
|
||||||
|
const tileWidth = Math.floor(imageWidth / columnCount);
|
||||||
|
elTileWidth.value = tileWidth;
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
elRowCount.addEventListener('input', () => {
|
||||||
|
if(!imageHeight) {
|
||||||
|
alert('Set an image first to calculate tile height from row count');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowCount = parseInt(elRowCount.value);
|
||||||
|
const tileHeight = Math.floor(imageHeight / rowCount);
|
||||||
|
elTileHeight.value = tileHeight;
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
elScale.addEventListener('input', () => {
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
elOutputPreview.addEventListener('mousemove', (e) => {
|
||||||
|
if(!image) return;
|
||||||
|
|
||||||
|
const scale = parseInt(elScale.value) || 1;
|
||||||
|
const tileWidth = parseInt(elTileWidth.value) || 0;
|
||||||
|
const tileHeight = parseInt(elTileHeight.value) || 0;
|
||||||
|
const columnCount = parseInt(elColumnCount.value) || 0;
|
||||||
|
const rowCount = parseInt(elRowCount.value) || 0;
|
||||||
|
|
||||||
|
const rect = elOutputPreview.getBoundingClientRect();
|
||||||
|
const x = Math.floor((e.clientX - rect.left) / scale);
|
||||||
|
const y = Math.floor((e.clientY - rect.top) / scale);
|
||||||
|
hoveredX = Math.floor(x / tileWidth);
|
||||||
|
hoveredY = Math.floor(y / tileHeight);
|
||||||
|
|
||||||
|
if(hoveredX < 0 || hoveredX >= columnCount || hoveredY < 0 || hoveredY >= rowCount) {
|
||||||
|
hoveredX = -1;
|
||||||
|
hoveredY = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
elOutputPreview.addEventListener('mouseleave', () => {
|
||||||
|
hoveredX = -1;
|
||||||
|
hoveredY = -1;
|
||||||
|
updatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
// File
|
||||||
|
elFileInput.addEventListener('change', (e) => {
|
||||||
|
elOutputError.style.display = 'none';
|
||||||
|
|
||||||
|
if(!elFileInput.files.length) {
|
||||||
|
elOutputError.textContent = 'No file selected';
|
||||||
|
elOutputError.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = elFileInput.files[0];
|
||||||
|
image = new Image();
|
||||||
|
image.onload = () => {
|
||||||
|
imageWidth = image.width;
|
||||||
|
imageHeight = image.height;
|
||||||
|
const tileWidth = parseInt(elTileWidth.value);
|
||||||
|
const tileHeight = parseInt(elTileHeight.value);
|
||||||
|
const columnCount = Math.floor(imageWidth / tileWidth);
|
||||||
|
const rowCount = Math.floor(imageHeight / tileHeight);
|
||||||
|
elColumnCount.value = columnCount;
|
||||||
|
elRowCount.value = rowCount;
|
||||||
|
updatePreview();
|
||||||
|
};
|
||||||
|
image.onerror = () => {
|
||||||
|
image = null;
|
||||||
|
elOutputError.textContent = 'Failed to load image';
|
||||||
|
elOutputError.style.display = 'block';
|
||||||
|
updatePreview();
|
||||||
|
};
|
||||||
|
image.src = URL.createObjectURL(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
btnDownloadTileset.addEventListener('click', () => {
|
||||||
|
if(!image) {
|
||||||
|
alert('No image loaded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tileWidth = parseInt(elTileWidth.value);
|
||||||
|
const tileHeight = parseInt(elTileHeight.value);
|
||||||
|
const columnCount = parseInt(elColumnCount.value);
|
||||||
|
const rowCount = parseInt(elRowCount.value);
|
||||||
|
const u0 = tileWidth / imageWidth;
|
||||||
|
const v0 = tileHeight / imageHeight;
|
||||||
|
const tileCount = columnCount * rowCount;
|
||||||
|
|
||||||
|
const headerBytes = new Uint8Array([
|
||||||
|
'D'.charCodeAt(0),// Dusk
|
||||||
|
'T'.charCodeAt(0),// Tileset
|
||||||
|
'F'.charCodeAt(0),// File/Format
|
||||||
|
0x00, // version
|
||||||
|
tileWidth & 0xFF,// Tile width (uint16_t)
|
||||||
|
(tileWidth >> 8) & 0xFF,
|
||||||
|
tileHeight & 0xFF,// Tile height (uint16_t)
|
||||||
|
(tileHeight >> 8) & 0xFF,
|
||||||
|
columnCount & 0xFF,// Column count (uint16_t)
|
||||||
|
(columnCount >> 8) & 0xFF,
|
||||||
|
rowCount & 0xFF,// Row count (uint16_t)
|
||||||
|
(rowCount >> 8) & 0xFF,
|
||||||
|
// Float32_t UV step (u0, v0)
|
||||||
|
...new Uint8Array(new Float32Array([u0]).buffer),
|
||||||
|
...new Uint8Array(new Float32Array([v0]).buffer),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Download file
|
||||||
|
const blob = new Blob([headerBytes], { type: 'application/octet-stream' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'tileset.dtf';
|
||||||
|
a.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
btnBackgroundWhite.addEventListener('click', () => {
|
||||||
|
document.body.style.background = 'white';
|
||||||
|
});
|
||||||
|
btnBackgroundTransparent.addEventListener('click', () => {
|
||||||
|
document.body.style.background = 'black';
|
||||||
|
});
|
||||||
|
btnBackgroundCheckerboard.addEventListener('click', () => {
|
||||||
|
document.body.style.background = 'repeating-conic-gradient(#ccc 0% 25%, #eee 0% 50%) 50% / 20px 20px';
|
||||||
|
});
|
||||||
|
btnBackgroundMagenta.addEventListener('click', () => {
|
||||||
|
document.body.style.background = 'magenta';
|
||||||
|
});
|
||||||
|
btnBackgroundBlue.addEventListener('click', () => {
|
||||||
|
document.body.style.background = 'blue';
|
||||||
|
});
|
||||||
|
btnBackgroundGreen.addEventListener('click', () => {
|
||||||
|
document.body.style.background = 'green';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Init
|
||||||
|
btnBackgroundCheckerboard.click();
|
||||||
|
updatePreview();
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user