tely/asset: Add new sprite packer spec file support
This commit is contained in:
		
							parent
							
								
									ebfcc899c1
								
							
						
					
					
						commit
						3f33b015ab
					
				
							
								
								
									
										
											BIN
										
									
								
								Data/Textures/sprite_spec.txt
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Data/Textures/sprite_spec.txt
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Data/Textures/terry_movement.txt
									 (Stored with Git LFS)
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Data/Textures/terry_movement.txt
									 (Stored with Git LFS)
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								External/tely
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								External/tely
									
									
									
									
										vendored
									
									
								
							@ -1 +1 @@
 | 
			
		||||
Subproject commit a3816ec63e2a930c067d2598e6c2f96815a4294c
 | 
			
		||||
Subproject commit f7b7d4f505c10ebd849793d9c3cf9c89fe696942
 | 
			
		||||
							
								
								
									
										137
									
								
								feely_pona.cpp
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								feely_pona.cpp
									
									
									
									
									
								
							@ -402,63 +402,83 @@ void TELY_DLL_Init(void *user_data)
 | 
			
		||||
        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/terry_movement.png", DQN_STRING_FMT(assets->textures_dir));
 | 
			
		||||
        sheet->tex_handle            = platform->func_load_texture(assets, DQN_STRING8("Terry Movement"), 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;
 | 
			
		||||
        };
 | 
			
		||||
        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_List<LoadedSprite> raw_sprites = Dqn_List_Init<LoadedSprite>(scratch.arena, 32);
 | 
			
		||||
            Dqn_String8 sheet_meta_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/terry_movement.txt", DQN_STRING_FMT(assets->textures_dir));
 | 
			
		||||
            Dqn_String8 sheet_meta_file = Dqn_Fs_Read(sheet_meta_path, scratch.allocator);
 | 
			
		||||
        Dqn_String8 sprite_spec_path      = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/terry_movement.txt", DQN_STRING_FMT(assets->textures_dir));
 | 
			
		||||
        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_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(";"));
 | 
			
		||||
        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(";"));
 | 
			
		||||
 | 
			
		||||
                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;
 | 
			
		||||
            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));
 | 
			
		||||
 | 
			
		||||
                first = split.rhs;
 | 
			
		||||
            } while (first.size);
 | 
			
		||||
                // 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, DQN_STRING8("Terry Movement"), sprite_sheet_path);
 | 
			
		||||
                DQN_ASSERTF(Dqn_Fs_Exists(sprite_sheet_path), "Required file does not exist '%.*s'", DQN_STRING_FMT(sprite_sheet_path));
 | 
			
		||||
 | 
			
		||||
            // 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;
 | 
			
		||||
                // 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<Dqn_Rect>(&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<TELY_AssetSpriteAnimation>(&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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TELY_AssetSpriteAnimation raw_anims[] = {
 | 
			
		||||
            {DQN_STRING8("Walk down"),  /*index*/ 0,  /*count*/ 7, /*seconds_per_frame*/ 1 / 8.f},
 | 
			
		||||
            {DQN_STRING8("Walk idle"),  /*index*/ 7,  /*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},
 | 
			
		||||
            {DQN_STRING8("Walk up"),    /*index*/ 29, /*count*/ 6, /*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));
 | 
			
		||||
        DQN_ASSERT(sheet->rects.size == sprite_rect_index);
 | 
			
		||||
        DQN_ASSERT(anims->size       == sprite_anim_index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    game->entities    = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8);
 | 
			
		||||
@ -851,7 +871,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("Walk 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)) {
 | 
			
		||||
@ -907,7 +927,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("Walk right")).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)) {
 | 
			
		||||
@ -1076,17 +1096,16 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
 | 
			
		||||
            TELY_AssetSpriteAnimation const *sprite_anim  = action->anim;
 | 
			
		||||
            uint16_t                         anim_frame   = DQN_CAST(uint16_t)(action->timer_s / sprite_anim->seconds_per_frame) % sprite_anim->count;
 | 
			
		||||
 | 
			
		||||
            Dqn_usize sprite_index        = sprite_anim->index + anim_frame;
 | 
			
		||||
            Dqn_usize sprite_sheet_row    = sprite_index / sprite_sheet->sprites_per_row;
 | 
			
		||||
            Dqn_usize sprite_sheet_column = sprite_index % sprite_sheet->sprites_per_row;
 | 
			
		||||
 | 
			
		||||
            Dqn_Rect src_rect = {};
 | 
			
		||||
            Dqn_usize sprite_index = sprite_anim->index + anim_frame;
 | 
			
		||||
            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.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;
 | 
			
		||||
                    Dqn_usize sprite_sheet_row    = sprite_index / sprite_sheet->sprites_per_row;
 | 
			
		||||
                    Dqn_usize sprite_sheet_column = sprite_index % sprite_sheet->sprites_per_row;
 | 
			
		||||
                    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: {
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
#define DQN_ONLY_V2
 | 
			
		||||
#define DQN_ONLY_SLICE
 | 
			
		||||
#define DQN_ONLY_SARRAY
 | 
			
		||||
#define DQN_ONLY_DSMAP
 | 
			
		||||
#define DQN_ONLY_LIST
 | 
			
		||||
#define DQN_ONLY_FS
 | 
			
		||||
#define _CRT_SECURE_NO_WARNINGS
 | 
			
		||||
@ -23,16 +24,52 @@ DQN_MSVC_WARNING_DISABLE(4244) // warning C4244: 'argument': conversion from 'in
 | 
			
		||||
#include "External/tely/external/stb/stb_image.h"
 | 
			
		||||
DQN_MSVC_WARNING_POP
 | 
			
		||||
 | 
			
		||||
struct SpriteSpecification
 | 
			
		||||
{
 | 
			
		||||
    uint16_t frame_count;
 | 
			
		||||
    uint16_t frames_per_second;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char const *argv[])
 | 
			
		||||
{
 | 
			
		||||
    if (argc != 2) {
 | 
			
		||||
        Dqn_Log_InfoF("USAGE: feely_pona_sprite_packer \"<directory with sprites>\"");
 | 
			
		||||
    if (argc != 3) {
 | 
			
		||||
        Dqn_Log_InfoF("USAGE: feely_pona_sprite_packer <sprite_spec.txt> <sprite directory>");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Dqn_Library_Init();
 | 
			
		||||
    Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
 | 
			
		||||
    Dqn_String8 dir           = Dqn_String8_InitCString8(argv[1]);
 | 
			
		||||
    Dqn_ThreadScratch scratch          = Dqn_Thread_GetScratch(nullptr);
 | 
			
		||||
    Dqn_String8       sprite_spec_path = Dqn_String8_InitCString8(argv[1]);
 | 
			
		||||
    Dqn_String8       dir              = Dqn_String8_InitCString8(argv[2]);
 | 
			
		||||
 | 
			
		||||
    // NOTE: Parse the sprite specifications =======================================================
 | 
			
		||||
    if (!Dqn_Fs_Exists(sprite_spec_path)) {
 | 
			
		||||
        Dqn_Log_ErrorF("Sprite specification file does not exist, we tried to find \"%.*s\" but it does not exist", DQN_STRING_FMT(sprite_spec_path));
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Dqn_DSMap<SpriteSpecification> sprite_spec_table = Dqn_DSMap_Init<SpriteSpecification>(1024);
 | 
			
		||||
    {
 | 
			
		||||
        Dqn_ThreadScratch inner_scratch               = Dqn_Thread_GetScratch(scratch.arena);
 | 
			
		||||
        Dqn_String8 sprite_spec_buffer                = Dqn_Fs_Read(sprite_spec_path, inner_scratch.allocator);
 | 
			
		||||
        Dqn_String8SplitAllocResult sprite_spec_lines = Dqn_String8_SplitAlloc(inner_scratch.allocator, sprite_spec_buffer, DQN_STRING8("\n"));
 | 
			
		||||
        DQN_FOR_UINDEX(line_index, sprite_spec_lines.size) {
 | 
			
		||||
            Dqn_String8 line                       = sprite_spec_lines.data[line_index];
 | 
			
		||||
            Dqn_String8SplitAllocResult line_parts = Dqn_String8_SplitAlloc(inner_scratch.allocator, line, DQN_STRING8(";"));
 | 
			
		||||
            DQN_ASSERTF(line_parts.size == 2, "Line must have 2 parts in the sprite specification\n"
 | 
			
		||||
                                              "\n"
 | 
			
		||||
                                              "<animation name>;<frames per second>\\n\n"
 | 
			
		||||
                                              "\n"
 | 
			
		||||
                                              "Line was '%.*s' loaded from '%.*s'", DQN_STRING_FMT(line), sprite_spec_path);
 | 
			
		||||
 | 
			
		||||
            Dqn_String8 anim_name                    = line_parts.data[0];
 | 
			
		||||
            Dqn_String8ToU64Result frames_per_second = Dqn_String8_ToU64(line_parts.data[1], 0);
 | 
			
		||||
            DQN_ASSERTF(frames_per_second.success, "Frames per second was not a convertible number, line was '%.*s'", line);
 | 
			
		||||
 | 
			
		||||
            Dqn_DSMapResult<SpriteSpecification> slot = Dqn_DSMap_MakeKeyString8Copy(&sprite_spec_table, scratch.allocator, anim_name);
 | 
			
		||||
            slot.value->frames_per_second             = DQN_CAST(uint16_t)frames_per_second.value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // NOTE: Get the list of files =================================================================
 | 
			
		||||
    Dqn_List<Dqn_String8> file_list = Dqn_List_Init<Dqn_String8>(scratch.arena, 128);
 | 
			
		||||
@ -49,7 +86,7 @@ int main(int argc, char const *argv[])
 | 
			
		||||
    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
 | 
			
		||||
    // 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;
 | 
			
		||||
@ -57,11 +94,32 @@ int main(int argc, char const *argv[])
 | 
			
		||||
        DQN_ASSERT(pixels);
 | 
			
		||||
        stbi_image_free(pixels);
 | 
			
		||||
 | 
			
		||||
        // NOTE: Add a rect to the packing context
 | 
			
		||||
        // NOTE: Add a rect to the packing context =================================================
 | 
			
		||||
        stbrp_rect *rect = Dqn_SArray_Make(&rects, Dqn_ZeroMem_Yes);
 | 
			
		||||
        rect->w          = x;
 | 
			
		||||
        rect->h          = y;
 | 
			
		||||
 | 
			
		||||
        // NOTE: Enumerate the number of frames for this animation =================================
 | 
			
		||||
        Dqn_String8 file_name                   = Dqn_String8_FileNameFromPath(*it.data);
 | 
			
		||||
        Dqn_String8 file_name_without_extension = Dqn_String8_BinarySplit(file_name, DQN_STRING8(".")).lhs;
 | 
			
		||||
        Dqn_String8BinarySplitResult split      = Dqn_String8_BinarySplitReverse(file_name_without_extension, DQN_STRING8("_"));
 | 
			
		||||
        Dqn_String8 anim_prefix                 = split.lhs;
 | 
			
		||||
 | 
			
		||||
        Dqn_DSMapResult<SpriteSpecification> slot = Dqn_DSMap_FindKeyString8(&sprite_spec_table, anim_prefix);
 | 
			
		||||
        DQN_ASSERTF(slot.found,
 | 
			
		||||
                    "\n\nSprite frame loaded from file\n"
 | 
			
		||||
                    "  '%.*s'\n"
 | 
			
		||||
                    "\n"
 | 
			
		||||
                    "however there's no corresponding entry in the specification file loaded at\n"
 | 
			
		||||
                    "  '%.*s'\n"
 | 
			
		||||
                    "\n"
 | 
			
		||||
                    "Add a line in format of <animation name>;<frames_per_second> to the file, e.g.\n"
 | 
			
		||||
                    "  %.*s;8\n",
 | 
			
		||||
                    DQN_STRING_FMT(*it.data),
 | 
			
		||||
                    DQN_STRING_FMT(sprite_spec_path),
 | 
			
		||||
                    DQN_STRING_FMT(anim_prefix));
 | 
			
		||||
 | 
			
		||||
        slot.value->frame_count++;
 | 
			
		||||
        Dqn_Log_InfoF("Packing sprite: %.*s", DQN_STRING_FMT(*it.data));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -78,12 +136,19 @@ int main(int argc, char const *argv[])
 | 
			
		||||
    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);
 | 
			
		||||
    Dqn_String8 atlas_path = Dqn_String8_InitF(scratch.allocator, "%.*s.png", DQN_STRING_FMT(dir));
 | 
			
		||||
 | 
			
		||||
    // 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_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) sprite_spec_table.occupied - 1);
 | 
			
		||||
 | 
			
		||||
    Dqn_Log_InfoF("Generating meta file: %.*s", DQN_STRING_FMT(meta_path));
 | 
			
		||||
    Dqn_String8 anim_prefix = {};
 | 
			
		||||
    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*/);
 | 
			
		||||
@ -105,15 +170,25 @@ 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                   = 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);
 | 
			
		||||
        // NOTE: Detect what animation we are currently processing =================================
 | 
			
		||||
        if (anim_prefix.size == 0 || !Dqn_String8_StartsWith(file_name_without_extension, 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;
 | 
			
		||||
 | 
			
		||||
            Dqn_DSMapResult<SpriteSpecification> 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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // NOTE: Write the sprite rectangles in ====================================================
 | 
			
		||||
        Dqn_Fs_WriteFileF(&meta_file, "%d;%d;%d;%d", 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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user