So right now for my character solver I compute the time of impact of a character AABB and convex polygons using an algorithm based on "Robust Continuous Collision Detection Between Arbitrary Polyhedra Using Trajectory Parameterization of Polyhedral Features" by J.M.P van Waveren (link)
Doom 3 uses this for all of it's collisions so it never has to deal with penetration. This seems undesireable for a couple of reasons so I've decided I'm going to switch to a position solver instead. After some googling I came across Erin Catto's 2014 GDC presention "Understanding Constraints" (link) where he briefly describes how his character solver works. I've written a simple C program (attached to this post) to play around with these concepts and I have some questions that hopefuly I will be able to get answered here.
Keys for the test program:
ESC - quit
r - reset
s - step
1-8 - switch to setup 1-8
Here's how I intepret the algorithm:
Let's call the current position of the character the 'initial position'. This position never changes during the algorithm.
Let's call the position we are trying to move the character to the 'target position'. This position also never changes during the algorithm.
Scan the environment for overlap with the capsule at the initial position and construct a list of contact planes.
Perform a plane solve on the target position to get the first solve position.
Perform a shape cast from the initial position to the solve position. Move to the first hit point and collect new contact planes there. Add these new planes to the list.
Perform a plane solve on the target position to get the next solve point.
If the distance between the new solve point and the old one is within some epsilon, we are done. Otherwise go back to step 3.
Plane solve code:
vec3_t p = target_position;
vec3_t a = p + local_capsule_a;
vec3_t b = p + local_capsule_b;
for(uint32_t i = 0; i < max_iter; i++)
{
vec3_t last_p = p;
for(uint32_t j = 0; j < plane_count; j++)
{
const plane3_t &plane = planes[j];
float d1 = point_distance(plane, a);
float d2 = point_distance(plane, b);
float dist;
if(d1 < d2)
dist = d1;
else
dist = d2;
dist -= capsule_radius;
if(dist < 0.0f)
{
p -= dist * plane.n;
a = p + local_capsule_a;
b = p + local_capsule_b;
}
}
vec3_t delta = p - last_p;
if(dot(delta, delta) < epsilon)
break;
}
solve_position = p;
Couple of issues:
1. Is this interpretation correct?
2. In the test program I am only adding one new plane per shape cast, is there a reason to collect more (all touching planes)?
3. In the test program I am using the segment planes directly. In 3D I would directly use the polygon planes. If I use the direction from the closest point on the segment to the circle centre I get into situations like this, where the character ends up away from the collision geometry:
4. If I naively add all overlapping planes during step 1 I get into sitations like this:
The green circle is at the source position. Step 1 will find both planes and the character will end up in the corner, not touching either segment. In the test program I solve this problem by clearing the plane list after step 2, but this is insufficient as I also need to select the proper planes after a shape cast. I can think of some approaches to solving this problem, but instead of experimenting right away, I'd like to know how other people have solved this. What is a good way to determine which planes to actually use?
test.c
↧