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

main
parent a91de6dee1
commit cd8c88b66b

706
main.c

@ -847,7 +847,7 @@ typedef struct
{ {
Vec3 position; Vec3 position;
Vec2 uv; Vec2 uv;
MD_u16 joint_indices[4]; MD_u16 joint_indices[4];
float joint_weights[4]; float joint_weights[4];
} ArmatureVertex; } ArmatureVertex;
@ -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,6 +5087,275 @@ 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)
{
if(it->mesh)
{
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);
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;
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));
}
sg_draw(0, (int)it->mesh->num_vertices, 1);
}
if(it->armature)
{
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)
{
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));
}
num_draw_calls += 1;
num_vertices += (int)it->armature->vertices_length;
sg_draw(0, (int)it->armature->vertices_length, 1);
}
}
// 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
{
// 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
Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0));
Vec3 x = MulM4V3(cur->matrix_local, V3(cur->length,0,0));
Vec3 y = MulM4V3(cur->matrix_local, V3(0,cur->length,0));
Vec3 z = MulM4V3(cur->matrix_local, V3(0,0,cur->length));
Mat4 final = M4D(1.0f);
final = MulM4(cur->inverse_model_space_pos, final);
for(Bone *cur_in_hierarchy = cur; cur_in_hierarchy; cur_in_hierarchy = cur_in_hierarchy->parent)
{
int bone_index = (int)(cur_in_hierarchy - armature->bones);
final = MulM4(transform_to_matrix(armature->anim_blended_poses[bone_index]), final);
}
from = MulM4V3(final, from);
x = MulM4V3(final, x);
y = MulM4V3(final, y);
z = MulM4V3(final, z);
Mat4 transform_matrix = transform_to_matrix(it->t);
from = MulM4V3(transform_matrix, from);
x = MulM4V3(transform_matrix, x);
y = MulM4V3(transform_matrix, y);
z = MulM4V3(transform_matrix, z);
dbgcol(LIGHTBLUE)
dbgsquare3d(y);
dbgcol(RED)
dbg3dline(from, x);
dbgcol(GREEN)
dbg3dline(from, y);
dbgcol(BLUE)
dbg3dline(from, z);
for(int col = 0; col < 4; col++)
{
Vec4 to_upload = final.Columns[col];
int bytes_per_pixel = 4;
int bytes_per_column_of_mat = bytes_per_pixel * 4;
int bytes_per_row = bytes_per_pixel * armature->bones_texture_width;
for(int elem = 0; elem < 4; elem++)
{
float after_decoding = decode_normalized_float32(encode_normalized_float32(to_upload.Elements[elem]));
assert(fabsf(after_decoding - to_upload.Elements[elem]) < 0.01f);
}
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*0], encode_normalized_float32(to_upload.Elements[0]).rgba, bytes_per_pixel);
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*1], encode_normalized_float32(to_upload.Elements[1]).rgba, bytes_per_pixel);
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*2], encode_normalized_float32(to_upload.Elements[2]).rgba, bytes_per_pixel);
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*3], encode_normalized_float32(to_upload.Elements[3]).rgba, bytes_per_pixel);
}
}
sg_update_image(armature->bones_texture, &(sg_image_data){
.subimage[0][0] = (sg_range){bones_tex, bones_tex_size},
});
MD_ReleaseScratch(scratch);
}
}
}
// do the shadow pass
Mat4 light_space_matrix;
{
Shadow_Volume_Params svp = calculate_shadow_volume_params(light_dir);
Mat4 shadow_view = LookAt_RH(V3(0, 0, 0), light_dir, V3(0, 1, 0));
Mat4 shadow_projection = Orthographic_RH_NO(svp.l, svp.r, svp.b, svp.t, svp.n, svp.f);
light_space_matrix = MulM4(shadow_projection, shadow_view);
sg_begin_pass(state.shadows.pass, &state.shadows.pass_action);
// shadows for meshes
sg_apply_pipeline(state.shadows.pip);
SLICE_ITER(DrawnThing, drawn_this_frame)
{
assert(it->mesh || it->armature);
if(it->mesh)
{
sg_bindings bindings = {0};
bindings.fs_images[SLOT_threedee_tex] = it->mesh->image;
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 = shadow_view,
.projection = shadow_projection,
};
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;
sg_draw(0, (int)it->mesh->num_vertices, 1);
}
}
// shadows for armatures
sg_apply_pipeline(state.shadows.armature_pip);
SLICE_ITER(DrawnThing, drawn_this_frame)
{
if(it->armature)
{
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.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 = shadow_view,
.projection = shadow_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));
num_draw_calls += 1;
num_vertices += (int)it->armature->vertices_length;
sg_draw(0, (int)it->armature->vertices_length, 1);
}
}
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
SLICE_ITER(DrawnThing, drawn_this_frame)
{
actually_draw_thing(it, light_space_matrix, false);
}
// draw armatures armature rendering
SLICE_ITER(DrawnThing, drawn_this_frame)
{
actually_draw_thing(it, light_space_matrix, false);
}
// zero out everything
SLICE_ITER(DrawnThing, drawn_this_frame)
{
*it = (DrawnThing){0};
}
sg_end_pass();
}
drawn_this_frame_length = 0;
}
}
void frame(void) void frame(void)
{ {
@ -4960,7 +5377,7 @@ void frame(void)
{ {
printf("Frametime: %.1f ms\n", dt*1000.0); printf("Frametime: %.1f ms\n", dt*1000.0);
sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height()); sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height());
sg_apply_pipeline(state.pip); sg_apply_pipeline(state.twodee_pipeline);
//colorquad(false, quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), RED); //colorquad(false, quad_at(V2(0.0, 100.0), V2(100.0f, 100.0f)), RED);
sg_image img = image_white_square; sg_image img = image_white_square;
@ -5112,7 +5529,7 @@ void frame(void)
Transform draw_with = entity_transform(it); Transform draw_with = entity_transform(it);
if(it->npc_kind == NPC_Player) if(it->npc_kind == NPC_Player)
{ {
draw_thing((DrawnThing){.armature = &player_armature, .t = draw_with}); draw_thing((DrawnThing){.armature = &player_armature, .t = draw_with, .outline = true});
} }
else if(it->npc_kind == NPC_Farmer) else if(it->npc_kind == NPC_Farmer)
{ {
@ -5127,233 +5544,14 @@ void frame(void)
} }
// Draw all the 3D drawn things. Draw the shadows, then draw the things with the shadows. flush_all_drawn_things(light_dir);
// 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
Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0));
Vec3 x = MulM4V3(cur->matrix_local, V3(cur->length,0,0));
Vec3 y = MulM4V3(cur->matrix_local, V3(0,cur->length,0));
Vec3 z = MulM4V3(cur->matrix_local, V3(0,0,cur->length));
Mat4 final = M4D(1.0f);
final = MulM4(cur->inverse_model_space_pos, final);
for(Bone *cur_in_hierarchy = cur; cur_in_hierarchy; cur_in_hierarchy = cur_in_hierarchy->parent)
{
int bone_index = (int)(cur_in_hierarchy - armature->bones);
final = MulM4(transform_to_matrix(armature->anim_blended_poses[bone_index]), final);
}
from = MulM4V3(final, from);
x = MulM4V3(final, x);
y = MulM4V3(final, y);
z = MulM4V3(final, z);
Mat4 transform_matrix = transform_to_matrix(it->t);
from = MulM4V3(transform_matrix, from);
x = MulM4V3(transform_matrix, x);
y = MulM4V3(transform_matrix, y);
z = MulM4V3(transform_matrix, z);
dbgcol(LIGHTBLUE)
dbgsquare3d(y);
dbgcol(RED)
dbg3dline(from, x);
dbgcol(GREEN)
dbg3dline(from, y);
dbgcol(BLUE)
dbg3dline(from, z);
for(int col = 0; col < 4; col++)
{
Vec4 to_upload = final.Columns[col];
int bytes_per_pixel = 4;
int bytes_per_column_of_mat = bytes_per_pixel * 4;
int bytes_per_row = bytes_per_pixel * armature->bones_texture_width;
for(int elem = 0; elem < 4; elem++)
{
float after_decoding = decode_normalized_float32(encode_normalized_float32(to_upload.Elements[elem]));
assert(fabsf(after_decoding - to_upload.Elements[elem]) < 0.01f);
}
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*0], encode_normalized_float32(to_upload.Elements[0]).rgba, bytes_per_pixel);
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*1], encode_normalized_float32(to_upload.Elements[1]).rgba, bytes_per_pixel);
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*2], encode_normalized_float32(to_upload.Elements[2]).rgba, bytes_per_pixel);
memcpy(&bones_tex[bytes_per_column_of_mat*col + bytes_per_row*i + bytes_per_pixel*3], encode_normalized_float32(to_upload.Elements[3]).rgba, bytes_per_pixel);
}
}
sg_update_image(armature->bones_texture, &(sg_image_data){
.subimage[0][0] = (sg_range){bones_tex, bones_tex_size},
});
MD_ReleaseScratch(scratch);
}
}
}
// do the shadow pass
Mat4 light_space_matrix;
{
Shadow_Volume_Params svp = calculate_shadow_volume_params(light_dir);
Mat4 shadow_view = LookAt_RH(V3(0, 0, 0), light_dir, V3(0, 1, 0));
Mat4 shadow_projection = Orthographic_RH_NO(svp.l, svp.r, svp.b, svp.t, svp.n, svp.f);
light_space_matrix = MulM4(shadow_projection, shadow_view);
sg_begin_pass(state.shadows.pass, &state.shadows.pass_action);
// shadows for meshes
sg_apply_pipeline(state.shadows.pip);
SLICE_ITER(DrawnThing, drawn_this_frame)
{
assert(it->mesh || it->armature);
if(it->mesh)
{
sg_bindings bindings = {0};
bindings.fs_images[SLOT_threedee_tex] = it->mesh->image;
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 = shadow_view,
.projection = shadow_projection,
};
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;
sg_draw(0, (int)it->mesh->num_vertices, 1);
}
}
// shadows for armatures
sg_apply_pipeline(state.shadows.armature_pip);
SLICE_ITER(DrawnThing, drawn_this_frame)
{
if(it->armature)
{
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.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 = shadow_view,
.projection = shadow_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));
num_draw_calls += 1; // draw the freaking outline. Play ball!
num_vertices += (int)it->armature->vertices_length; 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});
sg_draw(0, (int)it->armature->vertices_length, 1);
}
}
sg_end_pass();
}
// actually draw, IMPORTANT after this drawn_this_frame is zeroed out! // 2d drawing TODO move this to when the drawing is flushed.
{
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)
{
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);
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));
sg_draw(0, (int)it->mesh->num_vertices, 1);
}
}
// draw armatures armature rendering
sg_apply_pipeline(state.armature_pip);
SLICE_ITER(DrawnThing, drawn_this_frame)
{
if(it->armature)
{
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);
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));
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));
num_draw_calls += 1;
num_vertices += (int)it->armature->vertices_length;
sg_draw(0, (int)it->armature->vertices_length, 1);
}
}
// zero out everything
SLICE_ITER(DrawnThing, drawn_this_frame)
{
*it = (DrawnThing){0};
}
sg_end_pass();
}
drawn_this_frame_length = 0;
}
// 2d drawing
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