Guest User

IntersectionHelper.cs - WindowsPhoneDemoGame

a guest
Apr 12th, 2010
600
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 11.30 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Microsoft.Xna.Framework;
  6. using Microsoft.Xna.Framework.Graphics;
  7.  
  8. namespace WindowsPhoneGameDemoObjects
  9. {
  10.     class IntersectionHelper
  11.     {
  12.         /// <summary>
  13.         /// Проверка пересечения модели и BoundingSphere камеры
  14.         /// </summary>
  15.         /// <param name="model">Модель</param>
  16.         /// <param name="modelWorld">Матрица мира модели</param>
  17.         /// <param name="sphere">BoundingSphere камеры</param>
  18.         /// <param name="movement">Вектор движения камеры для выхода из коллизии</param>
  19.         /// <returns>true если есть пересечение, иначе false</returns>
  20.         public static bool Intersects(Model model, Matrix modelWorld, BoundingSphere sphere, out Vector3 movement)
  21.         {
  22.             movement = Vector3.Zero;
  23.             sphere = sphere.Transform(Matrix.Invert(modelWorld));
  24.             Vector3 untransformedMove;
  25.             if (!Intersects(model, sphere, out untransformedMove))
  26.                 return false;
  27.             movement = Vector3.Transform(untransformedMove, modelWorld);
  28.             movement -= modelWorld.Translation;
  29.             return true;
  30.         }
  31.  
  32.  
  33.         private static bool Intersects(Model model, BoundingSphere sphere, out Vector3 movement)
  34.         {
  35.             movement = Vector3.Zero;
  36.  
  37.             //Вначале извлекаем и трансформируем BoundingSphere модели, если нет пересечения с BoundingSphere камеры, то возвращаем false
  38.             Dictionary<string, object> tagData = (Dictionary<string, object>)model.Tag;
  39.             BoundingSphere modelSphere = (BoundingSphere)tagData["BoundingSphere"];
  40.             if (!modelSphere.Intersects(sphere))
  41.                 return false;
  42.  
  43.             Vector3[] vertices = (Vector3[])tagData["Vertices"];
  44.             Vector3 sphereCenter = sphere.Center;
  45.             float sphereRadiusSquared = sphere.Radius * sphere.Radius;  //Возведем радиус в квадрат для быстрой проверки
  46.  
  47.             bool hasCollision = false;
  48.  
  49.             Ray downRay = new Ray(sphere.Center, Vector3.Down); //Луч, исходяший из центра камеры и направленный вниз
  50.  
  51.             float rx = 0, ry = 0, rz = 0; //Итоговое движение назад
  52.  
  53.             Vector3 qMove, point1, point2, point3, v1, v2, normal, Offset, centerMinusOffset, centerPlusOffset;
  54.             //point1, point2, point3 - вершины из модели
  55.  
  56.             for (int i = 0; i < vertices.Length; i += 3)
  57.             {
  58.                 //default CullMode - CCW (против часовой)
  59.  
  60.                 point1 = vertices[i];
  61.                 point2 = vertices[i + 1];
  62.                 point3 = vertices[i + 2];
  63.  
  64.                 v1 = point3 - point1;
  65.                 v2 = point3 - point2;
  66.  
  67.                 //Скрещиваем направляющие, получаем вектор нормали
  68.                 Vector3.Cross(ref v1, ref v2, out normal);
  69.                 normal.Normalize();
  70.                 //normal - вектор нормали к треугольнику из точек p1, p2, p3
  71.                 //distanceSquared - квадрат расстояния от центра сферы камеры до плоскости треугльника
  72.                 //Уравнение плоскости normal.X * x0 + normal.Y * y0 + normal.Z * z0 + d = 0;
  73.  
  74.                 float d = 0 - normal.X * point1.X - normal.Y * point1.Y - normal.Z * point1.Z;
  75.  
  76.                 float distanceSquared = (float)(Math.Pow(normal.X * sphereCenter.X + normal.Y * sphereCenter.Y + normal.Z * sphereCenter.Z + d, 2)
  77.                     / (normal.X * normal.X + normal.Y * normal.Y + normal.Z * normal.Z));
  78.  
  79.                 if (distanceSquared >= sphereRadiusSquared)
  80.                     continue;
  81.  
  82.                 //distance - расстояние от центра сферы до плоскости
  83.                 float distance = (float)(Math.Abs(normal.X * sphereCenter.X + normal.Y * sphereCenter.Y + normal.Z * sphereCenter.Z + d)
  84.                     / Math.Sqrt(normal.X * normal.X + normal.Y * normal.Y + normal.Z * normal.Z));
  85.  
  86.                 //Вектор смещения относительно центра сферы
  87.                 Offset = normal * distance;
  88.  
  89.                 qMove = Vector3.Zero;
  90.  
  91.                 centerMinusOffset = sphere.Center - Offset;
  92.                 centerPlusOffset = sphere.Center + Offset;
  93.  
  94.                 if (IsPointInTriangle(ref centerMinusOffset, ref point1, ref point2, ref point3))
  95.                 {
  96.                     float moveAmount = sphere.Radius - distance;
  97.                     qMove = normal * moveAmount;
  98.                     hasCollision = true;
  99.                 }
  100.  
  101.                 if (IsPointInTriangle(ref centerPlusOffset, ref point1, ref point2, ref point3))
  102.                 {
  103.                     float moveAmount = sphere.Radius - distance;
  104.                     qMove = normal * (-moveAmount);
  105.                     hasCollision = true;
  106.                 }
  107.  
  108.                 rx = Math.Abs(qMove.X) > Math.Abs(rx) ? qMove.X : rx;
  109.                 ry = Math.Abs(qMove.Y) > Math.Abs(ry) ? qMove.Y : ry;
  110.                 rz = Math.Abs(qMove.Z) > Math.Abs(rz) ? qMove.Z : rz;
  111.  
  112.                 Vector3 edgeMove;
  113.                 if (EdgeSphereCollision(ref sphere, ref point1, ref point2, ref point3, out edgeMove))
  114.                 {
  115.                     if (edgeMove.LengthSquared() > (rx * rx + ry * ry + rz * rz))
  116.                     {
  117.                         rx = edgeMove.X;
  118.                         ry = edgeMove.Y;
  119.                         rz = edgeMove.Z;
  120.                     }
  121.                     hasCollision = true;
  122.                 }
  123.             }
  124.  
  125.             movement = new Vector3(rx, ry, rz);
  126.  
  127.             return hasCollision;
  128.         }
  129.  
  130.  
  131.         /// <summary>
  132.         /// Проверка пересечения сферы и граней треугольника
  133.         /// </summary>
  134.         /// <param name="sphere">Сфера</param>
  135.         /// <param name="p1">Первая вершина треугольника</param>
  136.         /// <param name="p2">Вторая вершина треугольника</param>
  137.         /// <param name="p3">Третья вершина треугольника</param>
  138.         /// <param name="movement">Вектор движения камеры для выхода из коллизии</param>
  139.         /// <returns>true если есть пересечение, иначе false</returns>
  140.         private static bool EdgeSphereCollision(ref BoundingSphere sphere, ref Vector3 p1, ref Vector3 p2, ref Vector3 p3, out Vector3 movement)
  141.         {
  142.             movement = Vector3.Zero;
  143.  
  144.  
  145.             Vector3 center = sphere.Center;
  146.             float radius = sphere.Radius * 1.0f;
  147.  
  148.             Vector3 lp1 = ClosestPointOnLine(ref center, ref p1, ref p2);
  149.             Vector3 lp2 = ClosestPointOnLine(ref center, ref p2, ref p3);
  150.             Vector3 lp3 = ClosestPointOnLine(ref center, ref p3, ref p1);
  151.  
  152.             float lp1Dist = (center - lp1).Length();
  153.             float lp2Dist = (center - lp2).Length();
  154.             float lp3Dist = (center - lp3).Length();
  155.  
  156.             Vector3 dir = Vector3.Zero;
  157.             float dist = 0;
  158.  
  159.             if (lp1Dist < radius)
  160.             {
  161.                 dir = center - lp1;
  162.                 dist = radius - lp1Dist;
  163.             }
  164.  
  165.             if (lp2Dist < radius)
  166.             {
  167.                 dir = center - lp2;
  168.                 dist = radius - lp2Dist;
  169.             }
  170.  
  171.             if (lp3Dist < radius)
  172.             {
  173.                 dir = center - lp3;
  174.                 dist = radius - lp3Dist;
  175.             }
  176.  
  177.             if (dist == 0)
  178.                 return false;
  179.  
  180.             dir.Normalize();
  181.             dir *= dist;
  182.             movement = dir;
  183.  
  184.             return true;
  185.         }
  186.  
  187.         /// <summary>
  188.         /// Принадлежит ли точка треугольнику
  189.         /// </summary>
  190.         /// <param name="p">Искомая точка</param>
  191.         /// <param name="a">Первая точка треугольника</param>
  192.         /// <param name="b">Вторая точка треугольника</param>
  193.         /// <param name="c">Третья точка треугольника</param>
  194.         /// <returns>true если принадлежит, иначе false</returns>
  195.         private static bool IsPointInTriangle(ref Vector3 p, ref Vector3 a, ref Vector3 b, ref Vector3 c)
  196.         {
  197.             //Коэффициенты для приближенного сравнения
  198.             float lowRange = 0.999995f;
  199.             float highRange = 1.000005f;
  200.  
  201.             //Считаем площадь треугольнка abc, потом площади abp, acp, bcp, сравниваем
  202.             float abc = TriangleSquare(ref a, ref b, ref c);
  203.  
  204.             float abp = TriangleSquare(ref a, ref b, ref p);
  205.             float acp = TriangleSquare(ref a, ref c, ref p);
  206.             float bcp = TriangleSquare(ref b, ref c, ref p);
  207.  
  208.             //Площадь возможных внутренних треугольников
  209.             float summ = abp + acp + bcp;
  210.  
  211.             //Отношение площадей
  212.             float qEff = summ / abc;
  213.  
  214.             //Если отношение площадей примерно равно едиинице, то точка в треугольнике
  215.             if (qEff > lowRange && qEff < highRange)
  216.                 return true;
  217.  
  218.             return false;
  219.         }
  220.  
  221.         /// <summary>
  222.         /// Площадь треуглольника по трем вершинам
  223.         /// </summary>
  224.         /// <param name="p1">Первая вершина</param>
  225.         /// <param name="p2">Вторая вершина</param>
  226.         /// <param name="p3">Третья вершина</param>
  227.         /// <returns></returns>
  228.         private static float TriangleSquare(ref Vector3 p1, ref Vector3 p2, ref Vector3 p3)
  229.         {
  230.             Vector3 v1 = p1 - p2;
  231.             Vector3 v2 = p1 - p3;
  232.             return (float)Math.Abs(Vector3.Cross(v1, v2).Length()) / 2;
  233.         }
  234.  
  235.         /// <summary>
  236.         /// Находит ближайшую точку на линии для точки point
  237.         /// </summary>
  238.         /// <param name="point">Исходная точка</param>
  239.         /// <param name="lineBegin">Начало линии</param>
  240.         /// <param name="lineEnd">Конец линии</param>
  241.         /// <returns></returns>
  242.         private static Vector3 ClosestPointOnLine(ref Vector3 point, ref Vector3 lineBegin, ref Vector3 lineEnd)
  243.         {
  244.             Vector3 diff = point - lineBegin;
  245.             Vector3 lineDirection = lineEnd - lineBegin;
  246.             float lineLength = lineDirection.Length();
  247.             lineDirection.Normalize();
  248.             float t = Vector3.Dot(lineDirection, diff);
  249.             if (t <= 0) return lineBegin;
  250.             if (t >= lineLength) return lineEnd;
  251.  
  252.             Vector3 ret = lineBegin + lineDirection * t;
  253.             return ret;
  254.         }
  255.     }
  256. }
Advertisement
Add Comment
Please, Sign In to add comment