tely: Add idle animation for the player
This commit is contained in:
parent
1062e138d6
commit
1ff0daba4a
BIN
Data/Textures/protagonist_walk.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk.txt
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk.txt
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0000_8.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0000_8.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0001_7.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0001_7.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0002_6.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0002_6.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0003_5.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0003_5.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0004_4.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0004_4.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0005_3.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0005_3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0006_2.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0006_2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Data/Textures/protagonist_walk/Idle_0000s_0007_1.png
(Stored with Git LFS)
Normal file
BIN
Data/Textures/protagonist_walk/Idle_0000s_0007_1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
2
External/tely
vendored
2
External/tely
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 5a56d52e71c4b66a9c6fa795a5a851f0dd4206cb
|
Subproject commit e245208bc9b7140eb668cd2caef80c40b2b50836
|
@ -82,6 +82,11 @@ set clang_dll_cmd=clang-cl %dll_compile_flags% /Fotely_dll_clang /Fetely_dll_c
|
|||||||
|
|
||||||
REM MSVC build =====================================================================================
|
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_platform=$platform_time = Measure-Command {%msvc_cmd% ^| Out-Default};
|
||||||
set msvc_build_dll=$dll_time = Measure-Command {%msvc_dll_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'
|
set msvc_print_dll_time=Write-Host 'msvc dll:'$dll_time.TotalSeconds's'
|
||||||
|
@ -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));
|
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->entities = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8);
|
||||||
game->root_entity = Dqn_VArray_Make(&game->entities, Dqn_ZeroMem_No);
|
game->root_entity = Dqn_VArray_Make(&game->entities, Dqn_ZeroMem_No);
|
||||||
Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle);
|
Dqn_FArray_Add(&game->parent_entity_stack, game->root_entity->handle);
|
||||||
@ -478,12 +540,14 @@ void TELY_DLL_Init(void *user_data)
|
|||||||
|
|
||||||
// NOTE: Hero
|
// 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");
|
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Terry");
|
||||||
entity->local_pos = Dqn_V2_InitNx2(1334, 396);
|
entity->local_pos = Dqn_V2_InitNx2(1334, 396);
|
||||||
entity->size_scale = Dqn_V2_InitNx1(4);
|
entity->sprite_sheet = sheet;
|
||||||
entity->sprite_sheet = &game->hero_sprite_sheet;
|
entity->sprite_anims = anims;
|
||||||
entity->sprite_anims = game->hero_sprite_anims;
|
entity->local_hit_box_size = Dqn_V2_InitV2I(sheet->sprite_size);
|
||||||
entity->local_hit_box_size = Dqn_V2_InitV2I(game->hero_sprite_sheet.sprite_size);
|
|
||||||
entity->flags |= FP_GameEntityFlag_Clickable;
|
entity->flags |= FP_GameEntityFlag_Clickable;
|
||||||
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
|
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
|
||||||
entity->flags |= FP_GameEntityFlag_MoveByMouse;
|
entity->flags |= FP_GameEntityFlag_MoveByMouse;
|
||||||
@ -540,7 +604,7 @@ void TELY_DLL_Init(void *user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Mob spawner
|
// NOTE: Mob spawner
|
||||||
{
|
if (0) {
|
||||||
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "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->local_pos = Dqn_V2_InitNx2(0, platform->core.window_size.y * .5f);
|
||||||
entity->flags |= FP_GameEntityFlag_Clickable;
|
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->state == FP_GameEntityState_Idle) {
|
||||||
if (action->flags & FP_GameEntityActionFlag_StateTransition) {
|
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);
|
FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, anim);
|
||||||
} else if (we_are_clicked_entity) {
|
} else if (we_are_clicked_entity) {
|
||||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
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->state == FP_GameEntityState_Run) {
|
||||||
if (action->flags & FP_GameEntityActionFlag_StateTransition) {
|
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);
|
FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, anim);
|
||||||
} else if (we_are_clicked_entity) {
|
} else if (we_are_clicked_entity) {
|
||||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
|
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 =============================================================
|
// NOTE: Render entity sprites =============================================================
|
||||||
if (entity->sprite_sheet && entity->action.anim) {
|
if (entity->sprite_sheet && entity->action.anim) {
|
||||||
|
|
||||||
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
|
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
|
||||||
FP_GameEntityAction const *action = &entity->action;
|
FP_GameEntityAction const *action = &entity->action;
|
||||||
TELY_AssetSpriteAnimation const *sprite_anim = action->anim;
|
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_usize sprite_sheet_column = sprite_index % sprite_sheet->sprites_per_row;
|
||||||
|
|
||||||
Dqn_Rect src_rect = {};
|
Dqn_Rect src_rect = {};
|
||||||
|
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.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.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.w = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.w;
|
||||||
src_rect.size.h = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.h;
|
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 = {};
|
Dqn_Rect dest_rect = {};
|
||||||
dest_rect.size = src_rect.size * entity->size_scale;
|
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;
|
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;
|
||||||
game->delta_s_accumulator -= PHYSICS_STEP) {
|
game->delta_s_accumulator -= PHYSICS_STEP) {
|
||||||
FP_Update(platform, game, renderer, input);
|
FP_Update(platform, game, renderer, input);
|
||||||
|
@ -148,6 +148,9 @@ struct FP_Game
|
|||||||
Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack;
|
Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack;
|
||||||
Dqn_VArray<FP_GameEntity> entities;
|
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 *root_entity;
|
||||||
FP_GameEntity *entity_free_list;
|
FP_GameEntity *entity_free_list;
|
||||||
|
|
||||||
|
120
feely_pona_sprite_packer.cpp
Normal file
120
feely_pona_sprite_packer.cpp
Normal file
@ -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;
|
||||||
|
}
|
BIN
project.rdbg
BIN
project.rdbg
Binary file not shown.
Loading…
Reference in New Issue
Block a user