• Main Page
  • Packages
  • Classes
  • Files
  • File List

C:/Doug/CSharp/XnaCollisionLib/XnaCollisionLib/CollisionManager.cs

Go to the documentation of this file.
00001 using System;
00002 using System.Collections.Generic;
00003 using Microsoft.Xna.Framework;
00004 using Microsoft.Xna.Framework.Graphics;
00005 
00006 namespace XnaCollisionLib
00007 {
00008     public struct CollisionData
00009     {
00010         public Vector3 ERadius;
00011         public Vector3 R3Velocity;
00012         public Vector3 R3Position;
00013         public Vector3 Velocity;
00014         public Vector3 NormalizedVelocity;
00015         public Vector3 BasePoint;
00016         public bool FoundCollision;
00017         public float NearestDistance;
00018         public Vector3 IntersectionPoint;
00019         public Triangle IntersectionTriangle;
00020         public int TriangleIndex;
00021         public int TriangleHits;
00022         public float SlidingSpeed;
00023     }
00024 
00025     public class CollisionManager
00026     {
00027         private CollisionSource _Source;
00028         private List<Triangle> _Triangles;
00029 
00030         public CollisionManager()
00031         {
00032             _Source = new CollisionSource();
00033             _Triangles = new List<Triangle>();
00034         }
00035 
00036         public CollisionSource Source
00037         {
00038             get { return _Source; }
00039         }
00040 
00041         public Nullable<float> GetRayCollisionResult(Ray ray, out Triangle hitTriangle)
00042         {
00043             return _Source.ISect(ray, out hitTriangle);
00044         }
00045 
00046         public Vector3 GetCollisionResult(
00047             Vector3 position,
00048             Vector3 radius,
00049             Vector3 velocity,
00050             Vector3 gravity,
00051             float slidingSpeed,
00052             out Triangle hitTriangle,
00053             out Vector3 hitPosition,
00054             out bool falling
00055             )
00056         {
00057             hitTriangle = new Triangle();
00058             hitPosition = new Vector3();
00059             falling = false;
00060 
00061             return CollideWithSource(position, radius, velocity, gravity, slidingSpeed, ref hitTriangle, ref hitPosition, ref falling);
00062         }
00063 
00064         private Vector3 CollideWithSource(
00065             Vector3 position,
00066             Vector3 radius,
00067             Vector3 velocity,
00068             Vector3 gravity,
00069             float slidingSpeed,
00070             ref Triangle hitTriangle,
00071             ref Vector3 hitPosition,
00072             ref bool falling
00073             )
00074         {
00075             if (Source.Count == 0 || radius == Vector3.Zero)
00076                 return position + velocity;
00077 
00078             _Triangles.Capacity = Source.Count;
00079 
00080             CollisionData colData = new CollisionData();
00081 
00082             colData.R3Position = position;
00083             colData.R3Velocity = velocity;
00084             colData.ERadius = radius;
00085             colData.NearestDistance = float.MaxValue;
00086             colData.SlidingSpeed = slidingSpeed;
00087             colData.TriangleHits = 0;
00088             colData.TriangleIndex = -1;
00089 
00090             Vector3 eSpacePosition = colData.R3Position / colData.ERadius;
00091             Vector3 eSpaceVelocity = colData.R3Velocity / colData.ERadius;
00092             Vector3 finalPosition = CollideWithSource(0, ref colData, eSpacePosition, eSpaceVelocity);
00093 
00094             falling = false;
00095             if (gravity != Vector3.Zero)
00096             {
00097                 colData.R3Position = finalPosition * colData.ERadius;
00098                 colData.R3Velocity = gravity;
00099                 colData.TriangleHits = 0;
00100 
00101                 eSpaceVelocity = gravity / colData.ERadius;
00102                 finalPosition = CollideWithSource(0, ref colData, finalPosition, eSpaceVelocity);
00103                 falling = colData.TriangleHits == 0;
00104             }
00105             if (colData.TriangleHits != 0)
00106             {
00107                 hitTriangle = colData.IntersectionTriangle;
00108                 hitTriangle.A *= colData.ERadius;
00109                 hitTriangle.B *= colData.ERadius;
00110                 hitTriangle.C *= colData.ERadius;
00111             }
00112             finalPosition *= colData.ERadius;
00113             hitPosition = colData.IntersectionPoint * colData.ERadius;
00114 
00115             return finalPosition;
00116         }
00117 
00118         private Vector3 CollideWithSource(int recursionDepth, ref CollisionData colData, Vector3 position, Vector3 velocity)
00119         {
00120             float veryCloseDistance = colData.SlidingSpeed;
00121 
00122             if (recursionDepth > 5)
00123                 return position;
00124 
00125             colData.Velocity = velocity;
00126             colData.NormalizedVelocity = Vector3.Normalize(velocity);
00127             colData.BasePoint = position;
00128             colData.FoundCollision = false;
00129             colData.NearestDistance = float.MaxValue;
00130 
00131             BoundingBox box = new BoundingBox(colData.R3Position, colData.R3Position);
00132 
00133             CollisionHelper.AddToBox(ref box, colData.R3Position + colData.R3Velocity);
00134             box.Min -= colData.ERadius;
00135             box.Max += colData.ERadius;
00136 
00137             Matrix scaleMatrix = Matrix.CreateScale(new Vector3(1, 1, 1) / colData.ERadius);
00138 
00139             _Triangles.Clear();
00140 
00141             Source.Query(box, _Triangles, scaleMatrix);
00142 
00143             for (int i = 0; i != _Triangles.Count; i++)
00144                 if (TestTriangleIntersection(ref colData, _Triangles[i]))
00145                     colData.TriangleIndex = i;
00146 
00147             if (!colData.FoundCollision)
00148                 return position + velocity;
00149 
00150             Vector3 destinationPoint = position + velocity;
00151             Vector3 newBasePoint = position;
00152 
00153             if (colData.NearestDistance >= veryCloseDistance)
00154             {
00155                 Vector3 v = Vector3.Normalize(velocity) * (colData.NearestDistance - veryCloseDistance);
00156 
00157                 newBasePoint = colData.BasePoint + v;
00158                 v.Normalize();
00159                 colData.IntersectionPoint -= (v * veryCloseDistance);
00160             }
00161 
00162             Vector3 slidePlaneOrigin = colData.IntersectionPoint;
00163             Vector3 slidePlaneNormal = Vector3.Normalize(newBasePoint - colData.IntersectionPoint);
00164             Plane slidingPlane = CollisionHelper.CreatePlane(slidePlaneNormal, slidePlaneOrigin);
00165             Vector3 newDestinationPoint = destinationPoint - (slidePlaneNormal * CollisionHelper.DistanceTo(slidingPlane, destinationPoint));
00166             Vector3 newVelocity = newDestinationPoint - colData.IntersectionPoint;
00167 
00168             if (newVelocity.Length() < veryCloseDistance)
00169                 return newBasePoint;
00170 
00171             return CollideWithSource(recursionDepth + 1, ref colData, newBasePoint, newVelocity);
00172         }
00173 
00174         private bool TestTriangleIntersection(ref CollisionData colData, Triangle triangle)
00175         {
00176             Plane trianglePlane = triangle.Plane;
00177 
00178             if (!triangle.IsFronFacing(colData.NormalizedVelocity))
00179                 return false;
00180 
00181             float t0, t1;
00182             bool embeddedInPlane = false;
00183             float signedDistToTrianglePlane = CollisionHelper.DistanceTo(trianglePlane, colData.BasePoint);
00184             float normalDotVelocity = Vector3.Dot(trianglePlane.Normal, colData.Velocity);
00185 
00186             if (CollisionHelper.IsZero(normalDotVelocity))
00187             {
00188                 if (signedDistToTrianglePlane >= 1)
00189                     return false;
00190                 else
00191                 {
00192                     embeddedInPlane = true;
00193                     t0 = 0;
00194                     t1 = 1;
00195                 }
00196             }
00197             else
00198             {
00199                 normalDotVelocity = CollisionHelper.Reciprocal(normalDotVelocity);
00200 
00201                 t0 = (-1.0f - signedDistToTrianglePlane) * normalDotVelocity;
00202                 t1 = (1.0f - signedDistToTrianglePlane) * normalDotVelocity;
00203 
00204                 if (t0 > t1)
00205                 {
00206                     float temp = t1;
00207 
00208                     t1 = t0;
00209                     t0 = temp;
00210                 }
00211 
00212                 if (t0 > 1.0f || t1 < 0.0f)
00213                     return false;
00214 
00215                 t0 = CollisionHelper.Clamp(t0, 0.0f, 1.0f);
00216                 t1 = CollisionHelper.Clamp(t1, 0.0f, 1.0f);
00217             }
00218 
00219             Vector3 collisionPoint = new Vector3(0, 0, 0);
00220             bool foundCollision = false;
00221             float t = 1.0f;
00222 
00223             if (!embeddedInPlane)
00224             {
00225                 Vector3 planeIntersectionPoint = (colData.BasePoint - trianglePlane.Normal) + colData.Velocity * t0;
00226 
00227                 if (triangle.IsPointInside(planeIntersectionPoint))
00228                 {
00229                     foundCollision = true;
00230                     t = t0;
00231                     collisionPoint = planeIntersectionPoint;
00232                 }
00233             }
00234 
00235             if (!foundCollision)
00236             {
00237                 Vector3 velocity = colData.Velocity;
00238                 Vector3 basePt = colData.BasePoint;
00239                 float velocityLenSq = velocity.LengthSquared();
00240                 float a, b, c, newT;
00241 
00242                 a = velocityLenSq;
00243                 b = 2.0f * Vector3.Dot(velocity, basePt - triangle.A);
00244                 c = (triangle.A - basePt).LengthSquared() - 1.0f;
00245                 if (GetLowestRoot(a, b, c, t, out newT))
00246                 {
00247                     t = newT;
00248                     foundCollision = true;
00249                     collisionPoint = triangle.A;
00250                 }
00251 
00252                 if (!foundCollision)
00253                 {
00254                     b = 2.0f * Vector3.Dot(velocity, basePt - triangle.B);
00255                     c = (triangle.B - basePt).LengthSquared() - 1.0f;
00256                     if (GetLowestRoot(a, b, c, t, out newT))
00257                     {
00258                         t = newT;
00259                         foundCollision = true;
00260                         collisionPoint = triangle.B;
00261                     }
00262                 }
00263 
00264                 if (!foundCollision)
00265                 {
00266                     b = 2.0f * Vector3.Dot(velocity, basePt - triangle.C);
00267                     c = (triangle.C - basePt).LengthSquared() - 1.0f;
00268                     if (GetLowestRoot(a, b, c, t, out newT))
00269                     {
00270                         t = newT;
00271                         foundCollision = true;
00272                         collisionPoint = triangle.C;
00273                     }
00274                 }
00275 
00276                 Vector3 edge = triangle.B - triangle.A;
00277                 Vector3 baseToVertex = triangle.A - basePt;
00278                 float edgeLenSq = edge.LengthSquared();
00279                 float edgeDotVelocity = Vector3.Dot(edge, velocity);
00280                 float edgeDotBaseToVertex = Vector3.Dot(edge, baseToVertex);
00281 
00282                 a = edgeLenSq * -velocityLenSq + edgeDotVelocity * edgeDotVelocity;
00283                 b = edgeLenSq * (2.0f * Vector3.Dot(velocity, baseToVertex)) - 2.0f * edgeDotVelocity * edgeDotBaseToVertex;
00284                 c = edgeLenSq * (1.0f - baseToVertex.LengthSquared()) + edgeDotBaseToVertex * edgeDotBaseToVertex;
00285 
00286                 if (GetLowestRoot(a, b, c, t, out newT))
00287                 {
00288                     float f = (edgeDotVelocity * newT - edgeDotBaseToVertex) / edgeLenSq;
00289                     if (f >= 0.0f && f <= 1.0f)
00290                     {
00291                         t = newT;
00292                         foundCollision = true;
00293                         collisionPoint = triangle.A + (edge * f);
00294                     }
00295                 }
00296 
00297                 edge = triangle.C - triangle.B;
00298                 baseToVertex = triangle.B - basePt;
00299                 edgeLenSq = edge.LengthSquared();
00300                 edgeDotVelocity = Vector3.Dot(edge,velocity);
00301                 edgeDotBaseToVertex = Vector3.Dot(edge,baseToVertex);
00302 
00303                 a = edgeLenSq * -velocityLenSq + edgeDotVelocity * edgeDotVelocity;
00304                 b = edgeLenSq * (2.0f * Vector3.Dot(velocity, baseToVertex)) - 2.0f * edgeDotVelocity * edgeDotBaseToVertex;
00305                 c = edgeLenSq * (1.0f - baseToVertex.LengthSquared()) + edgeDotBaseToVertex * edgeDotBaseToVertex;
00306 
00307                 if (GetLowestRoot(a, b, c, t, out newT))
00308                 {
00309                     float f = (edgeDotVelocity * newT - edgeDotBaseToVertex) / edgeLenSq;
00310                     if (f >= 0.0f && f <= 1.0f)
00311                     {
00312                         t = newT;
00313                         foundCollision = true;
00314                         collisionPoint = triangle.B + (edge * f);
00315                     }
00316                 }
00317 
00318                 edge = triangle.A - triangle.C;
00319                 baseToVertex = triangle.C - basePt;
00320                 edgeLenSq = edge.LengthSquared();
00321                 edgeDotVelocity = Vector3.Dot(edge, velocity);
00322                 edgeDotBaseToVertex = Vector3.Dot(edge, baseToVertex);
00323 
00324                 a = edgeLenSq * -velocityLenSq + edgeDotVelocity * edgeDotVelocity;
00325                 b = edgeLenSq * (2.0f * Vector3.Dot(velocity, baseToVertex)) - 2.0f * edgeDotVelocity * edgeDotBaseToVertex;
00326                 c = edgeLenSq * (1.0f - baseToVertex.LengthSquared()) + edgeDotBaseToVertex * edgeDotBaseToVertex;
00327 
00328                 if (GetLowestRoot(a, b, c, t, out newT))
00329                 {
00330                     float f = (edgeDotVelocity * newT - edgeDotBaseToVertex) / edgeLenSq;
00331                     if (f >= 0.0f && f <= 1.0f)
00332                     {
00333                         t = newT;
00334                         foundCollision = true;
00335                         collisionPoint = triangle.C + (edge * f);
00336                     }
00337                 }
00338             }
00339 
00340             if (foundCollision)
00341             {
00342                 float distToCollision = t * colData.Velocity.Length();
00343 
00344                 if (!colData.FoundCollision || distToCollision < colData.NearestDistance)
00345                 {
00346                     colData.NearestDistance = distToCollision;
00347                     colData.IntersectionPoint = collisionPoint;
00348                     colData.FoundCollision = true;
00349                     colData.IntersectionTriangle = triangle;
00350                     colData.TriangleHits++;
00351 
00352                     return true;
00353                 }
00354             }
00355             return false;
00356         }
00357 
00358         private bool GetLowestRoot(float a, float b, float c, float t, out float newT)
00359         {
00360             float determinant = b * b - 4.0f * a * c;
00361 
00362             newT = -1;
00363 
00364             if (determinant < 0.0f) return false;
00365 
00366             float sqrtD = (float)Math.Sqrt(determinant);
00367 
00368             float r1 = (-b - sqrtD) / (2 * a);
00369             float r2 = (-b + sqrtD) / (2 * a);
00370 
00371             if (r1 > r2)
00372             {
00373                 float temp = r2;
00374 
00375                 r2 = r1;
00376                 r1 = temp;
00377             }
00378 
00379             if (r1 > 0 && r1 < t)
00380             {
00381                 newT = r1;
00382 
00383                 return true;
00384             }
00385 
00386             if (r2 > 0 && r2 < t)
00387             {
00388                 newT = r2;
00389 
00390                 return true;
00391             }
00392             return false;
00393         }
00394     }
00395 }

Generated on Thu Jul 15 2010 19:56:39 for XnaCollisionLib by  doxygen 1.7.0