tely: Add idle animation for the player
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+1
-1
Submodule External/tely updated: 5a56d52e71...e245208bc9
@@ -82,6 +82,11 @@ set clang_dll_cmd=clang-cl %dll_compile_flags% /Fotely_dll_clang /Fetely_dll_c
|
||||
|
||||
REM MSVC build =====================================================================================
|
||||
|
||||
if not exist "%build_dir%\feely_pona_sprite_packer" (
|
||||
set msvc_sprite_packer_cmd=cl %common_compile_flags% %code_dir%\feely_pona_sprite_packer.cpp /Fofeely_pona_sprite_packer /Fefeely_pona_sprite_packer %common_link_flags%
|
||||
call powershell -Command "$duration = Measure-Command {%msvc_sprite_packer_cmd% | Out-Default}; Write-Host 'msvc (sprite packer):' $duration.TotalSeconds 'seconds'"
|
||||
)
|
||||
|
||||
set msvc_build_platform=$platform_time = Measure-Command {%msvc_cmd% ^| Out-Default};
|
||||
set msvc_build_dll=$dll_time = Measure-Command {%msvc_dll_cmd% ^| Out-Default};
|
||||
set msvc_print_dll_time=Write-Host 'msvc dll:'$dll_time.TotalSeconds's'
|
||||
|
||||
+85
-13
@@ -398,6 +398,68 @@ 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));
|
||||
}
|
||||
|
||||
{
|
||||
TELY_AssetSpriteSheet *sheet = &game->protag_walk_sprite_sheet;
|
||||
Dqn_Slice<TELY_AssetSpriteAnimation> *anims = &game->protag_walk_sprite_anims;
|
||||
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/protagonist_walk.png", DQN_STRING_FMT(assets->textures_dir));
|
||||
sheet->tex_handle = platform->func_load_texture(assets, DQN_STRING8("Protagonist Walk"), sheet_path);
|
||||
sheet->sprite_count = 28;
|
||||
sheet->sprites_per_row = 7;
|
||||
sheet->sprite_size = Dqn_V2I_InitNx2(185, 170);
|
||||
sheet->type = TELY_AssetSpriteSheetType_Rects;
|
||||
|
||||
struct LoadedSprite
|
||||
{
|
||||
Dqn_String8 name;
|
||||
Dqn_Rect rect;
|
||||
};
|
||||
|
||||
// NOTE: Load the sprite meta file =========================================================
|
||||
{
|
||||
Dqn_List<LoadedSprite> raw_sprites = Dqn_List_Init<LoadedSprite>(scratch.arena, 32);
|
||||
Dqn_String8 sheet_meta_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/protagonist_walk.txt", DQN_STRING_FMT(assets->textures_dir));
|
||||
Dqn_String8 sheet_meta_file = Dqn_Fs_Read(sheet_meta_path, scratch.allocator);
|
||||
|
||||
Dqn_String8BinarySplitResult split = {};
|
||||
Dqn_String8 first = sheet_meta_file;
|
||||
do {
|
||||
split = Dqn_String8_BinarySplit(first, DQN_STRING8("\n"));
|
||||
Dqn_String8BinarySplitResult name = Dqn_String8_BinarySplit(split.lhs, DQN_STRING8(";"));
|
||||
Dqn_String8BinarySplitResult x = Dqn_String8_BinarySplit(name.rhs, DQN_STRING8(";"));
|
||||
Dqn_String8BinarySplitResult y = Dqn_String8_BinarySplit(x.rhs, DQN_STRING8(";"));
|
||||
Dqn_String8BinarySplitResult width = Dqn_String8_BinarySplit(y.rhs, DQN_STRING8(";"));
|
||||
Dqn_String8BinarySplitResult height = Dqn_String8_BinarySplit(width.rhs, DQN_STRING8(";"));
|
||||
|
||||
LoadedSprite *loaded_anim = Dqn_List_Make(&raw_sprites, 1);
|
||||
loaded_anim->name = name.lhs;
|
||||
loaded_anim->rect.pos.x = DQN_CAST(Dqn_f32)Dqn_String8_ToU64(x.lhs, 0).value;
|
||||
loaded_anim->rect.pos.y = DQN_CAST(Dqn_f32)Dqn_String8_ToU64(y.lhs, 0).value;
|
||||
loaded_anim->rect.size.w = DQN_CAST(Dqn_f32)Dqn_String8_ToU64(width.lhs, 0).value;
|
||||
loaded_anim->rect.size.h = DQN_CAST(Dqn_f32)Dqn_String8_ToU64(height.lhs, 0).value;
|
||||
|
||||
first = split.rhs;
|
||||
} while (first.size);
|
||||
|
||||
// NOTE: Populate the sheet ============================================================
|
||||
sheet->rects = Dqn_Slice_Alloc<Dqn_Rect>(&platform->arena, raw_sprites.count, Dqn_ZeroMem_No);
|
||||
for (Dqn_ListIterator<LoadedSprite> it = {}; Dqn_List_Iterate(&raw_sprites, &it, 0); ) {
|
||||
sheet->rects.data[it.index] = it.data->rect;
|
||||
}
|
||||
}
|
||||
|
||||
TELY_AssetSpriteAnimation raw_anims[] = {
|
||||
// {DQN_STRING8("Walk down"), /*index*/ 0, /*count*/ 7, /*seconds_per_frame*/ 1 / 8.f},
|
||||
{DQN_STRING8("Walk idle"), /*index*/ 0, /*count*/ 8, /*seconds_per_frame*/ 1 / 8.f},
|
||||
// {DQN_STRING8("Walk left"), /*index*/ 15, /*count*/ 7, /*seconds_per_frame*/ 1 / 8.f},
|
||||
// {DQN_STRING8("Walk right"), /*index*/ 22, /*count*/ 7, /*seconds_per_frame*/ 1 / 8.f},
|
||||
};
|
||||
|
||||
*anims = Dqn_Slice_Alloc<TELY_AssetSpriteAnimation>(&platform->arena, DQN_ARRAY_UCOUNT(raw_anims), Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(anims->data, &raw_anims, sizeof(raw_anims[0]) * DQN_ARRAY_UCOUNT(raw_anims));
|
||||
}
|
||||
|
||||
game->entities = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8);
|
||||
game->root_entity = Dqn_VArray_Make(&game->entities, Dqn_ZeroMem_No);
|
||||
Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle);
|
||||
@@ -478,12 +540,14 @@ void TELY_DLL_Init(void *user_data)
|
||||
|
||||
// NOTE: Hero
|
||||
{
|
||||
TELY_AssetSpriteSheet *sheet = &game->protag_walk_sprite_sheet;
|
||||
Dqn_Slice<TELY_AssetSpriteAnimation> anims = game->protag_walk_sprite_anims;
|
||||
|
||||
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Terry");
|
||||
entity->local_pos = Dqn_V2_InitNx2(1334, 396);
|
||||
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->sprite_sheet = sheet;
|
||||
entity->sprite_anims = anims;
|
||||
entity->local_hit_box_size = Dqn_V2_InitV2I(sheet->sprite_size);
|
||||
entity->flags |= FP_GameEntityFlag_Clickable;
|
||||
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
|
||||
entity->flags |= FP_GameEntityFlag_MoveByMouse;
|
||||
@@ -540,7 +604,7 @@ void TELY_DLL_Init(void *user_data)
|
||||
}
|
||||
|
||||
// NOTE: Mob spawner
|
||||
{
|
||||
if (0) {
|
||||
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;
|
||||
@@ -775,7 +839,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
|
||||
|
||||
if (action->state == FP_GameEntityState_Idle) {
|
||||
if (action->flags & FP_GameEntityActionFlag_StateTransition) {
|
||||
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Idle")).index;
|
||||
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Walk idle")).index;
|
||||
FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, anim);
|
||||
} else if (we_are_clicked_entity) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
||||
@@ -831,7 +895,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
|
||||
|
||||
if (action->state == FP_GameEntityState_Run) {
|
||||
if (action->flags & FP_GameEntityActionFlag_StateTransition) {
|
||||
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Run")).index;
|
||||
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Walk right")).index;
|
||||
FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, anim);
|
||||
} else if (we_are_clicked_entity) {
|
||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
||||
@@ -995,7 +1059,6 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
|
||||
// NOTE: Render entity sprites =============================================================
|
||||
if (entity->sprite_sheet && entity->action.anim) {
|
||||
|
||||
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
|
||||
FP_GameEntityAction const *action = &entity->action;
|
||||
TELY_AssetSpriteAnimation const *sprite_anim = action->anim;
|
||||
@@ -1006,10 +1069,19 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
Dqn_usize sprite_sheet_column = sprite_index % sprite_sheet->sprites_per_row;
|
||||
|
||||
Dqn_Rect src_rect = {};
|
||||
src_rect.pos.x = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite_sheet->sprite_size.w);
|
||||
src_rect.pos.y = DQN_CAST(Dqn_f32)(sprite_sheet_row * sprite_sheet->sprite_size.y);
|
||||
src_rect.size.w = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.w;
|
||||
src_rect.size.h = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.h;
|
||||
switch (sprite_sheet->type) {
|
||||
case TELY_AssetSpriteSheetType_Uniform: {
|
||||
src_rect.pos.x = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite_sheet->sprite_size.w);
|
||||
src_rect.pos.y = DQN_CAST(Dqn_f32)(sprite_sheet_row * sprite_sheet->sprite_size.y);
|
||||
src_rect.size.w = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.w;
|
||||
src_rect.size.h = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.h;
|
||||
} break;
|
||||
|
||||
case TELY_AssetSpriteSheetType_Rects: {
|
||||
DQN_ASSERT(sprite_index < sprite_sheet->rects.size);
|
||||
src_rect = sprite_sheet->rects.data[sprite_index];
|
||||
} break;
|
||||
}
|
||||
|
||||
Dqn_Rect dest_rect = {};
|
||||
dest_rect.size = src_rect.size * entity->size_scale;
|
||||
@@ -1122,7 +1194,7 @@ void TELY_DLL_FrameUpdate(void *user_data)
|
||||
}
|
||||
|
||||
Dqn_f32 const PHYSICS_STEP = 1 / 60.f;
|
||||
for (game->delta_s_accumulator += input->delta_s;
|
||||
for (game->delta_s_accumulator += DQN_CAST(Dqn_f32)input->delta_s;
|
||||
game->delta_s_accumulator > PHYSICS_STEP;
|
||||
game->delta_s_accumulator -= PHYSICS_STEP) {
|
||||
FP_Update(platform, game, renderer, input);
|
||||
|
||||
@@ -148,6 +148,9 @@ struct FP_Game
|
||||
Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack;
|
||||
Dqn_VArray<FP_GameEntity> entities;
|
||||
|
||||
TELY_AssetSpriteSheet protag_walk_sprite_sheet;
|
||||
Dqn_Slice<TELY_AssetSpriteAnimation> protag_walk_sprite_anims;
|
||||
|
||||
FP_GameEntity *root_entity;
|
||||
FP_GameEntity *entity_free_list;
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#define DQN_ASAN_POISON 1
|
||||
#define DQN_ASAN_VET_POISON 1
|
||||
#define DQN_ONLY_RECT
|
||||
#define DQN_ONLY_WIN
|
||||
#define DQN_ONLY_V2
|
||||
#define DQN_ONLY_SLICE
|
||||
#define DQN_ONLY_SARRAY
|
||||
#define DQN_ONLY_LIST
|
||||
#define DQN_ONLY_FS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define DQN_IMPLEMENTATION
|
||||
#include "External/tely/External/dqn/dqn.h"
|
||||
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4244) // warning C4244: 'argument': conversion from 'int' to 'short', possible loss of data
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#include "External/tely/external/stb/stb_rect_pack.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "External/tely/external/stb/stb_image_write.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "External/tely/external/stb/stb_image.h"
|
||||
DQN_MSVC_WARNING_POP
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
Dqn_Log_InfoF("USAGE: feely_pona_sprite_packer \"<directory with sprites>\"");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Dqn_Library_Init();
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 dir = Dqn_String8_InitCString8(argv[1]);
|
||||
|
||||
// NOTE: Get the list of files =================================================================
|
||||
Dqn_List<Dqn_String8> file_list = Dqn_List_Init<Dqn_String8>(scratch.arena, 128);
|
||||
for (Dqn_Win_FolderIterator it = {}; Dqn_Win_FolderIterate(dir, &it); ) {
|
||||
if (Dqn_String8_EndsWithInsensitive(it.file_name, DQN_STRING8(".png"))) {
|
||||
Dqn_String8 *item = Dqn_List_Make(&file_list, Dqn_ZeroMem_Yes);
|
||||
*item = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STRING_FMT(dir), DQN_STRING_FMT(it.file_name));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Setup the rect-pack state =============================================================
|
||||
int atlas_size = 2048;
|
||||
stbrp_node *nodes = Dqn_Arena_NewArray(scratch.arena, stbrp_node, file_list.count, Dqn_ZeroMem_Yes);
|
||||
stbrp_context pack_context = {};
|
||||
stbrp_init_target(&pack_context, atlas_size, atlas_size, nodes, DQN_CAST(int)file_list.count);
|
||||
|
||||
// NOTE: Load the sprites to determine their dimensions for rect packing
|
||||
Dqn_SArray<stbrp_rect> rects = Dqn_SArray_Init<stbrp_rect>(scratch.arena, file_list.count, Dqn_ZeroMem_Yes);
|
||||
for (Dqn_ListIterator<Dqn_String8> it = {}; Dqn_List_Iterate<Dqn_String8>(&file_list, &it, 0);) {
|
||||
int x = 0, y = 0, channels_in_file = 0;
|
||||
stbi_uc *pixels = stbi_load(it.data->data, &x, &y, &channels_in_file, 4 /*desired_channels*/);
|
||||
DQN_ASSERT(pixels);
|
||||
stbi_image_free(pixels);
|
||||
|
||||
// NOTE: Add a rect to the packing context
|
||||
stbrp_rect *rect = Dqn_SArray_Make(&rects, Dqn_ZeroMem_Yes);
|
||||
rect->w = x;
|
||||
rect->h = y;
|
||||
|
||||
Dqn_Log_InfoF("Packing sprite: %.*s", DQN_STRING_FMT(*it.data));
|
||||
}
|
||||
|
||||
// NOTE: Pack the rects ========================================================================
|
||||
if (stbrp_pack_rects(&pack_context, rects.data, DQN_CAST(int)rects.size) != 1) {
|
||||
Dqn_Log_ErrorF("STB rect pack failed to pack font rects into rectangle [width=%d, height=%d, num_rects=%d]",
|
||||
atlas_size,
|
||||
atlas_size,
|
||||
file_list.count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: Load the files once more and generate the final image =================================
|
||||
int final_bpp = 4;
|
||||
int final_image_stride = atlas_size * final_bpp;
|
||||
char *final_image = Dqn_Arena_NewArray(scratch.arena, char, atlas_size * final_image_stride, Dqn_ZeroMem_Yes);
|
||||
|
||||
// NOTE: Generate the meta file ================================================================
|
||||
Dqn_String8 meta_path = Dqn_String8_InitF(scratch.allocator, "%.*s.txt", DQN_STRING_FMT(dir));
|
||||
Dqn_FsFile meta_file = Dqn_Fs_OpenFile(meta_path, Dqn_FsFileOpen_CreateAlways, Dqn_FsFileAccess_Write);
|
||||
|
||||
Dqn_Log_InfoF("Generating meta file: %.*s", DQN_STRING_FMT(meta_path));
|
||||
for (Dqn_ListIterator<Dqn_String8> it = {}; Dqn_List_Iterate<Dqn_String8>(&file_list, &it, 0);) {
|
||||
int w, h, channels_in_file;
|
||||
stbi_uc *loaded_image = stbi_load(it.data->data, &w, &h, &channels_in_file, 4 /*desired_channels*/);
|
||||
|
||||
stbrp_rect packed_rect = rects.data[it.index];
|
||||
char *src = DQN_CAST(char *)loaded_image;
|
||||
|
||||
// NOTE: Generate the image ================================================================
|
||||
for (int y = 0; y < packed_rect.h; y++) {
|
||||
char *row = final_image + ((packed_rect.y + y) * final_image_stride) + (packed_rect.x * final_bpp);
|
||||
for (int x = 0; x < packed_rect.w; x++) {
|
||||
*row++ = *src++;
|
||||
*row++ = *src++;
|
||||
*row++ = *src++;
|
||||
*row++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
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_Fs_WriteFileF(&meta_file, "%.*s;%d;%d;%d;%d", DQN_STRING_FMT(file_name_without_extension), packed_rect.x, packed_rect.y, packed_rect.w, packed_rect.h);
|
||||
if (it.index != (file_list.count - 1))
|
||||
Dqn_Fs_WriteFileF(&meta_file, "\n");
|
||||
}
|
||||
|
||||
Dqn_String8 atlas_path = Dqn_String8_InitF(scratch.allocator, "%.*s.png", DQN_STRING_FMT(dir));
|
||||
Dqn_Log_InfoF("Generating atlas: %.*s", DQN_STRING_FMT(atlas_path));
|
||||
stbi_write_png(atlas_path.data, atlas_size, atlas_size, 4, final_image, final_image_stride);
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user