implement shadows for billboards and animation system

This commit is contained in:
Tuomas Katajisto 2025-10-31 23:29:10 +02:00
parent cff9194721
commit 1667107e85
12 changed files with 1790 additions and 1536 deletions

8
.ignore Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
.build/
dist/
first
sample_game/
!game/
packs/*
first.dSYM/Contents

View File

@ -131,6 +131,8 @@ init_after_asset_pack :: () {
is_in_reflection_pass : bool = false;
delta_time_accumulator : float64 = 0;
frame :: () {
check_and_handle_window_resize();
delta_time = get_time() - last_frame_time;
@ -149,7 +151,15 @@ frame :: () {
dpis := state.dpi_scale;
#if OS != .WASM { tick_profiler(); }
if !in_editor_view then game_tick();
delta_time_accumulator += delta_time;
if !in_editor_view {
while delta_time_accumulator > (1.0/60.0) {
game_tick(1.0/60.0);
delta_time_accumulator -= (1.0/60.0);
}
}
fonsClearState(state.fons);
for event: Input.events_this_frame {
GR.getrect_handle_event(event);

View File

@ -1,5 +1,44 @@
g_animations: Table(string, Animation);
Animation_Player :: struct {
current_animation : *Animation;
queued_animation : *Animation = null;
current_frame : s32 = 0;
frame_start : float64 = 0;
}
animation_player_tick :: (player: *Animation_Player) {
if player.current_animation == null then return;
frame := player.current_animation.frames[player.current_frame];
current_time := get_time();
if cast(s32)((current_time - player.frame_start) * 1000) > frame.duration_ms {
player.current_frame += 1;
player.frame_start = current_time;
if player.current_frame >= player.current_animation.frames.count {
player.current_frame = 0;
if player.queued_animation != null {
player.current_animation = player.queued_animation;
player.queued_animation = null;
}
}
}
}
animation_draw :: (player: *Animation_Player, position: Vector3, flipX: bool = false, flipY: bool = false) {
animation_player_tick(player);
if player.current_animation == null then print("Trying to draw a null animation!!\n");
create_billboard_rendering_task(position, player.current_animation, player.current_frame, flipX, flipY);
}
animation_set :: (player: *Animation_Player, animation: string) {
player.current_frame = 0;
player.current_animation = table_find_pointer(*g_animations, animation);
}
animation_is :: (player: *Animation_Player, animation: string) -> bool {
return player.current_animation.name == animation;
}
Frame :: struct {
x: s32;
y: s32;

View File

@ -64,6 +64,11 @@ Render_Command_Update_Trixels :: struct {
Render_Command_Draw_Billboard :: struct {
#as using c : Render_Command;
c.type = .DRAW_BILLBOARD;
position : Vector3;
animation : *Animation;
frame : s32;
flipX : bool;
flipY : bool;
}
Render_Command_Draw_Trixels :: struct {

View File

@ -41,7 +41,8 @@ backend_handle_command :: (cmd: *Render_Command) {
set_light_command := cast(*Render_Command_Set_Light)cmd;
current_world_config = set_light_command.worldConfig;
case .DRAW_BILLBOARD;
backend_draw_billboard();
command := cast(*Render_Command_Draw_Billboard)cmd;
backend_draw_billboard(command.position, command.animation, command.frame, command.flipX);
}
}
@ -142,11 +143,11 @@ backend_draw_trile_positions_main :: (trile : string, amount : s32, worldConf: *
if !in_shadowmap_pass {
mvp = create_viewproj(*camera);
} else {
mvp = create_shadow_viewproj(*camera, worldConf);
shadow_mvp = mvp;
mvp = shadow_mvp;
}
vs_params : Trile_Vs_Params;
vs_params.mvp = mvp.floats;
vs_params.mvp_shadow = shadow_mvp.floats;
vs_params.camera = camera.position.component;
sg_apply_pipeline(gPipelines.trile.pipeline);
world_conf : Trile_World_Config;
@ -165,6 +166,8 @@ backend_draw_trile_positions_main :: (trile : string, amount : s32, worldConf: *
bindings.samplers[0] = gPipelines.trile.bind.samplers[0];
bindings.images[0] = trilegfx.trixel_colors;
bindings.images[1] = g_ssaobuf;
bindings.samplers[2] = g_shadowmap_sampler;
bindings.images[2] = g_shadowmap;
fs_params : Trile_Fs_Params;
fs_params.mvp_shadow = shadow_mvp.floats;
@ -225,25 +228,30 @@ backend_draw_ground :: (wc: *World_Config) {
sg_draw(0, 6, 2);
}
backend_draw_billboard :: () {
backend_draw_billboard :: (position: Vector3, anim: *Animation, frame_idx: s32, flipX: bool) {
if !anim then return;
mvp := create_viewproj(*camera);
vs_params : Billboard_Vs_Params;
anim := table_find_pointer(*g_animations, "player_idle");
if anim {
gPipelines.billboard.bind.images[0] = anim.sheet;
num := cast(s32)(get_time() / 0.1) % anim.frames.count;
frame := anim.frames[num];
vs_params.uvs = Vector4.{
cast(float) frame.x / cast(float)anim.sheet_w,
cast(float) frame.y / cast(float)anim.sheet_h,
cast(float) frame.w / cast(float)anim.sheet_w,
cast(float) frame.h / cast(float)anim.sheet_h,
}.component;
vs_params.size = Vector2.{cast(float)(frame.w / 16), cast(float)(frame.h / 16)}.component;
vs_params.cam = camera.position.component;
gPipelines.billboard.bind.images[0] = anim.sheet;
frame := anim.frames[frame_idx];
vs_params.uvs = Vector4.{
cast(float) frame.x / cast(float)anim.sheet_w,
cast(float) frame.y / cast(float)anim.sheet_h,
cast(float) frame.w / cast(float)anim.sheet_w,
cast(float) frame.h / cast(float)anim.sheet_h,
}.component;
if flipX {
vs_params.uvs[0] += vs_params.uvs[2];
vs_params.uvs[2] *= -1.0;
}
vs_params.mvp = mvp.floats;
vs_params.offset = Vector3.{10, 1, 5.5}.component;
vs_params.size = Vector2.{cast(float)(frame.w / 16), cast(float)(frame.h / 16)}.component;
vs_params.cam = camera.position.component;
if !in_shadowmap_pass {
vs_params.mvp = mvp.floats;
} else {
vs_params.mvp = shadow_mvp.floats;
}
vs_params.offset = position.component;
sg_apply_pipeline(gPipelines.billboard.pipeline);
sg_apply_bindings(*gPipelines.billboard.bind);
sg_apply_uniforms(UB_billboard_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) }));
@ -279,6 +287,7 @@ backend_process_command_buckets :: () {
// 2. Shadow pass
if current_world_config != null {
shadow_mvp = create_shadow_viewproj(*camera, current_world_config);
in_shadowmap_pass = true;
sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, attachments = g_shadowmap_attachments}));
for render_command_buckets.shadow {

View File

@ -15,7 +15,6 @@ fill_uniform_with_engine_data :: (uniform: *$A, enginedata: *$B) {
create_world_rendering_tasks :: (world: *World) {
create_sky_rendering_task(*world.conf);
create_set_light_rendering_task(*world.conf);
create_billboard_rendering_task();
for world.positions {
if it.positions.count < 1 then continue;
triletask := Rendering_Task_Trile.{};
@ -48,8 +47,8 @@ create_ground_rendering_task :: (world: *World) {
add_rendering_task(groundtask);
}
create_billboard_rendering_task :: () {
billboardtask := Rendering_Task_Billboard.{type = .BILLBOARD };
create_billboard_rendering_task :: (position: Vector3, animation: *Animation, frame: s32, flipX: bool, flipY: bool) {
billboardtask := Rendering_Task_Billboard.{type = .BILLBOARD, position = position, animation = animation, frame = frame, flipX = flipX, flipY = flipY };
add_rendering_task(billboardtask);
}

View File

@ -71,11 +71,10 @@ create_shadow_viewproj :: (cam: *Camera, conf: *World_Config) -> Matrix4 {
max_v.y = max(max_v.y, transformed_corner.y);
max_v.z = max(max_v.z, transformed_corner.z);
}
transformed_avg := (view * Vector4.{avg.x, avg.y, avg.z, 1.0}).xyz;
max_v.xy = transformed_avg.xy + Vector2.{50, 50};
min_v.xy = transformed_avg.xy - Vector2.{50, 50};
max_v.xy = avg.xy + Vector2.{50, 50};
min_v.xy = avg.xy - Vector2.{50, 50};
proj := matrix_ortho(min_v.x, max_v.x, min_v.y, max_v.y, -min_v.z, -max_v.z-100);
proj := matrix_ortho(min_v.x, max_v.x, min_v.y, max_v.y, -max_v.z-100, -min_v.z);
return view*proj;
}

View File

@ -42,6 +42,11 @@ Rendering_Task_Ground :: struct {
Rendering_Task_Billboard :: struct {
#as using t : Rendering_Task;
t.type = .BILLBOARD;
position : Vector3;
animation : *Animation;
frame : s32;
flipX : bool;
flipY : bool;
}
Rendering_Task_Trile :: struct {
@ -121,8 +126,16 @@ tasks_to_commands :: () {
array_add(*render_command_buckets.main, commandDrawGround);
array_add(*render_command_buckets.gbuffer, commandDrawGround);
case .BILLBOARD;
billboardTask := (cast(*Rendering_Task_Billboard)it);
commandDrawBillboard := New(Render_Command_Draw_Billboard,, temp);
commandDrawBillboard.position = billboardTask.position;
commandDrawBillboard.frame = billboardTask.frame;
commandDrawBillboard.flipX = billboardTask.flipX;
commandDrawBillboard.flipY = billboardTask.flipY;
commandDrawBillboard.animation = billboardTask.animation;
array_add(*render_command_buckets.main, commandDrawBillboard);
array_add(*render_command_buckets.shadow, commandDrawBillboard);
array_add(*render_command_buckets.reflection, commandDrawBillboard);
case .SET_CAMERA;
task := (cast(*Rendering_Task_Set_Camera)it);
command := New(Render_Command_Set_Camera,, temp);

View File

@ -122,7 +122,7 @@ vs_billboard_source_glsl430 := u8.[
{
discard;
}
color = vec4(_35.xyz * 0.300000011920928955078125, 1.0);
color = vec4(_35.xyz * 0.5, 1.0);
}
*/
@ -149,9 +149,8 @@ fs_billboard_source_glsl430 := u8.[
0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x69,0x73,
0x63,0x61,0x72,0x64,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,
0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x33,0x35,
0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x30,0x2e,0x33,0x30,0x30,0x30,0x30,0x30,0x30,
0x31,0x31,0x39,0x32,0x30,0x39,0x32,0x38,0x39,0x35,0x35,0x30,0x37,0x38,0x31,0x32,
0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,
0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
];
/*
#version 300 es
@ -235,7 +234,7 @@ vs_billboard_source_glsl300es := u8.[
{
discard;
}
color = vec4(_35.xyz * 0.300000011920928955078125, 1.0);
color = vec4(_35.xyz * 0.5, 1.0);
}
*/
@ -264,10 +263,8 @@ fs_billboard_source_glsl300es := u8.[
0x32,0x35,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,
0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,
0x34,0x28,0x5f,0x33,0x35,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x30,0x2e,0x33,0x30,
0x30,0x30,0x30,0x30,0x30,0x31,0x31,0x39,0x32,0x30,0x39,0x32,0x38,0x39,0x35,0x35,
0x30,0x37,0x38,0x31,0x32,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x7d,0x0a,
0x0a,0x00,
0x34,0x28,0x5f,0x33,0x35,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x30,0x2e,0x35,0x2c,
0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
];
/*
#include <metal_stdlib>
@ -387,7 +384,7 @@ vs_billboard_source_metal_macos := u8.[
{
discard_fragment();
}
out.color = float4(_27.xyz * 0.300000011920928955078125, 1.0);
out.color = float4(_27.xyz * 0.5, 1.0);
return out;
}
@ -424,11 +421,9 @@ fs_billboard_source_metal_macos := u8.[
0x20,0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x5f,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,
0x74,0x28,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x6f,
0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,
0x34,0x28,0x5f,0x32,0x37,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x30,0x2e,0x33,0x30,
0x30,0x30,0x30,0x30,0x30,0x31,0x31,0x39,0x32,0x30,0x39,0x32,0x38,0x39,0x35,0x35,
0x30,0x37,0x38,0x31,0x32,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,
0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,
0x0a,0x00,
0x34,0x28,0x5f,0x32,0x37,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x30,0x2e,0x35,0x2c,
0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,
0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
];
billboard_shader_desc :: (backend: sg_backend) -> sg_shader_desc {
desc: sg_shader_desc;

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ void main() {
#endif
vec4 sampled = texture(sampler2D(sprite, spritesmp), uv);
if(sampled.a < 0.01) discard;
color = vec4(sampled.rgb * 0.3, 1.0);
color = vec4(sampled.rgb * 0.5, 1.0);
}
@end

View File

@ -7,6 +7,7 @@ in vec4 instance;
layout(binding=0) uniform trile_vs_params {
mat4 mvp;
mat4 mvp_shadow;
vec3 camera;
};
@ -15,9 +16,11 @@ out vec3 to_center;
out vec3 vpos; // The actual position;
out vec3 ipos; // Trile space position;
out vec4 fnormal;
out vec4 light_proj_pos;
void main() {
gl_Position = mvp * vec4(position.xyz + instance.xyz, 1.0);
light_proj_pos = mvp_shadow * vec4(position.xyz + instance.xyz, 1.0);
fnormal = normal;
to_center = centre.xyz - position.xyz;
vpos = position.xyz + instance.xyz;
@ -54,6 +57,7 @@ in vec3 to_center;
in vec3 vpos;
in vec3 ipos;
in vec4 fnormal;
in vec4 light_proj_pos;
out vec4 frag_color;
layout(binding=3) uniform trile_fs_params {
@ -67,6 +71,8 @@ layout(binding = 0) uniform texture2D triletex;
layout(binding = 0) uniform sampler trilesmp;
layout(binding = 1) uniform texture2D ssaotex;
layout(binding = 1) uniform sampler ssaosmp;
layout(binding = 2) uniform texture2D shadowtex;
layout(binding = 2) uniform sampler shadowsmp;
const float PI = 3.1412854;
@ -233,7 +239,12 @@ void main() {
vec3 kD = vec3(1.0) - F;
kD *= 1.0 - metallic;
light += (kD * albedo / PI + specular) * NdotL * sunLightColor * sunIntensity;
vec3 light_pos = light_proj_pos.xyz / light_proj_pos.w;
light_pos = light_pos * 0.5 + 0.5;
light_pos.z -= 0.0005;
float shadowp = texture(sampler2DShadow(shadowtex, shadowsmp), light_pos);
light += shadowp * (kD * albedo / PI + specular) * NdotL * sunLightColor * sunIntensity;
vec3 R = reflect(-V, N);
vec3 modifier = vec3(1.0);
@ -244,6 +255,7 @@ void main() {
vec3 samp = sky(R, sunPosition);
// light += F * samp * modifier;
frag_color = vec4(mix(deepColor, light, smoothstep(0.0, planeHeight, vpos.y)), 1.0);
}