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

main
Cameron Murphy Reikes 10 months ago
parent a91de6dee1
commit cd8c88b66b

728
main.c

@ -1369,7 +1369,6 @@ ThreeDeeLevel load_level(MD_Arena *arena, MD_String8 binary_file)
}
#include "assets.gen.c"
#include "quad-sapp.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) } };
@ -2618,16 +2617,121 @@ static struct
{
sg_pass_action clear_everything_pass_action;
sg_pass_action clear_depth_buffer_pass_action;
sg_pipeline pip;
sg_pipeline twodee_pip;
sg_bindings bind;
sg_pipeline threedee_pip;
sg_pipeline armature_pip;
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;
} 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_vertices = 0;
@ -3094,15 +3198,15 @@ void init(void)
.label = "quad-vertices"
});
create_outline_gfx_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);
sg_shader shd = sg_make_shader(desc);
Color clearcol = colhex(0x98734c);
state.pip = sg_make_pipeline(&(sg_pipeline_desc)
state.twodee_pip = sg_make_pipeline(&(sg_pipeline_desc)
{
.shader = shd,
.depth = {
@ -3112,8 +3216,34 @@ void init(void)
.layout = {
.attrs =
{
[ATTR_quad_vs_position].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_quad_vs_texcoord0].format = SG_VERTEXFORMAT_FLOAT2,
[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
.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
@ -3128,6 +3258,7 @@ void init(void)
.label = "quad-pipeline",
});
desc = threedee_mesh_shader_desc(sg_query_backend());
assert(desc);
shd = sg_make_shader(desc);
@ -3463,14 +3594,28 @@ float cur_batch_data[1024*10] = { 0 };
int cur_batch_data_index = 0;
// @TODO check last tint as well, do this when factor into drawing parameters
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()
{
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.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_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);
sg_draw(0, cur_batch_data_index / FLOATS_PER_VERTEX, 1);
num_draw_calls += 1;
@ -3547,6 +3692,8 @@ typedef struct DrawParams
bool do_clipping;
Layer layer;
sg_pipeline custom_pipeline;
// for debugging purposes
int line_number;
} DrawParams;
@ -3891,6 +4038,7 @@ typedef struct
Mesh *mesh;
Armature *armature;
Transform t;
bool outline;
} DrawnThing;
int drawn_this_frame_length = 0;
@ -3911,6 +4059,7 @@ void draw_thing(DrawnThing params)
#endif
}
typedef struct TextParams
{
bool dry_run;
@ -4820,7 +4969,6 @@ Shadow_State init_shadow_state() {
return shadows;
}
typedef struct {
float l;
float r;
@ -4939,211 +5087,101 @@ Shadow_Volume_Params calculate_shadow_volume_params(Vec3 light_dir)
return result;
}
void frame(void)
void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outline)
{
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
if(it->mesh)
{
printf("Frametime: %.1f ms\n", dt*1000.0);
sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height());
sg_apply_pipeline(state.pip);
//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 });
if(for_outline)
sg_apply_pipeline(state.outline_mesh_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);
flush_quad_batch();
sg_end_pass();
sg_commit();
}
return;
#endif
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;
PROFILE_SCOPE("frame")
if(!for_outline)
{
uint64_t time_start_frame = stm_now();
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));
}
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);
sg_draw(0, (int)it->mesh->num_vertices, 1);
}
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 };
bool interact = false;
if (mobile_controls)
{
movement = SubV2(thumbstick_nub_pos, thumbstick_base_pos);
if (LenV2(movement) > 0.0f)
if(it->armature)
{
movement = MulV2F(NormV2(movement), LenV2(movement) / (thumbstick_base_size()*0.5f));
}
interact = pressed.interact;
}
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);
Mat4 model = transform_to_matrix(it->t);
threedee_skeleton_vs_params_t params = {
.model = model,
.view = view,
.projection = projection,
.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));
if(!for_outline)
{
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;
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));
}
if (LenV2(movement) > 1.0)
{
movement = NormV2(movement);
num_draw_calls += 1;
num_vertices += (int)it->armature->vertices_length;
sg_draw(0, (int)it->armature->vertices_length, 1);
}
}
// progress the animation, then blend the two animations if necessary, and finally
// output into anim_blended_poses
ARR_ITER(Armature*, armatures)
// I moved this out into its own separate function so that you could
// define helper functions to be used multiple times in it, and those functions
// would be near the actual 3d drawing in the file
void flush_all_drawn_things(Vec3 light_dir)
{
// Draw all the 3D drawn things. Draw the shadows, then draw the things with the shadows.
// Process armatures and upload their skeleton textures
{
Armature *cur = *it;
if(cur->go_to_animation.size > 0)
// Animate armatures, and upload their bone textures. Also debug draw their skeleton
{
if(MD_S8Match(cur->go_to_animation, cur->target_animation, 0))
SLICE_ITER(DrawnThing, drawn_this_frame)
{
}
else
if(it->armature)
{
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};
}
}
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);
if(cur->animation_blend_t < 1.0f)
for(MD_u64 i = 0; i < armature->bones_length; i++)
{
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;
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];
Bone *cur = &armature->bones[i];
// for debug drawing
Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0));
@ -5271,89 +5309,249 @@ void frame(void)
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!
{
sg_begin_default_pass(&state.clear_everything_pass_action, sapp_width(), sapp_height());
// draw meshes
sg_apply_pipeline(state.threedee_pip);
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};
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);
actually_draw_thing(it, light_space_matrix, false);
}
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};
fs_params.shadow_map_dimension = SHADOW_MAP_DIMENSION;
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_fs_params, &SG_RANGE(fs_params));
// zero out everything
SLICE_ITER(DrawnThing, drawn_this_frame)
{
*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
sg_apply_pipeline(state.armature_pip);
SLICE_ITER(DrawnThing, drawn_this_frame)
Vec2 movement = { 0 };
bool interact = false;
if (mobile_controls)
{
if(it->armature)
movement = SubV2(thumbstick_nub_pos, thumbstick_base_pos);
if (LenV2(movement) > 0.0f)
{
sg_bindings bindings = {0};
bindings.vs_images[SLOT_threedee_bones_tex] = it->armature->bones_texture;
bindings.fs_images[SLOT_threedee_tex] = it->armature->image;
bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img;
bindings.vertex_buffers[0] = it->armature->loaded_buffer;
sg_apply_bindings(&bindings);
movement = MulV2F(NormV2(movement), LenV2(movement) / (thumbstick_base_size()*0.5f));
}
interact = pressed.interact;
}
else
{
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);
threedee_skeleton_vs_params_t params = {
.model = model,
.view = view,
.projection = projection,
.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));
// progress the animation, then blend the two animations if necessary, and finally
// output into anim_blended_poses
ARR_ITER(Armature*, armatures)
{
Armature *cur = *it;
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));
if(cur->go_to_animation.size > 0)
{
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;
num_vertices += (int)it->armature->vertices_length;
sg_draw(0, (int)it->armature->vertices_length, 1);
if(cur->animation_blend_t < 1.0f)
{
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;
float x = cosf(t);
float z = sinf(t);
light_dir = NormV3(V3(x, -0.5f, z));
}
// zero out everything
SLICE_ITER(DrawnThing, drawn_this_frame)
// 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_apply_pipeline(state.pip);
sg_apply_pipeline(state.twodee_pip);
// Draw Tilemap draw tilemap tilemap drawing
#if 0
@ -6809,8 +7007,8 @@ void frame(void)
if (show_devtools)
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(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);
int num_entities = 0;
@ -6829,7 +7027,7 @@ void frame(void)
#endif // devtools
// @Place(actually render)
// @Place(actually render 2d)
PROFILE_SCOPE("flush rendering")
{
ARR_ITER_I(RenderingQueue, rendering_queues, i)
@ -6844,34 +7042,29 @@ void frame(void)
PROFILE_SCOPE("Draw quad")
{
Vec2 *points = d.quad.points;
quad_fs_params_t params = { 0 };
params.tint[0] = d.tint.R;
params.tint[1] = d.tint.G;
params.tint[2] = d.tint.B;
params.tint[3] = d.tint.A;
threedee_twodee_fs_params_t params = {
.tint = d.tint,
};
params.alpha_clip_threshold = d.alpha_clip_threshold;
if (d.do_clipping)
{
Vec2 aabb_clip_ul = into_clip_space(d.clip_to.upper_left);
Vec2 aabb_clip_lr = into_clip_space(d.clip_to.lower_right);
params.clip_ul[0] = aabb_clip_ul.x;
params.clip_ul[1] = aabb_clip_ul.y;
params.clip_lr[0] = aabb_clip_lr.x;
params.clip_lr[1] = aabb_clip_lr.y;
params.clip_ul = aabb_clip_ul;
params.clip_lr = aabb_clip_lr;
}
else
{
params.clip_ul[0] = -1.0;
params.clip_ul[1] = 1.0;
params.clip_lr[0] = 1.0;
params.clip_lr[1] = -1.0;
params.clip_ul = V2(-1.0, 1.0);
params.clip_lr = V2(1.0, -1.0);
}
// 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();
cur_batch_image = d.image;
cur_batch_params = params;
cur_batch_pipeline = d.custom_pipeline;
}
@ -6990,6 +7183,11 @@ void cleanup(void)
void event(const sapp_event *e)
{
if (e->key_repeat) return;
if (e->type == SAPP_EVENTTYPE_RESIZED)
{
create_outline_gfx_state();
}
if (e->type == SAPP_EVENTTYPE_TOUCHES_BEGAN)
{
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

@ -13,7 +13,6 @@ rmdir /S /q gen
mkdir gen
@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
@REM metadesk codegen

@ -269,7 +269,127 @@ void main() {
}
@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 armature vs_skeleton fs
@program mesh_shadow_mapping vs 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