I currently have a Sphere surrounding the camera which acts as a boundary for collision detection. After your help with the Ray Intersection methods, moving the idea forward to a method involving a Sphere wasn't too difficult.
However, although I now no longer have stuttering when colliding with walls that are 90 degrees or less, I am getting horrible stuttering when colliding with two walls that are exposed at 270 degrees.
E.G
(Wall 1)
|
|
|
|___________(Wall 2)
^
|
(Camera Direction)
If the camera hits the intersection between Wall 1 and Wall 2 it will stutter between each wall until pushed out. It wont do this for any other scenario, why?
Here is my code, I am using a Sphere/Triangle Collision method:
struct CollisionResult
{
public bool hasHit; // The following parameters are only valid when hasHit is true
//public float distanceSqr; // distance to intersection point
public float distance; // Distance to the intersection point
public Vector3 location; // Actual location of the hit (always a point on the triangle)
public Vector3 normal; // Normal of the hit
public static CollisionResult CreateNoHit()
{
CollisionResult result;
result.hasHit = false;
result.location = new Vector3(0, 0, 0);
result.normal = new Vector3(0, 0, 0);
//result.distanceSqr = 0;
result.distance = 0;
return result;
}
public void KeepClosest(CollisionResult result)
{
if (result.hasHit && (!hasHit || (result.distance < distance)))
{
this = result;
}
}
}
// In separate class:
public void BoundingSphereTriangleCollisionDetection(Camera camera)
{
Vector3 projectedPosition = camera.Position; // Position we will move to
BoundingSphere boundingSphere = camera.BoundingSphere; // Object's BoundingSphere
foreach (Triangle tri in Game1.levelTriangleList)
{
SphereTriangleCollision(projectedPosition, boundingSphere.Radius, tri, out result);
// Don't continue until all collisions have been resolved
while (result.hasHit)
{
// Distance to current Triangle from Object's BoundingSphere Centre (I.E. Object's Centre/Position)
//Console.WriteLine("Distance to Triangle: " + result.distance);
projectedPosition -= result.normal * (result.distance - boundingSphere.Radius - EPSILON);
SphereTriangleCollision(projectedPosition, boundingSphere.Radius, tri, out result);
}
}
camera.Position = projectedPosition;
// Bounding Sphere Position should be updated in Object Method (not here)
}
/// <summary>
/// Perform a Sphere/Triangle check. (Will work with XNA BoundingSpheres also)
///
/// :
/// - Spheres can only collide with the front side of triangles! (Much like Bounding Sphere/Plane behaviour)
/// - Always the closest possible intersection position is returned, which is always inside the triangle.
/// </summary>
/// <param name="position"></param>
/// <param name="sphereRadius"></param>
/// <param name="result"></param>
private void SphereTriangleCollision(Vector3 position, float sphereRadius, Triangle triangle, out CollisionResult result)
{
result = new CollisionResult();
Vector3 faceNormal = triangle.FaceNormal;
Vector3[] trianglePoints = triangle.Points;
Vector3[] triangleEdgeNormals = triangle.EdgeNormals;
Vector3 v = position - trianglePoints[0];
// Check which side of the plane we are on
float dist;
Vector3.Dot(ref v, ref faceNormal, out dist);
if (dist < 0)
{
return; // We need some more testing, since we're at the wrong side of the polygon!
}
if (dist > sphereRadius)
{
return; // We're too far away from the triangle
}
// If the above two rules are false there is a collision point
Vector3 collisionPoint = position - (dist * faceNormal);
//Console.WriteLine("Triangle Collision At " + collisionPoint);
// Is the collision point in the current Triangle?
if (PointInTriangle(ref collisionPoint, trianglePoints, triangleEdgeNormals))
{
CollisionResult t;
t.hasHit = true;
//t.distanceSqr = (collisionPoint - position).LengthSquared();
t.distance = (collisionPoint - position).Length();
t.normal = faceNormal;
t.location = collisionPoint;
result.KeepClosest(t);
return;
}
// If the point is not inside the triangle it could still intersect the triangle! (It would intersect the Triangle edge)
for (int i = 0; i < 3; i++)
{
Vector3 E = trianglePoints[(i + 1) % 3] - trianglePoints;
// Relative position to edge start.
Vector3 H = collisionPoint - trianglePoints;
// Distance of P to edge plane
float hn = Vector3.Dot(H, triangleEdgeNormals);
// Point is on the same side of the triangle, from the edge plane, try another
if (hn < 0.0f)
{
continue;
}
// Too far away from this edge's plane
if (hn > sphereRadius)
{
return;
}
// test intersection with polygon's edge
Vector3 intersectionPoint = new Vector3();
if (SpherePartialEdgeCollide(ref position, ref trianglePoints, ref E, ref intersectionPoint))
{
CollisionResult t;
t.hasHit = true;
//t.distanceSqr = (intersectionPoint - position).LengthSquared();
t.distance = (intersectionPoint - position).Length();
t.normal = faceNormal; //triangleEdgeNormals;
t.location = intersectionPoint;
result.KeepClosest(t);
}
}
}
private bool SpherePartialEdgeCollide(ref Vector3 start, ref Vector3 edgeV0, ref Vector3 edgeDir, ref Vector3 intersection)
{
// Find the point on line [edgeV0, edgeV0+edgeDir] that is closest to start
// See: http://softsurfer.com/Archive/algorithm_0102/algorithm_0102.htm
// Distance of a Point to a Ray or Segment
Vector3 w = start - edgeV0;
float c1 = Vector3.Dot(w, edgeDir);
if (c1 <= 0)
{
if ((start - edgeV0).LengthSquared() <= 1)
{
intersection = edgeV0;
return true;
}
else
{
return false;
}
}
float c2 = Vector3.Dot(edgeDir, edgeDir);
if (c2 <= c1)
{
Vector3 p1 = edgeV0 + edgeDir;
if ((start - p1).LengthSquared() <= 1)
{
intersection = p1;
return true;
}
else
{
return false;
}
}
float b = c1 / c2;
intersection = edgeV0 + b * edgeDir;
float distance = 0;
Vector3.DistanceSquared(ref start, ref intersection, out distance);
return (distance <= 1.0f);
}
/// <summary>
/// Determine for a given point (that lies on our triangle's plane) if
/// it lies within the triangle's borders or not.
///
/// Implemented by using the triangle's edge's planes.
/// </summary>
/// <param name="pt">a point on the triangle's plane</param>
/// <returns>true when point pt lies within the triangle</returns>
private bool PointInTriangle(ref Vector3 point, Vector3[] trianglePoints, Vector3[] triangleEdgeNormals)
{
Vector3 a = point - trianglePoints[0];
Vector3 b = point - trianglePoints[1];
Vector3 c = point - trianglePoints[2];
float v;
Vector3.Dot(ref a, ref triangleEdgeNormals[0], out v);
if (v >= 0)
{
return false;
}
Vector3.Dot(ref b, ref triangleEdgeNormals[1], out v);
if (v >= 0)
{
return false;
}
Vector3.Dot(ref c, ref triangleEdgeNormals[2], out v);
if (v >= 0)
{
return false;
}
return true;
}
I'm not sure why it is happening, can anyone enlighten me please?
Thank you.
[Fixed formatting - Zahlman]
[Edited by - Zahlman on May 6, 2009 3:05:46 PM]