import old collision detection code

This commit is contained in:
jacob 2026-01-04 04:11:02 -06:00
parent 235cf72018
commit 54f77c87be
5 changed files with 767 additions and 67 deletions

View File

@ -108,9 +108,9 @@ Struct(CLD_GjkData)
CLD_MenkowskiSimplex simplex;
Vec2 final_dir;
// If 1, simplex represents triangle inside of CLD_Menkowski difference
// If 1, simplex represents triangle inside of menkowski difference
// encapsulating the origin. If 0, simplex represents the closest
// feature on CLD_Menkowski difference to the origin.
// feature on menkowski difference to the origin.
b32 overlapping;
#if COLLIDER_DEBUG
@ -124,7 +124,7 @@ Struct(CLD_GjkData)
Struct(CLD_EpaData)
{
Vec2 normal;
CLD_MenkowskiFeature closest_feature; // Represents closest feature (edge or point) to origin on CLD_Menkowski difference
CLD_MenkowskiFeature closest_feature; // Represents closest feature (edge or point) to origin on menkowski difference
#if COLLIDER_DEBUG
CLD_Prototype prototype;

View File

@ -947,7 +947,7 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
d3d_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
d3d_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
d3d_desc.Format = DXGI_FORMAT_UNKNOWN;
d3d_desc.Width = AlignU64(MaxU64(desc.buffer.size, min_buffer_size), 4);
d3d_desc.Width = AlignU64ToNextPow2(MaxU64(desc.buffer.size, min_buffer_size));
d3d_desc.Height = 1;
d3d_desc.DepthOrArraySize = 1;
d3d_desc.MipLevels = 1;

View File

@ -153,32 +153,12 @@ S_Shape S_MulXformShape(Xform xf, S_Shape shape)
return result;
}
Vec2 S_SupportPointFromShape(S_Shape shape, Vec2 dir)
{
// FIXME: Properly handle rounded polygons
Vec2 result = Zi;
Vec2 dir_norm = NormVec2(dir);
f32 max_dot = -Inf;
for (i32 i = 0; i < shape.points_count; ++i)
{
Vec2 p = shape.points[i];
f32 dot = DotVec2(p, dir_norm);
if (dot > max_dot)
{
max_dot = dot;
result = p;
}
}
result = AddVec2(result, MulVec2(dir_norm, shape.radius));
return result;
}
Rng2 S_BoundingBoxFromShape(S_Shape shape)
{
Vec2 left = S_SupportPointFromShape(shape, VEC2(-1, 0));
Vec2 top = S_SupportPointFromShape(shape, VEC2(0, -1));
Vec2 right = S_SupportPointFromShape(shape, VEC2(1, 0));
Vec2 bottom = S_SupportPointFromShape(shape, VEC2(0, 1));
Vec2 left = S_SupportPointFromShape(shape, VEC2(-1, 0)).p;
Vec2 top = S_SupportPointFromShape(shape, VEC2(0, -1)).p;
Vec2 right = S_SupportPointFromShape(shape, VEC2(1, 0)).p;
Vec2 bottom = S_SupportPointFromShape(shape, VEC2(0, 1)).p;
Rng2 result = Zi;
result.p0 = VEC2(left.x, top.y);
@ -186,6 +166,673 @@ Rng2 S_BoundingBoxFromShape(S_Shape shape)
return result;
}
////////////////////////////////////////////////////////////
//~ Collision
// NOTE: Everything here is pretty much copied directly from the old physics
// prototype. It's slow and does more than what we need. For example we should
// probably just switch from GJK to SAT for shape collision testing.
S_SupportPoint S_SupportPointFromShapeEx(S_Shape shape, Vec2 dir, i32 ignore_idx)
{
S_SupportPoint result = Zi;
Vec2 dir_norm = NormVec2(dir);
f32 max_dot = -Inf;
if (shape.points_count == 1)
{
// Don't ignore for single-point colliders
ignore_idx = -1;
}
for (i32 point_idx = 0; point_idx < shape.points_count; ++point_idx)
{
if (point_idx != ignore_idx)
{
Vec2 p = shape.points[point_idx];
f32 dot = DotVec2(p, dir_norm);
if (dot > max_dot)
{
max_dot = dot;
result.p = p;
result.id = point_idx;
}
}
}
result.p = AddVec2(result.p, MulVec2(dir_norm, shape.radius));
return result;
}
S_SupportPoint S_SupportPointFromShape(S_Shape shape, Vec2 dir)
{
return S_SupportPointFromShapeEx(shape, dir, -1);
}
S_MenkowskiPoint S_MenkowskiPointFromShapes(S_Shape shape0, S_Shape shape1, Vec2 dir)
{
S_MenkowskiPoint result = Zi;
result.s0 = S_SupportPointFromShape(shape0, dir);
result.s1 = S_SupportPointFromShape(shape1, NegVec2(dir));
result.p = SubVec2(result.s0.p, result.s1.p);
return result;
}
S_ClippedLine S_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal)
{
Vec2 vab0 = SubVec2(b0, a0);
Vec2 vab1 = SubVec2(b1, a1);
Vec2 va0a1 = SubVec2(a1, a0);
Vec2 vb0b1 = SubVec2(b1, b0);
f32 vab0_w = WedgeVec2(vab0, normal);
f32 vab1_w = WedgeVec2(vab1, normal);
f32 va0a1_w = WedgeVec2(va0a1, normal);
f32 vb0b1_w = WedgeVec2(vb0b1, normal);
// FIXME: Handle 0 denominator
f32 a0t;
f32 b0t;
{
f32 w = 1 / vab0_w;
a0t = ClampF32(va0a1_w * w, 0, 1);
b0t = ClampF32(vb0b1_w * -w, 0, 1);
}
f32 a1t;
f32 b1t;
{
f32 w = 1 / vab1_w;
a1t = ClampF32(-va0a1_w * w, 0, 1);
b1t = ClampF32(-vb0b1_w * -w, 0, 1);
}
S_ClippedLine result = Zi;
result.a0_clipped = AddVec2(a0, MulVec2(vab0, a0t));
result.a1_clipped = AddVec2(a1, MulVec2(vab1, a1t));
result.b0_clipped = AddVec2(b0, MulVec2(vab0, -b0t));
result.b1_clipped = AddVec2(b1, MulVec2(vab1, -b1t));
return result;
}
Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal)
{
Vec2 vab = SubVec2(b, a);
Vec2 vap = SubVec2(p, a);
f32 vab_w = WedgeVec2(vab, normal);
f32 vap_w = WedgeVec2(vap, normal);
f32 t;
{
f32 w = 1 / vab_w;
t = ClampF32(vap_w * w, 0, 1);
}
Vec2 result = AddVec2(a, MulVec2(vab, t));
return result;
}
S_CollisionData S_CollisionDataFromShapes(S_Shape shape0, S_Shape shape1)
{
S_CollisionData result = Zi;
TempArena scratch = BeginScratchNoConflict();
f32 tolerance = 0.005f; // How close can non-overlapping shapes be before collision is considered
f32 min_unique_pt_dist_sq = (0.001f * 0.001f); // NOTE: Should always be less than tolerance, since colliding = 1 if origin is within this distance.
u32 max_iterations = 64; // To prevent extremely large prototypes when origin is in exact center of rounded feature
//////////////////////////////
//- GJK
S_MenkowskiSimplex simplex = Zi;
Vec2 non_overlapping_dir = Zi;
b32 is_overlapping = 0;
{
S_MenkowskiPoint m = Zi;
// First point is support point in shape's general directions to eachother
Vec2 dir = SubVec2(shape1.centroid, shape0.centroid);
if (IsVec2Zero(dir)) dir = VEC2(1, 0);
simplex.a = S_MenkowskiPointFromShapes(shape0, shape1, dir);
simplex.count = 1;
Vec2 removed_a = Zi;
Vec2 removed_b = Zi;
u32 num_removed = 0;
for (;;)
{
//////////////////////////////
//- Find initial points in simplex
if (simplex.count == 1)
{
// Second point is support point towards origin
dir = NegVec2(simplex.a.p);
m = S_MenkowskiPointFromShapes(shape0, shape1, dir);
// Check that new point is far enough away from existing point
if (Vec2LenSq(SubVec2(m.p, simplex.a.p)) < min_unique_pt_dist_sq)
{
is_overlapping = 0;
break;
}
simplex.b = simplex.a;
simplex.a = m;
simplex.count = 2;
// Third point is support point in direction of line normal towards origin
dir = PerpVec2TowardsDir(SubVec2(simplex.b.p, simplex.a.p), NegVec2(simplex.a.p));
}
//////////////////////////////
//- Find third piont in simplex
{
m = S_MenkowskiPointFromShapes(shape0, shape1, dir);
// Check that new point is far enough away from existing points
if (
Vec2LenSq(SubVec2(m.p, simplex.a.p)) < min_unique_pt_dist_sq ||
Vec2LenSq(SubVec2(m.p, simplex.b.p)) < min_unique_pt_dist_sq || (
(num_removed >= 1) && (
(Vec2LenSq(SubVec2(m.p, removed_a)) < min_unique_pt_dist_sq) ||
(num_removed >= 2 && Vec2LenSq(SubVec2(m.p, removed_b)) < min_unique_pt_dist_sq)
)
) ||
AbsF32(WedgeVec2(SubVec2(simplex.b.p, simplex.a.p), SubVec2(m.p, simplex.a.p))) < min_unique_pt_dist_sq
)
{
is_overlapping = 0;
break;
}
simplex.c = simplex.b;
simplex.b = simplex.a;
simplex.a = m;
simplex.count = 3;
if (
(AbsF32(WedgeVec2(SubVec2(simplex.b.p, simplex.a.p), NegVec2(simplex.a.p))) <= min_unique_pt_dist_sq) ||
(AbsF32(WedgeVec2(SubVec2(simplex.c.p, simplex.b.p), NegVec2(simplex.b.p))) <= min_unique_pt_dist_sq) ||
(AbsF32(WedgeVec2(SubVec2(simplex.c.p, simplex.a.p), NegVec2(simplex.a.p))) <= min_unique_pt_dist_sq)
)
{
// Simplex lies on origin
is_overlapping = 1;
break;
}
}
//////////////////////////////
//- Determine origin region
Vec2 vab = SubVec2(simplex.b.p, simplex.a.p);
Vec2 vac = SubVec2(simplex.c.p, simplex.a.p);
Vec2 vbc = SubVec2(simplex.c.p, simplex.b.p);
Vec2 rab_dir = PerpVec2TowardsDir(vab, NegVec2(vac));
Vec2 rac_dir = PerpVec2TowardsDir(vac, NegVec2(vab));
Vec2 rbc_dir = PerpVec2TowardsDir(vbc, vab);
f32 rab_dot = DotVec2(rab_dir, NegVec2(simplex.a.p));
f32 rac_dot = DotVec2(rac_dir, NegVec2(simplex.a.p));
f32 rbc_dot = DotVec2(rbc_dir, NegVec2(simplex.b.p));
f32 vab_dot = DotVec2(vab, NegVec2(simplex.a.p)) / Vec2LenSq(vab);
f32 vac_dot = DotVec2(vac, NegVec2(simplex.a.p)) / Vec2LenSq(vac);
f32 vbc_dot = DotVec2(vbc, NegVec2(simplex.b.p)) / Vec2LenSq(vbc);
if (rab_dot >= 0 && vab_dot >= 0 && vab_dot <= 1)
{
// Region ab, remove c
num_removed = 1;
removed_a = simplex.c.p;
simplex.count = 2;
dir = rab_dir; // Next third point is in direction of region ab
}
else if (rac_dot >= 0 && vac_dot >= 0 && vac_dot <= 1)
{
// Region ac, remove b
num_removed = 1;
removed_a = simplex.b.p;
simplex.count = 2;
simplex.b = simplex.c;
dir = rac_dir; // Next third point is in direction of region ac
}
else if (rbc_dot >= 0 && vbc_dot >= 0 && vbc_dot <= 1)
{
// Region bc, remove a
num_removed = 1;
removed_a = simplex.a.p;
simplex.count = 2;
simplex.a = simplex.b;
simplex.b = simplex.c;
dir = rbc_dir; // Next third point is in direction of region bc
}
else if (vab_dot <= 0 && vac_dot <= 0)
{
// Region a, remove bc
num_removed = 2;
removed_a = simplex.b.p;
removed_b = simplex.c.p;
simplex.count = 1;
}
else if (vab_dot >= 1 && vbc_dot <= 0)
{
// Region b, remove ac
num_removed = 2;
removed_a = simplex.a.p;
removed_b = simplex.c.p;
simplex.count = 1;
simplex.a = simplex.b;
}
else if (vac_dot >= 1 && vbc_dot >= 1)
{
// Region c, remove ab
num_removed = 2;
removed_a = simplex.a.p;
removed_b = simplex.b.p;
simplex.count = 1;
simplex.a = simplex.c;
}
else
{
// No region, must be in simplex
is_overlapping = 1;
break;
}
}
if (!is_overlapping)
{
non_overlapping_dir = dir;
}
}
//////////////////////////////
//- EPA
Vec2 normal = Zi;
S_MenkowskiSimplex closest_feature = Zi;
{
S_MenkowskiPoint *proto = 0;
u32 proto_count = 0;
if (is_overlapping)
{
proto = ArenaNext(scratch.arena, S_MenkowskiPoint);
{
Assert(simplex.count == 3);
S_MenkowskiPoint *tmp = PushStructsNoZero(scratch.arena, S_MenkowskiPoint, 3);
tmp[0] = simplex.a;
tmp[1] = simplex.b;
tmp[2] = simplex.c;
proto_count = 3;
}
i32 winding = WindingFromVec2(SubVec2(simplex.c.p, simplex.a.p), SubVec2(simplex.b.p, simplex.a.p));
u32 epa_iterations = 0;
for (;;)
{
++epa_iterations;
// Find dir from origin to closest edge
// FIXME: Winding order of ps & pe index
f32 closest_len_sq = Inf;
S_MenkowskiPoint closest_a = Zi;
S_MenkowskiPoint closest_b = Zi;
u32 closest_b_index = 0;
for (u32 i = 0; i < proto_count; ++i)
{
u32 a_index = i;
u32 b_index = (i < proto_count - 1) ? (i + 1) : 0;
S_MenkowskiPoint a = proto[a_index];
S_MenkowskiPoint b = proto[b_index];
Vec2 vab = SubVec2(b.p, a.p);
Vec2 vao = NegVec2(a.p);
f32 proj_ratio = ClampF32(DotVec2(vao, vab) / Vec2LenSq(vab), 0, 1);
Vec2 proj = AddVec2(a.p, MulVec2(vab, proj_ratio));
f32 proj_len_sq = Vec2LenSq(proj);
if (proj_len_sq < closest_len_sq - min_unique_pt_dist_sq)
{
closest_a = a;
closest_b = b;
closest_b_index = b_index;
closest_len_sq = proj_len_sq;
}
}
Vec2 vab = SubVec2(closest_b.p, closest_a.p);
// Find new point in dir
Vec2 dir = MulVec2(PerpVec2(vab), winding);
S_MenkowskiPoint m = S_MenkowskiPointFromShapes(shape0, shape1, dir);
// if (debug)
// {
// // If debug step count is reached, we still want to inspect the normal at the step
// normal = NormVec2(dir);
// closest_feature.a = closest_a;
// closest_feature.b = closest_b;
// closest_feature.count = 2;
// }
// Check validity of new point
{
b32 valid = 1;
{
// NOTE: Changing this value affects how stable normals are for circular colliders
//const f32 validity_epsilon = min_unique_pt_dist_sq; // Arbitrary
//const f32 validity_epsilon = 0.00000000001f; // Arbitrary
const f32 validity_epsilon = min_unique_pt_dist_sq; // Arbitrary
Vec2 vam = SubVec2(m.p, closest_a.p);
Vec2 vbm = SubVec2(closest_b.p, closest_a.p);
f32 dot = DotVec2(vab, vam) / Vec2LenSq(vab);
if (dot >= -validity_epsilon && dot <= 1 - validity_epsilon && (WedgeVec2(vab, vam) * -winding) >= -validity_epsilon)
{
// New point is not between edge
valid = 0;
}
else if (Vec2LenSq(vam) < min_unique_pt_dist_sq || Vec2LenSq(vbm) < min_unique_pt_dist_sq)
{
// New point is too close to existing
valid = 0;
}
}
if (!valid || epa_iterations >= max_iterations)
{
normal = NormVec2(dir);
closest_feature.a = closest_a;
closest_feature.b = closest_b;
closest_feature.count = 2;
break;
}
}
// Expand prototype
PushStructNoZero(scratch.arena, S_MenkowskiPoint);
++proto_count;
// Shift points in prototype to make room
for (u32 i = proto_count - 1; i > closest_b_index; --i)
{
u32 shift_from = (i > 0) ? i - 1 : proto_count - 1;
u32 shift_to = i;
proto[shift_to] = proto[shift_from];
}
// Insert new point into prototype
proto[closest_b_index] = m;
}
}
else
{
normal = NormVec2(non_overlapping_dir);
closest_feature.count = simplex.count;
closest_feature.a = simplex.a;
closest_feature.b = simplex.b;
}
}
//////////////////////////////
//- Determine collision
b32 is_colliding = 0;
{
if (is_overlapping)
{
is_colliding = 1;
}
else
{
// Shapes not overlapping, determine if distance between shapes within tolerance
if (closest_feature.count == 1)
{
Vec2 p = NegVec2(closest_feature.a.p);
if (Vec2LenSq(p) <= (tolerance * tolerance))
{
is_colliding = 1;
}
}
else
{
// Project origin to determine if distance is within tolerance.
Assert(closest_feature.count == 2);
Vec2 vab = SubVec2(closest_feature.b.p, closest_feature.a.p);
Vec2 vao = NegVec2(closest_feature.a.p);
f32 ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1);
Vec2 p = AddVec2(closest_feature.a.p, MulVec2(vab, ratio));
if (Vec2LenSq(p) <= (tolerance * tolerance))
{
is_colliding = 1;
}
}
}
}
//////////////////////////////
//- Compute collision points
// Clip to determine final points
i32 collision_points_count = 0;
S_CollisionPoint collision_points[2] = Zi;
if (is_colliding)
{
// Max vertices must be < 16 to fit in 4 bit ids
StaticAssert(countof(shape0.points) <= 16);
{
b32 collapse0 = 0;
b32 collapse1 = 0;
S_SupportPoint a0 = closest_feature.a.s0;
S_SupportPoint a1 = closest_feature.a.s1;
S_SupportPoint b0 = closest_feature.b.s0;
S_SupportPoint b1 = closest_feature.b.s1;
// FIXME: Manually account for shapes w/ 1 & 2 points
if (closest_feature.count == 2)
{
if (a0.id == b0.id)
{
if (shape0.points_count > 1)
{
b0 = S_SupportPointFromShapeEx(shape0, normal, b0.id);
}
else
{
collapse0 = 1;
b0 = a0;
}
}
if (a1.id == b1.id)
{
if (shape1.points_count > 1)
{
b1 = S_SupportPointFromShapeEx(shape1, NegVec2(normal), b1.id);
}
else
{
collapse1 = 1;
b1 = a1;
}
}
}
else
{
collapse0 = 1;
collapse1 = 1;
b0 = a0;
b1 = a1;
}
Vec2 vab0 = SubVec2(b0.p, a0.p);
Vec2 vab1 = SubVec2(b1.p, a1.p);
Vec2 vab0_norm = NormVec2(vab0);
Vec2 vab1_norm = NormVec2(vab1);
// Swap points based on normal direction for consistent clipping
if (WedgeVec2(normal, vab0) < 0)
{
S_SupportPoint tmp = a0;
a0 = b0;
b0 = tmp;
vab0 = NegVec2(vab0);
}
if (WedgeVec2(normal, vab1) < 0)
{
S_SupportPoint tmp = a1;
a1 = b1;
b1 = tmp;
vab1 = NegVec2(vab1);
}
// Collapse lines that are too far in the direction of the normal to be accurately clipped
f32 collapse_epsilon = 0.05f;
collapse0 = collapse0 || AbsF32(WedgeVec2(normal, vab0_norm)) < collapse_epsilon;
collapse1 = collapse1 || AbsF32(WedgeVec2(normal, vab1_norm)) < collapse_epsilon;
// Collapse lines into deepest point
if (collapse0)
{
if (DotVec2(normal, vab0) > 0)
{
a0 = b0;
}
else
{
// TODO: Remove this (debugging)
b0 = a0;
}
}
if (collapse1)
{
if (DotVec2(normal, vab1) < 0)
{
a1 = b1;
}
else
{
// TODO: Remove this (debugging)
b1 = a1;
}
}
f32 a_sep = Inf;
f32 b_sep = Inf;
Vec2 a_midpoint = Zi;
Vec2 b_midpoint = Zi;
b32 ignore_a = 1;
b32 ignore_b = 1;
if (!collapse0 && !collapse1)
{
// Clip line to line
S_ClippedLine clip_result = S_ClipLineToLine(a0.p, b0.p, a1.p, b1.p, normal);
Vec2 a0_clipped = clip_result.a0_clipped;
Vec2 a1_clipped = clip_result.a1_clipped;
Vec2 b0_clipped = clip_result.b0_clipped;
Vec2 b1_clipped = clip_result.b1_clipped;
// Calc midpoint between clipped a & b
Vec2 va0a1_clipped = SubVec2(a1_clipped, a0_clipped);
Vec2 vb0b1_clipped = SubVec2(b1_clipped, b0_clipped);
a_sep = DotVec2(va0a1_clipped, normal);
b_sep = DotVec2(vb0b1_clipped, normal);
a_midpoint = AddVec2(a0_clipped, MulVec2(va0a1_clipped, 0.5f));
b_midpoint = AddVec2(b0_clipped, MulVec2(vb0b1_clipped, 0.5f));
ignore_a = 0;
ignore_b = 0;
Vec2 vfin = SubVec2(b_midpoint, a_midpoint);
if (Vec2LenSq(vfin) < (0.005 * 0.005))
{
if (a_sep > b_sep)
{
ignore_a = 1;
}
else
{
ignore_b = 1;
}
}
}
else
{
Vec2 p0 = a0.p;
Vec2 p1 = a1.p;
// TODO: Choose ID based on closest clipped point
if (collapse1 && !collapse0)
{
// Project a1 onto vab0
p0 = S_ClipPointToLine(a0.p, b0.p, a1.p, normal);
}
if (collapse0 && !collapse1)
{
// Project a0 onto vab1
p1 = S_ClipPointToLine(a1.p, b1.p, a0.p, normal);
}
// Calc midpoint
Vec2 vsep = SubVec2(p1, p0);
a_midpoint = AddVec2(p0, MulVec2(vsep, 0.5f));
a_sep = DotVec2(normal, p1) - DotVec2(normal, p0);
ignore_a = 0;
}
// Insert points
if (!ignore_a && a_sep < tolerance)
{
S_CollisionPoint *point = &collision_points[collision_points_count++];
point->id = a0.id | (a1.id << 4);
point->separation = a_sep;
point->p = a_midpoint;
}
if (!ignore_b && b_sep < tolerance)
{
S_CollisionPoint *point = &collision_points[collision_points_count++];
point->id = b0.id | (b1.id << 4);
point->separation = b_sep;
point->p = b_midpoint;
}
}
}
//////////////////////////////
//- Compute closest points
Vec2 closest_p0 = Zi;
Vec2 closest_p1 = Zi;
if (closest_feature.count == 1)
{
closest_p0 = closest_feature.a.s0.p;
closest_p1 = closest_feature.a.s1.p;
}
else
{
Assert(closest_feature.count == 2);
// FIXME: Winding order dependent?
f32 ratio = 0;
{
// Determine ratio between edge a & b that projected origin lies
Vec2 vab = SubVec2(closest_feature.b.p, closest_feature.a.p);
Vec2 vao = NegVec2(closest_feature.a.p);
ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1);
}
// Shape 0
closest_p0 = SubVec2(closest_feature.b.s0.p, closest_feature.a.s0.p);
closest_p0 = MulVec2(closest_p0, ratio);
closest_p0 = AddVec2(closest_p0, closest_feature.a.s0.p);
// Shape 1
closest_p1 = SubVec2(closest_feature.b.s1.p, closest_feature.a.s1.p);
closest_p1 = MulVec2(closest_p1, ratio);
closest_p1 = AddVec2(closest_p1, closest_feature.a.s1.p);
}
CopyStructs(result.collision_points, collision_points, countof(collision_points));
result.collision_points_count = collision_points_count;
result.closest_p0 = closest_p0;
result.closest_p1 = closest_p1;
EndScratch(scratch);
return result;
}
////////////////////////////////////////////////////////////
//~ Lookup helpers
@ -594,7 +1241,7 @@ void S_TickForever(WaveLaneCtx *lane)
test_rect.p1 = VEC2(1, 1);
constraint->shape1 = S_ShapeFromDesc(
.radius = 0.2,
.radius = 0.5,
.count = 4,
.points[0] = VEC2(test_rect.p0.x, test_rect.p0.y),
.points[1] = VEC2(test_rect.p1.x, test_rect.p0.y),
@ -653,8 +1300,12 @@ void S_TickForever(WaveLaneCtx *lane)
Vec2 normal = NormVec2(shape_dir);
Vec2 neg_normal = NegVec2(normal);
Vec2 shape0_pt = S_SupportPointFromShape(shape0, shape_dir);
Vec2 shape1_pt = S_SupportPointFromShape(shape1, neg_shape_dir);
// Vec2 shape0_pt = S_SupportPointFromShape(shape0, shape_dir);
// Vec2 shape1_pt = S_SupportPointFromShape(shape1, neg_shape_dir);
S_CollisionData collision_data = S_CollisionDataFromShapes(shape0, shape1);
Vec2 shape0_pt = collision_data.closest_p0;
Vec2 shape1_pt = collision_data.closest_p1;
Vec2 sep = SubVec2(shape1_pt, shape0_pt);
f32 sep_along_normal = DotVec2(sep, normal);
@ -663,13 +1314,11 @@ void S_TickForever(WaveLaneCtx *lane)
// f32 sep_normal = DotVec2(sep, normal);
S_DebugDrawPoint(shape0_pt, Color_Cyan);
S_DebugDrawPoint(shape1_pt, Color_Cyan);
S_DebugDrawPoint(collision_data.collision_points[0].p, Color_Red);
S_DebugDrawPoint(collision_data.collision_points[0].p, Color_Red);
if (sep_along_normal < 0)
{
@ -732,7 +1381,7 @@ void S_TickForever(WaveLaneCtx *lane)
{
Vec4 color = VEC4(0.8, 0.8, 0.8, 1);
Vec2 p0 = world_shape.centroid;
Vec2 p1 = S_SupportPointFromShape(world_shape, ent->look);
Vec2 p1 = S_SupportPointFromShape(world_shape, ent->look).p;
S_DebugDrawLine(p0, p1, color);
}
}

View File

@ -110,6 +110,52 @@ Struct(S_EntList)
u64 count;
};
////////////////////////////////////////////////////////////
//~ Collision types
Struct(S_SupportPoint)
{
Vec2 p;
i32 id; // Index of the originating piont in the shape
};
Struct(S_CollisionPoint)
{
Vec2 p;
f32 separation;
u32 id; // Based on polygon edge-to-edge
};
Struct(S_MenkowskiPoint)
{
Vec2 p; // Menkowski difference point
S_SupportPoint s0; // Support point of first shape in dir
S_SupportPoint s1; // Support point of second shape in -dir
};
Struct(S_MenkowskiSimplex)
{
i32 count;
S_MenkowskiPoint a, b, c;
};
Struct(S_ClippedLine)
{
Vec2 a0_clipped, b0_clipped;
Vec2 a1_clipped, b1_clipped;
};
Struct(S_CollisionData)
{
// Contact manifold
i32 collision_points_count;
S_CollisionPoint collision_points[2];
// Closest points
Vec2 closest_p0;
Vec2 closest_p1;
};
////////////////////////////////////////////////////////////
//~ Constraint types
@ -324,10 +370,18 @@ S_Shape S_ShapeFromDescEx(S_ShapeDesc desc);
#define S_ShapeFromDesc(...) S_ShapeFromDescEx((S_ShapeDesc) { __VA_ARGS__ })
S_Shape S_MulXformShape(Xform xf, S_Shape shape);
Vec2 S_SupportPointFromShape(S_Shape shape, Vec2 dir);
Rng2 S_BoundingBoxFromShape(S_Shape shape);
////////////////////////////////////////////////////////////
//~ Collision
S_SupportPoint S_SupportPointFromShapeEx(S_Shape shape, Vec2 dir, i32 ignore_idx);
S_SupportPoint S_SupportPointFromShape(S_Shape shape, Vec2 dir);
S_MenkowskiPoint S_MenkowskiPointFromShapes(S_Shape shape0, S_Shape shape1, Vec2 dir);
S_ClippedLine S_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal);
Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal);
S_CollisionData S_CollisionDataFromShapes(S_Shape shape0, S_Shape shape1);
////////////////////////////////////////////////////////////
//~ Lookup helpers

