diff options
| author | 3gg <3gg@shellblade.net> | 2025-09-07 10:55:19 -0700 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-09-07 10:56:41 -0700 |
| commit | 819c7899b3452a405bac6300fe44460ca9e5dcd6 (patch) | |
| tree | aed7f2a888d2565c45c37749649522729a0f87ca | |
| parent | e667ae699b432930932b446834a0c2ead085b996 (diff) | |
| -rw-r--r-- | src/gfx2d.c | 36 | ||||
| -rw-r--r-- | tools/mkasset.py | 56 |
2 files changed, 57 insertions, 35 deletions
diff --git a/src/gfx2d.c b/src/gfx2d.c index 1c8b06f..eaed2b8 100644 --- a/src/gfx2d.c +++ b/src/gfx2d.c | |||
| @@ -26,6 +26,9 @@ | |||
| 26 | /// Take the minimum of two values. | 26 | /// Take the minimum of two values. |
| 27 | #define min(a, b) ((a) < (b) ? (a) : (b)) | 27 | #define min(a, b) ((a) < (b) ? (a) : (b)) |
| 28 | 28 | ||
| 29 | /// The 0-tile denotes "no tile". The renderer skips drawing 0-tiles. | ||
| 30 | static const Tile NoTile = 0; | ||
| 31 | |||
| 29 | typedef struct ivec2 { | 32 | typedef struct ivec2 { |
| 30 | int x, y; | 33 | int x, y; |
| 31 | } ivec2; | 34 | } ivec2; |
| @@ -606,6 +609,10 @@ static void draw_tile_ortho(Gfx2d* gfx, Tile tile, int x, int y) { | |||
| 606 | assert(x < gfx->map->world_width); | 609 | assert(x < gfx->map->world_width); |
| 607 | assert(y < gfx->map->world_height); | 610 | assert(y < gfx->map->world_height); |
| 608 | 611 | ||
| 612 | if (tile == NoTile) { | ||
| 613 | return; | ||
| 614 | } | ||
| 615 | |||
| 609 | const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile); | 616 | const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile); |
| 610 | const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile); | 617 | const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile); |
| 611 | 618 | ||
| @@ -626,6 +633,10 @@ static void draw_tile_iso(Gfx2d* gfx, Tile tile, int iso_x, int iso_y) { | |||
| 626 | assert(iso_x < gfx->map->world_width); | 633 | assert(iso_x < gfx->map->world_width); |
| 627 | assert(iso_y < gfx->map->world_height); | 634 | assert(iso_y < gfx->map->world_height); |
| 628 | 635 | ||
| 636 | if (tile == NoTile) { | ||
| 637 | return; | ||
| 638 | } | ||
| 639 | |||
| 629 | const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile); | 640 | const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile); |
| 630 | const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile); | 641 | const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile); |
| 631 | 642 | ||
| @@ -677,18 +688,19 @@ static void draw_map_iso(Gfx2d* gfx) { | |||
| 677 | assert(gfx); | 688 | assert(gfx); |
| 678 | assert(gfx->map); | 689 | assert(gfx->map); |
| 679 | 690 | ||
| 680 | // TODO: Support for multiple layers. | 691 | for (uint16_t l = 0; l < gfx->map->num_layers; ++l) { |
| 681 | const Tm_Layer* layer = tm_map_get_layer(gfx->map, 0); | 692 | const Tm_Layer* layer = tm_map_get_layer(gfx->map, l); |
| 682 | 693 | ||
| 683 | // TODO: Culling. | 694 | // TODO: Culling. |
| 684 | // Ex: map the screen corners to tile space to cull. | 695 | // Ex: map the screen corners to tile space to cull. |
| 685 | // Ex: walk in screen space and fetch the tile. | 696 | // Ex: walk in screen space and fetch the tile. |
| 686 | // The tile-centric approach might be more cache-friendly since the | 697 | // The tile-centric approach might be more cache-friendly since the |
| 687 | // screen-centric approach would juggle multiple tiles throughout the scan. | 698 | // screen-centric approach would juggle multiple tiles throughout the scan. |
| 688 | for (int wy = 0; wy < gfx->map->world_height; ++wy) { | 699 | for (int wy = 0; wy < gfx->map->world_height; ++wy) { |
| 689 | for (int wx = 0; wx < gfx->map->world_width; ++wx) { | 700 | for (int wx = 0; wx < gfx->map->world_width; ++wx) { |
| 690 | const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy); | 701 | const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy); |
| 691 | draw_tile_iso(gfx, tile, wx, wy); | 702 | draw_tile_iso(gfx, tile, wx, wy); |
| 703 | } | ||
| 692 | } | 704 | } |
| 693 | } | 705 | } |
| 694 | } | 706 | } |
diff --git a/tools/mkasset.py b/tools/mkasset.py index a402e3c..1be0573 100644 --- a/tools/mkasset.py +++ b/tools/mkasset.py | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | # | 12 | # |
| 13 | import argparse | 13 | import argparse |
| 14 | import ctypes | 14 | import ctypes |
| 15 | import struct | ||
| 15 | import sys | 16 | import sys |
| 16 | from enum import IntEnum | 17 | from enum import IntEnum |
| 17 | from typing import Generator | 18 | from typing import Generator |
| @@ -80,7 +81,8 @@ def convert_tsx(input_filepath, output_filepath): | |||
| 80 | assert (tileset.tag == "tileset") | 81 | assert (tileset.tag == "tileset") |
| 81 | 82 | ||
| 82 | # Header. | 83 | # Header. |
| 83 | tileset_tile_count = int(tileset.attrib["tilecount"]) | 84 | # +1 for the 0-tile. |
| 85 | tileset_tile_count = int(tileset.attrib["tilecount"]) + 1 | ||
| 84 | tileset_tile_width = int(tileset.attrib["tilewidth"]) | 86 | tileset_tile_width = int(tileset.attrib["tilewidth"]) |
| 85 | tileset_tile_height = int(tileset.attrib["tileheight"]) | 87 | tileset_tile_height = int(tileset.attrib["tileheight"]) |
| 86 | 88 | ||
| @@ -88,17 +90,14 @@ def convert_tsx(input_filepath, output_filepath): | |||
| 88 | print(f"Tile width: {tileset_tile_width}") | 90 | print(f"Tile width: {tileset_tile_width}") |
| 89 | print(f"Tile height: {tileset_tile_height}") | 91 | print(f"Tile height: {tileset_tile_height}") |
| 90 | 92 | ||
| 91 | pixels = [] # List of byte arrays | 93 | pixels = bytearray() |
| 92 | pixels_offset = 0 | ||
| 93 | 94 | ||
| 94 | def output_tile(output, tile_width, tile_height, tile_image_bytes): | 95 | def output_tile(output, tile_width, tile_height, tile_image_bytes): |
| 95 | # Expecting RGBA pixels. | 96 | # Expecting RGBA pixels. |
| 96 | assert (len(tile_image_bytes) == (tile_width * tile_height * 4)) | 97 | assert (len(tile_image_bytes) == (tile_width * tile_height * 4)) |
| 97 | 98 | ||
| 98 | nonlocal pixels_offset | 99 | tile_pixels_offset = len(pixels) |
| 99 | tile_pixels_offset = pixels_offset | 100 | pixels.extend(tile_image_bytes) |
| 100 | pixels.append(tile_image_bytes) | ||
| 101 | pixels_offset += len(tile_image_bytes) | ||
| 102 | 101 | ||
| 103 | output.write(ctypes.c_uint16(tile_width)) | 102 | output.write(ctypes.c_uint16(tile_width)) |
| 104 | output.write(ctypes.c_uint16(tile_height)) | 103 | output.write(ctypes.c_uint16(tile_height)) |
| @@ -111,6 +110,10 @@ def convert_tsx(input_filepath, output_filepath): | |||
| 111 | output.write(ctypes.c_uint16(tileset_tile_height)) | 110 | output.write(ctypes.c_uint16(tileset_tile_height)) |
| 112 | output.write(ctypes.c_uint16(0)) # Pad. | 111 | output.write(ctypes.c_uint16(0)) # Pad. |
| 113 | 112 | ||
| 113 | # Write the 0-tile, which denotes "no tile", at index 0. | ||
| 114 | zero_pixels = bytes(tileset_tile_width * tileset_tile_height * 4) | ||
| 115 | output_tile(output, tileset_tile_width, tileset_tile_height, zero_pixels) | ||
| 116 | |||
| 114 | # A tileset made up of multiple images contains various 'tile' children, | 117 | # A tileset made up of multiple images contains various 'tile' children, |
| 115 | # each with their own 'image': | 118 | # each with their own 'image': |
| 116 | # | 119 | # |
| @@ -174,8 +177,7 @@ def convert_tsx(input_filepath, output_filepath): | |||
| 174 | output_tile(output, tile_width, tile_height, image_bytes) | 177 | output_tile(output, tile_width, tile_height, image_bytes) |
| 175 | 178 | ||
| 176 | # Write the pixel data. | 179 | # Write the pixel data. |
| 177 | for bytes in pixels: | 180 | output.write(pixels) |
| 178 | output.write(bytes) | ||
| 179 | 181 | ||
| 180 | 182 | ||
| 181 | def convert_tmx(input_filepath, output_filepath): | 183 | def convert_tmx(input_filepath, output_filepath): |
| @@ -187,8 +189,10 @@ def convert_tmx(input_filepath, output_filepath): | |||
| 187 | map_height = int(root.attrib["height"]) | 189 | map_height = int(root.attrib["height"]) |
| 188 | base_tile_width = int(root.attrib["tilewidth"]) | 190 | base_tile_width = int(root.attrib["tilewidth"]) |
| 189 | base_tile_height = int(root.attrib["tileheight"]) | 191 | base_tile_height = int(root.attrib["tileheight"]) |
| 190 | num_layers = 1 | ||
| 191 | flags = Orientation.Isometric if (root.attrib["orientation"] == "isometric") else Orientation.Orthogonal | 192 | flags = Orientation.Isometric if (root.attrib["orientation"] == "isometric") else Orientation.Orthogonal |
| 193 | firstgid = 1 | ||
| 194 | layers = [] | ||
| 195 | tileset_path = "" | ||
| 192 | 196 | ||
| 193 | print(f"Map width: {map_width}") | 197 | print(f"Map width: {map_width}") |
| 194 | print(f"Map height: {map_height}") | 198 | print(f"Map height: {map_height}") |
| @@ -196,8 +200,6 @@ def convert_tmx(input_filepath, output_filepath): | |||
| 196 | print(f"Tile height: {base_tile_height}") | 200 | print(f"Tile height: {base_tile_height}") |
| 197 | print(f"Orientation: {flags}") | 201 | print(f"Orientation: {flags}") |
| 198 | 202 | ||
| 199 | tileset_path = None | ||
| 200 | |||
| 201 | with open(output_filepath, 'bw') as output: | 203 | with open(output_filepath, 'bw') as output: |
| 202 | for child in root: | 204 | for child in root: |
| 203 | if child.tag == "tileset": | 205 | if child.tag == "tileset": |
| @@ -205,19 +207,12 @@ def convert_tmx(input_filepath, output_filepath): | |||
| 205 | 207 | ||
| 206 | tileset = child | 208 | tileset = child |
| 207 | tileset_path = tileset.attrib["source"] | 209 | tileset_path = tileset.attrib["source"] |
| 210 | firstgid = int(tileset.attrib["firstgid"]) | ||
| 208 | 211 | ||
| 209 | print(f"Tile set: {tileset_path}") | 212 | print(f"Tile set: {tileset_path}") |
| 210 | 213 | ||
| 211 | tileset_path = tileset_path.replace("tsx", "ts") | 214 | tileset_path = tileset_path.replace("tsx", "ts") |
| 212 | 215 | ||
| 213 | # Write the header. | ||
| 214 | output.write(to_char_array(tileset_path, MAX_PATH_LENGTH)) | ||
| 215 | output.write(ctypes.c_uint16(map_width)) | ||
| 216 | output.write(ctypes.c_uint16(map_height)) | ||
| 217 | output.write(ctypes.c_uint16(base_tile_width)) | ||
| 218 | output.write(ctypes.c_uint16(base_tile_height)) | ||
| 219 | output.write(ctypes.c_uint16(num_layers)) | ||
| 220 | output.write(ctypes.c_uint16(flags)) | ||
| 221 | elif child.tag == "layer": | 216 | elif child.tag == "layer": |
| 222 | layer = child | 217 | layer = child |
| 223 | layer_id = int(layer.attrib["id"]) | 218 | layer_id = int(layer.attrib["id"]) |
| @@ -238,12 +233,27 @@ def convert_tmx(input_filepath, output_filepath): | |||
| 238 | 233 | ||
| 239 | csv = data.text.strip() | 234 | csv = data.text.strip() |
| 240 | rows = csv.split('\n') | 235 | rows = csv.split('\n') |
| 236 | tiles = bytearray() | ||
| 241 | for row in rows: | 237 | for row in rows: |
| 242 | tile_ids = [x.strip() for x in row.split(',') if x] | 238 | tile_ids = [x.strip() for x in row.split(',') if x] |
| 243 | for tile_id in tile_ids: | 239 | for tile_id in tile_ids: |
| 244 | # TODO: We need to handle 'firsgid' in TMX files. | 240 | # Subtract firstgid and then add 1 to make tiles 1-based. |
| 245 | # For now, assume it's 1 and do -1 to make the tiles 0-based. | 241 | tile_id = int(tile_id) - firstgid + 1 |
| 246 | output.write(ctypes.c_uint16(int(tile_id) - 1)) | 242 | tiles.extend(struct.pack("=h", tile_id)) |
| 243 | layers.append(tiles) | ||
| 244 | |||
| 245 | # Write the header. | ||
| 246 | output.write(to_char_array(tileset_path, MAX_PATH_LENGTH)) | ||
| 247 | output.write(ctypes.c_uint16(map_width)) | ||
| 248 | output.write(ctypes.c_uint16(map_height)) | ||
| 249 | output.write(ctypes.c_uint16(base_tile_width)) | ||
| 250 | output.write(ctypes.c_uint16(base_tile_height)) | ||
| 251 | output.write(ctypes.c_uint16(len(layers))) | ||
| 252 | output.write(ctypes.c_uint16(flags)) | ||
| 253 | |||
| 254 | # Write the layers. | ||
| 255 | for layer in layers: | ||
| 256 | output.write(layer) | ||
| 247 | 257 | ||
| 248 | 258 | ||
| 249 | def get_num_cols(image, sprite_width): | 259 | def get_num_cols(image, sprite_width): |
