From f72ca9def558fed5c76b3232695857f786b20fcf Mon Sep 17 00:00:00 2001 From: Sven Balzer <4653051+Kyuusokuna@users.noreply.github.com> Date: Sun, 2 Aug 2026 12:40:24 +0200 Subject: [PATCH] add basic animation --- src/main.cpp | 45 +++++++++++++++++++++++------------- src/renderer_sdlgpu.c | 12 +++++++--- src/renderer_wgpu.c | 9 ++++++-- src/shaders/spirv/basic.spv | Bin 2488 -> 2832 bytes src/shaders/src/basic.slang | 7 +++++- src/shaders/wgsl/basic.wgsl | 3 ++- 6 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0059ed6..46ed1e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -188,6 +188,7 @@ static Uint16 indices[] = { struct Instance { vec2 pos; + uint32_t sprite_selection; }; static Instance player_instance = {{ 0.0f, 0.0f }}; @@ -205,16 +206,6 @@ struct Map { static Map current_map; -struct Player { - i32vec2 position; - i32vec2 target_position; - - bool is_moving; - vec2 visual_position; -}; - -static Player player; - struct alignas(16) PerFrame { i32vec2 drag_start; i32vec2 mouse; @@ -233,6 +224,19 @@ typedef enum : Uint8 { DIR_RIGHT, } Direction; +struct Player { + i32vec2 position; + i32vec2 target_position; + + Direction direction; + + bool is_moving; + vec2 visual_position; + Uint64 animation_time; +}; + +static Player player; + typedef enum : Uint8 { TILEKIND_ERROR = 0, TILEKIND_NONE = 1, @@ -905,7 +909,7 @@ static bool init_resources() { return false; } - player_texture = create_texture("decorations/strawberry.png"); + player_texture = create_texture("characters/Basic Charakter Spritesheet.png"); if (!player_texture) { log_error("Failed to create shader texture."); return false; @@ -1434,6 +1438,8 @@ static void update_state_game() { const bool *keyboard_state = SDL_GetKeyboardState(NULL); if (player.is_moving) { + player.animation_time += delta_time; + vec2 leftover_movement = vec2(player.target_position) - player.visual_position; vec2 movement = clamp(min(vec2(0, 0), leftover_movement), normalize(leftover_movement) * (delta_t * character_speed * (keyboard_state[SDL_SCANCODE_LSHIFT] ? 2.0f : 1.0f)), max(vec2(0, 0), leftover_movement)); player.visual_position += movement; @@ -1446,14 +1452,17 @@ static void update_state_game() { if (!player.is_moving && (queued_movement || keyboard_state[SDL_SCANCODE_W] || keyboard_state[SDL_SCANCODE_A] || keyboard_state[SDL_SCANCODE_S] || keyboard_state[SDL_SCANCODE_D])) { switch (queued_movement_direction) { - case DIR_UP: { player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, 1), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; - case DIR_LEFT: { player.target_position = clamp(ivec2(0, 0), player.position + ivec2(-1, 0), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; - case DIR_DOWN: { player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, -1), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; - case DIR_RIGHT: { player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 1, 0), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_UP: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, 1), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_LEFT: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2(-1, 0), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_DOWN: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, -1), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_RIGHT: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 1, 0), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; } } queued_movement = false; + if (!player.is_moving) + player.animation_time = 0; + if (!MIX_TrackPlaying(music_track)) { MIX_SetTrackAudio(music_track, music_setting_off_piano); MIX_PlayTrack(music_track, 0); @@ -1467,7 +1476,11 @@ static void render_game() { ZoneScopedN("update buffers"); { ZoneScopedN("player_instance_buffer"); - player_instance.pos = player.visual_position; + + Uint64 variation_index = (player.animation_time / (100 * SDL_NS_PER_MS)) % 4; + + player_instance.pos = player.visual_position; + player_instance.sprite_selection = variation_index << 16 | player.direction; renderer_buffer_update(player_instance_buffer, 0, sizeof(player_instance), &player_instance); } diff --git a/src/renderer_sdlgpu.c b/src/renderer_sdlgpu.c index 2301402..9971dc6 100644 --- a/src/renderer_sdlgpu.c +++ b/src/renderer_sdlgpu.c @@ -460,7 +460,7 @@ bool renderer_init(SDL_Window *window_) { }, { .slot = 1, - .pitch = 8, + .pitch = 12, .input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE, .instance_step_rate = 0, @@ -487,8 +487,14 @@ bool renderer_init(SDL_Window *window_) { .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, .offset = 0, }, + { + .location = 3, + .buffer_slot = 1, + .format = SDL_GPU_VERTEXELEMENTFORMAT_UINT, + .offset = 8, + }, }, - .num_vertex_attributes = 3, + .num_vertex_attributes = 4, }, .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, @@ -815,7 +821,7 @@ bool renderer_init(SDL_Window *window_) { transfer_buffer = SDL_CreateGPUTransferBuffer(device, &(SDL_GPUTransferBufferCreateInfo){ .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, - .size = 64 * 1024, + .size = 512 * 1024, }); copy_command_buffer = SDL_AcquireGPUCommandBuffer(device); diff --git a/src/renderer_wgpu.c b/src/renderer_wgpu.c index b63988e..93c4511 100644 --- a/src/renderer_wgpu.c +++ b/src/renderer_wgpu.c @@ -747,14 +747,19 @@ bool renderer_init(SDL_Window *window_) { }, { .stepMode = WGPUVertexStepMode_Instance, - .arrayStride = 8, - .attributeCount = 1, + .arrayStride = 12, + .attributeCount = 2, .attributes = (WGPUVertexAttribute[]){ { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 2, }, + { + .format = WGPUVertexFormat_Uint32, + .offset = 8, + .shaderLocation = 3, + }, }, }, }, diff --git a/src/shaders/spirv/basic.spv b/src/shaders/spirv/basic.spv index 7ca8a50aaa6b0137e4e25f3c8996cd98ee2624ef..b4ea3cd78b6684a85b4d486c617f46da77814bb5 100644 GIT binary patch literal 2832 zcmZvdTWefZ7=~Apnb_1|dax&}HEFA@HVi$aRcK8j6{1pT)C(^s(`2WNWHMoACr$4f zRPe@{c)Te12Rxu69>7Btyb=^e|BE+*&$DN(b}C)vo$t85wZ8XTJMHT|bWaF9A%vsh zZ+>0dLvJ`4rIC9>e^^CMU3lq2ZMm4Mjh#M@8SV>xigNc*7%cT(ep`*^VtqBsi)<~N z!=41Yz!=yLj)0*s5Gu&?pv<4o8<$(zVu5`FJP)=}-#8aU>*F4sv#Q+3I?ctUm7;c~ zoiEJQm)gsr%0JKP`1J%l17SaM_R`Aye3sW+jUsQZots~1H;U1*(XsJ*LACxehqO?y zx3boyEVsdG*b)vPSDV?D`cmG0C7Ugp?ZsH56StSndCN(+KQ+9~dv_AR=MPa<#Mu~Ks1ZOeQ2dD@<5ANC}_>fSeToqDpqJa_Ncc7M8`yr=Va zQptOc5H+s88?7HX4E9h@F0|!HdrP#v!!B%l8wTz@7VXa0t-n(8Xv}@?-+{EA=NZ2r zXC=8V+Qz->;nL4fDJ$Voq-X2* zEahXs*(ZAx-DSNR(zER)-R%kV5U!0IKZ|Bx=4eku+x&g}ZJ9yZpJ#f{w6tCO+1)_S z0Qb)#UHas19_bz3JIeg6yAI?>q&vNa&YF-P--z5=?=7Tz^=WTL+xS^*`}UjaiMF%x zn@YC)`gW{gzWf~lDeivXf3oGn_b(^r%g1DwZN7hdQojAp0hjsScLSvJtak%gFZKT$ zy8VhXaAGa#N5C-HB*(o6fj(=Pa~SyukkenOuYEN7e3v5SJ&xX;=l>J> ze6Qv$QO~z_99SdSC(zBEjy;`3H%Gs<<=_->j;Zg{=;rCa9`~I=*YEouhiT_*KkAc6 zfA2-qUr<|YA-P;2aXFsFse-TFI=TC4Gcqdo#|AlU@areuGch;ZY z*GJLT=Q8j0$mz%E_HE8!JnIwmRp7bClmFA`-x~dY#M%84?Kfi0&#=waKN#Qm=jhh- ztr=f0(fNDw1-fUN^J=W|CAxmUwbaK~==RXvM>_Ls{O&O)&FdR<=k+puIp=SIK670c z>2L?~O<+v8&Gg53Uh}=YBT4HQsop>3Hu?q32K9JC&1m7k@JB2ZwInC*5!6%1S z6dz3V4^W93<0bJ139pzKefGcc#f0xWvo{QemHcMyb=_;Nz0Qn{9)Bc+kr2Xc_?us8 zZx{_PMXu%1Pz{&h3#Z>cUEe5H>WvdGAcn`nn5>MR!b=(j-Bzd9+-m1V`$~8o`9-iF z%!0jODoliNcmtHat9k2Ux7{m{KLzt(54p8d9H+4|yR}rP#T=d9`esqToaJjP&Gl>p zeWko(pjV^sL^uLpzOcEv+RmHZR*`qEyuP}YwTijMT;pW3AX~Nc!7VhK-FEjvJGZ$? z*cFb#w>s_1&GkHcuf1G!vR=%gy;AlwxNmdI_)6Ga_DU!D`-@qpS1h*jR+pH2^Qh}- z?EmMa&b}YaR|)6PF~?#vZ#zc3CPE*cdfK9{ERQ|CY)7H&NgwIKp2ovAx`N8IRhX*t zerUbvP3p_pT9)Hmedv2F@RGiVv$7{;-&$E(;O%?^ZEumqvvhpG(2d{Ixf8=azWp9x zPV-FzDakXO-3euPOP?gWcY}=_*d5;en#Rjn56jMZIKQ&_2R)6IGfzGA(MzcZ@83oq zj#r)mej(bObGmOIS*>%r-0VTk2RK*J)xbgg?OoZKDDR50HS9;Ww;C|db5Wk;EY*;m zn>OQ&e;jT;>(TxMT)TSXwH*dhlAkQ4{;5OQpWdOg9}*}0pa0q;q?`~5pmpL}j|Hg*IgpIe;OpC!V% z{|StS^`dy${@o<}d&N diff --git a/src/shaders/src/basic.slang b/src/shaders/src/basic.slang index 9389277..f55ce24 100644 --- a/src/shaders/src/basic.slang +++ b/src/shaders/src/basic.slang @@ -10,6 +10,7 @@ struct VertexShaderInput { // Per Instance [vk::location(2)] float2 world_pos; + [vk::location(3)] uint sprite_selection; }; struct VertexShaderOutput { @@ -27,7 +28,11 @@ VertexShaderOutput main_vertex(VertexShaderInput input) { var output: VertexShaderOutput; output.pos = mul(float4(input.world_pos + input.pos.xy, 0, 1), view_projection_matrix); - output.uv = input.uv; + + uint sprite_x = bitfieldExtract(input.sprite_selection, 16, 16); + uint sprite_y = bitfieldExtract(input.sprite_selection, 0, 16); + + output.uv = (float2(sprite_x, sprite_y) + input.uv) * (1.0 / 4.0); return output; } diff --git a/src/shaders/wgsl/basic.wgsl b/src/shaders/wgsl/basic.wgsl index 1291d0e..0f97f35 100644 --- a/src/shaders/wgsl/basic.wgsl +++ b/src/shaders/wgsl/basic.wgsl @@ -20,6 +20,7 @@ struct vertexInput_0 @location(0) pos_1 : vec3, @location(1) uv_1 : vec2, @location(2) world_pos_0 : vec2, + @location(3) sprite_selection_0 : u32, }; @vertex @@ -27,7 +28,7 @@ fn main_vertex( _S1 : vertexInput_0, @builtin(vertex_index) vertex_index_0 : u32 { var output_0 : VertexShaderOutput_0; output_0.pos_0 = (((mat4x4(view_projection_matrix_0.data_0[i32(0)][i32(0)], view_projection_matrix_0.data_0[i32(1)][i32(0)], view_projection_matrix_0.data_0[i32(2)][i32(0)], view_projection_matrix_0.data_0[i32(3)][i32(0)], view_projection_matrix_0.data_0[i32(0)][i32(1)], view_projection_matrix_0.data_0[i32(1)][i32(1)], view_projection_matrix_0.data_0[i32(2)][i32(1)], view_projection_matrix_0.data_0[i32(3)][i32(1)], view_projection_matrix_0.data_0[i32(0)][i32(2)], view_projection_matrix_0.data_0[i32(1)][i32(2)], view_projection_matrix_0.data_0[i32(2)][i32(2)], view_projection_matrix_0.data_0[i32(3)][i32(2)], view_projection_matrix_0.data_0[i32(0)][i32(3)], view_projection_matrix_0.data_0[i32(1)][i32(3)], view_projection_matrix_0.data_0[i32(2)][i32(3)], view_projection_matrix_0.data_0[i32(3)][i32(3)])) * (vec4(_S1.world_pos_0 + _S1.pos_1.xy, 0.0f, 1.0f)))); - output_0.uv_0 = _S1.uv_1; + output_0.uv_0 = (vec2(f32((u32(_S1.sprite_selection_0>>(u32(16)))&((((u32(1)<>(u32(0)))&((((u32(1)<(0.25f); return output_0; }