start on scuffed gjk collision detection
This commit is contained in:
parent
17f7e3daaa
commit
86f1f1f6e0
@ -595,7 +595,10 @@ struct clip_rect {
|
||||
#define QUAD_UNIT_SQUARE (struct quad) { V2(0, 0), V2(0, 1), V2(1, 1), V2(1, 0) }
|
||||
#define QUAD_UNIT_SQUARE_CENTERED (struct quad) { V2(-0.5f, -0.5f), V2(0.5f, -0.5f), V2(0.5f, 0.5f), V2(-0.5f, 0.5f) }
|
||||
struct quad {
|
||||
struct v2 p1, p2, p3, p4;
|
||||
union {
|
||||
struct { struct v2 p1, p2, p3, p4; };
|
||||
struct { struct v2 e[4]; };
|
||||
};
|
||||
};
|
||||
|
||||
/* (T)ranslation, (R)otation, (S)cale */
|
||||
|
||||
@ -246,7 +246,7 @@ void draw_solid_arrow_line(struct renderer_canvas *canvas, struct v2 start, stru
|
||||
|
||||
struct v2 head_start = v2_add(end, head_start_dir);
|
||||
|
||||
struct v2 head_p1_dir = v2_mul(v2_perp(head_start_dir), head_width_ratio);
|
||||
struct v2 head_p1_dir = v2_mul(v2_perp_cw(head_start_dir), head_width_ratio);
|
||||
struct v2 head_p2_dir = v2_neg(head_p1_dir);
|
||||
|
||||
struct v2 head_p1 = v2_add(head_start, head_p1_dir);
|
||||
|
||||
21
src/entity.h
21
src/entity.h
@ -73,6 +73,27 @@ struct entity {
|
||||
struct entity_handle first;
|
||||
struct entity_handle last;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* TODO: Remove this (testing) */
|
||||
b32 colliding;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Activation */
|
||||
|
||||
|
||||
165
src/game.c
165
src/game.c
@ -177,6 +177,10 @@ INTERNAL void spawn_test_entities(void)
|
||||
|
||||
e->sprite = sprite_tag_from_path(STR("res/graphics/box.ase"));
|
||||
|
||||
entity_enable_prop(e, ENTITY_PROP_PHYSICAL);
|
||||
e->mass_unscaled = 70;
|
||||
e->ground_friction = 1000;
|
||||
|
||||
entity_set_xform(e, XFORM_TRS(.t = pos, .s = size, .r = rot));
|
||||
}
|
||||
|
||||
@ -195,6 +199,117 @@ INTERNAL void spawn_test_entities(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Collision test
|
||||
* ========================== */
|
||||
|
||||
INTERNAL struct v2 quad_furthest_point(struct quad quad, struct v2 dir)
|
||||
{
|
||||
struct v2 furthest = quad.e[0];
|
||||
f32 furthest_dot = v2_dot(dir, furthest);
|
||||
for (u32 i = 1; i < ARRAY_COUNT(quad.e); ++i) {
|
||||
struct v2 p = quad.e[i];
|
||||
f32 dot = v2_dot(dir, p);
|
||||
if (dot > furthest_dot) {
|
||||
furthest = p;
|
||||
furthest_dot = dot;
|
||||
}
|
||||
}
|
||||
return furthest;
|
||||
}
|
||||
|
||||
INTERNAL struct v2 quad_support_point(struct quad q0, struct quad q1, struct v2 dir)
|
||||
{
|
||||
return v2_sub(quad_furthest_point(q0, dir), quad_furthest_point(q1, v2_neg(dir)));
|
||||
}
|
||||
|
||||
#if 0
|
||||
INTERNAL struct v2 normal_towards_origin(struct v2 p0, struct v2 p1)
|
||||
{
|
||||
struct v2 dir = v2_norm(v2_perp_cw(v2_sub(p1, p0)));
|
||||
#if 1
|
||||
if (v2_dot(dir, p0) > 0) {
|
||||
dir = v2_neg(dir);
|
||||
}
|
||||
#else
|
||||
dir = v2_mul(dir, 1 - ((v2_dot(dir, p0) > 0) << 1));
|
||||
#endif
|
||||
return dir;
|
||||
}
|
||||
#else
|
||||
INTERNAL struct v2 normal_towards_point(struct v2 start, struct v2 end, struct v2 p)
|
||||
{
|
||||
struct v2 dir = v2_norm(v2_perp_cw(v2_sub(end, start)));
|
||||
#if 1
|
||||
if (v2_dot(dir, v2_sub(p, start)) < 0) {
|
||||
dir = v2_neg(dir);
|
||||
}
|
||||
#else
|
||||
dir = v2_mul(dir, 1 - ((v2_dot(dir, v2_sub(p, start)) > 0) << 1));
|
||||
#endif
|
||||
return dir;
|
||||
}
|
||||
#endif
|
||||
|
||||
INTERNAL b32 quad_collision_test(struct quad q0, struct quad q1)
|
||||
{
|
||||
struct v2 q0_center = v2_div(v2_add(q0.p4, v2_add(q0.p3, v2_add(q0.p2, q0.p1))), 4);
|
||||
struct v2 q1_center = v2_div(v2_add(q1.p4, v2_add(q1.p3, v2_add(q1.p2, q1.p1))), 4);
|
||||
|
||||
/* Simplex */
|
||||
struct v2 s[3] = { 0 };
|
||||
u32 s_len = 0;
|
||||
|
||||
/* Append first point to simplex */
|
||||
struct v2 dir = v2_norm(v2_sub(q1_center, q0_center));
|
||||
s[0] = quad_support_point(q0, q1, dir);
|
||||
s_len = 1;
|
||||
|
||||
dir = v2_norm(v2_neg(s[0])); /* Next point is towards origin */
|
||||
while (true) {
|
||||
/* Determine support point */
|
||||
struct v2 p = quad_support_point(q0, q1, dir);
|
||||
if (v2_dot(dir, p) < 0) {
|
||||
/* Point did not cross origin */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s_len == 1) {
|
||||
s[1] = p;
|
||||
++s_len;
|
||||
} else if (s_len == 2) {
|
||||
/* Line case */
|
||||
/* Next dir is line normal towards origin */
|
||||
s[2] = p;
|
||||
++s_len;
|
||||
dir = normal_towards_point(s[0], s[1], V2(0, 0));
|
||||
} else {
|
||||
s[0] = s[1];
|
||||
s[1] = s[2];
|
||||
s[2] = p;
|
||||
|
||||
/* Triangle case */
|
||||
struct v2 a = s[2];
|
||||
struct v2 b = s[1];
|
||||
struct v2 c = s[0];
|
||||
|
||||
dir = v2_neg(normal_towards_point(a, b, c)); /* Normal dir of ab pointing away from c */
|
||||
if (v2_dot(dir, v2_neg(a)) >= 0) {
|
||||
/* Point is in region ab, remove c from simplex */
|
||||
} else {
|
||||
dir = v2_neg(normal_towards_point(a, c, b)); /* Normal dir of ac pointing away from b */
|
||||
if (v2_dot(dir, v2_neg(a)) >= 0) {
|
||||
/* Point is in region ac, remove b from simplex */
|
||||
s[1] = s[0];
|
||||
} else {
|
||||
/* Point must be in simplex */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Update
|
||||
* ========================== */
|
||||
@ -576,7 +691,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
activate_now(force);
|
||||
} else {
|
||||
/* If velocity is below clamp_epsilon, stop entity movement. */
|
||||
f32 mass = ent->mass_unscaled * xform_get_determinant(xf);
|
||||
f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
|
||||
struct entity *impulse = entity_alloc(ent);
|
||||
entity_enable_prop(impulse, ENTITY_PROP_IMPULSE);
|
||||
impulse->force = v2_mul(v2_neg(velocity), mass);
|
||||
@ -587,16 +702,16 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Simulate physics
|
||||
* Integrate forces & impulses
|
||||
* ========================== */
|
||||
|
||||
/* TODO: Run physics on top-level entities, and then calculate child xforms as part of physics step (rather than caching at each xform_get). */
|
||||
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;
|
||||
if (entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) {
|
||||
if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL)) continue;
|
||||
|
||||
struct xform xf = entity_get_xform(ent);
|
||||
f32 mass = ent->mass_unscaled * xform_get_determinant(xf);
|
||||
f32 mass = ent->mass_unscaled * math_fabs(xform_get_determinant(xf));
|
||||
|
||||
/* Determine acceleration from forces and impulses */
|
||||
struct v2 acceleration = V2(0, 0);
|
||||
@ -617,6 +732,46 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
xf.og = v2_add(xf.og, v2_add(tick_velocity, v2_mul(acceleration, dt)));
|
||||
entity_set_xform(ent, xf);
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Collision
|
||||
* ========================== */
|
||||
|
||||
for (u64 e0_index = 0; e0_index < store->reserved; ++e0_index) {
|
||||
struct entity *e0 = &store->entities[e0_index];
|
||||
if (!(e0->valid && entity_has_prop(e0, ENTITY_PROP_ACTIVE))) continue;
|
||||
if (!entity_has_prop(e0, ENTITY_PROP_PHYSICAL)) continue;
|
||||
|
||||
struct xform e0_xf = entity_get_xform(e0);
|
||||
struct quad e0_quad;
|
||||
{
|
||||
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, e0->sprite);
|
||||
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), e0->animation_frame);
|
||||
e0_quad = xform_mul_quad(e0_xf, quad_from_rect(slice.rect));
|
||||
}
|
||||
|
||||
b32 colliding = false;
|
||||
for (u64 e1_index = 0; e1_index < store->reserved; ++e1_index) {
|
||||
struct entity *e1 = &store->entities[e1_index];
|
||||
if (e1 == e0) continue;
|
||||
if (!(e1->valid && entity_has_prop(e1, ENTITY_PROP_ACTIVE))) continue;
|
||||
if (!entity_has_prop(e1, ENTITY_PROP_PHYSICAL)) continue;
|
||||
|
||||
struct xform e1_xf = entity_get_xform(e1);
|
||||
struct quad e1_quad;
|
||||
{
|
||||
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, e1->sprite);
|
||||
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("shape"), e1->animation_frame);
|
||||
e1_quad = xform_mul_quad(e1_xf, quad_from_rect(slice.rect));
|
||||
}
|
||||
|
||||
if (quad_collision_test(e0_quad, e1_quad)) {
|
||||
colliding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
e0->colliding = colliding;
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
|
||||
14
src/math.h
14
src/math.h
@ -543,11 +543,18 @@ INLINE f32 v2_len_squared(struct v2 a)
|
||||
return a.x * a.x + a.y * a.y;
|
||||
}
|
||||
|
||||
INLINE struct v2 v2_perp(struct v2 a)
|
||||
/* Clockwise perpendicular vector */
|
||||
INLINE struct v2 v2_perp_cw(struct v2 a)
|
||||
{
|
||||
return V2(-a.y, a.x);
|
||||
}
|
||||
|
||||
/* Counterclockwise perpendicular vector */
|
||||
INLINE struct v2 v2_perp_ccw(struct v2 a)
|
||||
{
|
||||
return V2(a.y, -a.x);
|
||||
}
|
||||
|
||||
INLINE struct v2 v2_norm(struct v2 a)
|
||||
{
|
||||
f32 l = v2_len_squared(a);
|
||||
@ -1008,9 +1015,8 @@ INLINE struct quad quad_from_line(struct v2 start, struct v2 end, f32 thickness)
|
||||
{
|
||||
f32 width = thickness / 2.f;
|
||||
|
||||
struct v2 rel = v2_sub(end, start);
|
||||
struct v2 dir = v2_norm(rel);
|
||||
struct v2 dir_perp = v2_perp(dir);
|
||||
struct v2 dir = v2_norm(v2_sub(end, start));
|
||||
struct v2 dir_perp = v2_perp_cw(dir);
|
||||
|
||||
struct v2 left = v2_mul(dir_perp, -width);
|
||||
struct v2 right = v2_mul(dir_perp, width);
|
||||
|
||||
14
src/user.c
14
src/user.c
@ -912,6 +912,14 @@ INTERNAL void user_update(void)
|
||||
if (!sprite_tag_is_nil(ent->sprite)) {
|
||||
struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, sprite);
|
||||
|
||||
u32 quad_color = RGBA_32_F(1, 0, 0.5, 1);
|
||||
u32 point_color = RGBA_32_F(1, 0, 0, 1);
|
||||
u32 ray_color = RGBA_32_F(1, 0, 0.5, 1);
|
||||
|
||||
if (ent->colliding) {
|
||||
quad_color = RGBA_32_F(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
for (u64 i = 0; i < sheet->slice_groups_count; ++i) {
|
||||
struct sprite_sheet_slice_group *group = &sheet->slice_groups[i];
|
||||
if (string_ends_with(group->name, STR(".ray"))) continue;
|
||||
@ -926,16 +934,16 @@ INTERNAL void user_update(void)
|
||||
struct quad quad = quad_from_rect(slice.rect);
|
||||
quad = xform_mul_quad(sprite_xform, quad);
|
||||
quad = xform_mul_quad(G.world_view, quad);
|
||||
draw_solid_quad_line(G.viewport_canvas, quad, 2, RGBA_32_F(1, 0, 0.5, 1));
|
||||
draw_solid_quad_line(G.viewport_canvas, quad, 2, quad_color);
|
||||
}
|
||||
|
||||
draw_solid_circle(G.viewport_canvas, center, 3, RGBA_32_F(1, 0, 0, 1), 20);
|
||||
draw_solid_circle(G.viewport_canvas, center, 3, point_color, 20);
|
||||
|
||||
if (slice.has_ray) {
|
||||
struct v2 ray = xform_basis_mul_v2(sprite_xform, slice.dir);
|
||||
ray = xform_basis_mul_v2(G.world_view, ray);
|
||||
ray = v2_mul(v2_norm(ray), 25);
|
||||
draw_solid_arrow_ray(G.viewport_canvas, center, ray, 2, 10, RGBA_32_F(1, 0, 0.5, 1));
|
||||
draw_solid_arrow_ray(G.viewport_canvas, center, ray, 2, 10, ray_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user