Add 3d outline rendering, combine 3d and 2d shaders

main
parent a91de6dee1
commit cd8c88b66b

724
main.c

@ -1369,7 +1369,6 @@ ThreeDeeLevel load_level(MD_Arena *arena, MD_String8 binary_file)
} }
#include "assets.gen.c" #include "assets.gen.c"
#include "quad-sapp.glsl.h"
#include "threedee.glsl.h" #include "threedee.glsl.h"
AABB level_aabb = { .upper_left = { 0.0f, 0.0f }, .lower_right = { TILE_SIZE * LEVEL_TILES, -(TILE_SIZE * LEVEL_TILES) } }; AABB level_aabb = { .upper_left = { 0.0f, 0.0f }, .lower_right = { TILE_SIZE * LEVEL_TILES, -(TILE_SIZE * LEVEL_TILES) } };
@ -2618,16 +2617,121 @@ static struct
{ {
sg_pass_action clear_everything_pass_action; sg_pass_action clear_everything_pass_action;
sg_pass_action clear_depth_buffer_pass_action; sg_pass_action clear_depth_buffer_pass_action;
sg_pipeline pip; sg_pipeline twodee_pip;
sg_bindings bind; sg_bindings bind;
sg_pipeline threedee_pip; sg_pipeline threedee_pip;
sg_pipeline armature_pip; sg_pipeline armature_pip;
sg_bindings threedee_bind; sg_bindings threedee_bind;
sg_image outline_pass_image;
sg_pass outline_pass;
sg_pipeline outline_mesh_pip;
sg_pipeline outline_armature_pip;
sg_pipeline twodee_outline_pip;
Shadow_State shadows; Shadow_State shadows;
} state; } state;
// is a function, because also called when window resized to recreate the pass and the image.
// its target image must be the same size as the viewport. Is the reason. Cowabunga!
void create_outline_gfx_state()
{
if(state.outline_pass.id != 0)
{
sg_destroy_pass(state.outline_pass);
}
if(state.outline_pass_image.id != 0)
{
sg_destroy_image(state.outline_pass_image);
}
const sg_shader_desc *shd_desc = threedee_mesh_outline_shader_desc(sg_query_backend());
assert(shd_desc);
sg_shader shd = sg_make_shader(shd_desc);
state.outline_mesh_pip = sg_make_pipeline(&(sg_pipeline_desc)
{
.shader = shd,
.depth = {
0
},
.sample_count = 1,
.layout = {
.attrs =
{
[ATTR_threedee_vs_pos_in].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_threedee_vs_uv_in].format = SG_VERTEXFORMAT_FLOAT2,
}
},
.colors[0].blend = (sg_blend_state) { // allow transparency
.enabled = true,
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_rgb = SG_BLENDOP_ADD,
.src_factor_alpha = SG_BLENDFACTOR_ONE,
.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_alpha = SG_BLENDOP_ADD,
},
.label = "outline-mesh-pipeline",
});
shd_desc = threedee_armature_outline_shader_desc(sg_query_backend());
assert(shd_desc);
shd = sg_make_shader(shd_desc);
state.outline_armature_pip = sg_make_pipeline(&(sg_pipeline_desc)
{
.shader = shd,
.depth = {
.pixel_format = SG_PIXELFORMAT_NONE,
},
.sample_count = 1,
.layout = {
.attrs =
{
[ATTR_threedee_vs_skeleton_pos_in].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_threedee_vs_skeleton_uv_in].format = SG_VERTEXFORMAT_FLOAT2,
[ATTR_threedee_vs_skeleton_indices_in].format = SG_VERTEXFORMAT_USHORT4N,
[ATTR_threedee_vs_skeleton_weights_in].format = SG_VERTEXFORMAT_FLOAT4,
}
},
.colors[0].blend = (sg_blend_state) { // allow transparency
.enabled = true,
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_rgb = SG_BLENDOP_ADD,
.src_factor_alpha = SG_BLENDFACTOR_ONE,
.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_alpha = SG_BLENDOP_ADD,
},
.label = "outline-armature-pipeline",
});
sg_image_desc desc = {
.render_target = true,
.width = sapp_width(),
.height = sapp_height(),
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_BORDER,
.wrap_v = SG_WRAP_CLAMP_TO_BORDER,
.border_color = SG_BORDERCOLOR_OPAQUE_WHITE,
.sample_count = 1,
.label = "outline-pass-render-target",
};
state.outline_pass_image = sg_make_image(&desc);
state.outline_pass = sg_make_pass(&(sg_pass_desc){
.color_attachments[0].image = state.outline_pass_image,
.depth_stencil_attachment = {
0
},
.label = "outline-pass",
});
}
int num_draw_calls = 0; int num_draw_calls = 0;
int num_vertices = 0; int num_vertices = 0;
@ -3094,15 +3198,15 @@ void init(void)
.label = "quad-vertices" .label = "quad-vertices"
}); });
create_outline_gfx_state();
state.shadows = init_shadow_state(); state.shadows = init_shadow_state();
const sg_shader_desc *desc = quad_program_shader_desc(sg_query_backend()); const sg_shader_desc *desc = threedee_twodee_shader_desc(sg_query_backend());
assert(desc); assert(desc);
sg_shader shd = sg_make_shader(desc); sg_shader shd = sg_make_shader(desc);
Color clearcol = colhex(0x98734c); Color clearcol = colhex(0x98734c);
state.pip = sg_make_pipeline(&(sg_pipeline_desc) state.twodee_pip = sg_make_pipeline(&(sg_pipeline_desc)
{ {
.shader = shd, .shader = shd,
.depth = { .depth = {
@ -3112,8 +3216,34 @@ void init(void)
.layout = { .layout = {
.attrs = .attrs =
{ {
[ATTR_quad_vs_position].format = SG_VERTEXFORMAT_FLOAT3, [ATTR_threedee_vs_twodee_position].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_quad_vs_texcoord0].format = SG_VERTEXFORMAT_FLOAT2, [ATTR_threedee_vs_twodee_texcoord0].format = SG_VERTEXFORMAT_FLOAT2,
}
},
.colors[0].blend = (sg_blend_state) { // allow transparency
.enabled = true,
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_rgb = SG_BLENDOP_ADD,
.src_factor_alpha = SG_BLENDFACTOR_ONE,
.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.op_alpha = SG_BLENDOP_ADD,
},
.label = "quad-pipeline",
});
state.twodee_outline_pip = sg_make_pipeline(&(sg_pipeline_desc)
{
.shader = sg_make_shader(threedee_twodee_outline_shader_desc(sg_query_backend())),
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true
},
.layout = {
.attrs =
{
[ATTR_threedee_vs_twodee_position].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_threedee_vs_twodee_texcoord0].format = SG_VERTEXFORMAT_FLOAT2,
} }
}, },
.colors[0].blend = (sg_blend_state) { // allow transparency .colors[0].blend = (sg_blend_state) { // allow transparency
@ -3128,6 +3258,7 @@ void init(void)
.label = "quad-pipeline", .label = "quad-pipeline",
}); });
desc = threedee_mesh_shader_desc(sg_query_backend()); desc = threedee_mesh_shader_desc(sg_query_backend());
assert(desc); assert(desc);
shd = sg_make_shader(desc); shd = sg_make_shader(desc);
@ -3463,14 +3594,28 @@ float cur_batch_data[1024*10] = { 0 };
int cur_batch_data_index = 0; int cur_batch_data_index = 0;
// @TODO check last tint as well, do this when factor into drawing parameters // @TODO check last tint as well, do this when factor into drawing parameters
sg_image cur_batch_image = { 0 }; sg_image cur_batch_image = { 0 };
quad_fs_params_t cur_batch_params = { 0 }; threedee_twodee_fs_params_t cur_batch_params = { 0 };
sg_pipeline cur_batch_pipeline = { 0 };
void flush_quad_batch() void flush_quad_batch()
{ {
if (cur_batch_image.id == 0 || cur_batch_data_index == 0) return; // flush called when image changes, image starts out null! if (cur_batch_image.id == 0 || cur_batch_data_index == 0) return; // flush called when image changes, image starts out null!
if(cur_batch_pipeline.id != 0)
{
sg_apply_pipeline(cur_batch_pipeline);
}
else
{
sg_apply_pipeline(state.twodee_pip);
}
state.bind.vertex_buffer_offsets[0] = sg_append_buffer(state.bind.vertex_buffers[0], &(sg_range) { cur_batch_data, cur_batch_data_index*sizeof(*cur_batch_data) }); state.bind.vertex_buffer_offsets[0] = sg_append_buffer(state.bind.vertex_buffers[0], &(sg_range) { cur_batch_data, cur_batch_data_index*sizeof(*cur_batch_data) });
state.bind.fs_images[SLOT_quad_tex] = cur_batch_image; state.bind.fs_images[SLOT_threedee_twodee_tex] = cur_batch_image;
sg_apply_bindings(&state.bind); sg_apply_bindings(&state.bind);
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_quad_fs_params, &SG_RANGE(cur_batch_params)); cur_batch_params.tex_size = img_size(cur_batch_image);
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_twodee_fs_params, &SG_RANGE(cur_batch_params));
cur_batch_params.tex_size = V2(0,0); // unsure if setting the tex_size to something nonzero fucks up the batching so I'm just resetting it back here
assert(cur_batch_data_index % FLOATS_PER_VERTEX == 0); assert(cur_batch_data_index % FLOATS_PER_VERTEX == 0);
sg_draw(0, cur_batch_data_index / FLOATS_PER_VERTEX, 1); sg_draw(0, cur_batch_data_index / FLOATS_PER_VERTEX, 1);
num_draw_calls += 1; num_draw_calls += 1;
@ -3547,6 +3692,8 @@ typedef struct DrawParams
bool do_clipping; bool do_clipping;
Layer layer; Layer layer;
sg_pipeline custom_pipeline;
// for debugging purposes // for debugging purposes
int line_number; int line_number;
} DrawParams; } DrawParams;
@ -3891,6 +4038,7 @@ typedef struct
Mesh *mesh; Mesh *mesh;
Armature *armature; Armature *armature;
Transform t; Transform t;
bool outline;
} DrawnThing; } DrawnThing;
int drawn_this_frame_length = 0; int drawn_this_frame_length = 0;
@ -3911,6 +4059,7 @@ void draw_thing(DrawnThing params)
#endif #endif
} }
typedef struct TextParams typedef struct TextParams
{ {
bool dry_run; bool dry_run;
@ -4820,7 +4969,6 @@ Shadow_State init_shadow_state() {
return shadows; return shadows;
} }
typedef struct { typedef struct {
float l; float l;
float r; float r;
@ -4939,211 +5087,101 @@ Shadow_Volume_Params calculate_shadow_volume_params(Vec3 light_dir)
return result; return result;
} }
void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outline)
void frame(void)
{
static float speed_factor = 1.0f;
// elapsed_time
double unwarped_dt_double = 0.0;
{ {
unwarped_dt_double = stm_sec(stm_diff(stm_now(), last_frame_time)); if(it->mesh)
unwarped_dt_double = fmin(unwarped_dt_double, MINIMUM_TIMESTEP * 5.0); // clamp dt at maximum 5 frames, avoid super huge dt
elapsed_time += unwarped_dt_double*speed_factor;
unwarped_elapsed_time += unwarped_dt_double;
last_frame_time = stm_now();
}
double dt_double = unwarped_dt_double*speed_factor;
float unwarped_dt = (float)unwarped_dt_double;
float dt = (float)dt_double;
#if 0
{ {
printf("Frametime: %.1f ms\n", dt*1000.0); if(for_outline)
sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height()); sg_apply_pipeline(state.outline_mesh_pip);
sg_apply_pipeline(state.pip); else
sg_apply_pipeline(state.threedee_pip);
sg_bindings bindings = {0};
bindings.fs_images[SLOT_threedee_tex] = it->mesh->image;
if(!for_outline)
bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.vertex_buffers[0] = it->mesh->loaded_buffer;
sg_apply_bindings(&bindings);
//colorquad(false, quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), RED); Mat4 model = transform_to_matrix(it->t);
sg_image img = image_white_square; threedee_vs_params_t vs_params = {
AABB region = full_region(img); .model = model,
//region.lower_right.X *= 0.5f; .view = view,
draw_quad((DrawParams) { false, quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), img, region, WHITE }); .projection = projection,
.directional_light_space_matrix = light_space_matrix,
};
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_vs_params, &SG_RANGE(vs_params));
num_draw_calls += 1;
num_vertices += (int)it->mesh->num_vertices;
if(!for_outline)
{
threedee_fs_params_t fs_params = {0};
fs_params.shadow_map_dimension = SHADOW_MAP_DIMENSION;
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_fs_params, &SG_RANGE(fs_params));
}
flush_quad_batch(); sg_draw(0, (int)it->mesh->num_vertices, 1);
sg_end_pass();
sg_commit();
} }
return;
#endif
PROFILE_SCOPE("frame") if(it->armature)
{ {
uint64_t time_start_frame = stm_now(); if(for_outline)
sg_apply_pipeline(state.outline_armature_pip);
else
sg_apply_pipeline(state.armature_pip);
sg_bindings bindings = {0};
bindings.vs_images[SLOT_threedee_bones_tex] = it->armature->bones_texture;
bindings.fs_images[SLOT_threedee_tex] = it->armature->image;
if(!for_outline)
bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.vertex_buffers[0] = it->armature->loaded_buffer;
sg_apply_bindings(&bindings);
Vec3 player_pos = V3(gs.player->pos.x, 0.0, gs.player->pos.y); Mat4 model = transform_to_matrix(it->t);
//dbgline(V2(0,0), V2(500, 500)); threedee_skeleton_vs_params_t params = {
const float vertical_to_horizontal_ratio = CAM_VERTICAL_TO_HORIZONTAL_RATIO; .model = model,
const float cam_distance = CAM_DISTANCE; .view = view,
Vec3 away_from_player; .projection = projection,
{ .directional_light_space_matrix = light_space_matrix,
float ratio = vertical_to_horizontal_ratio; .bones_tex_size = V2((float)it->armature->bones_texture_width,(float)it->armature->bones_texture_height),
float x = sqrtf( (cam_distance * cam_distance) / (1 + (ratio*ratio)) ); };
float y = ratio * x; sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_skeleton_vs_params, &SG_RANGE(params));
away_from_player = V3(x, y, 0.0);
}
away_from_player = MulM4V4(Rotate_RH(-PI32/3.0f + PI32, V3(0,1,0)), IsPoint(away_from_player)).xyz;
Vec3 cam_pos = AddV3(player_pos, away_from_player);
Vec2 movement = { 0 }; if(!for_outline)
bool interact = false;
if (mobile_controls)
{ {
movement = SubV2(thumbstick_nub_pos, thumbstick_base_pos); threedee_fs_params_t fs_params = {0};
if (LenV2(movement) > 0.0f) fs_params.shadow_map_dimension = SHADOW_MAP_DIMENSION;
{ sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_fs_params, &SG_RANGE(fs_params));
movement = MulV2F(NormV2(movement), LenV2(movement) / (thumbstick_base_size()*0.5f));
}
interact = pressed.interact;
} }
else
{ num_draw_calls += 1;
movement = V2( num_vertices += (int)it->armature->vertices_length;
(float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A], sg_draw(0, (int)it->armature->vertices_length, 1);
(float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S]
);
interact = pressed.interact;
} }
if (LenV2(movement) > 1.0)
{
movement = NormV2(movement);
} }
// progress the animation, then blend the two animations if necessary, and finally // I moved this out into its own separate function so that you could
// output into anim_blended_poses // define helper functions to be used multiple times in it, and those functions
ARR_ITER(Armature*, armatures) // would be near the actual 3d drawing in the file
void flush_all_drawn_things(Vec3 light_dir)
{ {
Armature *cur = *it; // Draw all the 3D drawn things. Draw the shadows, then draw the things with the shadows.
// Process armatures and upload their skeleton textures
if(cur->go_to_animation.size > 0)
{ {
if(MD_S8Match(cur->go_to_animation, cur->target_animation, 0)) // Animate armatures, and upload their bone textures. Also debug draw their skeleton
{ {
} SLICE_ITER(DrawnThing, drawn_this_frame)
else
{ {
memcpy(cur->current_poses, cur->anim_blended_poses, cur->bones_length * sizeof(*cur->current_poses)); if(it->armature)
cur->target_animation = cur->go_to_animation;
cur->animation_blend_t = 0.0f;
cur->go_to_animation = (MD_String8){0};
}
}
if(cur->animation_blend_t < 1.0f)
{ {
cur->animation_blend_t += dt / ANIMATION_BLEND_TIME; MD_ArenaTemp scratch = MD_GetScratch(0, 0);
Armature *armature = it->armature;
Animation *to_anim = get_anim_by_name(cur, cur->target_animation); int bones_tex_size = 4 * armature->bones_texture_width * armature->bones_texture_height;
assert(to_anim); MD_u8 *bones_tex = MD_ArenaPush(scratch.arena, bones_tex_size);
for(MD_u64 i = 0; i < cur->bones_length; i++) for(MD_u64 i = 0; i < armature->bones_length; i++)
{ {
Transform *output_transform = &cur->anim_blended_poses[i]; Bone *cur = &armature->bones[i];
Transform from_transform = cur->current_poses[i];
Transform to_transform = get_animated_bone_transform(&to_anim->tracks[i], &cur->bones[i], (float)elapsed_time);
*output_transform = lerp_transforms(from_transform, cur->animation_blend_t, to_transform);
}
}
else
{
Animation *cur_anim = get_anim_by_name(cur, cur->target_animation);
for(MD_u64 i = 0; i < cur->bones_length; i++)
{
cur->anim_blended_poses[i] = get_animated_bone_transform(&cur_anim->tracks[i], &cur->bones[i], (float)elapsed_time);
}
}
}
Vec3 light_dir;
{
float spin_factor = 0.5f;
float t = (float)elapsed_time * spin_factor;
float x = cosf(t);
float z = sinf(t);
light_dir = NormV3(V3(x, -0.5f, z));
}
// make movement relative to camera forward
Vec3 facing = NormV3(SubV3(player_pos, cam_pos));
Vec3 right = Cross(facing, V3(0,1,0));
Vec2 forward_2d = NormV2(V2(facing.x, facing.z));
Vec2 right_2d = NormV2(V2(right.x, right.z));
movement = AddV2(MulV2F(forward_2d, movement.y), MulV2F(right_2d, movement.x));
view = Translate(V3(0.0, 1.0, -5.0f));
//view = LookAt_RH(V3(0,1,-5
if(flycam)
{
Basis basis = flycam_basis();
view = LookAt_RH(flycam_pos, AddV3(flycam_pos, basis.forward), V3(0, 1, 0));
//view = flycam_matrix();
}
else
{
view = LookAt_RH(cam_pos, player_pos, V3(0, 1, 0));
}
projection = Perspective_RH_NO(FIELD_OF_VIEW, screen_size().x / screen_size().y, NEAR_PLANE_DISTANCE, FAR_PLANE_DISTANCE);
for(PlacedMesh *cur = level_threedee.placed_mesh_list; cur; cur = cur->next)
{
draw_thing((DrawnThing){.mesh = cur->draw_with, .t = cur->t});
}
ENTITIES_ITER(gs.entities)
{
if(it->is_npc || it->is_character)
{
Transform draw_with = entity_transform(it);
if(it->npc_kind == NPC_Player)
{
draw_thing((DrawnThing){.armature = &player_armature, .t = draw_with});
}
else if(it->npc_kind == NPC_Farmer)
{
farmer_armature.go_to_animation = MD_S8Lit("Dance");
draw_thing((DrawnThing){.armature = &farmer_armature, .t = draw_with});
}
else
{
draw_thing((DrawnThing){.mesh = &mesh_player, .t = draw_with});
}
}
}
// Draw all the 3D drawn things. Draw the shadows, then draw the things with the shadows.
// Process armatures and upload their skeleton textures
{
// Animate armatures, and upload their bone textures. Also debug draw their skeleton
{
SLICE_ITER(DrawnThing, drawn_this_frame)
{
if(it->armature)
{
MD_ArenaTemp scratch = MD_GetScratch(0, 0);
Armature *armature = it->armature;
int bones_tex_size = 4 * armature->bones_texture_width * armature->bones_texture_height;
MD_u8 *bones_tex = MD_ArenaPush(scratch.arena, bones_tex_size);
for(MD_u64 i = 0; i < armature->bones_length; i++)
{
Bone *cur = &armature->bones[i];
// for debug drawing // for debug drawing
Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0)); Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0));
@ -5271,89 +5309,249 @@ void frame(void)
sg_end_pass(); sg_end_pass();
} }
// do the outline pass
{
sg_begin_pass(state.outline_pass, &(sg_pass_action) {
.colors[0] = {
.action = SG_ACTION_CLEAR,
.value = { 0.0f, 0.0f, 0.0f, 0.0f },
}
});
SLICE_ITER(DrawnThing, drawn_this_frame)
{
if(it->outline)
{
actually_draw_thing(it, light_space_matrix, true);
}
}
sg_end_pass();
}
// actually draw, IMPORTANT after this drawn_this_frame is zeroed out! // actually draw, IMPORTANT after this drawn_this_frame is zeroed out!
{ {
sg_begin_default_pass(&state.clear_everything_pass_action, sapp_width(), sapp_height()); sg_begin_default_pass(&state.clear_everything_pass_action, sapp_width(), sapp_height());
// draw meshes // draw meshes
sg_apply_pipeline(state.threedee_pip);
SLICE_ITER(DrawnThing, drawn_this_frame) SLICE_ITER(DrawnThing, drawn_this_frame)
{ {
if(it->mesh) actually_draw_thing(it, light_space_matrix, false);
}
// draw armatures armature rendering
SLICE_ITER(DrawnThing, drawn_this_frame)
{ {
sg_bindings bindings = {0}; actually_draw_thing(it, light_space_matrix, false);
bindings.fs_images[SLOT_threedee_tex] = it->mesh->image; }
bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.vertex_buffers[0] = it->mesh->loaded_buffer;
sg_apply_bindings(&bindings);
Mat4 model = transform_to_matrix(it->t);
threedee_vs_params_t vs_params = {
.model = model,
.view = view,
.projection = projection,
.directional_light_space_matrix = light_space_matrix,
};
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_vs_params, &SG_RANGE(vs_params));
num_draw_calls += 1;
num_vertices += (int)it->mesh->num_vertices;
threedee_fs_params_t fs_params = {0}; // zero out everything
fs_params.shadow_map_dimension = SHADOW_MAP_DIMENSION; SLICE_ITER(DrawnThing, drawn_this_frame)
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_fs_params, &SG_RANGE(fs_params)); {
*it = (DrawnThing){0};
}
sg_end_pass();
}
drawn_this_frame_length = 0;
}
}
sg_draw(0, (int)it->mesh->num_vertices, 1); void frame(void)
{
static float speed_factor = 1.0f;
// elapsed_time
double unwarped_dt_double = 0.0;
{
unwarped_dt_double = stm_sec(stm_diff(stm_now(), last_frame_time));
unwarped_dt_double = fmin(unwarped_dt_double, MINIMUM_TIMESTEP * 5.0); // clamp dt at maximum 5 frames, avoid super huge dt
elapsed_time += unwarped_dt_double*speed_factor;
unwarped_elapsed_time += unwarped_dt_double;
last_frame_time = stm_now();
}
double dt_double = unwarped_dt_double*speed_factor;
float unwarped_dt = (float)unwarped_dt_double;
float dt = (float)dt_double;
#if 0
{
printf("Frametime: %.1f ms\n", dt*1000.0);
sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height());
sg_apply_pipeline(state.twodee_pipeline);
//colorquad(false, quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), RED);
sg_image img = image_white_square;
AABB region = full_region(img);
//region.lower_right.X *= 0.5f;
draw_quad((DrawParams) { false, quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), img, region, WHITE });
flush_quad_batch();
sg_end_pass();
sg_commit();
} }
return;
#endif
PROFILE_SCOPE("frame")
{
uint64_t time_start_frame = stm_now();
Vec3 player_pos = V3(gs.player->pos.x, 0.0, gs.player->pos.y);
//dbgline(V2(0,0), V2(500, 500));
const float vertical_to_horizontal_ratio = CAM_VERTICAL_TO_HORIZONTAL_RATIO;
const float cam_distance = CAM_DISTANCE;
Vec3 away_from_player;
{
float ratio = vertical_to_horizontal_ratio;
float x = sqrtf( (cam_distance * cam_distance) / (1 + (ratio*ratio)) );
float y = ratio * x;
away_from_player = V3(x, y, 0.0);
} }
away_from_player = MulM4V4(Rotate_RH(-PI32/3.0f + PI32, V3(0,1,0)), IsPoint(away_from_player)).xyz;
Vec3 cam_pos = AddV3(player_pos, away_from_player);
// draw armatures armature rendering Vec2 movement = { 0 };
sg_apply_pipeline(state.armature_pip); bool interact = false;
SLICE_ITER(DrawnThing, drawn_this_frame) if (mobile_controls)
{ {
if(it->armature) movement = SubV2(thumbstick_nub_pos, thumbstick_base_pos);
if (LenV2(movement) > 0.0f)
{ {
sg_bindings bindings = {0}; movement = MulV2F(NormV2(movement), LenV2(movement) / (thumbstick_base_size()*0.5f));
bindings.vs_images[SLOT_threedee_bones_tex] = it->armature->bones_texture; }
bindings.fs_images[SLOT_threedee_tex] = it->armature->image; interact = pressed.interact;
bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img; }
bindings.vertex_buffers[0] = it->armature->loaded_buffer; else
sg_apply_bindings(&bindings); {
movement = V2(
(float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A],
(float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S]
);
interact = pressed.interact;
}
if (LenV2(movement) > 1.0)
{
movement = NormV2(movement);
}
Mat4 model = transform_to_matrix(it->t); // progress the animation, then blend the two animations if necessary, and finally
threedee_skeleton_vs_params_t params = { // output into anim_blended_poses
.model = model, ARR_ITER(Armature*, armatures)
.view = view, {
.projection = projection, Armature *cur = *it;
.directional_light_space_matrix = light_space_matrix,
.bones_tex_size = V2((float)it->armature->bones_texture_width,(float)it->armature->bones_texture_height),
};
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_skeleton_vs_params, &SG_RANGE(params));
threedee_fs_params_t fs_params = {0}; if(cur->go_to_animation.size > 0)
fs_params.shadow_map_dimension = SHADOW_MAP_DIMENSION; {
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_fs_params, &SG_RANGE(fs_params)); if(MD_S8Match(cur->go_to_animation, cur->target_animation, 0))
{
}
else
{
memcpy(cur->current_poses, cur->anim_blended_poses, cur->bones_length * sizeof(*cur->current_poses));
cur->target_animation = cur->go_to_animation;
cur->animation_blend_t = 0.0f;
cur->go_to_animation = (MD_String8){0};
}
}
num_draw_calls += 1; if(cur->animation_blend_t < 1.0f)
num_vertices += (int)it->armature->vertices_length; {
sg_draw(0, (int)it->armature->vertices_length, 1); cur->animation_blend_t += dt / ANIMATION_BLEND_TIME;
Animation *to_anim = get_anim_by_name(cur, cur->target_animation);
assert(to_anim);
for(MD_u64 i = 0; i < cur->bones_length; i++)
{
Transform *output_transform = &cur->anim_blended_poses[i];
Transform from_transform = cur->current_poses[i];
Transform to_transform = get_animated_bone_transform(&to_anim->tracks[i], &cur->bones[i], (float)elapsed_time);
*output_transform = lerp_transforms(from_transform, cur->animation_blend_t, to_transform);
}
}
else
{
Animation *cur_anim = get_anim_by_name(cur, cur->target_animation);
for(MD_u64 i = 0; i < cur->bones_length; i++)
{
cur->anim_blended_poses[i] = get_animated_bone_transform(&cur_anim->tracks[i], &cur->bones[i], (float)elapsed_time);
}
} }
} }
Vec3 light_dir;
{
float spin_factor = 0.5f;
float t = (float)elapsed_time * spin_factor;
// zero out everything float x = cosf(t);
SLICE_ITER(DrawnThing, drawn_this_frame) float z = sinf(t);
light_dir = NormV3(V3(x, -0.5f, z));
}
// make movement relative to camera forward
Vec3 facing = NormV3(SubV3(player_pos, cam_pos));
Vec3 right = Cross(facing, V3(0,1,0));
Vec2 forward_2d = NormV2(V2(facing.x, facing.z));
Vec2 right_2d = NormV2(V2(right.x, right.z));
movement = AddV2(MulV2F(forward_2d, movement.y), MulV2F(right_2d, movement.x));
view = Translate(V3(0.0, 1.0, -5.0f));
//view = LookAt_RH(V3(0,1,-5
if(flycam)
{ {
*it = (DrawnThing){0}; Basis basis = flycam_basis();
view = LookAt_RH(flycam_pos, AddV3(flycam_pos, basis.forward), V3(0, 1, 0));
//view = flycam_matrix();
} }
sg_end_pass(); else
{
view = LookAt_RH(cam_pos, player_pos, V3(0, 1, 0));
} }
drawn_this_frame_length = 0;
projection = Perspective_RH_NO(FIELD_OF_VIEW, screen_size().x / screen_size().y, NEAR_PLANE_DISTANCE, FAR_PLANE_DISTANCE);
for(PlacedMesh *cur = level_threedee.placed_mesh_list; cur; cur = cur->next)
{
draw_thing((DrawnThing){.mesh = cur->draw_with, .t = cur->t});
} }
ENTITIES_ITER(gs.entities)
{
if(it->is_npc || it->is_character)
{
Transform draw_with = entity_transform(it);
if(it->npc_kind == NPC_Player)
{
draw_thing((DrawnThing){.armature = &player_armature, .t = draw_with, .outline = true});
}
else if(it->npc_kind == NPC_Farmer)
{
farmer_armature.go_to_animation = MD_S8Lit("Dance");
draw_thing((DrawnThing){.armature = &farmer_armature, .t = draw_with});
}
else
{
draw_thing((DrawnThing){.mesh = &mesh_player, .t = draw_with});
}
}
}
flush_all_drawn_things(light_dir);
// 2d drawing // draw the freaking outline. Play ball!
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.outline_pass_image), WHITE, .layer = LAYER_UI_FG, .custom_pipeline = state.twodee_outline_pip});
// 2d drawing TODO move this to when the drawing is flushed.
sg_begin_default_pass(&state.clear_depth_buffer_pass_action, sapp_width(), sapp_height()); sg_begin_default_pass(&state.clear_depth_buffer_pass_action, sapp_width(), sapp_height());
sg_apply_pipeline(state.pip); sg_apply_pipeline(state.twodee_pip);
// Draw Tilemap draw tilemap tilemap drawing // Draw Tilemap draw tilemap tilemap drawing
#if 0 #if 0
@ -6809,8 +7007,8 @@ void frame(void)
if (show_devtools) if (show_devtools)
PROFILE_SCOPE("statistics") PROFILE_SCOPE("statistics")
{ {
// shadow map
draw_quad((DrawParams){quad_at(V2(screen_size().x - 512.0f, screen_size().y), V2(512.0f, 512.0f)), IMG(state.shadows.color_img), WHITE, .layer = LAYER_UI_FG}); draw_quad((DrawParams){quad_at(V2(screen_size().x - 512.0f, screen_size().y), V2(512.0f, 512.0f)), IMG(state.shadows.color_img), WHITE, .layer = LAYER_UI_FG});
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y/2.0f), MulV2F(screen_size(), 0.1f)), IMG(state.outline_pass_image), WHITE, .layer = LAYER_UI_FG});
Vec2 pos = V2(0.0, screen_size().Y); Vec2 pos = V2(0.0, screen_size().Y);
int num_entities = 0; int num_entities = 0;
@ -6829,7 +7027,7 @@ void frame(void)
#endif // devtools #endif // devtools
// @Place(actually render) // @Place(actually render 2d)
PROFILE_SCOPE("flush rendering") PROFILE_SCOPE("flush rendering")
{ {
ARR_ITER_I(RenderingQueue, rendering_queues, i) ARR_ITER_I(RenderingQueue, rendering_queues, i)
@ -6844,34 +7042,29 @@ void frame(void)
PROFILE_SCOPE("Draw quad") PROFILE_SCOPE("Draw quad")
{ {
Vec2 *points = d.quad.points; Vec2 *points = d.quad.points;
quad_fs_params_t params = { 0 }; threedee_twodee_fs_params_t params = {
params.tint[0] = d.tint.R; .tint = d.tint,
params.tint[1] = d.tint.G; };
params.tint[2] = d.tint.B;
params.tint[3] = d.tint.A;
params.alpha_clip_threshold = d.alpha_clip_threshold; params.alpha_clip_threshold = d.alpha_clip_threshold;
if (d.do_clipping) if (d.do_clipping)
{ {
Vec2 aabb_clip_ul = into_clip_space(d.clip_to.upper_left); Vec2 aabb_clip_ul = into_clip_space(d.clip_to.upper_left);
Vec2 aabb_clip_lr = into_clip_space(d.clip_to.lower_right); Vec2 aabb_clip_lr = into_clip_space(d.clip_to.lower_right);
params.clip_ul[0] = aabb_clip_ul.x; params.clip_ul = aabb_clip_ul;
params.clip_ul[1] = aabb_clip_ul.y; params.clip_lr = aabb_clip_lr;
params.clip_lr[0] = aabb_clip_lr.x;
params.clip_lr[1] = aabb_clip_lr.y;
} }
else else
{ {
params.clip_ul[0] = -1.0; params.clip_ul = V2(-1.0, 1.0);
params.clip_ul[1] = 1.0; params.clip_lr = V2(1.0, -1.0);
params.clip_lr[0] = 1.0;
params.clip_lr[1] = -1.0;
} }
// if the rendering call is different, and the batch must be flushed // if the rendering call is different, and the batch must be flushed
if (d.image.id != cur_batch_image.id || memcmp(&params, &cur_batch_params, sizeof(params)) != 0) if (d.image.id != cur_batch_image.id || memcmp(&params, &cur_batch_params, sizeof(params)) != 0 || d.custom_pipeline.id != cur_batch_pipeline.id)
{ {
flush_quad_batch(); flush_quad_batch();
cur_batch_image = d.image; cur_batch_image = d.image;
cur_batch_params = params; cur_batch_params = params;
cur_batch_pipeline = d.custom_pipeline;
} }
@ -6990,6 +7183,11 @@ void cleanup(void)
void event(const sapp_event *e) void event(const sapp_event *e)
{ {
if (e->key_repeat) return; if (e->key_repeat) return;
if (e->type == SAPP_EVENTTYPE_RESIZED)
{
create_outline_gfx_state();
}
if (e->type == SAPP_EVENTTYPE_TOUCHES_BEGAN) if (e->type == SAPP_EVENTTYPE_TOUCHES_BEGAN)
{ {
if (!mobile_controls) if (!mobile_controls)

@ -1,45 +1,2 @@
@module quad
@vs vs
in vec3 position;
in vec2 texcoord0;
out vec2 uv;
out vec2 pos;
void main() {
gl_Position = vec4(position.xyz, 1.0);
uv = texcoord0;
pos = position.xy;
}
@end
@fs fs
uniform sampler2D tex;
uniform fs_params {
vec4 tint;
// both in clip space
vec2 clip_ul;
vec2 clip_lr;
float alpha_clip_threshold;
};
in vec2 uv;
in vec2 pos;
out vec4 frag_color;
void main() {
// clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y
if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard;
frag_color = texture(tex, uv) * tint;
if(frag_color.a <= alpha_clip_threshold)
{
discard;
}
//frag_color = vec4(pos.x,0.0,0.0,1.0);
}
@end
@program program vs fs @program program vs fs

@ -13,7 +13,6 @@ rmdir /S /q gen
mkdir gen mkdir gen
@REM shaders @REM shaders
thirdparty\sokol-shdc.exe --input quad.glsl --output gen\quad-sapp.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error
@REM metadesk codegen @REM metadesk codegen

@ -269,7 +269,127 @@ void main() {
} }
@end @end
@vs vs_twodee
in vec3 position;
in vec2 texcoord0;
out vec2 uv;
out vec2 pos;
void main() {
gl_Position = vec4(position.xyz, 1.0);
uv = texcoord0;
pos = position.xy;
}
@end
@fs fs_twodee
uniform sampler2D twodee_tex;
uniform twodee_fs_params {
vec4 tint;
// both in clip space
vec2 clip_ul;
vec2 clip_lr;
float alpha_clip_threshold;
vec2 tex_size;
};
in vec2 uv;
in vec2 pos;
out vec4 frag_color;
void main() {
// clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y
if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard;
frag_color = texture(twodee_tex, uv) * tint;
if(frag_color.a <= alpha_clip_threshold)
{
discard;
}
//frag_color = vec4(pos.x,0.0,0.0,1.0);
}
@end
@fs fs_twodee_outline
uniform sampler2D twodee_tex;
uniform twodee_fs_params {
vec4 tint;
// both in clip space
vec2 clip_ul;
vec2 clip_lr;
float alpha_clip_threshold;
vec2 tex_size;
};
in vec2 uv;
in vec2 pos;
out vec4 frag_color;
void main() {
// clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y
if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard;
float left = texture(twodee_tex, uv + vec2(-1, 0)/tex_size).a;
float right = texture(twodee_tex, uv + vec2(1, 0)/tex_size).a;
float up = texture(twodee_tex, uv + vec2(0, 1)/tex_size).a;
float down = texture(twodee_tex, uv + vec2(0, -1)/tex_size).a;
if(
false
|| left > 0.1 && right < 0.1
|| left < 0.1 && right > 0.1
|| up < 0.1 && down > 0.1
|| up > 0.1 && down < 0.1
)
{
frag_color = vec4(1.0);
}
else
{
frag_color = vec4(0.0);
}
}
@end
@fs fs_outline
uniform sampler2D tex;
in vec3 pos;
in vec2 uv;
in vec4 light_space_fragment_position;
in vec3 light_dir;
in vec4 world_space_frag_pos;
out vec4 frag_color;
void main() {
vec4 col = texture(tex, uv);
if(col.a < 0.5)
{
discard;
}
frag_color = vec4(vec3(1.0), col.a);
}
@end
@program mesh vs fs @program mesh vs fs
@program armature vs_skeleton fs @program armature vs_skeleton fs
@program mesh_shadow_mapping vs fs_shadow_mapping @program mesh_shadow_mapping vs fs_shadow_mapping
@program armature_shadow_mapping vs_skeleton fs_shadow_mapping @program armature_shadow_mapping vs_skeleton fs_shadow_mapping
@program mesh_outline vs fs_outline
@program armature_outline vs_skeleton fs_outline
@program twodee vs_twodee fs_twodee
@program twodee_outline vs_twodee fs_twodee_outline

Loading…
Cancel
Save