Complete automatic animated tile importing

main
Cameron Murphy Reikes 2 years ago
parent 2d0361e71d
commit 8b7ad42e79

@ -14,7 +14,12 @@
{ {
filepath: "mystery_tile.png", filepath: "mystery_tile.png",
} }
@tileset ruins_animated:
{
image: image_animated_terrain,
filepath: "copyrighted/ruins_animated.tsx",
}
@level level0: @level level0:
{ {
filepath: "testsmalllevel.json", filepath: "level0.json",
} }

@ -3,13 +3,13 @@
"infinite":false, "infinite":false,
"layers":[ "layers":[
{ {
"data":[53, 53, 53, 53, 53, 53, 53, 53, "data":[53, 53, 53, 209, 160, 53, 53, 53,
53, 53, 53, 158, 213, 53, 53, 53, 53, 209, 159, 210, 265, 53, 53, 53,
53, 53, 209, 210, 212, 160, 53, 53, 209, 210, 263, 263, 212, 213, 53, 53,
53, 158, 210, 263, 263, 212, 213, 53, 261, 263, 263, 263, 263, 265, 53, 53,
53, 366, 314, 263, 263, 316, 368, 53, 313, 314, 263, 263, 263, 265, 53, 53,
53, 53, 366, 314, 316, 368, 53, 53, 53, 313, 314, 316, 367, 317, 53, 53,
53, 53, 53, 366, 368, 53, 53, 53, 53, 53, 366, 317, 53, 53, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53], 53, 53, 53, 53, 53, 53, 53, 53],
"height":8, "height":8,
"id":1, "id":1,
@ -31,6 +31,10 @@
{ {
"firstgid":1, "firstgid":1,
"source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Ancient Ruins-Animated Terrains-16 frames.tsx" "source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Ancient Ruins-Animated Terrains-16 frames.tsx"
},
{
"firstgid":2341,
"source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Terrain - Ancient Ruins.tsx"
}], }],
"tilewidth":32, "tilewidth":32,
"type":"map", "type":"map",

@ -68,6 +68,33 @@ char *nullterm(MD_String8 s) {
return to_return; return to_return;
} }
char* fillnull(char *s, char c) {
while(*s != '\0') {
if(*s == c) {
*s = '\0';
return s + 1;
}
s++;
}
assert(false, MD_S8Lit("Couldn't find char"));
return NULL;
}
char* goto_end_of(char *tomove, size_t max_move, char *pattern) {
size_t pattern_len = strlen(pattern);
for(int i = 0; i < max_move; i++) {
if(strncmp(tomove, pattern, pattern_len) == 0) {
tomove += pattern_len;
return tomove;
}
tomove++;
}
return NULL;
}
#define list_printf(list_ptr, ...) MD_S8ListPush(cg_arena, list_ptr, MD_S8Fmt(cg_arena, __VA_ARGS__))
int main(int argc, char **argv) { int main(int argc, char **argv) {
cg_arena = MD_ArenaAlloc(); cg_arena = MD_ArenaAlloc();
assert(cg_arena, MD_S8Lit("Memory")); assert(cg_arena, MD_S8Lit("Memory"));
@ -86,6 +113,7 @@ int main(int argc, char **argv) {
MD_String8List declarations_list = {0}; MD_String8List declarations_list = {0};
MD_String8List load_list = {0}; MD_String8List load_list = {0};
MD_String8List level_decl_list = {0}; MD_String8List level_decl_list = {0};
MD_String8List tileset_decls = {0};
for(MD_EachNode(node, parse.node->first_child)) { for(MD_EachNode(node, parse.node->first_child)) {
if(MD_S8Match(node->first_tag->string, MD_S8Lit("image"), 0)) { if(MD_S8Match(node->first_tag->string, MD_S8Lit("image"), 0)) {
MD_String8 variable_name = MD_S8Fmt(cg_arena, "image_%.*s", MD_S8VArg(node->string)); MD_String8 variable_name = MD_S8Fmt(cg_arena, "image_%.*s", MD_S8VArg(node->string));
@ -100,6 +128,44 @@ int main(int argc, char **argv) {
MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name))); MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name)));
MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath))); MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath)));
} }
if(MD_S8Match(node->first_tag->string, MD_S8Lit("tileset"), 0)) {
MD_String8 variable_name = MD_S8Fmt(cg_arena, "tileset_%.*s", MD_S8VArg(node->string));
log("New tileset %.*s\n", MD_S8VArg(variable_name));
MD_String8 filepath = asset_file_path(ChildValue(node, MD_S8Lit("filepath")));
MD_String8 tileset_file_contents = MD_LoadEntireFile(cg_arena, filepath);
list_printf(&tileset_decls, "TileSet %.*s = {\n", MD_S8VArg(variable_name));
list_printf(&tileset_decls, ".img = &%.*s,\n", MD_S8VArg(ChildValue(node, MD_S8Lit("image"))));
list_printf(&tileset_decls, ".animated = {\n");
char *end = tileset_file_contents.str + tileset_file_contents.size;
char *cur = tileset_file_contents.str;
while(cur < end) {
cur = goto_end_of(cur, end - cur, "<tile id=\"");
if(cur == NULL) break;
char *new_cur = fillnull(cur, '"');
int frame_from = atoi(cur);
cur = new_cur;
list_printf(&tileset_decls, "{ .id_from = %d, .frames = { ", frame_from);
char *end_of_anim = goto_end_of(cur, end - cur, "</animation>");
int num_frames = 0;
while(true) {
char *next_frame = goto_end_of(cur, end - cur, "<frame tileid=\"");
if(next_frame == NULL || next_frame > end_of_anim) break;
char *new_cur = fillnull(next_frame, '"');
int frame = atoi(next_frame);
list_printf(&tileset_decls, "%d, ", frame);
num_frames++;
cur = new_cur;
}
list_printf(&tileset_decls, "}, .num_frames = %d },\n", num_frames);
}
list_printf(&tileset_decls, "}};\n");
}
if(MD_S8Match(node->first_tag->string, MD_S8Lit("level"), 0)) { if(MD_S8Match(node->first_tag->string, MD_S8Lit("level"), 0)) {
MD_String8 variable_name = MD_S8Fmt(cg_arena, "level_%.*s", MD_S8VArg(node->string)); MD_String8 variable_name = MD_S8Fmt(cg_arena, "level_%.*s", MD_S8VArg(node->string));
log("New level variable %.*s\n", MD_S8VArg(variable_name)); log("New level variable %.*s\n", MD_S8VArg(variable_name));
@ -107,7 +173,6 @@ int main(int argc, char **argv) {
MD_ParseResult level_parse = MD_ParseWholeFile(cg_arena, filepath); MD_ParseResult level_parse = MD_ParseWholeFile(cg_arena, filepath);
assert(level_parse.node != 0, MD_S8Lit("Failed to load level file")); assert(level_parse.node != 0, MD_S8Lit("Failed to load level file"));
//dump(level_parse.node);
MD_Node *layers = MD_ChildFromString(level_parse.node->first_child, MD_S8Lit("layers"), 0); MD_Node *layers = MD_ChildFromString(level_parse.node->first_child, MD_S8Lit("layers"), 0);
int width = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("width"), 0)->first_child->string)); int width = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("width"), 0)->first_child->string));
int height = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("height"), 0)->first_child->string)); int height = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("height"), 0)->first_child->string));
@ -128,53 +193,14 @@ int main(int argc, char **argv) {
} }
num_index += 1; num_index += 1;
} }
//fprintf(output, "},\n};\n");
#if 0
jsmn_parser p;
jsmntok_t t[1024*5];
jsmn_init(&p);
const char *json_string = level_file_contents.str;
int r = jsmn_parse(&p, json_string, level_file_contents.size, t, sizeof(t) / sizeof(t[0]));
assert(r > 0, MD_S8Lit("Failed to parse json"));
int layer_obj_token = 0;
fprintf(output, "Level %.*s = {\n.tiles = {\n", MD_S8VArg(variable_name));
// this code is absolutely disgusting but I think it's fine because this is meta code, I guess time will tell if this needs to be cleaner or not... sorry future self!
for(int i = 0; i < r; i++) {
if(jsoneq(json_string, &t[i], "data")) {
jsmntok_t *arr = &t[i+1];
assert(arr->type == JSMN_ARRAY, MD_S8Lit("Expected array after data"));
int width = getnextofname(json_string, &t[i+1], "width");
int height = getnextofname(json_string, &t[i+1], "height");
fprintf(output, "{ ");
int last_token_index = i + 2 + arr->size;
for(int ii = i + 2; ii < last_token_index; ii++) {
jsmntok_t *num_to_add = &t[ii];
assert(num_to_add->type == JSMN_PRIMITIVE, MD_S8Lit("not a number"));
int num_index = ii - (i + 2);
fprintf(output, "%.*s, ", num_to_add->end - num_to_add->start, json_string + num_to_add->start);
if(num_index % width == width - 1) {
if(ii == last_token_index - 1) {
fprintf(output, "},\n}\n}; // %.*s\n", MD_S8VArg(variable_name));
} else {
fprintf(output, "},\n{ ");
}
}
}
}
}
#endif
} }
} }
MD_StringJoin join = MD_ZERO_STRUCT; MD_StringJoin join = MD_ZERO_STRUCT;
MD_String8 declarations = MD_S8ListJoin(cg_arena, declarations_list, &join); MD_String8 declarations = MD_S8ListJoin(cg_arena, declarations_list, &join);
MD_String8 loads = MD_S8ListJoin(cg_arena, load_list, &join); MD_String8 loads = MD_S8ListJoin(cg_arena, load_list, &join);
fprintf(output, "%.*s\nvoid load_assets() {\n%.*s\n}", MD_S8VArg(declarations), MD_S8VArg(loads)); fprintf(output, "%.*s\nvoid load_assets() {\n%.*s\n}\n", MD_S8VArg(declarations), MD_S8VArg(loads));
fprintf(output, "%.*s\n", MD_S8VArg(MD_S8ListJoin(cg_arena, tileset_decls, &join)));
fclose(output); fclose(output);
return 0; return 0;

