From b70a86aa336cd79765146c6d4a641c14df0681ec Mon Sep 17 00:00:00 2001 From: doyle Date: Sun, 24 Sep 2023 14:20:27 +1000 Subject: [PATCH] tely/asset: Fix sprite packer emitting wrong anim rect count --- Data/Textures/smoochie_resized_25%.txt | 4 +- feely_pona.cpp | 203 ++++++++++++++----------- feely_pona_game.cpp | 8 +- feely_pona_game.h | 9 +- feely_pona_sprite_packer.cpp | 20 +-- project.rdbg | Bin 1214 -> 1106 bytes 6 files changed, 139 insertions(+), 105 deletions(-) diff --git a/Data/Textures/smoochie_resized_25%.txt b/Data/Textures/smoochie_resized_25%.txt index 10fac37..6847ada 100644 --- a/Data/Textures/smoochie_resized_25%.txt +++ b/Data/Textures/smoochie_resized_25%.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f4e8c8421191e968407559cf3394cf5f038701a3d01a87bae5867d52aa00363 -size 681 +oid sha256:c962c735f3d12d6dc8c1c51a6c26f0d30170b0071bbaa147bd65df9071a784b7 +size 713 diff --git a/feely_pona.cpp b/feely_pona.cpp index 5956ed5..ec36a30 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -3,6 +3,98 @@ #include "feely_pona_unity.h" #endif +struct FP_LoadSpriteSheetFromSpecResult +{ + TELY_AssetSpriteSheet sheet; + Dqn_Slice anims; +}; + +FP_LoadSpriteSheetFromSpecResult FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_Assets *assets, Dqn_Arena *arena, Dqn_String8 sheet_name) +{ + FP_LoadSpriteSheetFromSpecResult result = {}; + + TELY_AssetSpriteSheet *sheet = &result.sheet; + Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); + sheet->sprite_size = Dqn_V2I_InitNx2(185, 170); + sheet->type = TELY_AssetSpriteSheetType_Rects; + + // NOTE: Load the sprite meta file ========================================================= + Dqn_String8 sprite_spec_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.txt", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(sheet_name)); + Dqn_String8 sprite_spec_buffer = platform->func_load_file(scratch.arena, sprite_spec_path); + Dqn_String8SplitAllocResult lines = Dqn_String8_SplitAlloc(scratch.allocator, sprite_spec_buffer, DQN_STRING8("\n")); + Dqn_usize sprite_rect_index = 0; + Dqn_usize sprite_anim_index = 0; + + DQN_FOR_UINDEX(line_index, lines.size) { + Dqn_String8 line = lines.data[line_index]; + Dqn_String8SplitAllocResult line_splits = Dqn_String8_SplitAlloc(scratch.allocator, line, DQN_STRING8(";")); + + if (line_index == 0) { + DQN_ASSERTF(line_splits.size == 4, "Expected 4 splits for @file lines"); + DQN_ASSERT(Dqn_String8_StartsWith(line_splits.data[0], DQN_STRING8("@file"), Dqn_String8EqCase_Sensitive)); + + // NOTE: Sprite sheet path + Dqn_String8 sprite_sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(line_splits.data[1])); + sheet->tex_handle = platform->func_load_texture(assets, sheet_name, sprite_sheet_path); + DQN_ASSERTF(Dqn_Fs_Exists(sprite_sheet_path), "Required file does not exist '%.*s'", DQN_STRING_FMT(sprite_sheet_path)); + + // NOTE: Total sprite frame count + Dqn_String8ToU64Result total_frame_count = Dqn_String8_ToU64(line_splits.data[2], 0); + DQN_ASSERT(total_frame_count.success); + sheet->rects = Dqn_Slice_Alloc(arena, total_frame_count.value, Dqn_ZeroMem_No); + + // NOTE: Total animation count + Dqn_String8ToU64Result total_anim_count = Dqn_String8_ToU64(line_splits.data[3], 0); + DQN_ASSERT(total_anim_count.success); + result.anims = Dqn_Slice_Alloc(arena, total_anim_count.value, Dqn_ZeroMem_No); + + // TODO: Sprite size? + + // TODO: Texture name? + continue; + } + + if (Dqn_String8_StartsWith(line, DQN_STRING8("@anim"))) { + DQN_ASSERTF(line_splits.size == 4, "Expected 4 splits for @anim lines"); + Dqn_String8 anim_name = line_splits.data[1]; + Dqn_String8ToU64Result frames_per_second = Dqn_String8_ToU64(line_splits.data[2], 0); + Dqn_String8ToU64Result frame_count = Dqn_String8_ToU64(line_splits.data[3], 0); + + DQN_ASSERT(anim_name.size); + DQN_ASSERT(frame_count.success); + DQN_ASSERT(frames_per_second.success); + + Dqn_Allocator allocator = Dqn_Arena_Allocator(arena); + TELY_AssetSpriteAnimation *anim = result.anims.data + sprite_anim_index++; + anim->label = Dqn_String8_Copy(allocator, anim_name); + anim->index = DQN_CAST(uint16_t)sprite_rect_index; + anim->count = DQN_CAST(uint16_t)frame_count.value; + anim->seconds_per_frame = 1.f / frames_per_second.value; + } else { + DQN_ASSERTF(line_splits.size == 4, "Expected 4 splits for sprite frame lines"); + Dqn_String8ToU64Result x = Dqn_String8_ToU64(line_splits.data[0], 0); + Dqn_String8ToU64Result y = Dqn_String8_ToU64(line_splits.data[1], 0); + Dqn_String8ToU64Result w = Dqn_String8_ToU64(line_splits.data[2], 0); + Dqn_String8ToU64Result h = Dqn_String8_ToU64(line_splits.data[3], 0); + + DQN_ASSERT(x.success); + DQN_ASSERT(y.success); + DQN_ASSERT(w.success); + DQN_ASSERT(h.success); + + sheet->rects.data[sprite_rect_index++] = + Dqn_Rect_InitNx4(DQN_CAST(Dqn_f32) x.value, + DQN_CAST(Dqn_f32) y.value, + DQN_CAST(Dqn_f32) w.value, + DQN_CAST(Dqn_f32) h.value); + } + } + + DQN_ASSERT(sheet->rects.size == sprite_rect_index); + DQN_ASSERT(result.anims.size == sprite_anim_index); + return result; +} + extern "C" __declspec(dllexport) void TELY_DLL_Reload(void *user_data) { @@ -398,89 +490,19 @@ void TELY_DLL_Init(void *user_data) DQN_MEMCPY(game->hero_sprite_anims.data, &hero_anims, sizeof(hero_anims[0]) * DQN_ARRAY_UCOUNT(hero_anims)); } + // NOTE: Load sprite sheets ==================================================================== { - TELY_AssetSpriteSheet *sheet = &game->protag_walk_sprite_sheet; - Dqn_Slice *anims = &game->protag_walk_sprite_anims; - - Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); - sheet->sprite_size = Dqn_V2I_InitNx2(185, 170); - sheet->type = TELY_AssetSpriteSheetType_Rects; - - // NOTE: Load the sprite meta file ========================================================= - Dqn_String8 sheet_name = DQN_STRING8("terry_walk_resized_25%"); - - Dqn_String8 sprite_spec_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.txt", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(sheet_name)); - Dqn_String8 sprite_spec_buffer = platform->func_load_file(scratch.arena, sprite_spec_path); - Dqn_String8SplitAllocResult lines = Dqn_String8_SplitAlloc(scratch.allocator, sprite_spec_buffer, DQN_STRING8("\n")); - Dqn_usize sprite_rect_index = 0; - Dqn_usize sprite_anim_index = 0; - - DQN_FOR_UINDEX(line_index, lines.size) { - Dqn_String8 line = lines.data[line_index]; - Dqn_String8SplitAllocResult line_splits = Dqn_String8_SplitAlloc(scratch.allocator, line, DQN_STRING8(";")); - - if (line_index == 0) { - DQN_ASSERTF(line_splits.size == 4, "Expected 4 splits for @file lines"); - DQN_ASSERT(Dqn_String8_StartsWith(line_splits.data[0], DQN_STRING8("@file"), Dqn_String8EqCase_Sensitive)); - - // NOTE: Sprite sheet path - Dqn_String8 sprite_sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(line_splits.data[1])); - sheet->tex_handle = platform->func_load_texture(assets, sheet_name, sprite_sheet_path); - DQN_ASSERTF(Dqn_Fs_Exists(sprite_sheet_path), "Required file does not exist '%.*s'", DQN_STRING_FMT(sprite_sheet_path)); - - // NOTE: Total sprite frame count - Dqn_String8ToU64Result total_frame_count = Dqn_String8_ToU64(line_splits.data[2], 0); - DQN_ASSERT(total_frame_count.success); - sheet->rects = Dqn_Slice_Alloc(&platform->arena, total_frame_count.value, Dqn_ZeroMem_No); - - // NOTE: Total animation count - Dqn_String8ToU64Result total_anim_count = Dqn_String8_ToU64(line_splits.data[3], 0); - DQN_ASSERT(total_anim_count.success); - *anims = Dqn_Slice_Alloc(&platform->arena, total_anim_count.value, Dqn_ZeroMem_No); - - // TODO: Sprite size? - - // TODO: Texture name? - continue; - } - - if (Dqn_String8_StartsWith(line, DQN_STRING8("@anim"))) { - DQN_ASSERTF(line_splits.size == 4, "Expected 4 splits for @anim lines"); - Dqn_String8 anim_name = line_splits.data[1]; - Dqn_String8ToU64Result frames_per_second = Dqn_String8_ToU64(line_splits.data[2], 0); - Dqn_String8ToU64Result frame_count = Dqn_String8_ToU64(line_splits.data[3], 0); - - DQN_ASSERT(anim_name.size); - DQN_ASSERT(frame_count.success); - DQN_ASSERT(frames_per_second.success); - - TELY_AssetSpriteAnimation *anim = anims->data + sprite_anim_index++; - anim->label = Dqn_String8_Copy(platform->allocator, anim_name); - anim->index = DQN_CAST(uint16_t)sprite_rect_index; - anim->count = DQN_CAST(uint16_t)frame_count.value; - anim->seconds_per_frame = 1.f / frames_per_second.value; - } else { - DQN_ASSERTF(line_splits.size == 4, "Expected 4 splits for sprite frame lines"); - Dqn_String8ToU64Result x = Dqn_String8_ToU64(line_splits.data[0], 0); - Dqn_String8ToU64Result y = Dqn_String8_ToU64(line_splits.data[1], 0); - Dqn_String8ToU64Result w = Dqn_String8_ToU64(line_splits.data[2], 0); - Dqn_String8ToU64Result h = Dqn_String8_ToU64(line_splits.data[3], 0); - - DQN_ASSERT(x.success); - DQN_ASSERT(y.success); - DQN_ASSERT(w.success); - DQN_ASSERT(h.success); - - sheet->rects.data[sprite_rect_index++] = - Dqn_Rect_InitNx4(DQN_CAST(Dqn_f32) x.value, - DQN_CAST(Dqn_f32) y.value, - DQN_CAST(Dqn_f32) w.value, - DQN_CAST(Dqn_f32) h.value); - } + { + FP_LoadSpriteSheetFromSpecResult terry = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_walk_resized_25%")); + game->terry_sprite_sheet = terry.sheet; + game->terry_sprite_anims = terry.anims; } - DQN_ASSERT(sheet->rects.size == sprite_rect_index); - DQN_ASSERT(anims->size == sprite_anim_index); + { + FP_LoadSpriteSheetFromSpecResult smoochie = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("smoochie_resized_25%")); + game->smoochie_sprite_sheet = smoochie.sheet; + game->smoochie_sprite_anims = smoochie.anims; + } } game->entities = Dqn_VArray_Init(&platform->arena, 1024 * 8); @@ -563,8 +585,8 @@ void TELY_DLL_Init(void *user_data) // NOTE: Hero { - TELY_AssetSpriteSheet *sheet = &game->protag_walk_sprite_sheet; - Dqn_Slice anims = game->protag_walk_sprite_anims; + TELY_AssetSpriteSheet *sheet = &game->terry_sprite_sheet; + Dqn_Slice anims = game->terry_sprite_anims; FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Terry"); entity->local_pos = Dqn_V2_InitNx2(1334, 396); @@ -628,8 +650,8 @@ void TELY_DLL_Init(void *user_data) } } - // NOTE: Mob spawner - if (0) { + // NOTE: Mob spawner =========================================================================== + { FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob spawner"); entity->local_pos = Dqn_V2_InitNx2(0, platform->core.window_size.y * .5f); entity->flags |= FP_GameEntityFlag_Clickable; @@ -637,6 +659,7 @@ void TELY_DLL_Init(void *user_data) entity->flags |= FP_GameEntityFlag_MoveByMouse; entity->flags |= FP_GameEntityFlag_MoveByGamepad; entity->flags |= FP_GameEntityFlag_MobSpawner; + entity->spawn_cap = 1; } uint16_t font_size = 18; @@ -1000,9 +1023,15 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, } // NOTE: Mob spawner ======================================================================= - if ((entity->flags & FP_GameEntityFlag_MobSpawner) && input->timer_s >= entity->next_spawn_timestamp_s) { - entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f); - FP_Game_EntityAddMob(game, entity_world_pos); + if (entity->flags & FP_GameEntityFlag_MobSpawner) { + if (entity->spawn_count < entity->spawn_cap) { + if (input->timer_s >= entity->next_spawn_timestamp_s) { + entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f); + entity->spawn_count++; + + FP_Game_EntityAddMob(game, entity_world_pos); + } + } } } Dqn_Profiler_EndZone(update_zone); diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index b56a9fd..e795ef8 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -669,10 +669,10 @@ static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos) { FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob"); entity->local_pos = pos; - entity->size_scale = Dqn_V2_InitNx1(4); - entity->sprite_sheet = &game->hero_sprite_sheet; - entity->sprite_anims = game->hero_sprite_anims; - entity->local_hit_box_size = Dqn_V2_InitV2I(game->hero_sprite_sheet.sprite_size); + entity->size_scale = Dqn_V2_InitNx1(.5f); + entity->sprite_sheet = &game->smoochie_sprite_sheet; + entity->sprite_anims = game->smoochie_sprite_anims; + entity->local_hit_box_size = Dqn_V2_InitV2I(game->smoochie_sprite_sheet.sprite_size); entity->flags |= FP_GameEntityFlag_Clickable; entity->flags |= FP_GameEntityFlag_MoveByKeyboard; entity->flags |= FP_GameEntityFlag_MoveByMouse; diff --git a/feely_pona_game.h b/feely_pona_game.h index 2b8aa4c..f83c168 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -107,6 +107,8 @@ struct FP_GameEntity Dqn_V2 attack_box_offset; uint64_t next_spawn_timestamp_s; + uint64_t spawn_count; + uint64_t spawn_cap; uint64_t flags; bool facing_left; @@ -149,8 +151,11 @@ struct FP_Game Dqn_FArray parent_entity_stack; Dqn_VArray entities; - TELY_AssetSpriteSheet protag_walk_sprite_sheet; - Dqn_Slice protag_walk_sprite_anims; + TELY_AssetSpriteSheet terry_sprite_sheet; + Dqn_Slice terry_sprite_anims; + + TELY_AssetSpriteSheet smoochie_sprite_sheet; + Dqn_Slice smoochie_sprite_anims; FP_GameEntity *root_entity; FP_GameEntity *entity_free_list; diff --git a/feely_pona_sprite_packer.cpp b/feely_pona_sprite_packer.cpp index c2f40ad..70f2c3c 100644 --- a/feely_pona_sprite_packer.cpp +++ b/feely_pona_sprite_packer.cpp @@ -171,11 +171,13 @@ int main(int argc, char const *argv[]) // NOTE: Generate the meta file ================================================================ // NOTE: Count the number of animations we loaded frames fore - Dqn_usize num_animations = 0; + Dqn_usize num_anims = 0; + Dqn_usize num_anim_rects = 0; for (Dqn_usize index = 1 /*Sentinel*/; index < sprite_spec_table.occupied; index++) { Dqn_DSMapSlot *slot = sprite_spec_table.slots + index; if (slot->value.frame_count) { - num_animations++; + num_anims++; + num_anim_rects += slot->value.frame_count; } } @@ -184,8 +186,8 @@ int main(int argc, char const *argv[]) Dqn_Fs_WriteFileF(&meta_file, "@file;%.*s;%d;%d\n", DQN_STRING_FMT(Dqn_String8_FileNameFromPath(atlas_path)), - DQN_CAST(int) file_list.count, - DQN_CAST(int) num_animations); + DQN_CAST(int) num_anim_rects, + DQN_CAST(int) num_anims); Dqn_Log_InfoF("Generating meta file: %.*s", DQN_STRING_FMT(meta_path)); Dqn_String8 anim_prefix = {}; @@ -210,15 +212,13 @@ int main(int argc, char const *argv[]) stbi_image_free(loaded_image); // NOTE: Write the sprite and the rects to the sheet - Dqn_String8 file_name = Dqn_String8_FileNameFromPath(*it.data); - Dqn_String8 file_name_without_extension = Dqn_String8_BinarySplit(file_name, DQN_STRING8(".")).lhs; + Dqn_String8 file_name = Dqn_String8_FileNameFromPath(*it.data); + Dqn_String8 file_name_to_anim_prefix = Dqn_String8_BinarySplitReverse(file_name, DQN_STRING8("_")).lhs; // NOTE: Detect what animation we are currently processing ================================= - if (anim_prefix.size == 0 || !Dqn_String8_StartsWith(file_name_without_extension, anim_prefix)) { + if (anim_prefix.size == 0 || file_name_to_anim_prefix != anim_prefix) { // NOTE: Anim prefix is different, we are starting a new animation- mark it accordingly - Dqn_String8BinarySplitResult split = Dqn_String8_BinarySplitReverse(file_name_without_extension, DQN_STRING8("_")); - anim_prefix = split.lhs; - + anim_prefix = file_name_to_anim_prefix; Dqn_DSMapResult slot = Dqn_DSMap_FindKeyString8(&sprite_spec_table, anim_prefix); Dqn_Fs_WriteFileF(&meta_file, "@anim;%.*s;%u;%u\n", DQN_STRING_FMT(anim_prefix), slot.value->frames_per_second, slot.value->frame_count); } diff --git a/project.rdbg b/project.rdbg index 8d525d5212511f0bdc89734e232ff0e459f466c0..e550847dfee9e6f5fc621a3614ce51bd1f88026a 100644 GIT binary patch delta 159 zcmdnTd5L2~B@+u10|Ue4KTK-&Hb6#NW=?8+USe))d`W(MVqRu$d_hraT4se_N@7W( zjsi~U;>@ban#=)wUO+QU3@ptmj0{XH6eia)>xdTT=I1A8WTwU!r2u;PP$nlc zFBK{QW`e}IpyKfbiA6wdKwfb{QD#YMd~rc)GSJMtRG3+q;w&HoCf72lv9mx-nf!<; tfW-;Op6tM^!&Q=6R8$#Xo|uz8xr8}l@)Bl^$q$(2z_b91#AH<#T>w?vKC%D+