import old collision detection code
This commit is contained in:
parent
235cf72018
commit
54f77c87be
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user