Capsule calculations
Hi! I need to do some calculations with capsules not much different from a capsule collider, but i can't use colliders for this one. So the first question is: do you know of any mono or unity class that already has this? Or if there isn't any then how could i create a method that decides whether or not a capsule contains a point? And how about a method about overlapping capsules?
I'm thinking i need 2 points and a radius to describe the capsule. I already have a way of telling if a sphere contains a point and if it overlaps with another sphere if that helps.
So any ideas?
Answer by HarshadK · Dec 02, 2016 at 12:00 PM
For point inside capsule: check for point inside spheres and point inside cylinder. These are articles with math and code for point inside cylinder.
For capsule-capsule test, here is an article discussing the same: Swept Capsule-Capsule intersection (3D). Even though the article is titled for sweep test, it has info and source on simple capsule-capsule intersection.
Answer by JedBeryll · Dec 02, 2016 at 02:02 PM
In case anyone else needs this:
public class Capsule {
public Vector3 Pos1 { get; set; }
public Vector3 Pos2 { get; set; }
public float Radius { get; set; }
public Vector3 Direction {
get {
return Pos2-Pos1;
}
}
public Vector3 Center {
get {
return (Pos1 + Pos2) / 2f;
}
}
public Capsule (Vector3 p1, Vector3 p2, float radius) {
Pos1 = p1;
Pos2 = p2;
Radius = radius;
}
public Capsule (Vector3 center, Vector3 direction, float length, float radius) {
Vector3 d2 = direction.normalized * length / 2f;
Pos1 = center - d2;
Pos2 = center + d2;
Radius = radius;
}
public bool Contains(Vector3 point) {
if (Sphere.Contains(Pos1, Radius, point) || Sphere.Contains(Pos2, Radius, point)) {
return true;
}
Vector3 pDir = point - Pos1;
float dot = Vector3.Dot(Direction, pDir);
float lengthsq = Direction.sqrMagnitude;
if (dot < 0f || dot > lengthsq) return false;
float dsq = pDir.x * pDir.x + pDir.y * pDir.y + pDir.z * pDir.z - dot * dot / lengthsq;
if (dsq > Radius * Radius) {
return false;
}
else {
return true;
}
}
}
}
public class Sphere {
public Vector3 Center { get; set; }
public float Radius { get; set; }
public Sphere (Vector3 position, float rad) {
Center = position;
Radius = rad;
}
public bool Contains (Vector3 point) {
return Contains (Center, Radius, point);
}
public static bool Contains (Sphere sphere, Vector3 point) {
return Contains (sphere.Center, sphere.Radius, point);
}
public static bool Contains (Vector3 spherePos, float radius, Vector3 point) {
float dist = (spherePos - point).sqrMagnitude;
return dist <= radius * radius;
}
}
nice! this looks great.
you might also consider extending it to a tapered capsule, where the two spheres have different radii. Given what you've already done, i think the only tricky math would be around lines 43-45. You could project the point onto the axis, normalize the distance, and do an inverse lerp to compare the distance from the axis to a tapered version of the radius.
tapered capsules are useful for modeling arms / limbs / trees, etc.
I don't think i will be needing that so i won't be working on it, but it would be nice to have so if anyone has a working version of it, you're welcome to post it here :)
float dsq = pDir.x pDir.x + pDir.y pDir.y + pDir.z + pDir.z - dot * dot / lengthsq;
I think this line has a typo.
pDir.z should be multiplied by pDir.z
Answer by Cowboy_Jow · Aug 07, 2019 at 07:09 AM
They way I have thought of is to take the cylinder points and using triangle math to see if it is within a distance from the center of the cylinder, and check if it is within either of the 2 spheres. here is the math tutorial: https://www.youtube.com/watch?v=CkH_LoD0ZoQ
here is my check if a point is inside a capsule script:
public static bool checkincapsul(Vector3 point, Vector3 capstart, Vector3 capend, float rad)
{
//get the height of the triangle
float[] sides = new float[] { Vector3.Distance(capstart, capend), Vector3.Distance(capend, point), Vector3.Distance(point, capstart) };
float s = (sides[0] + sides[1] + sides[2]) / 2;
float a = Mathf.Sqrt(s * (s - sides[0]) * (s - sides[1]) * (s - sides[2]));
float h = a / (sides[0] * 0.5f);
//check if the height is within radius of the cylinder or the 2 spheres
bool cap = false;
if (h <= rad || sides[1] <= rad || sides[2] <= rad) cap = true;
return cap;
}
[1]: https://www.youtube.com/watch?v=CkH_LoD0ZoQ