From 1096c7eac2d69a0fc90ad06b0b454c6091d542c0 Mon Sep 17 00:00:00 2001 From: Austin Morlan Date: Fri, 12 Jun 2020 22:41:45 -0700 Subject: [PATCH] Part 8: Tile System --- README.md | 2 + assets/export/tiles/black.bmp | Bin 0 -> 1334 bytes assets/export/tiles/dark_grey.bmp | Bin 0 -> 1334 bytes assets/export/tiles/light_grey.bmp | Bin 0 -> 1334 bytes assets/export/tiles/white.bmp | Bin 0 -> 1334 bytes assets/source/palettes/grey.pal | 7 + assets/source/tiles/black.aseprite | Bin 0 -> 322 bytes assets/source/tiles/dark_grey.aseprite | Bin 0 -> 322 bytes assets/source/tiles/light_grey.aseprite | Bin 0 -> 322 bytes assets/source/tiles/white.aseprite | Bin 0 -> 310 bytes game/src/main.c | 276 +++++++++++------- game/src/text.c | 4 +- tools/CMakeLists.txt | 4 +- .../{font_generator.c => font_processor.c} | 0 tools/src/palette_processor.c | 105 +++++++ tools/src/tile_processor.c | 132 +++++++++ 16 files changed, 425 insertions(+), 105 deletions(-) create mode 100644 assets/export/tiles/black.bmp create mode 100644 assets/export/tiles/dark_grey.bmp create mode 100644 assets/export/tiles/light_grey.bmp create mode 100644 assets/export/tiles/white.bmp create mode 100644 assets/source/palettes/grey.pal create mode 100644 assets/source/tiles/black.aseprite create mode 100644 assets/source/tiles/dark_grey.aseprite create mode 100644 assets/source/tiles/light_grey.aseprite create mode 100644 assets/source/tiles/white.aseprite rename tools/src/{font_generator.c => font_processor.c} (100%) create mode 100644 tools/src/palette_processor.c create mode 100644 tools/src/tile_processor.c diff --git a/README.md b/README.md index 1a176df..d060754 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,5 @@ if (ret != ESP_OK) { * `IDF_TOOLS_PATH` is the path to the toolchain * `source export.sh` +![Current Status](https://austinmorlan.com/posts/embedded_game_programming_8/media/demo.gif) + diff --git a/assets/export/tiles/black.bmp b/assets/export/tiles/black.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e6a3b69dfa6ae5003cea8eaebd4d3ba0cace3357 GIT binary patch literal 1334 zcmZ?rHDhG}12Yx|1`Qxf0E!tII3N;?3=Bft5X=B%gYf_V{~1=VUd<2^5&}^(N{)uW aXb6np5MV|Ez+gb)0hvROkKzH$XaxWfC<)B~ literal 0 HcmV?d00001 diff --git a/assets/export/tiles/dark_grey.bmp b/assets/export/tiles/dark_grey.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a9ea37e8090561c7ccda2da3206050e4d26bd9eb GIT binary patch literal 1334 zcmZ?rHDhG}12Yx|1`Qxf0E!tII3N;?3=Bft5X=B%gYf_V{~1=VUd<2^5&}^(N{)uW aXb6np5MV+Az+gb)0hvROkKzH$XaxYUxd{yb literal 0 HcmV?d00001 diff --git a/assets/export/tiles/light_grey.bmp b/assets/export/tiles/light_grey.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0741f13cd23a4174c6ad54c5165a37e233b9aff3 GIT binary patch literal 1334 zcmZ?rHDhG}12Yx|1`Qxf0E!tII3N;?3=Bft5X=B%gYf_V{~1=VUd<2^5&}^(N{)uW aXb6np5MV?Cz+gb)0hvROkKzH$XaxXJSqM!4 literal 0 HcmV?d00001 diff --git a/assets/export/tiles/white.bmp b/assets/export/tiles/white.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a8b9952d73f7cf01a0776755deb838a7eab9c6c0 GIT binary patch literal 1334 zcmZ?rHDhG}12Yx|1`Qxf0E!tII3N;?3=Bft5X=B%gYf_V{~1=VUd<2^5&}^(N{)uW MXb6mkzz7Ke0PpGtjQ{`u literal 0 HcmV?d00001 diff --git a/assets/source/palettes/grey.pal b/assets/source/palettes/grey.pal new file mode 100644 index 0000000..173741f --- /dev/null +++ b/assets/source/palettes/grey.pal @@ -0,0 +1,7 @@ +JASC-PAL +0100 +4 +255 255 255 +171 171 171 +84 84 84 +0 0 0 diff --git a/assets/source/tiles/black.aseprite b/assets/source/tiles/black.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..4cde534371e298f60b3abecb76cf78264132062f GIT binary patch literal 322 zcmZ=_WMFu(l#xLI2ss#l9EKDih5%+D#lpY{5(LU|Km@5w9s-*8>mw`JR#qTe42ank zz*ZvJWCUbODzHFBK<+|-|3JXNuzK}sAQ=)80wjUpKgb-Q+kpg78Yl^r0g5PrL>2gf zBn*I*b1^t2CTFJ?<(KBAfNWw_fXM!5g3t`$(5aY{oZ!HI#)F}kTSy|SrsV$uMg}E) GCPx5&tR$iU literal 0 HcmV?d00001 diff --git a/assets/source/tiles/dark_grey.aseprite b/assets/source/tiles/dark_grey.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..f26daff3d49d8d014fd35530fc01886260e85bfc GIT binary patch literal 322 zcmZ=_WMFu(l#xLI2ss#l9EKDih5%+D#lpY{5(LU|Km@5w9s-*8>mw`JR#qTe42ank zz*ZvJWCUbODzHFBK<+|-|3JXNuzK}sAQ=)80wjUpKgb-Q+kpg78Yl^r0g5PrL>2gf zBn*I*b1^t2CTFJ?<(KBAfNWw_fXM!5g3t`$(5aY{oZ!Ijmw`JR#qTe42ank zz*ZvJWCUbODzHFBK<+|-|3JXNuzK}sAQ=)80wjUpKgb-Q+kpg78Yl^r0g5PrL>2gf zBn*I*b1^t2CTFJ?<(KBAfNWw_fXM!5g3t`$(5aY{oZ!ITmw`JR#qTe42ank zz*ZvJWCUbODzHFBK<+|-|3JXNuzK}sAQ=)80wjUpKgb-Q+kpg78Yl^r0g5PrL>2gf tBn*I*b1^t2CTFJ?<(KBAr~&z`3J}@ 39) + { + xLeft = 0; + } + } + + for (int tileY = 0; tileY < 15; ++tileY) + { + for (int tileX = xLeft; tileX < xLeft + 20; ++tileX) + { + int tile = tileX % 40; + + int tileIndex = tileBuffer[tileY][tile]; + + DrawTile(tileIndex, tileX - xLeft, tileY); + } + } + + + char string[5]; + snprintf(string, 5, "%02d", xLeft); + DrawText(gFramebuffer, string, 2, 0, 0, palette[3]); + + int tileRight = (xLeft + 20) % 40; + snprintf(string, 5, "%02d", tileRight); + DrawText(gFramebuffer, string, 2, 18, 0, palette[3]); + if (input.menu) { - const char* snapFilename = "/sdcard/framebuf"; + snprintf(snapFilename, 20, "/sdcard/frame%02d", frameIndex); ESP_LOGI(LOG_TAG, "Writing snapshot to %s", snapFilename); @@ -139,19 +222,8 @@ void app_main(void) fwrite(gFramebuffer, 1, LCD_WIDTH * LCD_HEIGHT * sizeof(gFramebuffer[0]), snapFile); fclose(snapFile); - } - - uint32_t batteryLevel = Odroid_ReadBatteryLevel(); - ESP_LOGI(LOG_TAG, "Battery level: %u\n", batteryLevel); - - if (batteryLevel < 3600) - { - Odroid_EnableBatteryLight(); - } - else - { - Odroid_DisableBatteryLight(); + ++frameIndex; } diff --git a/game/src/text.c b/game/src/text.c index b719c04..25073f6 100644 --- a/game/src/text.c +++ b/game/src/text.c @@ -8,8 +8,8 @@ static const int MAX_GLYPHS_PER_COL = LCD_HEIGHT / GLYPH_HEIGHT; void DrawText(uint16_t* framebuffer, char* string, int length, int x, int y, uint16_t color) { - assert(x + length < MAX_GLYPHS_PER_ROW); - assert(y < MAX_GLYPHS_PER_COL); + assert(x + length <= MAX_GLYPHS_PER_ROW); + assert(y <= MAX_GLYPHS_PER_COL); for (int charIndex = 0; charIndex < length; ++charIndex) { diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 0a32c12..ac11799 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -4,5 +4,7 @@ project(tools) set(CMAKE_C_FLAGS "-Wall -Werror") -add_executable(font_generator src/font_generator.c) +add_executable(font_processor src/font_processor.c) +add_executable(palette_processor src/palette_processor.c) +add_executable(tile_processor src/tile_processor.c) diff --git a/tools/src/font_generator.c b/tools/src/font_processor.c similarity index 100% rename from tools/src/font_generator.c rename to tools/src/font_processor.c diff --git a/tools/src/palette_processor.c b/tools/src/palette_processor.c new file mode 100644 index 0000000..0b714f6 --- /dev/null +++ b/tools/src/palette_processor.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 3) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + + return 1; + } + + const char* inFilename = argv[1]; + const char* outFilename = argv[2]; + + // Read the file + uint16_t* palette; + int paletteSize; + { + FILE* inFile = fopen(inFilename, "r"); + assert(inFile); + + char* line; + size_t len = 0; + + getline(&line, &len, inFile); + assert(strncmp(line, "JASC-PAL", 8) == 0); + + getline(&line, &len, inFile); + assert(strncmp(line, "0100", 4) == 0); + + getline(&line, &len, inFile); + paletteSize = atoi(line); + + palette = malloc(sizeof(*palette) * paletteSize); + assert(palette); + + // Each line is of form R G B + for (int i = 0; i < paletteSize; ++i) + { + getline(&line, &len, inFile); + + char* tok = strtok(line, " "); + int red = atoi(tok); + + tok = strtok(NULL, " "); + int green = atoi(tok); + + tok = strtok(NULL, " "); + int blue = atoi(tok); + + uint16_t rgb565 = + ((red >> 3u) << 11u) + | ((green >> 2u) << 5u) + | (blue >> 3u); + + uint16_t endianSwap = ((rgb565 & 0xFFu) << 8u) | (rgb565 >> 8u); + + palette[i] = endianSwap; + } + + fclose(inFile); + } + + + printf("Input: %s\n", inFilename); + printf("Output: %s\n", outFilename); + printf("Palette Size: %d\n", paletteSize); + + + // Output to the file + { + FILE* outFile = fopen(outFilename, "w"); + assert(outFile); + + fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n"); + fprintf(outFile, "\n"); + fprintf(outFile, "#pragma once\n"); + fprintf(outFile, "\n"); + fprintf(outFile, "\n"); + + fprintf(outFile, "uint16_t palette[%d] =\n", paletteSize); + fprintf(outFile, "{\n"); + + for (int i = 0; i < paletteSize; ++i) + { + fprintf(outFile, " 0x%04X,\n", palette[i]); + } + + fprintf(outFile, "};\n"); + fprintf(outFile, "\n"); + + fclose(outFile); + } + + printf("\n"); + printf("DONE\n"); + + + return 0; +} + diff --git a/tools/src/tile_processor.c b/tools/src/tile_processor.c new file mode 100644 index 0000000..01cc046 --- /dev/null +++ b/tools/src/tile_processor.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include + + +int main(int argc, char** argv) +{ + if (argc != 3) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + + return 1; + } + + const char* inFilename = argv[1]; + const char* outFilename = argv[2]; + + + int tileWidth; + int tileHeight; + uint8_t* tileBuffer; + { + FILE* inFile = fopen(inFilename, "rb"); + assert(inFile); + + #pragma pack(push,1) + struct BmpHeader + { + char magic[2]; + uint32_t totalSize; + uint32_t reserved; + uint32_t offset; + uint32_t headerSize; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t depth; + uint32_t compression; + uint32_t imageSize; + int32_t horizontalResolution; + int32_t verticalResolution; + uint32_t paletteColorCount; + uint32_t importantColorcount; + } bmpHeader; + #pragma pack(pop) + + // Read the BMP header so we know where the image data is located + fread(&bmpHeader, 1, sizeof(bmpHeader), inFile); + assert(bmpHeader.magic[0] == 'B' && bmpHeader.magic[1] == 'M'); + assert(bmpHeader.depth == 8); + assert(bmpHeader.headerSize == 40); + + // Go to location in file of image data + fseek(inFile, bmpHeader.offset, SEEK_SET); + + // Read in the image data + tileBuffer = malloc(bmpHeader.imageSize); + assert(tileBuffer); + fread(tileBuffer, 1, bmpHeader.imageSize, inFile); + + tileWidth = bmpHeader.width; + tileHeight = bmpHeader.height; + + fclose(inFile); + } + + + printf("Input: %s\n", inFilename); + printf("Output: %s\n", outFilename); + printf("Width: %d\n", tileWidth); + printf("Height: %d\n", tileHeight); + + FILE* outFile = fopen(outFilename, "w"); + assert(outFile); + + + // Generate the preamble + { + fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n"); + fprintf(outFile, "\n"); + fprintf(outFile, "#pragma once\n"); + fprintf(outFile, "\n"); + fprintf(outFile, "#include \n"); + fprintf(outFile, "\n"); + fprintf(outFile, "\n"); + } + + + // Generate the font map with the calculated glyph bytes + { + fprintf(outFile, "static const uint8_t tile[%d][%d] =\n", tileHeight, tileWidth); + fprintf(outFile, "{\n"); + fprintf(outFile, " "); + + int count = 0; + + for (int row = 0; row < tileHeight; ++row) + { + for (int col = 0; col < tileWidth; ++col) + { + // BMP is laid out bottom-to-top, but we want top-to-bottom (0-indexed) + int y = tileHeight - row - 1; + + uint8_t paletteIndex = tileBuffer[y * tileWidth + col]; + + fprintf(outFile, "%d,", paletteIndex); + ++count; + + // Put a newline after sixteen values to keep it orderly + if ((count % 16) == 0) + { + fprintf(outFile, "\n"); + fprintf(outFile, " "); + + count = 0; + } + } + } + + fprintf(outFile, "};\n"); + } + + fclose(outFile); + + printf("\n"); + printf("DONE\n"); + + return 0; +}