Guest
Public paste!

XNA Collision Detection

By: a guest | Nov 5th, 2009 | Syntax: C# | Size: 20.92 KB | Hits: 157 | Expires: Never
Copy text to clipboard
  1.         private bool GetLowestRoot(float a, float b, float c, float maxR, ref float root)
  2.         {
  3.             // Check if a solution exists
  4.             float determinant = b * b - 4.0f * a * c;
  5.  
  6.             // If determinant is negative it means no solutions.
  7.             if (determinant < 0.0f) return false;
  8.  
  9.             // calculate the two roots: (if determinant == 0 then
  10.             // x1==x2 but let’s disregard that slight optimization)
  11.             float sqrtD = (float)Math.Sqrt(determinant);
  12.             float r1 = (-b - sqrtD) / (2 * a);
  13.             float r2 = (-b + sqrtD) / (2 * a);
  14.  
  15.             // Sort so x1 <= x2
  16.             if (r1 > r2)
  17.             {
  18.                 float temp = r2;
  19.                 r2 = r1;
  20.                 r1 = temp;
  21.             }
  22.            
  23.             // Get lowest root:
  24.             if (r1 > 0 && r1 < maxR)
  25.             {
  26.                 root = r1;
  27.                 return true;
  28.             }
  29.            
  30.             // It is possible that we want x2 - this can happen
  31.             // if x1 < 0
  32.             if (r2 > 0 && r2 < maxR)
  33.             {
  34.                 root = r2;
  35.                 return true;
  36.             }
  37.  
  38.             // No (valid) solutions
  39.             return false;
  40.         }
  41.  
  42.  
  43.         /// <summary>
  44.         /// Check point P to see if it is within the triangle A B C.
  45.         /// </summary>
  46.         private bool CheckPointInTriangle(Vector3 P, Vector3 A, Vector3 B, Vector3 C)
  47.         {
  48.             //Distance Vectors        
  49.             Vector3 p1 = C - A;
  50.             Vector3 p2 = B - A;
  51.             Vector3 p3 = P - A;
  52.  
  53.             //Get all the Vector Dots
  54.             float dot11 = Vector3.Dot(p1, p1);
  55.             float dot12 = Vector3.Dot(p1, p2);
  56.             float dot13 = Vector3.Dot(p1, p3);
  57.             float dot22 = Vector3.Dot(p2, p2);
  58.             float dot23 = Vector3.Dot(p2, p3);
  59.  
  60.             //Barycentric Co-ordinates
  61.             float invDenom = 1 / (dot11 * dot22 - dot12 * dot12);
  62.             float u = (dot22 * dot13 - dot12 * dot23) * invDenom;
  63.             float v = (dot11 * dot23 - dot12 * dot13) * invDenom;
  64.  
  65.             // Is 'P' in the Triangle?
  66.             return (u > 0) && (v > 0) && (u + v < 1);
  67.         }
  68.  
  69.  
  70.         /// <summary>
  71.         /// Check a triangle for collision. Assumes p1,p2 and p3 are given in ellipsoid space.
  72.         /// </summary>
  73.         private void CheckTriangle(CollisionPacket colPackage, Vector3 p1, Vector3 p2, Vector3 p3)
  74.         {
  75.             // Make the plane containing this triangle.
  76.             tPlane trianglePlane = new tPlane(p1, p2, p3);
  77.  
  78.             // Is triangle front-facing to the velocity vector?
  79.             // We only check front-facing triangles
  80.             // (your choice of course)
  81.  
  82.             //if (trianglePlane.isFrontFacingTo(colPackage.normalizedVelocity))
  83.             if(true)
  84.             {
  85.                 // Get interval of plane intersection:
  86.                 double t0, t1;
  87.                 bool embeddedInPlane = false;
  88.  
  89.                 // Calculate the signed distance from sphere
  90.                 // position to triangle plane
  91.                 double signedDistToTrianglePlane = trianglePlane.signedDistanceTo(colPackage.basePoint);
  92.  
  93.                 // cache this as we’re going to use it a few times below:
  94.                 float normalDotVelocity = Vector3.Dot(trianglePlane.normal, colPackage.velocity);
  95.  
  96.                 // if sphere is travelling parrallel to the plane:
  97.                 if (normalDotVelocity == 0.0f){
  98.                     if (Math.Abs(signedDistToTrianglePlane) >= 1.0f){
  99.                         // Sphere is not embedded in plane.
  100.                         // No collision possible:
  101.                         return;
  102.                     }else{
  103.                         // sphere is embedded in plane.
  104.                         // It intersects in the whole range [0..1]
  105.                         embeddedInPlane = true;
  106.                         t0 = 0.0;
  107.                         t1 = 1.0;
  108.                     }
  109.                 }else{
  110.                     // N dot D is not 0. Calculate intersection interval:
  111.                     t0 = (-1.0 - signedDistToTrianglePlane) / normalDotVelocity;
  112.                     t1 = (1.0 - signedDistToTrianglePlane) / normalDotVelocity;
  113.                    
  114.                     // Swap so t0 < t1
  115.                     if (t0 > t1){
  116.                         double temp = t1;
  117.                         t1 = t0;
  118.                         t0 = temp;
  119.                     }
  120.                    
  121.                     // Check that at least one result is within range:
  122.                     if (t0 > 1.0f || t1 < 0.0f){
  123.                         // Both t values are outside values [0,1]
  124.                         // No collision possible:
  125.                         return;
  126.                     }
  127.  
  128.                     // Clamp to [0,1]
  129.                     if (t0 < 0.0) t0 = 0.0;
  130.                     if (t1 < 0.0) t1 = 0.0;
  131.                     if (t0 > 1.0) t0 = 1.0;
  132.                     if (t1 > 1.0) t1 = 1.0;
  133.                 }
  134.  
  135.                 // OK, at this point we have two time values t0 and t1
  136.                 // between which the swept sphere intersects with the
  137.                 // triangle plane. If any collision is to occur it must
  138.                 // happen within this interval.
  139.                 Vector3 collisionPoint = new Vector3();
  140.                 bool foundCollison = false;
  141.                 float t = 1.0f;
  142.  
  143.                 // First we check for the easy case - collision inside
  144.                 // the triangle. If this happens it must be at time t0
  145.                 // as this is when the sphere rests on the front side
  146.                 // of the triangle plane. Note, this can only happen if
  147.                 // the sphere is not embedded in the triangle plane.
  148.                 if (!embeddedInPlane)
  149.                 {
  150.                     Vector3 planeIntersectionPoint;
  151.  
  152.                     planeIntersectionPoint =
  153.                         (trianglePlane.isFrontFacingTo(colPackage.normalizedVelocity)) ?
  154.                             (colPackage.basePoint - trianglePlane.normal) :
  155.                             (colPackage.basePoint + trianglePlane.normal);
  156.                     planeIntersectionPoint += Vector3.Multiply(colPackage.velocity, (float)t0);
  157.  
  158.  
  159.  
  160.                     if (CheckPointInTriangle(planeIntersectionPoint, p1, p2, p3))
  161.                     {
  162.                         foundCollison = true;
  163.                         t = (float)t0;
  164.                         //myDebug.write(trianglePlane.isFrontFacingTo(colPackage.normalizedVelocity).ToString() + " - " + colPackage.normalizedVelocity.ToString());
  165.                         collisionPoint = planeIntersectionPoint;
  166.                         //myDebug.writeVector("Found collision in a plane: ", planeIntersectionPoint * colPackage.eRadius);
  167.                         //myDebug.writeVector("Base Point: ", colPackage.basePoint * colPackage.eRadius);
  168.                         colPackage.collisionTri.v1 = p1;
  169.                         colPackage.collisionTri.v2 = p2;
  170.                         colPackage.collisionTri.v3 = p3;
  171.                     }
  172.                 }
  173.  
  174.                 // if we haven’t found a collision already we’ll have to
  175.                 // sweep sphere against points and edges of the triangle.
  176.                 // Note: A collision inside the triangle (the check above)
  177.                 // will always happen before a vertex or edge collision!
  178.                 // This is why we can skip the swept test if the above
  179.                 // gives a collision!
  180.                 if (foundCollison == false)
  181.                 {
  182.                     // some commonly used terms:
  183.                     Vector3 velocity = colPackage.velocity;
  184.                     Vector3 basePoint = colPackage.basePoint;
  185.                     float velocitySquaredLength = velocity.LengthSquared();
  186.                     float a, b, c; // Params for equation
  187.                     float newT = 0.0f;
  188.  
  189.                     // For each vertex or edge a quadratic equation have to
  190.                     // be solved. We parameterize this equation as
  191.                     // a*t^2 + b*t + c = 0 and below we calculate the
  192.                     // parameters a,b and c for each test.
  193.                     // Check against points:
  194.                     a = velocitySquaredLength;
  195.  
  196.                     // P1
  197.                     b = 2 * Vector3.Dot(velocity, basePoint - p1);
  198.                     c = (p1 - basePoint).LengthSquared() - 1.0f;
  199.                     if (GetLowestRoot(a, b, c, t, ref newT))
  200.                     {
  201.                         t = newT;
  202.                         foundCollison = true;
  203.                         collisionPoint = p1;
  204.                         myDebug.writeVector("Found collision in a p1 corner:", p1 * colPackage.eRadius);
  205.                     }
  206.  
  207.                     // P2
  208.                     b = 2 * Vector3.Dot(velocity, basePoint - p2);
  209.                     c = (p2 - basePoint).LengthSquared() - 1.0f;
  210.                     if (GetLowestRoot(a, b, c, t, ref newT))
  211.                     {
  212.                         t = newT;
  213.                         foundCollison = true;
  214.                         collisionPoint = p2;
  215.                         myDebug.writeVector("Found collision in a p2 corner: ", p2 * colPackage.eRadius);
  216.                     }
  217.  
  218.                     // P3
  219.                     b = 2 * Vector3.Dot(velocity, basePoint - p3);
  220.                     c = (p3 - basePoint).LengthSquared() - 1.0f;
  221.                     if (GetLowestRoot(a, b, c, t, ref newT))
  222.                     {
  223.                         t = newT;
  224.                         foundCollison = true;
  225.                         collisionPoint = p3;
  226.                         myDebug.writeVector("Found collision in a p3 corner: ", p3 * colPackage.eRadius);
  227.                     }
  228.  
  229.                     //------------------------------
  230.                     // Check agains edges:
  231.                     //------------------------------
  232.  
  233.                     // p1 -> p2:
  234.                     //------------------------------
  235.                     Vector3 edge = p2 - p1;
  236.                     Vector3 baseToVertex = p1 - basePoint;
  237.                     float edgeSquaredLength = edge.LengthSquared();
  238.                     float edgeDotVelocity = Vector3.Dot(edge, velocity);
  239.                     float edgeDotBaseToVertex = Vector3.Dot(edge, baseToVertex);
  240.  
  241.                     // Calculate parameters for equation
  242.                     a = (edgeSquaredLength * -velocitySquaredLength) + (edgeDotVelocity * edgeDotVelocity);
  243.                     b = edgeSquaredLength * (2 * Vector3.Dot(velocity, baseToVertex)) - (2 * edgeDotVelocity * edgeDotBaseToVertex);
  244.                     c = edgeSquaredLength * (1 - baseToVertex.LengthSquared()) + (edgeDotBaseToVertex * edgeDotBaseToVertex);
  245.  
  246.                     // Does the swept sphere collide against infinite edge?
  247.                     if (GetLowestRoot(a, b, c, t, ref newT))
  248.                     {
  249.                         // Check if intersection is within line segment:
  250.                         float f = (edgeDotVelocity * newT - edgeDotBaseToVertex) / edgeSquaredLength;
  251.                         if (f >= 0.0 && f <= 1.0)
  252.                         {
  253.                             // intersection took place within segment.
  254.                             t = newT;
  255.                             foundCollison = true;
  256.                             collisionPoint = p1 + (f * edge);
  257.                             myDebug.writeVector("Found collision on the p1 - p2 edge:", collisionPoint * colPackage.eRadius);
  258.                         }
  259.                     }
  260.  
  261.                     // p2 -> p3:
  262.                     //------------------------------
  263.                     edge = p3 - p2;
  264.                     baseToVertex = p2 - basePoint;
  265.                     edgeSquaredLength = edge.LengthSquared();
  266.                     edgeDotVelocity = Vector3.Dot(edge, velocity);
  267.                     edgeDotBaseToVertex = Vector3.Dot(edge, baseToVertex);
  268.  
  269.                     a = (edgeSquaredLength * -velocitySquaredLength) + (edgeDotVelocity * edgeDotVelocity);
  270.                     b = edgeSquaredLength * (2 * Vector3.Dot(velocity, baseToVertex)) - (2.0f * edgeDotVelocity * edgeDotBaseToVertex);
  271.                     c = edgeSquaredLength * (1 - baseToVertex.LengthSquared()) + (edgeDotBaseToVertex * edgeDotBaseToVertex);
  272.  
  273.                     if (GetLowestRoot(a, b, c, t, ref newT))
  274.                     {
  275.                         float f = (edgeDotVelocity * newT - edgeDotBaseToVertex) / edgeSquaredLength;
  276.                         if (f >= 0.0 && f <= 1.0)
  277.                         {
  278.                             t = newT;
  279.                             foundCollison = true;
  280.                             collisionPoint = p2 + (f * edge);
  281.                             myDebug.writeVector("Found collision on the p2 - p3 edge:", collisionPoint * colPackage.eRadius);
  282.                         }
  283.                     }
  284.  
  285.                     // p3 -> p1:
  286.                     //------------------------------
  287.                     edge = p1 - p3;
  288.                     baseToVertex = p3 - basePoint;
  289.                     edgeSquaredLength = edge.LengthSquared();
  290.                     edgeDotVelocity = Vector3.Dot(edge, velocity);
  291.                     edgeDotBaseToVertex = Vector3.Dot(edge, baseToVertex);
  292.  
  293.                     a = edgeSquaredLength * -velocitySquaredLength + (edgeDotVelocity * edgeDotVelocity);
  294.                     b = edgeSquaredLength * (2 * Vector3.Dot(velocity, baseToVertex)) - (2.0f * edgeDotVelocity * edgeDotBaseToVertex);
  295.                     c = edgeSquaredLength * (1 - baseToVertex.LengthSquared()) + (edgeDotBaseToVertex * edgeDotBaseToVertex);
  296.  
  297.                     if (GetLowestRoot(a, b, c, t, ref newT))
  298.                     {
  299.                         float f = (edgeDotVelocity * newT - edgeDotBaseToVertex) / edgeSquaredLength;
  300.                         if (f >= 0.0 && f <= 1.0)
  301.                         {
  302.                             t = newT;
  303.                             foundCollison = true;
  304.                             collisionPoint = p3 + (f * edge);
  305.                             myDebug.writeVector("Found collision on the p3 - p1 edge:", collisionPoint * colPackage.eRadius);
  306.                         }
  307.                     }
  308.                 }//Emd If Collision Found
  309.  
  310.                 //------------------------------
  311.                 // Set result:
  312.                 //------------------------------
  313.                 if (foundCollison == true)
  314.                 {
  315.                     // distance to collision: ’t’ is time of collision
  316.                     float distToCollision = t * colPackage.velocity.Length();
  317.                    
  318.                     // Does this triangle qualify for the closest hit?
  319.                     // it does if it’s the first hit or the closest
  320.                     if (colPackage.foundCollision == false || distToCollision < colPackage.nearestDistance){
  321.  
  322.                         // Collision information nessesary for sliding
  323.                         colPackage.nearestDistance = distToCollision;
  324.                         colPackage.intersectionPoint = collisionPoint;
  325.                         colPackage.foundCollision = true;
  326.                         colPackage.t = t;
  327.                     }
  328.                 }
  329.             }//End if not backface
  330.         }
  331.  
  332.  
  333.         /// <summary>
  334.         ///
  335.         /// </summary>
  336.         private Vector3 CollideWithWorld(Vector3 pos, Vector3 vel)
  337.         {
  338.  
  339.             float unitsPerMeter = 1000.0f; //Set this to match application scale.
  340.             // All hard-coded distances in this function is
  341.             // scaled to fit the setting above..
  342.             float unitScale = unitsPerMeter / 100.0f;
  343.             float veryCloseDistance = 0.005f * unitScale;
  344.  
  345.             // do we need to worry?
  346.             if (collisionRecursionDepth > 5)
  347.                 return pos;
  348.  
  349.             // Ok, we need to worry:
  350.             collisionPackage.velocity = vel;
  351.             collisionPackage.normalizedVelocity = vel;
  352.             collisionPackage.normalizedVelocity.Normalize();
  353.             collisionPackage.basePoint = pos;
  354.             collisionPackage.foundCollision = false;
  355.  
  356.             //Check all triangles for collision.
  357.             foreach (TriangleVertexIndice t in idcs)
  358.             {
  359.                 CheckTriangle(collisionPackage,
  360.                                         vtcs[t.i0] / collisionPackage.eRadius,
  361.                                         vtcs[t.i1] / collisionPackage.eRadius,
  362.                                         vtcs[t.i2] / collisionPackage.eRadius);
  363.                 if (collisionPackage.foundCollision)
  364.                 {
  365.                     //Console.WriteLine("Collision Found! "+ collisionPackage.intersectionPoint.ToString());
  366.                     collisionPoint.tri = collisionPackage.collisionTri;
  367.                     collisionPoint.tri.v1 *= collisionPackage.eRadius;
  368.                     collisionPoint.tri.v2 *= collisionPackage.eRadius;
  369.                     collisionPoint.tri.v3 *= collisionPackage.eRadius;
  370.                     collisionPoint.colpack = collisionPackage;
  371.                     //break;
  372.  
  373.                 }
  374.             }
  375.  
  376.             // If no collision we just move along the velocity
  377.             if (collisionPackage.foundCollision == false)
  378.             {
  379.                 return pos + vel;
  380.             }
  381.  
  382.             // *** Collision occured ***
  383.             // The original destination point
  384.             Vector3 destinationPoint = pos + vel;
  385.             Vector3 newBasePoint = pos;
  386.  
  387.             // only update if we are not already very close
  388.             // and if so we only move very close to intersection..not
  389.             // to the exact spot.
  390.  
  391.             if (collisionPackage.nearestDistance >= veryCloseDistance)
  392.             {
  393.                 Vector3 V = vel;
  394.                 V.Normalize();
  395.                 V = Vector3.Multiply(V, (float)(collisionPackage.nearestDistance - veryCloseDistance));
  396.                 //V.SetLength = (collisionPackage.nearestDistance - veryCloseDistance);
  397.  
  398.                 newBasePoint = collisionPackage.basePoint + V;
  399.  
  400.                 // Adjust polygon intersection point (so sliding
  401.                 // plane will be unaffected by the fact that we
  402.                 // move slightly less than collision tells us)
  403.                 V.Normalize();
  404.                 collisionPackage.intersectionPoint -= (veryCloseDistance * V);
  405.             }
  406.  
  407.             // Determine the sliding plane
  408.             Vector3 slidePlaneOrigin = collisionPackage.intersectionPoint;
  409.             Vector3 slidePlaneNormal = newBasePoint - collisionPackage.intersectionPoint;
  410.             slidePlaneNormal.Normalize();
  411.             tPlane slidingPlane = new tPlane(slidePlaneOrigin, slidePlaneNormal);
  412.  
  413.             // Again, sorry about formatting.. but look carefully ;)
  414.             Vector3 newDestinationPoint = destinationPoint - Vector3.Multiply(slidePlaneNormal, (float)slidingPlane.signedDistanceTo(destinationPoint));
  415.  
  416.             // Generate the slide vector, which will become our new velocity vector for the next iteration
  417.             Vector3 newVelocityVector = newDestinationPoint - collisionPackage.intersectionPoint;
  418.  
  419.             // Recurse: Don't recurse if the new velocity is very small
  420.             if (newVelocityVector.Length() < veryCloseDistance)
  421.             {
  422.                 return newBasePoint;
  423.             }
  424.  
  425.             collisionRecursionDepth++;
  426.             return CollideWithWorld(newBasePoint, newVelocityVector);
  427.         }
  428.  
  429.  
  430.         /// <summary>
  431.         /// Conversion to eSpace for collideWithWorld.
  432.         /// </summary>
  433.         public Vector3 CollideAndSlide(Vector3 pos, Vector3 vel, Vector3 gravity)
  434.         {
  435.  
  436.  
  437.             // Do collision detection:
  438.             collisionPackage.R3Position = pos;
  439.             collisionPackage.R3Position.Y += collisionPackage.eRadius.Y;
  440.             collisionPackage.R3Velocity = vel;
  441.  
  442.             // calculate position and velocity in eSpace
  443.             Vector3 eSpacePosition = collisionPackage.R3Position / collisionPackage.eRadius;
  444.             Vector3 eSpaceVelocity = collisionPackage.R3Velocity / collisionPackage.eRadius;
  445.  
  446.             // Iterate until we have our final position.
  447.             collisionRecursionDepth = 0;
  448.             Vector3 finalPosition = CollideWithWorld(eSpacePosition, eSpaceVelocity);
  449.            
  450.             // [gravity]
  451.             collisionPackage.R3Position = finalPosition * collisionPackage.eRadius;
  452.             collisionPackage.R3Velocity = gravity;
  453.             eSpaceVelocity = gravity / collisionPackage.eRadius;
  454.             collisionRecursionDepth = 0;
  455.             finalPosition = CollideWithWorld(finalPosition, eSpaceVelocity);
  456.             // [/gravity]
  457.  
  458.            
  459.             // Convert final result back to R3:
  460.             finalPosition = finalPosition * collisionPackage.eRadius;
  461.             finalPosition.Y -= collisionPackage.eRadius.Y;
  462.  
  463.             // Move the entity (application specific function)
  464.             return finalPosition;
  465.         }