From ac042a33061af4ddc3c3bca7c3bfbcb93eb46741 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 8 Oct 2024 16:01:30 -0500 Subject: [PATCH] rounded collisions working except for clipping --- src/collider.c | 216 ++++++++++++++++++++++++++++--------------------- src/collider.h | 2 +- src/game.c | 29 +++++-- src/user.c | 20 +++-- 4 files changed, 161 insertions(+), 106 deletions(-) diff --git a/src/collider.c b/src/collider.c index 9d709ef7..c80070c1 100644 --- a/src/collider.c +++ b/src/collider.c @@ -4,11 +4,22 @@ #include "scratch.h" #if COLLIDER_DEBUG -//u32 collider_debug_steps = U32_MAX; -u32 collider_debug_steps = 50; +u32 collider_debug_steps = U32_MAX; +//u32 collider_debug_steps = 50; #endif -#define DBGSTEP if (dbg_step++ >= collider_debug_steps) goto abort +INTERNAL void _dbgbreakable(void) +{ + DEBUGBREAKABLE; +} + +#define DBGSTEP \ + dbg_step++; \ + if (dbg_step >= collider_debug_steps) { \ + goto abort; \ + } else if (dbg_step >= collider_debug_steps - 1) { \ + _dbgbreakable(); \ + } struct v2 collider_support_point(struct collider_shape *a, struct xform xf, struct v2 dir) { @@ -16,14 +27,11 @@ struct v2 collider_support_point(struct collider_shape *a, struct xform xf, stru u32 count = a->count; f32 radius = a->radius; - struct xform xf_inv = xform_invert(xf); /* TODO: Calculate this outside of function */ - dir = xform_basis_mul_v2(xf_inv, dir); - /* TODO: Could probably binary search for largest dot since shape is convex */ - struct v2 furthest = points[0]; - f32 furthest_dot = v2_dot(dir, furthest); - for (u32 i = 1; i < count; ++i) { - struct v2 p = points[i]; + struct v2 furthest = ZI; + f32 furthest_dot = -F32_INFINITY; + for (u32 i = 0; i < count; ++i) { + struct v2 p = xform_mul_v2(xf, points[i]); f32 dot = v2_dot(dir, p); if (dot > furthest_dot) { furthest = p; @@ -32,12 +40,10 @@ struct v2 collider_support_point(struct collider_shape *a, struct xform xf, stru } if (radius > 0.0) { - dir = v2_with_len(dir, radius); + dir = v2_mul_v2(v2_with_len(dir, radius), xform_get_scale(xf)); furthest = v2_add(furthest, dir); } - furthest = xform_mul_v2(xf, furthest); - return furthest; } @@ -73,12 +79,13 @@ struct collider_collision_points_result collider_collision_points(struct collide struct v2 *points1 = shape1->points; u32 count0 = shape0->count; u32 count1 = shape1->count; - //f32 radius0 = shape0->radius; - //f32 radius1 = shape1->radius; + f32 radius0 = shape0->radius; + f32 radius1 = shape1->radius; + (UNUSED)radius0; + (UNUSED)radius1; /* TODO: Parameterize */ - const f32 tolerance = 0.0025f; - //const f32 min_unique_pt_dist_sq = 0.001f * 0.001f; + const f32 tolerance = 0.005f; const f32 min_unique_pt_dist_sq = 0.001f * 0.001f; b32 colliding = false; @@ -157,6 +164,7 @@ struct collider_collision_points_result collider_collision_points(struct collide } /* Determine voronoi region of the simplex in which the origin lies */ + DBGSTEP; i32 voronoi_mask = 0; struct v2 vab = v2_sub(s.b, s.a); struct v2 vac = v2_sub(s.c, s.a); @@ -164,9 +172,19 @@ struct collider_collision_points_result collider_collision_points(struct collide struct v2 rab_dir = v2_perp_towards_dir(vab, v2_neg(vac)); struct v2 rac_dir = v2_perp_towards_dir(vac, v2_neg(vab)); struct v2 rbc_dir = v2_perp_towards_dir(vbc, vab); +#if 0 voronoi_mask |= (v2_dot(rab_dir, v2_neg(s.a)) > 0) << 0; /* Regions ab, a, and b*/ voronoi_mask |= (v2_dot(rac_dir, v2_neg(s.a)) > 0) << 1; /* Regions ac, a, and c */ voronoi_mask |= (v2_dot(rbc_dir, v2_neg(s.b)) > 0) << 2; /* Regions bc, b, and c */ +#else + voronoi_mask = (v2_dot(rab_dir, v2_neg(s.a)) > 0) << 0; /* Regions ab, a, and b*/ + if (!voronoi_mask) { + voronoi_mask = (v2_dot(rac_dir, v2_neg(s.a)) > 0) << 1; /* Regions ac, a, and c */ + if (!voronoi_mask) { + voronoi_mask = (v2_dot(rbc_dir, v2_neg(s.b)) > 0) << 2; /* Regions bc, b, and c */ + } + } +#endif /* Remove point or edge and determine next direction based on voronoi region */ switch (voronoi_mask) { case 0: @@ -350,13 +368,14 @@ struct collider_collision_points_result collider_collision_points(struct collide CT_ASSERT(ARRAY_COUNT(shape0->points) <= 16); DBGSTEP; + { + const f32 wedge_epsilon = 0.001f; + #if 0 - { - const f32 wedge_epsilon = 0.001f; - /* shape0 a -> b winding = clockwise */ u32 id_a0; u32 id_b0; + b32 a0_is_deepest; struct v2 a0; struct v2 b0; struct v2 vab0; @@ -364,6 +383,7 @@ struct collider_collision_points_result collider_collision_points(struct collide /* shape1 a -> b winding = counterclockwise */ u32 id_a1; u32 id_b1; + b32 a1_is_deepest; struct v2 a1; struct v2 b1; struct v2 vab1; @@ -386,12 +406,14 @@ struct collider_collision_points_result collider_collision_points(struct collide a0 = a; b0 = p; vab0 = vap; + a0_is_deepest = false; } else { id_a0 = p_i; id_b0 = b_i; a0 = p; b0 = b; vab0 = vpb; + a0_is_deepest = true; } } { @@ -415,93 +437,53 @@ struct collider_collision_points_result collider_collision_points(struct collide a1 = a; b1 = p; vab1 = vap; + a1_is_deepest = false; } else { id_a1 = p_i; id_b1 = b_i; a1 = p; b1 = b; vab1 = vpb; + a1_is_deepest = true; } } - f32 a0t = 0; - f32 a1t = 0; - f32 b0t = 0; - f32 b1t = 0; + if (radius0 > 0.0) { + struct v2 scale = xform_get_scale(xf0); - struct v2 vba0 = v2_neg(vab0); - struct v2 vba1 = v2_neg(vab1); - { - { - struct v2 va0a1 = v2_sub(a1, a0); - struct v2 va1a0 = v2_neg(va0a1); - { - f32 w = v2_wedge(vab0, normal); - if (w != 0) { - w = 1 / w; - a0t = v2_wedge(va0a1, normal) * w; - } - } - { - f32 w = v2_wedge(vab1, normal); - if (w != 0) { - w = 1 / w; - a1t = v2_wedge(va1a0, normal) * w; - } - } - } - { - struct v2 vb0b1 = v2_sub(b1, b0); - struct v2 vb1b0 = v2_neg(vb0b1); - { - f32 w = v2_wedge(vba0, normal); - if (w != 0) { - w = 1 / w; - b0t = v2_wedge(vb0b1, normal) * w; - } - } - { - f32 w = v2_wedge(vba1, normal); - if (w != 0) { - w = 1 / w; - b1t = v2_wedge(vb1b0, normal) * w; - } - } + struct v2 normal_radius = v2_with_len(normal, radius0); + normal_radius = v2_mul_v2(normal_radius, scale); + + struct v2 perp_radius = v2_with_len(v2_neg(v2_perp(v2_sub(b0, a0))), radius0); + perp_radius = v2_mul_v2(perp_radius, scale); + + if (a0_is_deepest) { + a0 = v2_add(a0, normal_radius); + b0 = v2_add(b0, perp_radius); + } else { + a0 = v2_add(a0, perp_radius); + b0 = v2_add(b0, normal_radius); } } - a0t = clamp_f32(a0t, 0, 1); - a1t = clamp_f32(a1t, 0, 1); - b0t = clamp_f32(b0t, 0, 1); - b1t = clamp_f32(b1t, 0, 1); + if (radius1 > 0.0) { + struct v2 scale = xform_get_scale(xf1); - struct v2 a0_clipped = v2_add(a0, v2_mul(vab0, a0t)); - struct v2 a1_clipped = v2_add(a1, v2_mul(vab1, a1t)); - struct v2 b0_clipped = v2_add(b0, v2_mul(vba0, b0t)); - struct v2 b1_clipped = v2_add(b1, v2_mul(vba1, b1t)); + struct v2 normal_radius = v2_with_len(normal, radius1); + normal_radius = v2_mul_v2(normal_radius, scale); - struct v2 va0a1_clipped = v2_sub(a1_clipped, a0_clipped); - struct v2 vb0b1_clipped = v2_sub(b1_clipped, b0_clipped); + struct v2 perp_radius = v2_with_len(v2_neg(v2_perp(v2_sub(b1, a1))), radius1); + perp_radius = v2_mul_v2(perp_radius, scale); - f32 a_sep = v2_dot(va0a1_clipped, normal); - f32 b_sep = v2_dot(vb0b1_clipped, normal); - if (a_sep < tolerance) { - struct collider_collision_point *point = &points[num_points++]; - point->id = id_a0 | (id_a1 << 4); - point->separation = a_sep; - point->point = v2_add(a0_clipped, v2_mul(va0a1_clipped, 0.5f)); + if (a1_is_deepest) { + a1 = v2_sub(a1, normal_radius); + b1 = v2_sub(b1, perp_radius); + } else { + a1 = v2_sub(a1, perp_radius); + b1 = v2_sub(b1, normal_radius); + } } - if (b_sep < tolerance) { - struct collider_collision_point *point = &points[num_points++]; - point->id = id_b0 | (id_b1 << 4); - point->separation = b_sep; - point->point = v2_add(b0_clipped, v2_mul(vb0b1_clipped, 0.5f)); - } - } #else - { - const f32 wedge_epsilon = 0.001f; - /* shape0 a -> b winding = clockwise */ u32 id_a0; u32 id_b0; @@ -572,6 +554,54 @@ struct collider_collision_points_result collider_collision_points(struct collide } } + if (radius0 > 0.0) { + struct v2 scale = xform_get_scale(xf0); + + struct v2 normal_radius = v2_with_len(normal, radius0); + normal_radius = v2_mul_v2(normal_radius, scale); + + struct v2 perp_radius = v2_with_len(v2_neg(v2_perp(v2_sub(b0, a0))), radius0); + perp_radius = v2_mul_v2(perp_radius, scale); + + if (v2_dot(a0, normal) >= v2_dot(b0, normal)) { + a0 = v2_add(a0, normal_radius); + b0 = v2_add(b0, perp_radius); + } else { + a0 = v2_add(a0, perp_radius); + b0 = v2_add(b0, normal_radius); + } + } + + if (radius1 > 0.0) { + struct v2 scale = xform_get_scale(xf1); + + struct v2 normal_radius = v2_with_len(normal, radius1); + normal_radius = v2_mul_v2(normal_radius, scale); + + struct v2 perp_radius = v2_with_len(v2_neg(v2_perp(v2_sub(b1, a1))), radius1); + perp_radius = v2_mul_v2(perp_radius, scale); + + if (v2_dot(a1, normal) <= v2_dot(b1, normal)) { + a1 = v2_sub(a1, normal_radius); + b1 = v2_sub(b1, perp_radius); + } else { + a1 = v2_sub(a1, perp_radius); + b1 = v2_sub(b1, normal_radius); + } + } +#endif + + + + + + + + + + + + f32 a0t = 0; f32 a1t = 0; f32 b0t = 0; @@ -651,7 +681,6 @@ struct collider_collision_points_result collider_collision_points(struct collide res.b0 = b0_clipped; res.b1 = b1_clipped; } -#endif } res.solved = true; @@ -688,12 +717,11 @@ abort: * ========================== */ /* TODO: Remove this (debugging) */ -struct v2_array menkowski(struct arena *arena, struct collider_shape *shape0, struct collider_shape *shape1, struct xform xf0, struct xform xf1) +struct v2_array menkowski(struct arena *arena, struct collider_shape *shape0, struct collider_shape *shape1, struct xform xf0, struct xform xf1, u32 detail) { 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); + for (u64 i = 0; i < detail; ++i) { + f32 angle = ((f32)i / detail) * (2 * PI); struct v2 dir = v2_from_angle(angle); struct v2 p = menkowski_point(shape0, shape1, xf0, xf1, dir); if (res.count == 0 || !v2_eq(p, res.points[res.count - 1])) { diff --git a/src/collider.h b/src/collider.h index 2dbd7680..b00f1b35 100644 --- a/src/collider.h +++ b/src/collider.h @@ -46,7 +46,7 @@ struct collider_collision_points_result { struct collider_collision_points_result collider_collision_points(struct collider_shape *shape0, struct collider_shape *shape1, struct xform xf0, struct xform xf1); -struct v2_array menkowski(struct arena *arena, struct collider_shape *shape0, struct collider_shape *shape1, struct xform xf0, struct xform xf1); +struct v2_array menkowski(struct arena *arena, struct collider_shape *shape0, struct collider_shape *shape1, struct xform xf0, struct xform xf1, u32 detail); struct v2_array cloud(struct arena *arena, struct collider_shape *shape0, struct collider_shape *shape1, struct xform xf0, struct xform xf1); #endif diff --git a/src/game.c b/src/game.c index 8bb9b0e3..26188dc1 100644 --- a/src/game.c +++ b/src/game.c @@ -144,11 +144,12 @@ INTERNAL void spawn_test_entities(f32 offset) //struct v2 size = V2(1, 1); struct v2 size = V2(0.5, 0.5); //f32 r = PI; - f32 r = PI / 4; + //f32 r = PI / 4; //f32 r = PI / 3; //f32 r = 0.05; //f32 r = PI / 2; - //f32 r = 0; + f32 r = 0; + //f32 skew = PI / 4; f32 skew = 0; struct entity *e = entity_alloc(root); @@ -208,7 +209,8 @@ INTERNAL void spawn_test_entities(f32 offset) #if 1 if (!G.first_spawn) { struct v2 pos = V2(0.5, -1); - //struct v2 pos = V2(0.5, 20); + //struct v2 pos = V2(0.5, 29); + //struct v2 pos = V2(0.5, 24); //struct v2 pos = V2(1, -1); struct v2 size = V2(1, 1); //struct v2 size = V2(500, 50); @@ -521,6 +523,8 @@ INTERNAL void warm_start_contacts(void) struct v2 vcp1 = v2_sub(p1, e1_xf.og); struct v2 impulse = v2_add(v2_mul(normal, contact->normal_impulse), v2_mul(tangent, contact->tangent_impulse)); + impulse = v2_mul(impulse, 0.5f); + v0 = v2_sub(v0, v2_mul(impulse, contact->inv_m0)); v1 = v2_add(v1, v2_mul(impulse, contact->inv_m1)); w0 -= v2_wedge(vcp0, impulse) * contact->inv_i0; @@ -571,7 +575,7 @@ INTERNAL struct soft_result make_soft(f32 hertz, f32 zeta, f32 h) INTERNAL void solve_collisions(f32 dt, b32 apply_bias) { -#if 0 +#if 1 struct entity_store *store = G.tick.entity_store; for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { struct entity *manifold = &store->entities[entity_index]; @@ -951,6 +955,19 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), ent->animation_frame); ent->local_collider = collider_from_quad(xform_mul_quad(ent->sprite_local_xform, quad_from_rect(slice.rect))); ent->local_collider.radius = 0.1; + + if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { +#if 0 + ent->local_collider.points[0] = V2(0, 0); + ent->local_collider.count = 1; + ent->local_collider.radius = 0.5; +#else + ent->local_collider.points[0] = V2(0, 0.5); + ent->local_collider.points[1] = V2(0, -0.5); + ent->local_collider.count = 2; + ent->local_collider.radius = 0.25; +#endif + } } } @@ -1187,7 +1204,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) * Create forces from control focus (aim) * ========================== */ -#if 1 +#if 0 for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { struct entity *ent = &store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; @@ -1411,6 +1428,8 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) for (u32 i = 0; i < GAME_PHYSICS_SUBSTEPS; ++i) { #if GAME_PHYSICS_ENABLE_WARM_STARTING warm_start_contacts(); +#else + (UNUSED)warm_start_contacts; #endif solve_collisions(substep_dt, true); integrate_positions_from_velocities(substep_dt); diff --git a/src/user.c b/src/user.c index f2269855..7536f513 100644 --- a/src/user.c +++ b/src/user.c @@ -867,7 +867,7 @@ INTERNAL void user_update(void) #endif /* Draw sprite */ - if (!sprite_tag_is_nil(sprite)) { + if (!sprite_tag_is_nil(sprite) && ((false))) { /* Calculate sprite xform */ sprite_xform = xform_mul(xf, ent->sprite_local_xform); @@ -993,14 +993,16 @@ INTERNAL void user_update(void) /* Draw collider */ if (entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) { struct collider_shape collider = ent->local_collider; - u32 color = ent->colliding ? RGBA_32_F(1, 1, 1, 1) : RGBA_32_F(1, 1, 1, 0.25); - f32 thickness = 2; - u32 detail = 32; + u32 color = ent->colliding ? RGBA_32_F(1, 1, 1, 0.5) : RGBA_32_F(1, 1, 0, 0.25); + f32 thickness = 1; + //u32 detail = 32; + u32 detail = 256; //draw_solid_collider_line(G.viewport_canvas, collider, xform_mul(G.world_view, xf), thickness, color, detail); draw_solid_collider_line(G.viewport_canvas, G.world_view, collider, xf, thickness, color, detail); } /* Draw collision */ +#if 1 if (entity_has_prop(ent, ENTITY_PROP_MANIFOLD)) { struct entity *e0 = entity_from_handle(store, ent->manifold_e0); struct entity *e1 = entity_from_handle(store, ent->manifold_e1); @@ -1011,6 +1013,8 @@ INTERNAL void user_update(void) struct collider_shape e0_collider = e0->local_collider; struct collider_shape e1_collider = e1->local_collider; + (UNUSED)e0_collider; + (UNUSED)e1_collider; #if 1 /* Draw menkowski */ @@ -1018,9 +1022,10 @@ INTERNAL void user_update(void) u32 color = ent->res.solved ? RGBA_32_F(0, 0, 0.25, 1) : RGBA_32_F(0, 0.25, 0.25, 1); f32 thickness = 2; + u32 detail = 512; (UNUSED)thickness; - struct v2_array m = menkowski(temp.arena, &e0_collider, &e1_collider, e0_xf, e1_xf); + struct v2_array m = menkowski(temp.arena, &e0_collider, &e1_collider, e0_xf, e1_xf, detail); //struct v2_array m = menkowski(temp.arena, e0_collider, e1_collider, v2_sub(ent->xf1.og, ent->xf0.og)); for (u64 i = 0; i < m.count; ++i) m.points[i] = xform_mul_v2(G.world_view, m.points[i]); @@ -1125,7 +1130,7 @@ INTERNAL void user_update(void) struct v2 end = xform_mul_v2(G.world_view, v2_add(point, v2_mul(v2_norm(ent->manifold_normal), len))); draw_solid_arrow_line(G.viewport_canvas, start, end, arrow_thickness, arrow_height, color); } -#if 0 +#if 1 /* Draw info text */ { struct font *disp_font = font_load_async(STR("res/fonts/fixedsys.ttf"), 12.0f); @@ -1158,6 +1163,7 @@ INTERNAL void user_update(void) } } +#if 1 /* Draw clipping */ { f32 thickness = 2; @@ -1173,8 +1179,10 @@ INTERNAL void user_update(void) draw_solid_line(G.viewport_canvas, start, end, thickness, color); } } +#endif #endif } +#endif /* Draw hierarchy */ if (entity_has_prop(parent, ENTITY_PROP_ACTIVE) && !parent->is_root) {