View File

@ -191,7 +191,7 @@ void V_DrawShape(S_Shape shape, Vec4 srgb, i32 detail, V_DrawFlag flags)
{
f32 rad = ((f32)i / (f32)detail) * Tau;
Vec2 dir = Vec2FromAngle(rad);
Vec2 sp = S_SupportPointFromShape(shape, dir);
Vec2 sp = S_SupportPointFromShape(shape, dir).p;
draw_points.points[i] = sp;
}
V_DrawPoly(draw_points, srgb, flags);
@ -237,8 +237,7 @@ V_WidgetTheme V_GetWidgetTheme(void)
theme.icon_font = UI_BuiltinIconFont();
// theme.font_size = 14;
// theme.font_size = TweakFloat("Font size", 14, 6, 50, .precision = 0);
theme.font_size = TweakFloat("Font size", 14, 6, 50, .precision = 2);
theme.font_size = TweakFloat("Font size", 14, 6, 50, .precision = 0);
theme.h1 = 2.00;
theme.h2 = 1.50;
theme.h3 = 1.25;
@ -247,8 +246,6 @@ V_WidgetTheme V_GetWidgetTheme(void)
theme.h6 = 0.75;
theme.micro = 0.50;
// theme.rounding = 0;
// theme.rounding = 1;
theme.rounding = TweakFloat("Rounding", 1, 0, 1);
theme.text_padding_x = 5;
@ -1511,7 +1508,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_Push(BorderColor, window_border_color);
// UI_Push(BorderSize, theme.window_bd_sz);
UI_Push(BorderSize, 1);
UI_Push(Rounding, UI_RGROW(0.095 * theme.rounding));
UI_Push(Rounding, UI_RGROW(0.06 * theme.rounding));
UI_Push(Width, UI_FNT(40, 0));
UI_Push(Height, UI_SHRINK(0, 0));
UI_Push(ChildLayoutAxis, Axis_Y);
@ -1707,28 +1704,6 @@ void V_TickForever(WaveLaneCtx *lane)
String new_tweak_str = tweak_var.value;
b32 is_default = MatchString(new_tweak_str, tweak_var.initial);
// Tweak label
{
UI_BuildSpacer(UI_PIX(spacing, 1), Axis_X);
if (is_default)
{
UI_SetNext(TextColor, theme.col.hint);
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h5);
}
else
{
UI_SetNext(TextColor, Color_White);
}
UI_SetNext(ChildAlignment, UI_Region_Left);
UI_SetNext(Width, UI_SHRINK(0, 1));
UI_SetNext(Height, UI_SHRINK(0, 1));
UI_SetNext(Text, new_tweak_str);
UI_SetNext(Flags, UI_BoxFlag_DrawText);
UI_SetNext(BackgroundColor, 0);
UI_SetNext(BorderColor, 0);
UI_BuildBox();
}
// Reset button
if (!is_default)
{
@ -1769,6 +1744,28 @@ void V_TickForever(WaveLaneCtx *lane)
UI_BuildIconEx(reset_key, theme.icon_font, UI_Icon_Loop2);
}
// Tweak label
{
UI_BuildSpacer(UI_PIX(spacing, 1), Axis_X);
if (is_default)
{
UI_SetNext(TextColor, theme.col.hint);
}
else
{
UI_SetNext(TextColor, Color_White);
}
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h5);
UI_SetNext(ChildAlignment, UI_Region_Left);
UI_SetNext(Width, UI_SHRINK(0, 1));
UI_SetNext(Height, UI_SHRINK(0, 1));
UI_SetNext(Text, new_tweak_str);
UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_NoTextTruncation);
UI_SetNext(BackgroundColor, 0);
UI_SetNext(BorderColor, 0);
UI_BuildBox();
}
UI_BuildSpacer(UI_PIX(spacing, 1), Axis_X);
switch (tweak_var.kind)
@ -2299,7 +2296,7 @@ void V_TickForever(WaveLaneCtx *lane)
S_Ent *ent = &cmd->ent;
*ent = *S_EntFromKey(&V.lookup, V.player_key);
ent->key = V.player_key;
ent->move_speed = 0.1;
ent->move_speed = 0.05;
ent->local_shape = S_ShapeFromDesc(
.mass = 10,
.count = 1,