143
main.c

@ -25,12 +25,16 @@ typedef struct TileInstance {
uint16_t kind; uint16_t kind;
} TileInstance; } TileInstance;
typedef struct TileInfo { typedef struct AnimatedTile {
uint16_t kind; uint16_t id_from;
int num_frames; // each frame of animation is same size, to the right of the region int num_frames;
HMM_Vec2 region_upper_left; uint16_t frames[32];
} AnimatedTile;
typedef struct TileSet {
sg_image *img; sg_image *img;
} TileInfo; AnimatedTile animated[128];
} TileSet;
#define LEVEL_TILES 60 #define LEVEL_TILES 60
#define TILE_SIZE 32 // in pixels #define TILE_SIZE 32 // in pixels
@ -70,93 +74,6 @@ sg_image load_image(const char *path) {
#include "quad-sapp.glsl.h" #include "quad-sapp.glsl.h"
#include "assets.gen.c" #include "assets.gen.c"
// the `kind`, or the ID of the tile, is off by one from what tiled displays in editor. Sucks!
TileInfo tiles[] = {
{
.kind = 53,
.num_frames = 15,
.region_upper_left = { 0, 32 },
.img = &image_animated_terrain,
},
{
.kind = 158,
.num_frames = 15,
.region_upper_left = { 672, 128 },
.img = &image_animated_terrain,
},
{
.kind = 159,
.num_frames = 15,
.region_upper_left = { 672, 96 },
.img = &image_animated_terrain,
},
{
.kind = 160,
.num_frames = 15,
.region_upper_left = { 160, 192 },
.img = &image_animated_terrain,
},
{
.kind = 210,
.num_frames = 15,
.region_upper_left = { 672, 160 },
.img = &image_animated_terrain,
},
{
.kind = 261,
.num_frames = 15,
.region_upper_left = { 672, 224 },
.img = &image_animated_terrain,
},
{
.kind = 367,
.num_frames = 15,
.region_upper_left = { 1184, 160 },
.img = &image_animated_terrain,
},
{
.kind = 316,
.num_frames = 15,
.region_upper_left = { 160, 96 },
.img = &image_animated_terrain,
},
{
.kind = 209,
.num_frames = 15,
.region_upper_left = { 672, 128 },
.img = &image_animated_terrain,
},
{
.kind = 213,
.num_frames = 15,
.region_upper_left = { 672, 64 },
.img = &image_animated_terrain,
},
{
.kind = 263,
.num_frames = 1,
.region_upper_left = { 64, 160 },
.img = &image_animated_terrain,
},
{
.kind = 213,
.num_frames = 15,
.region_upper_left = { 672, 64 },
.img = &image_animated_terrain,
},
{
.kind = 213,
.num_frames = 15,
.region_upper_left = { 672, 64 },
.img = &image_animated_terrain,
},
};
TileInfo mystery_tile = {
.img = &image_mystery_tile,
.num_frames = 1,
.region_upper_left = { 0, 0 },
};
sg_image image_font = {0}; sg_image image_font = {0};
const float font_size = 64.0; const float font_size = 64.0;
stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
@ -451,6 +368,15 @@ void colorbox(bool world_space, HMM_Vec2 upper_left, HMM_Vec2 lower_right, Color
draw_quad(world_space, points, image_white_square, full_region(image_white_square), color); draw_quad(world_space, points, image_white_square, full_region(image_white_square), color);
} }
HMM_Vec2 tile_id_to_coord(sg_image tileset_image, HMM_Vec2 tile_size, uint16_t tile_id) {
int tiles_per_row = (int)(img_size(tileset_image).X / tile_size.X);
int tile_index = tile_id - 1;
int tile_image_row = tile_index / tiles_per_row;
int tile_image_col = tile_index - tile_image_row*tiles_per_row;
HMM_Vec2 tile_image_coord = HMM_V2((float)tile_image_col * tile_size.X, (float)tile_image_row*tile_size.Y);
return tile_image_coord;
}
// returns bounds. To measure text you can set dry run to true and get the bounds // returns bounds. To measure text you can set dry run to true and get the bounds
AABB draw_text(bool world_space, bool dry_run, const char *text, size_t length, HMM_Vec2 pos, Color color) { AABB draw_text(bool world_space, bool dry_run, const char *text, size_t length, HMM_Vec2 pos, Color color) {
@ -555,30 +481,33 @@ void frame(void) {
for(int col = 0; col < LEVEL_TILES; col++) for(int col = 0; col < LEVEL_TILES; col++)
{ {
TileInstance cur = cur_level->tiles[row][col]; TileInstance cur = cur_level->tiles[row][col];
TileSet tileset = tileset_ruins_animated;
if(cur.kind != 0){ if(cur.kind != 0){
HMM_Vec2 points[4] = {0}; HMM_Vec2 points[4] = {0};
HMM_Vec2 tile_size = HMM_V2(TILE_SIZE, TILE_SIZE); HMM_Vec2 tile_size = HMM_V2(TILE_SIZE, TILE_SIZE);
quad_points_corner_size(points, tilecoord_to_world(col, row), tile_size); quad_points_corner_size(points, tilecoord_to_world(col, row), tile_size);
TileInfo *info = NULL;
for(int i = 0; i < sizeof(tiles)/sizeof(*tiles); i++) { sg_image tileset_image = *tileset.img;
if(tiles[i].kind == cur.kind) {
info = &tiles[i]; HMM_Vec2 tile_image_coord = tile_id_to_coord(tileset_image, tile_size, cur.kind);
break;
AnimatedTile *anim = NULL;
for(int i = 0; i < sizeof(tileset.animated)/sizeof(*tileset.animated); i++) {
if(tileset.animated[i].id_from == cur.kind-1) {
anim = &tileset.animated[i];
} }
} }
if(info == NULL) info = &mystery_tile; if(anim) {
double time_per_frame = 0.1;
int frame_index = (int)(time/time_per_frame) % anim->num_frames;
tile_image_coord = tile_id_to_coord(tileset_image, tile_size, anim->frames[frame_index]+1);
}
AABB region; AABB region;
region.upper_left = info->region_upper_left; region.upper_left = tile_image_coord;
region.lower_right = HMM_AddV2(region.upper_left, tile_size); region.lower_right = HMM_AddV2(region.upper_left, tile_size);
double time_per_frame = 0.1;
int frame_index = (int)(time/time_per_frame) % info->num_frames; draw_quad(true, points, tileset_image, region, WHITE);
float width = region.lower_right.X - region.upper_left.X;
assert(width > 0.0f);
region.upper_left.X += width*(float)frame_index;
region.lower_right.X += width*(float)frame_index;
draw_quad(true, points, *info->img, region, WHITE);
} }
} }
} }

@ -8,6 +8,7 @@ mkdir assets\copyrighted
copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Characters\NPC Merchant-idle.png" "assets\copyrighted\merchant.png" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Characters\NPC Merchant-idle.png" "assets\copyrighted\merchant.png" || goto :error
copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\wall-1 - 3 tiles tall.png" "assets\copyrighted\wall-1 - 3 tiles tall.png" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\wall-1 - 3 tiles tall.png" "assets\copyrighted\wall-1 - 3 tiles tall.png" || goto :error
copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\Tileset-Animated Terrains-16 frames.png" "assets\copyrighted\animated_terrain.png" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\Tileset-Animated Terrains-16 frames.png" "assets\copyrighted\animated_terrain.png" || goto :error
copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\TiledMap Editor\Ancient Ruins-Animated Terrains-16 frames.tsx" "assets\copyrighted\ruins_animated.tsx" || goto :error
rmdir /S /q gen rmdir /S /q gen
mkdir gen mkdir gen

Loading…
Cancel
Save