diff --git a/main.c b/main.c index 9585d33..3051b0c 100644 --- a/main.c +++ b/main.c @@ -847,7 +847,7 @@ typedef struct { Vec3 position; Vec2 uv; - MD_u16 joint_indices[4]; + MD_u16 joint_indices[4]; float joint_weights[4]; } ArmatureVertex; @@ -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,6 +5087,275 @@ Shadow_Volume_Params calculate_shadow_volume_params(Vec3 light_dir) 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) { @@ -4960,7 +5377,7 @@ void frame(void) { printf("Frametime: %.1f ms\n", dt*1000.0); 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); sg_image img = image_white_square; @@ -5112,7 +5529,7 @@ void frame(void) Transform draw_with = entity_transform(it); 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) { @@ -5127,233 +5544,14 @@ void frame(void) } - // 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)); + flush_all_drawn_things(light_dir); - num_draw_calls += 1; - num_vertices += (int)it->armature->vertices_length; - sg_draw(0, (int)it->armature->vertices_length, 1); - } - } - sg_end_pass(); - } + // 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}); - // 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) - { - 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 + // 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(¶ms, &cur_batch_params, sizeof(params)) != 0) + if (d.image.id != cur_batch_image.id || memcmp(¶ms, &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) diff --git a/quad.glsl b/quad.glsl index 8568a80..d54b019 100644 --- a/quad.glsl +++ b/quad.glsl @@ -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 diff --git a/run_codegen.bat b/run_codegen.bat index 6e9c9a3..8e684f1 100644 --- a/run_codegen.bat +++ b/run_codegen.bat @@ -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 diff --git a/threedee.glsl b/threedee.glsl index 0642539..68f1099 100644 --- a/threedee.glsl +++ b/threedee.glsl @@ -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 +