re-add/enable gjk debug visualization

This commit is contained in:
jacob 2024-09-04 10:26:04 -05:00
parent 8c36552659
commit f608000b85
6 changed files with 117 additions and 96 deletions

View File

@ -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;

View File

@ -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

View File

@ -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
};

104
src/gjk.c
View File

@ -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;
}

View File

@ -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

View File

@ -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