From: Ben Sherratt Date: Sun, 19 May 2024 18:50:10 +0000 (+0100) Subject: Sprites X-Git-Url: https://git.bts.cx/benzene.git/commitdiff_plain/9dd75ef0de4f64d4847b3b50658e322f9b144eea?ds=inline Sprites --- diff --git a/src/bz/game/scene.c b/src/bz/game/scene.c index 907fccb..f00ace6 100644 --- a/src/bz/game/scene.c +++ b/src/bz/game/scene.c @@ -180,15 +180,17 @@ BZSceneID bzGameSceneSwitch(BZMemoryArenaID arena, BZAudioPlaybackEngineID audio const char *spritesheetName = json_object_get_string(sceneJsonObject, "spritesheet"); if (spritesheetName != NULL) { - size_t imageWidth, imageHeight; - void *imageData = bzGfxLoadAsepriteImage(arena, &imageWidth, &imageHeight, spritesheetName); // FIXME, temp arena - bzGfxPrepareSpritesheet(arena, imageWidth, imageHeight, imageData); + size_t imageFrames, imageWidth, imageHeight; + void *imageData = bzGfxLoadAsepriteImage(arena, &imageFrames, &imageWidth, &imageHeight, spritesheetName); // FIXME, temp arena + bzGfxPrepareSpritesheet(arena, imageFrames, imageWidth, imageHeight, imageData); //free(imageData); } const char *fontName = json_object_get_string(sceneJsonObject, "font"); - BZFont *font = bzGfxLoadFont(arena, fontName); // FIXME, temp arena?? - bzGfxPrepareFont(arena, font); + if (fontName != NULL) { + BZFont *font = bzGfxLoadFont(arena, fontName); // FIXME, temp arena?? + bzGfxPrepareFont(arena, font); + } JSON_Array *actorDefinitionsArray = json_object_get_array(sceneJsonObject, "actors"); size_t actorDefinitionsArrayCount = json_array_get_count(actorDefinitionsArray); diff --git a/src/bz/gfx/aseprite.c b/src/bz/gfx/aseprite.c index 69dc446..046ca24 100644 --- a/src/bz/gfx/aseprite.c +++ b/src/bz/gfx/aseprite.c @@ -1,12 +1,15 @@ #include +#include +#include #include #include #include #include #include -#include // Apparently stb_image.h need this?? +#include // Apparently stb_image.h needs this?? #include +#include // memset #include // Static assertions @@ -23,8 +26,6 @@ typedef double ASE_DOUBLE; typedef uint64_t ASE_QWORD; typedef int64_t ASE_LONG64; - - struct PACKED_STRUCT ASE_STRING { ASE_WORD length; //ASE_BYTE characters[]; @@ -53,14 +54,14 @@ struct PACKED_STRUCT ASE_RECT { typedef struct ASE_RECT ASE_RECT; static_assert(sizeof(ASE_RECT) == 16, "ASE_RECT is wrong size"); -struct PACKED_STRUCT ASE_PIXEL_RBZA { +struct PACKED_STRUCT ASE_PIXEL_RBGA { ASE_BYTE r; ASE_BYTE g; ASE_BYTE b; ASE_BYTE a; }; -typedef struct ASE_PIXEL_RBZA ASE_PIXEL_RBZA; -static_assert(sizeof(ASE_PIXEL_RBZA) == 4, "ASE_PIXEL_RBZA is wrong size"); +typedef struct ASE_PIXEL_RBGA ASE_PIXEL_RBGA; +static_assert(sizeof(ASE_PIXEL_RBGA) == 4, "ASE_PIXEL_RBGA is wrong size"); struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE { ASE_BYTE v; @@ -119,6 +120,25 @@ struct PACKED_STRUCT ASE_Chunk { typedef struct ASE_Chunk ASE_Chunk; static_assert(sizeof(ASE_Chunk) == 6, "ASE_Chunk is wrong size"); +enum ASE_ChunkTypes { + ASE_ChunkType_LayerChunk = 0x2004, + ASE_ChunkType_CelChunk = 0x2005, + ASE_ChunkType_CelExtraChunk = 0x2006, + ASE_ChunkType_ColorProfileChunk = 0x2007, + ASE_ChunkType_ExternalFilesChunk = 0x2008, + ASE_ChunkType_TagsChunk = 0x2018, + ASE_ChunkType_PaletteChunk = 0x2019, + ASE_ChunkType_UserDataChunk = 0x2020, + ASE_ChunkType_SliceChunk = 0x2022, + ASE_ChunkType_TilesetChunk = 0x2023, + + // Ignore these, deprecated or unused... + ASE_ChunkType_OldPaletteChunk1 = 0x0004, + ASE_ChunkType_OldPaletteChunk2 = 0x0011, + ASE_ChunkType_MaskChunk = 0x2016, + ASE_ChunkType_PathChunk = 0x2017, +}; + #if 0 struct PACKED_STRUCT ASE_Chunk_OldPalette { ASE_WORD numPackets; @@ -158,6 +178,10 @@ struct PACKED_STRUCT ASE_Chunk_Layer { typedef struct ASE_Chunk_Layer ASE_Chunk_Layer; static_assert(sizeof(ASE_Chunk_Layer) == 18, "ASE_Chunk_Layer is wrong size"); +enum ASE_Chunk_Layer_BlendModes { + ASE_Chunk_Layer_BlendMode_Normal = 0, // Should always be this +}; + struct PACKED_STRUCT ASE_Chunk_Layer_Tileset { ASE_DWORD tilesetIndex; }; @@ -176,6 +200,13 @@ struct PACKED_STRUCT ASE_Chunk_Cel { typedef struct ASE_Chunk_Cel ASE_Chunk_Cel; static_assert(sizeof(ASE_Chunk_Cel) == 16, "ASE_Chunk_Cel is wrong size"); +enum ASE_Chunk_Cel_CelTypes { + ASE_Chunk_Cel_CelType_RawImage = 0, + ASE_Chunk_Cel_CelType_LinkedCel = 1, + ASE_Chunk_Cel_CelType_CompressedImage = 2, + ASE_Chunk_Cel_CelType_CompressedTilemap = 3, +}; + struct PACKED_STRUCT ASE_Chunk_Cel_RawImage { ASE_WORD pixelWidth; ASE_WORD pixelHeight; @@ -255,6 +286,13 @@ struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry { typedef struct ASE_Chunk_ExternalFiles_Entry ASE_Chunk_ExternalFiles_Entry; static_assert(sizeof(ASE_Chunk_ExternalFiles_Entry) == 14, "ASE_Chunk_ExternalFiles_Entry is wrong size"); +enum ASE_Chunk_ExternalFiles_Entry_Types { + ASE_Chunk_ExternalFiles_EntryType_ExternalPalette = 0, + ASE_Chunk_ExternalFiles_EntryType_ExternalTileset = 1, + ASE_Chunk_ExternalFiles_EntryType_PropertiesExtension = 2, + ASE_Chunk_ExternalFiles_EntryType_TileManagement = 3, +}; + struct PACKED_STRUCT ASE_Chunk_Mask { ASE_SHORT positionX; ASE_SHORT positionY; @@ -311,7 +349,34 @@ struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name { typedef struct ASE_Chunk_Palette_Entry_Name ASE_Chunk_Palette_Entry_Name; static_assert(sizeof(ASE_Chunk_Palette_Entry_Name) == 2, "ASE_Chunk_Palette_Entry_Name is wrong size"); -void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) { +static bool readASEHeader(ASE_Header *headerOut, BZResourceID handle) { + bzAssert(headerOut != NULL); + bzResourcesReadBytes(handle, headerOut, sizeof(ASE_Header)); + bzAssert(headerOut->magicNumber == 0xA5E0); + return true; +} + +static bool readASEFrame(ASE_Frame *frameOut, ASE_DWORD *numChunksOut, BZResourceID handle) { + bzAssert(frameOut != NULL); + bzAssert(numChunksOut != NULL); + + bzResourcesReadBytes(handle, frameOut, sizeof(ASE_Frame)); + + ASE_DWORD numChunks = frameOut->___numChunks; + + if (numChunks == 0xFFFF) { + numChunks = frameOut->numChunks; + if (numChunks == 0) { + numChunks = frameOut->___numChunks; // See docs. + } + } + + *numChunksOut = numChunks; + + return true; +} + +void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *framesOut, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) { bzMakeIdentifier(identifier, identifierFmt); BZResourceID handle = bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier); @@ -320,9 +385,7 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he //PHYSFS_readBytes(handle, data, length); ASE_Header header; - bzResourcesReadBytes(handle, &header, sizeof(header)); - - bzAssert(header.magicNumber == 0xA5E0); + readASEHeader(&header, handle); size_t pixelDataSize; switch (header.depth) { @@ -335,7 +398,7 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he break; case 32: - pixelDataSize = sizeof(ASE_PIXEL_RBZA); + pixelDataSize = sizeof(ASE_PIXEL_RBGA); break; default: @@ -343,32 +406,65 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he break; } - uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.width * header.height * pixelDataSize); + uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.frames * header.width * header.height * pixelDataSize); // Side by side loading needs to happen... + *framesOut = (size_t)header.frames; *widthOut = (size_t)header.width; *heightOut = (size_t)header.height; - for (ASE_WORD frame = 0; frame < header.frames; ++frame) { + bzMemoryArenaPushWatermark(arena); + size_t zBufferSize = header.width * header.height * sizeof(ASE_SHORT); + ASE_SHORT *zBuffer = (ASE_SHORT *)bzMemoryAlloc(arena, zBufferSize); + + size_t frameImageSize = header.width * header.height * pixelDataSize; + + for (ASE_WORD frameIdx = 0; frameIdx < header.frames; ++frameIdx) { ASE_Frame frame; - bzResourcesReadBytes(handle, &frame, sizeof(frame)); + ASE_DWORD numChunks = 0; + readASEFrame(&frame, &numChunks, handle); + bzLog("FRAME %d: %d chunks", frameIdx, numChunks); + + memset(zBuffer, 0, zBufferSize); + + bool layerVisible[16]; + size_t nextLayer = 0; + + size_t layerStartOffset[16]; - for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) { + for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) { size_t chunkStartPosition = bzResourcesTell(handle); ASE_Chunk chunk; bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); switch (chunk.type) { - case 0x2005: { + case ASE_ChunkType_LayerChunk: { + bzLog("LAYER %d: %d chunks", frameIdx, numChunks); + + + ASE_Chunk_Layer layer; + bzResourcesReadBytes(handle, &layer, sizeof(layer)); + + layerVisible[nextLayer] = (layer.flags & 1) > 0; + ++nextLayer; + + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + break; + } + + case ASE_ChunkType_CelChunk: { + bzLog("CELL %d: %d chunks", frameIdx, numChunks); ASE_Chunk_Cel cel; bzResourcesReadBytes(handle, &cel, sizeof(cel)); - if (cel.celType == 2) { +///// ASE_Chunk_Cel_CelType_LinkedCel aaaahhh + + if (layerVisible[cel.layerIndex] && cel.celType == ASE_Chunk_Cel_CelType_CompressedImage) { bzMemoryArenaPushWatermark(arena); ASE_Chunk_Cel_CompressedImage image; bzResourcesReadBytes(handle, &image, sizeof(image)); - size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk); + size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk_Cel) - sizeof(ASE_Chunk); int8_t *compressedData = bzMemoryAlloc(arena, compressedSize); // If we do this on the stack (alloca) the Playdate will explode! bzResourcesReadBytes(handle, compressedData, compressedSize); @@ -381,16 +477,21 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he size_t outX = cel.positionX + x; size_t outY = cel.positionY + y; - for (size_t i = 0; i < pixelDataSize; ++i) { - outData[(outY * header.width + outX) * pixelDataSize + i] = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i]; + ASE_SHORT *currentZ = &zBuffer[outY * header.width + outX]; + + if (*currentZ <= cel.zIndex) { + for (size_t i = 0; i < pixelDataSize; ++i) { + uint8_t pixelData = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i]; + if (pixelData > 0) { // FIXME, alpha... + outData[frameIdx * frameImageSize + (outY * header.width + outX) * pixelDataSize + i] = pixelData; + } + } + *currentZ = cel.zIndex; } } } bzMemoryArenaPopWatermark(arena); - - // FIXME - //bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); } else { // Skip this chunk... bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); @@ -407,6 +508,8 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he } } + bzMemoryArenaPopWatermark(arena); + bzResourcesCloseResource(handle); return outData; @@ -416,9 +519,7 @@ size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const cha BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s", filename); ASE_Header header; - bzResourcesReadBytes(handle, &header, sizeof(header)); - - bzAssert(header.magicNumber == 0xA5E0); + readASEHeader(&header, handle); size_t colorCount = 0; @@ -426,14 +527,23 @@ size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const cha ASE_Frame frame; bzResourcesReadBytes(handle, &frame, sizeof(frame)); - for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) { + ASE_DWORD numChunks = frame.___numChunks; + + if (numChunks == 0xFFFF) { + numChunks = frame.numChunks; + if (numChunks == 0) { + numChunks = frame.___numChunks; // See docs. + } + } + + for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) { size_t chunkStartPosition = bzResourcesTell(handle); ASE_Chunk chunk; bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); switch (chunk.type) { - case 0x2019: { + case ASE_ChunkType_PaletteChunk: { ASE_Chunk_Palette palette; bzResourcesReadBytes(handle, &palette, sizeof(palette)); diff --git a/src/bz/gfx/aseprite.h b/src/bz/gfx/aseprite.h index a5d540c..e6cdc9b 100644 --- a/src/bz/gfx/aseprite.h +++ b/src/bz/gfx/aseprite.h @@ -9,7 +9,7 @@ extern "C" { #endif -extern void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...); +extern void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *framesOut, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...); #ifdef __cplusplus } diff --git a/src/bz/gfx/gfx.c b/src/bz/gfx/gfx.c index 0f26e3b..ccfdc2c 100644 --- a/src/bz/gfx/gfx.c +++ b/src/bz/gfx/gfx.c @@ -72,12 +72,23 @@ static BZMatrix globalViewMatrix = { .a = 1, .b = 0, .c = 0, .d = 1, .x = 0, .y //uint32_t intermediateBuffer[canvasHeight][canvasWidth]; -#define SS_WIDTH 128 -#define SS_HEIGHT 128 -#define kSpriteSheetStrideShift 7 - +//#define SS_WIDTH 128 +//#define SS_HEIGHT 128 +//#define kSpriteSheetStrideShift 7 //static uint8_t spritesheet[SS_HEIGHT][SS_WIDTH]; -static uint8_t *spritesheet = NULL; +//static uint8_t *spritesheet = NULL; + +struct BZSpritesheet { + size_t width; + size_t height; + size_t spriteWidth; + size_t spriteHeight; + size_t spriteCount; + uint8_t data[]; +}; +typedef struct BZSpritesheet BZSpritesheet; + +static BZSpritesheet *spritesheet = NULL; static BZFont *currentFont; @@ -131,13 +142,19 @@ void bzGfxPrepareCanvasBuffer(BZMemoryArenaID arena, size_t width, size_t height bzLog("Data: %d %d %d %d", canvasWidth, canvasMemorySize, bufferStrideShift, bufferStride); } -void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t width, size_t height, void *data) { +void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t frames, size_t width, size_t height, void *data) { uint8_t *imageData = (uint8_t *)data; - bzLog("Preparing spritesheet %dx%d", width, height); + bzLog("Preparing spritesheet %dx%dx%d", frames, width, height); - size_t spritesheetMemorySize = width * height * sizeof(uint8_t); - spritesheet = bzMemoryAlloc(arena, spritesheetMemorySize); - memcpy(spritesheet, data, spritesheetMemorySize); + size_t spritesheetDataSize = frames * width * height * sizeof(uint8_t); + spritesheet = bzMemoryAlloc(arena, sizeof(BZSpritesheet) + spritesheetDataSize); + + spritesheet->width = width; + spritesheet->height = height * frames; + spritesheet->spriteCount = frames; + spritesheet->spriteWidth = width; + spritesheet->spriteHeight = height; + memcpy(&spritesheet->data, data, spritesheetDataSize); } void bzGfxPrepareFont(BZMemoryArenaID arena, void *font) { @@ -241,8 +258,9 @@ void bzPSet(BZCoordinate x, BZCoordinate y, BZPaletteColor c) { BZPaletteColor bzSGet(int x, int y) { bzAssert(spritesheet != NULL); - if (x >= 0 && x < SS_WIDTH && y >=0 && y < SS_HEIGHT) { - return spritesheet[(y << kSpriteSheetStrideShift) + x]; + if (x >= 0 && x < spritesheet->width && y >=0 && y < spritesheet->height) { + return spritesheet->data[y * spritesheet->width + x]; + //return spritesheet->data[(y << kSpriteSheetStrideShift) + x]; } else { return 0; } @@ -550,11 +568,17 @@ void bzSpr(size_t n, BZCoordinate x, BZCoordinate y) { } void bzSprExt(size_t n, BZCoordinate x, BZCoordinate y, BZCoordinate w, BZCoordinate h, bool flipX, bool flipY) { - int px = (n % 16) * 8; - int py = (n / 16) * 8; - int pw = w * 8; - int ph = h * 8; + int px = 0;//(n % 16) * 8; + int py = n * spritesheet->spriteHeight;//(n / 16) * 8; + int pw = w * spritesheet->spriteWidth; + int ph = h * spritesheet->spriteHeight; bzSSprExt(px, py, pw, ph, x, y, pw, ph, flipX, flipY); + +// int px = (n % 16) * 8; +// int py = (n / 16) * 8; +// int pw = w * 8; +// int ph = h * 8; +// bzSSprExt(px, py, pw, ph, x, y, pw, ph, flipX, flipY); } void bzSSpr(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy) { @@ -572,7 +596,7 @@ void bzSSprExt(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate s for (size_t y = 0; y < osh - (clipTop + clipBottom); ++y) { for (size_t x = 0; x < osw - (clipLeft + clipRight); ++x) { - int color = gPalettes[spritesheet[((clipTop+osy+y) << kSpriteSheetStrideShift) + clipLeft+osx+x]]; + int color = gPalettes[spritesheet->data[(clipTop+osy+y) * spritesheet->width + clipLeft+osx+x]]; if (color > 0) bzBufferSet(currentBuffer, xMinOut+x, yMinOut+y, color); // FIXME, scaled up and 0 check removal?? } } diff --git a/src/bz/gfx/gfx_internal.h b/src/bz/gfx/gfx_internal.h index c03c73a..9c84b60 100644 --- a/src/bz/gfx/gfx_internal.h +++ b/src/bz/gfx/gfx_internal.h @@ -12,7 +12,7 @@ extern "C" { extern void bzGfxPreparePalettes(BZMemoryArenaID arena, size_t paletteCount, size_t colorCount); extern void bzGfxPrepareCanvasBuffer(BZMemoryArenaID arena, size_t width, size_t height); -extern void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t width, size_t height, void *data); +extern void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t frames, size_t width, size_t height, void *data); extern void bzGfxPrepareFont(BZMemoryArenaID arena, void *font); extern void bzGfxComposite(void); diff --git a/src/bz/memory/arena.c b/src/bz/memory/arena.c index 079bb8f..42ffc98 100644 --- a/src/bz/memory/arena.c +++ b/src/bz/memory/arena.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include // memset... :rolleyes: @@ -28,7 +29,9 @@ static size_t allocateSize(size_t size) { BZMemoryArenaID bzMemoryArenaCreate(BZMemoryArenaID hostArena, size_t size, const char *nameFmt, ...) { bzAssert(size == allocateSize(size)); if (nextUserArena < kMaxUserArenas) { - return bzMemoryArenaAllocate(&userArenas[++nextUserArena], hostArena, size); + BZMemoryArenaID arena = bzMemoryArenaAllocate(&userArenas[++nextUserArena], hostArena, size); + bzInsertIdentifier(((char *)arena->identifier), nameFmt); + return arena; } else { bzError("Too many arenas"); return NULL; @@ -107,7 +110,7 @@ void *_bzMemoryAlloc(BZMemoryArenaID arena, size_t size, const char *filename, s arena->allocationOffsetBottom += fullSize; } - bzAssertMessage(ptr != NULL, "Arena is out of memory, failed %s:%d", filename, lineNumber); + bzAssertMessage(ptr != NULL, "Arena '%s' is out of memory, failed %s:%d", arena->identifier, filename, lineNumber); return ptr; } diff --git a/src/bz/memory/arena_internal.h b/src/bz/memory/arena_internal.h index 8eab3ba..339e443 100644 --- a/src/bz/memory/arena_internal.h +++ b/src/bz/memory/arena_internal.h @@ -3,6 +3,8 @@ #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -10,6 +12,8 @@ extern "C" { #define MAX_WATERMARKS 16 struct BZMemoryArena { + char *identifier[kBZMaxIdentifierLength]; + size_t maxSize; uint8_t *memory; bool zeroOut;