diff --git a/src/entity.h b/src/entity.h index 2fa6b218..87fad3e1 100644 --- a/src/entity.h +++ b/src/entity.h @@ -56,7 +56,8 @@ struct entity_store { - +/* TODO: Remove this */ +#include "gjk.h" @@ -95,6 +96,7 @@ struct entity { b32 colliding; struct entity_handle colliding_with; struct v2 point; + struct gjk_simplex simplex; diff --git a/src/game.c b/src/game.c index 4804b02e..74cd1327 100644 --- a/src/game.c +++ b/src/game.c @@ -27,11 +27,6 @@ GLOBAL struct { struct atomic_u64 prev_tick_id; struct world prev_tick; struct world tick; - -#if RTC - u32 gjk_steps; -#endif - } G = { 0 }, DEBUG_ALIAS(G, G_game); /* ========================== * @@ -49,10 +44,6 @@ struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr, (UNUSED)sheet_sr; (UNUSED)sound_sr; -#if RTC - G.gjk_steps = U32_MAX; -#endif - /* Initialize game cmd storage */ G.game_cmds_mutex = sys_mutex_alloc(); G.game_cmds_arena = arena_alloc(GIGABYTE(64)); @@ -287,13 +278,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) spawn_test_entities(); } break; -#if RTC - /* Debug */ - case GAME_CMD_KIND_SET_GJK_STEPS: { - G.gjk_steps = cmd.gjk_steps; - } break; -#endif - default: break; }; } @@ -779,6 +763,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) struct v2 point0 = V2(0, 0); struct v2 point1 = V2(0, 0); struct entity *colliding_with = entity_nil(); + struct gjk_simplex simplex = { 0 }; for (u64 e1_index = 0; e1_index < store->reserved; ++e1_index) { struct entity *e1 = &store->entities[e1_index]; if (e1 == e0) continue; @@ -799,7 +784,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) }; } -#if 1 +#if 0 struct v2 e0_velocity = v2_div(v2_sub(e0_xf.og, e0->verlet_xform.og), dt); struct v2 e1_velocity = v2_div(v2_sub(e1_xf.og, e1->verlet_xform.og), dt); struct v2 pendir = v2_sub(e0_velocity, e1_velocity); /* What if both point in opposite directions? */ @@ -815,32 +800,21 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) point0 = res.p0; point1 = res.p1; colliding_with = e1; - //final_simplex = res.simplex; + simplex = res.simplex; if (colliding) { + +#if 1 struct v2 pen = v2_sub(point1, point0); - -#if 0 - /* Pen movement test */ - f32 e0_ratio = v2_dot(pendir, e0_velocity); - //f32 e1_ratio = v2_dot(pendir, e1_velocity); - f32 e1_ratio = v2_dot(pendir, e1_velocity); - { - struct v2 pen = v2_mul(e0_velocity, e0_ratio); - - struct xform xf = e0_xf; - xf.og = v2_add(xf.og, pen); - entity_set_xform(e0, xf); - - //e0->verlet_xform.og = v2_add(e0->verlet_xform.og, pen); - } -#else f32 epsilon = 0.0001; pen = v2_add(pen, v2_mul(v2_norm(pen), epsilon)); + /* Pen movement test */ #if 0 + f32 epsilon = 0.000; pen = v2_add(pen, v2_mul(v2_norm(pen), epsilon)); +#endif struct xform xf = e0_xf; xf.og = v2_add(xf.og, pen); entity_set_xform(e0, xf); - e0->verlet_xform.og = v2_add(e0->verlet_xform.og, pen); - //e0->verlet_xform.og = xf.og; + //e0->verlet_xform.og = v2_add(e0->verlet_xform.og, pen); + e0->verlet_xform.og = xf.og; #endif } } @@ -848,6 +822,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) e0->colliding = colliding; e0->colliding_with = colliding_with->handle; e0->point = point0; + e0->simplex = simplex; if (colliding_with->valid) { colliding_with->colliding = colliding; @@ -862,6 +837,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) * Player aim * ========================== */ +#if 0 for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { struct entity *ent = &store->entities[entity_index]; if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; @@ -924,6 +900,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) entity_set_xform(ent, xf); } } +#endif /* ========================== * * Initialize bullet kinematics from sources diff --git a/src/game.h b/src/game.h index bf884288..a21c8495 100644 --- a/src/game.h +++ b/src/game.h @@ -24,11 +24,6 @@ enum game_cmd_kind { GAME_CMD_KIND_PAUSE, GAME_CMD_KIND_STEP, -#if RTC - /* Debug */ - GAME_CMD_KIND_SET_GJK_STEPS, -#endif - GAME_CMD_KIND_COUNT }; diff --git a/src/gjk.c b/src/gjk.c index 1970ab17..54239080 100644 --- a/src/gjk.c +++ b/src/gjk.c @@ -3,16 +3,11 @@ #include "arena.h" #include "scratch.h" -struct menkowski_point { - struct v2 p0; /* Support point of first shape in dir */ - struct v2 p1; /* Support point of second shape in -dir */ - struct v2 p; /* Menkowski difference point */ -}; +#if GJK_DEBUG +u32 gjk_debug_steps = U32_MAX; +#endif -struct simplex { - u32 len; - struct menkowski_point a, b, c; -}; +#define DBGSTEP if (dbg_step++ >= gjk_debug_steps) goto abort INTERNAL struct v2 poly_support(struct v2_array a, struct v2 dir) { @@ -35,9 +30,9 @@ INTERNAL struct v2 menkowski_point(struct v2_array poly0, struct v2_array shape1 return v2_sub(poly_support(poly0, dir), poly_support(shape1, v2_neg(dir))); } -INTERNAL struct menkowski_point menkowski_point_extended(struct v2_array poly0, struct v2_array poly1, struct v2 dir) +INTERNAL struct gjk_menkowski_point menkowski_point_extended(struct v2_array poly0, struct v2_array poly1, struct v2 dir) { - struct menkowski_point res; + struct gjk_menkowski_point res; res.p0 = poly_support(poly0, dir); res.p1 = poly_support(poly1, v2_neg(dir)); res.p = v2_sub(res.p0, res.p1); @@ -93,24 +88,34 @@ b32 gjk_boolean(struct v2_array shape0, struct v2_array shape1) struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array shape1, struct v2 penetration_dir) { + struct temp_arena scratch = scratch_begin_no_conflict(); struct gjk_extended_result res = { 0 }; /* TODO: Verify epsilon */ f32 unique_epsilon = 0.00001; b32 use_penetration_dir = false; - struct simplex s = { 0 }; + struct gjk_simplex s = { 0 }; + b32 colliding = false; + struct v2 shape0_p = { 0 }; + struct v2 shape1_p = { 0 }; + +#if GJK_DEBUG + u32 dbg_step = 0; +#endif /* ========================== * * GJK collision check * ========================== */ struct v2 dir = { 0 }; - struct menkowski_point m = { 0 }; + struct gjk_menkowski_point m = { 0 }; { + DBGSTEP; /* First point is support point in shape's general directions to eachother */ s.a = menkowski_point_extended(shape0, shape1, v2_sub(shape1.points[0], shape0.points[0])); s.len = 1; + DBGSTEP; /* Second point is support point towards origin */ dir = v2_neg(s.a.p); m = menkowski_point_extended(shape0, shape1, dir); @@ -120,6 +125,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array s.len = 2; while (true) { + DBGSTEP; /* Third point is support point in direction of line normal towards origin */ dir = v2_perp_towards_dir(v2_sub(s.b.p, s.a.p), v2_neg(s.a.p)); m = menkowski_point_extended(shape0, shape1, dir); @@ -133,6 +139,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array s.a = m; s.len = 3; + DBGSTEP; dir = v2_perp_towards_dir(v2_sub(s.b.p, s.a.p), v2_neg(v2_sub(s.c.p, s.a.p))); /* Normal of ab pointing away from c */ struct v2 a_to_origin = v2_neg(s.a.p); if (v2_dot(dir, a_to_origin) >= 0) { @@ -146,7 +153,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array s.b = s.c; s.len = 2; } else { - res.colliding = true; + colliding = true; break; } } @@ -154,7 +161,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } } - if (res.colliding) { + if (colliding) { use_penetration_dir = !v2_eq(penetration_dir, V2(0, 0)); if (use_penetration_dir) { @@ -165,6 +172,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array while (true) { /* Second point is support point towards penetration_dir */ if (s.len == 1) { + DBGSTEP; dir = v2_sub(v2_mul(penetration_dir, v2_dot(penetration_dir, s.a.p) / v2_dot(penetration_dir, penetration_dir)), s.a.p); m = menkowski_point_extended(shape0, shape1, dir); if (v2_eq(m.p, s.a.p)) { @@ -178,6 +186,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } if (s.len == 2) { + DBGSTEP; m = menkowski_point_extended(shape0, shape1, dir); if (math_fabs(v2_wedge(v2_sub(s.b.p, s.a.p), v2_sub(m.p, s.a.p))) < unique_epsilon) { /* New point is already on the current line */ @@ -189,6 +198,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array s.len = 3; } + DBGSTEP; i32 a_wedgesign = math_fsign(v2_wedge(penetration_dir, s.a.p)); i32 b_wedgesign = math_fsign(v2_wedge(penetration_dir, s.b.p)); i32 c_wedgesign = math_fsign(v2_wedge(penetration_dir, s.c.p)); @@ -212,17 +222,15 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } else { /* ========================== * - * Expand simplex towards closest edge (EPA) + * Expand simplex towards closest edge to origin inside menkowski (EPA) * ========================== */ - struct temp_arena scratch = scratch_begin_no_conflict(); - - struct menkowski_point *proto = arena_dry_push(scratch.arena, struct menkowski_point); + struct gjk_menkowski_point *proto = arena_dry_push(scratch.arena, struct gjk_menkowski_point); u32 proto_count = 0; { ASSERT(s.len == 3); - struct menkowski_point *tmp = arena_push_array(scratch.arena, struct menkowski_point, 3); + struct gjk_menkowski_point *tmp = arena_push_array(scratch.arena, struct gjk_menkowski_point, 3); tmp[0] = s.a; tmp[1] = s.b; tmp[2] = s.c; @@ -230,6 +238,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } while (true) { + DBGSTEP; struct v2 pen = V2(0, 0); f32 pen_len_sq = F32_INFINITY; @@ -258,6 +267,11 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } } + /* TODO: Move to break (debugging) */ + s.a = proto[pen_ps_index]; + s.b = proto[pen_pe_index]; + s.len = 2; + /* Find new point in dir */ m = menkowski_point_extended(shape0, shape1, pen); @@ -274,9 +288,6 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } } if (!unique) { - s.a = proto[pen_ps_index]; - s.b = proto[pen_pe_index]; - s.len = 2; break; } } @@ -292,13 +303,11 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } proto[pen_pe_index] = m; } - - scratch_end(scratch); } } else { /* ========================== * - * Move simplex towards closest edge to origin + * Move simplex towards closest edge to origin outside menkowski * ========================== */ if (s.len == 2) { @@ -309,6 +318,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array while (true) { /* Second point is support point towards origin */ if (s.len == 1) { + DBGSTEP; m = menkowski_point_extended(shape0, shape1, v2_neg(s.a.p)); if (v2_eq(m.p, s.a.p) || v2_eq(m.p, V2(0, 0))) { break; @@ -323,6 +333,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } if (s.len == 2) { + DBGSTEP; m = menkowski_point_extended(shape0, shape1, dir); if (math_fabs(v2_wedge(v2_sub(s.b.p, s.a.p), v2_sub(m.p, s.a.p))) < unique_epsilon || v2_eq(m.p, V2(0, 0))) { /* New point is already on the current line */ @@ -334,6 +345,7 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array s.len = 3; } + DBGSTEP; /* Remove point or edge and determine next direction based on voronoi regions */ struct v2 rab_dir = v2_perp_towards_dir(v2_sub(s.b.p, s.a.p), v2_neg(v2_sub(s.c.p, s.a.p))); struct v2 rac_dir = v2_perp_towards_dir(v2_sub(s.c.p, s.a.p), v2_neg(v2_sub(s.b.p, s.a.p))); @@ -374,9 +386,10 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array } /* Resolve points */ + DBGSTEP; if (s.len == 1) { - res.p0 = s.a.p0; - res.p1 = s.a.p1; + shape0_p = s.a.p0; + shape1_p = s.a.p1; } else if (s.len == 2) { /* FIXME: Winding order dependent? */ f32 ratio; @@ -392,14 +405,37 @@ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array ratio = clamp_f32(v2_dot(vab, vao) / v2_dot(vab, vab), 0, 1); } /* Shape 0 */ - res.p0 = v2_sub(s.b.p0, s.a.p0); - res.p0 = v2_mul(res.p0, ratio); - res.p0 = v2_add(res.p0, s.a.p0); + shape0_p = v2_sub(s.b.p0, s.a.p0); + shape0_p = v2_mul(shape0_p, ratio); + shape0_p = v2_add(shape0_p, s.a.p0); /* Shape 1 */ - res.p1 = v2_sub(s.b.p1, s.a.p1); - res.p1 = v2_mul(res.p1, ratio); - res.p1 = v2_add(res.p1, s.a.p1); + shape1_p = v2_sub(s.b.p1, s.a.p1); + shape1_p = v2_mul(shape1_p, ratio); + shape1_p = v2_add(shape1_p, s.a.p1); } +abort: + res.colliding = colliding; + res.p0 = shape0_p; + res.p1 = shape1_p; + res.simplex = s; + scratch_end(scratch); + return res; +} + +/* TODO: Remove this (debugging) */ +struct v2_array menkowski(struct arena *arena, struct v2_array poly0, struct v2_array poly1) +{ + struct v2_array res = { .points = arena_dry_push(arena, struct v2) }; + u64 rays = 500; + for (u64 i = 0; i < rays; ++i) { + f32 angle = ((f32)i / rays) * (2 * PI); + struct v2 dir = v2_from_angle(angle); + struct v2 p = menkowski_point_extended(poly0, poly1, dir).p; + if (res.count == 0 || !v2_eq(p, res.points[res.count - 1])) { + *arena_push(arena, struct v2) = p; + ++res.count; + } + } return res; } diff --git a/src/gjk.h b/src/gjk.h index 25d4e0f4..b9c3ab47 100644 --- a/src/gjk.h +++ b/src/gjk.h @@ -1,9 +1,29 @@ #ifndef GJK_H #define GJK_H +#define GJK_DEBUG RTC + +#if GJK_DEBUG +extern u32 gjk_debug_steps; +#endif + +struct gjk_menkowski_point { + struct v2 p0; /* Support point of first shape in dir */ + struct v2 p1; /* Support point of second shape in -dir */ + struct v2 p; /* Menkowski difference point */ +}; + +struct gjk_simplex { + u32 len; + struct gjk_menkowski_point a, b, c; +}; + struct gjk_extended_result { b32 colliding; struct v2 p0, p1; /* Closest points (or penetrating points if colliding) on each shape */ + + /* For debugging */ + struct gjk_simplex simplex; }; /* Returns simple true or false indicating shape collision */ @@ -17,4 +37,6 @@ b32 gjk_boolean(struct v2_array shape0, struct v2_array shape1); * supplied direction. */ struct gjk_extended_result gjk_extended(struct v2_array shape0, struct v2_array shape1, struct v2 penetration_dir); +struct v2_array menkowski(struct arena *arena, struct v2_array poly0, struct v2_array poly1); + #endif diff --git a/src/user.c b/src/user.c index e592c8ac..8638fe67 100644 --- a/src/user.c +++ b/src/user.c @@ -69,10 +69,6 @@ GLOBAL struct { struct v2 viewport_center; struct v2 viewport_cursor; struct v2 world_cursor; - -#if RTC - u32 gjk_steps; -#endif } G = { 0 }, DEBUG_ALIAS(G, G_user); /* ========================== * @@ -148,9 +144,6 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, G.viewport_bg_canvas = renderer_canvas_alloc(); G.viewport_canvas = renderer_canvas_alloc(); G.window = window; -#if RTC - G.gjk_steps = U32_MAX; -#endif sys_window_register_event_callback(G.window, &window_event_callback); G.user_thread = sys_thread_alloc(&user_thread_entry_point, NULL, STR("[P1] User thread")); @@ -997,7 +990,7 @@ INTERNAL void user_update(void) (UNUSED)e1; (UNUSED)colliding; -#if 0 +#if 1 /* Draw menkowski */ if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { struct quad ent_quad; @@ -1046,7 +1039,7 @@ INTERNAL void user_update(void) draw_solid_circle(G.viewport_canvas, point, thickness, color, 10); } -#if 0 +#if 1 /* Draw simplex */ { f32 thickness = 2; @@ -1076,7 +1069,9 @@ INTERNAL void user_update(void) draw_solid_poly_line(G.viewport_canvas, simplex_array, simplex.len > 2, thickness, line_color); } } +#endif +#if 0 /* Draw pen */ if (colliding) { f32 thickness = 2; @@ -1148,7 +1143,8 @@ INTERNAL void user_update(void) { /* Queue player move cmd */ - f32 move_speed = G.bind_states[USER_BIND_KIND_WALK].is_held ? 0.25f : 1.0f; + const f32 walk_ratio = 0.25f; + f32 move_speed = G.bind_states[USER_BIND_KIND_WALK].is_held ? walk_ratio : 1.0f; struct v2 input_move_dir = { 0 }; { for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(G.bind_states); ++bind) { @@ -1199,20 +1195,13 @@ INTERNAL void user_update(void) } #if RTC - /* Queue debug cmds */ - /* Gjk steps */ { - i64 new_steps = (i64)G.gjk_steps; + i64 new_steps = gjk_debug_steps; new_steps += G.bind_states[USER_BIND_KIND_INCR_GJK_STEPS].num_presses_and_repeats; new_steps -= G.bind_states[USER_BIND_KIND_DECR_GJK_STEPS].num_presses_and_repeats; if (G.bind_states[USER_BIND_KIND_RESET_GJK_STEPS].num_presses_and_repeats > 0) new_steps = 0; - G.gjk_steps = (u32)clamp_i64(new_steps, 0, U32_MAX); - queue_game_cmd(scratch.arena, &cmd_list, (struct game_cmd) { - .kind = GAME_CMD_KIND_SET_GJK_STEPS, - .state = 1, - .gjk_steps = G.gjk_steps - }); + gjk_debug_steps = (u32)clamp_i64(new_steps, 0, U32_MAX); } #endif } @@ -1272,7 +1261,7 @@ INTERNAL void user_update(void) pos.y += spacing; #if RTC - draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("gjk steps: %F"), FMT_UINT(G.gjk_steps))); + draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("gjk steps: %F"), FMT_UINT(gjk_debug_steps))); pos.y += spacing; #endif