CartBlanche пре 1 месец
родитељ
комит
8f35115068
33 измењених фајлова са 2711 додато и 2447 уклоњено
  1. 27 0
      CollisionSample/.vscode/launch.json
  2. 88 0
      CollisionSample/.vscode/tasks.json
  3. 0 6
      CollisionSample/App.config
  4. 0 72
      CollisionSample/CollisionSample.csproj
  5. 48 0
      CollisionSample/CollisionSample.sln
  6. BIN
      CollisionSample/Content/Font.xnb
  7. 672 692
      CollisionSample/Core/BoundingOrientedBox.cs
  8. 14 0
      CollisionSample/Core/CollisionSample.Core.csproj
  9. 558 565
      CollisionSample/Core/CollisionSample.cs
  10. 0 0
      CollisionSample/Core/Content/Background.png
  11. 0 0
      CollisionSample/Core/Content/Game.ico
  12. 0 0
      CollisionSample/Core/Content/GameThumbnail.png
  13. 16 0
      CollisionSample/Core/Content/app.manifest
  14. 325 335
      CollisionSample/Core/DebugDraw.cs
  15. 89 95
      CollisionSample/Core/FrameRateCounter.cs
  16. 40 40
      CollisionSample/Core/GeomUtil.cs
  17. 564 578
      CollisionSample/Core/TriangleTest.cs
  18. 36 0
      CollisionSample/Platforms/Android/Activity1.cs
  19. 22 0
      CollisionSample/Platforms/Android/CollisionSample.Android.csproj
  20. 8 0
      CollisionSample/Platforms/Android/Properties/AndroidManifest.xml
  21. 4 0
      CollisionSample/Platforms/Android/Resources/Values/Strings.xml
  22. 6 0
      CollisionSample/Platforms/Android/Resources/Values/Styles.xml
  23. BIN
      CollisionSample/Platforms/Android/Resources/drawable/icon.png
  24. 27 0
      CollisionSample/Platforms/Desktop/CollisionSample.DesktopGL.csproj
  25. 14 0
      CollisionSample/Platforms/Desktop/Program.cs
  26. 28 0
      CollisionSample/Platforms/Windows/CollisionSample.Windows.csproj
  27. 15 0
      CollisionSample/Platforms/Windows/Program.cs
  28. 19 0
      CollisionSample/Platforms/iOS/AppDelegate.cs
  29. 27 0
      CollisionSample/Platforms/iOS/CollisionSample.iOS.csproj
  30. 0 0
      CollisionSample/Platforms/iOS/Info.plist
  31. 13 0
      CollisionSample/Platforms/iOS/Program.cs
  32. 0 64
      CollisionSample/Program.cs
  33. 51 0
      CollisionSample/README.md

+ 27 - 0
CollisionSample/.vscode/launch.json

@@ -0,0 +1,27 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch DesktopGL",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-desktopgl",
+            "program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net8.0/Collision",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Launch Windows",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-windows",
+            "program": "${workspaceFolder}/Platforms/Windows/bin/Debug/net8.0-windows/Collision.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        }
+    ]
+}

+ 88 - 0
CollisionSample/.vscode/tasks.json

@@ -0,0 +1,88 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "build-desktopgl",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/Platforms/Desktop/CollisionSample.DesktopGL.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            },
+            "presentation": {
+                "reveal": "silent"
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "build-windows",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/Platforms/Windows/CollisionSample.Windows.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "group": "build",
+            "presentation": {
+                "reveal": "silent"
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "build-android",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/CollisionSample.Android.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "group": "build",
+            "presentation": {
+                "reveal": "always"
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "publish",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "publish",
+                "${workspaceFolder}/CollisionSample.DesktopGL.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "group": "build",
+            "presentation": {
+                "reveal": "silent"
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "watch",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "watch",
+                "run",
+                "--project",
+                "${workspaceFolder}/CollisionSample.DesktopGL.csproj"
+            ],
+            "group": "build",
+            "presentation": {
+                "reveal": "always"
+            },
+            "problemMatcher": "$msCompile"
+        }
+    ]
+}

+ 0 - 6
CollisionSample/App.config

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<configuration>
-  <startup useLegacyV2RuntimeActivationPolicy="true">
-    <supportedRuntime version="v4.0"/>
-  </startup>
-</configuration>

+ 0 - 72
CollisionSample/CollisionSample.csproj

@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{AFFFC991-4956-45EF-8746-C1AE3FC7A245}</ProjectGuid>
-    <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <OutputType>Exe</OutputType>
-    <RootNamespace>CollisionSample</RootNamespace>
-    <AssemblyName>Collision</AssemblyName>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG; MONOMAC</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <PlatformTarget>x86</PlatformTarget>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
-    <DebugType>none</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <PlatformTarget>x86</PlatformTarget>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Drawing" />
-    <Reference Include="MonoMac" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="Info.plist" />
-    <None Include="App.config" />
-    <None Include="Background.png" />
-    <None Include="Game.ico" />
-    <None Include="GameThumbnail.png" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
-  <ItemGroup>
-    <ProjectReference Include="..\..\MonoGame.Framework\MonoGame.Framework.MacOS.csproj">
-      <Project>{36C538E6-C32A-4A8D-A39C-566173D7118E}</Project>
-      <Name>MonoGame.Framework.MacOS</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="BoundingOrientedBox.cs" />
-    <Compile Include="CollisionSample.cs" />
-    <Compile Include="DebugDraw.cs" />
-    <Compile Include="FrameRateCounter.cs" />
-    <Compile Include="GeomUtil.cs" />
-    <Compile Include="Program.cs" />
-    <Compile Include="TriangleTest.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Content\" />
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="Content\Font.xnb" />
-  </ItemGroup>
-</Project>

+ 48 - 0
CollisionSample/CollisionSample.sln

@@ -0,0 +1,48 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CollisionSample", "CollisionSample.csproj", "{5AFF6754-07AD-AE68-CA0D-F7A4E2A23328}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CollisionSample.Windows", "CollisionSample.Windows.csproj", "{AFFFC991-4956-45EF-8746-C1AE3FC7A246}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CollisionSample.DesktopGL", "CollisionSample.DesktopGL.csproj", "{AFFFC991-4956-45EF-8746-C1AE3FC7A247}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CollisionSample.Android", "CollisionSample.Android.csproj", "{AFFFC991-4956-45EF-8746-C1AE3FC7A248}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CollisionSample.iOS", "CollisionSample.iOS.csproj", "{AFFFC991-4956-45EF-8746-C1AE3FC7A249}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{5AFF6754-07AD-AE68-CA0D-F7A4E2A23328}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5AFF6754-07AD-AE68-CA0D-F7A4E2A23328}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5AFF6754-07AD-AE68-CA0D-F7A4E2A23328}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5AFF6754-07AD-AE68-CA0D-F7A4E2A23328}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A246}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A246}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A246}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A247}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A247}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A247}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A247}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A248}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A248}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A248}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A248}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A249}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A249}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A249}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A249}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {1EBD85DC-5A1C-4282-8227-E0F27DB079BC}
+	EndGlobalSection
+EndGlobal

BIN
CollisionSample/Content/Font.xnb


+ 672 - 692
CollisionSample/BoundingOrientedBox.cs → CollisionSample/Core/BoundingOrientedBox.cs

@@ -1,692 +1,672 @@
-//-----------------------------------------------------------------------------
-// BoundingOrientedBox.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-using System;
-using System.Collections.Generic;
-using Microsoft.Xna.Framework;
-
-
-namespace CollisionSample
-{
-    /// <summary>
-    /// Bounding volume using an oriented bounding box.
-    /// </summary>
-    public struct BoundingOrientedBox : IEquatable<BoundingOrientedBox>
-    {
-        #region Constants
-        public const int CornerCount = 8;
-
-        // Epsilon value used in ray tests, where a ray might hit the box almost edge-on.
-        const float RAY_EPSILON = 1e-20F;
-        #endregion
-
-        #region Fields
-        public Vector3 Center;
-        public Vector3 HalfExtent;
-        public Quaternion Orientation;
-        #endregion
-
-        #region Constructors
-
-        /// <summary>
-        /// Create an oriented box with the given center, half-extents, and orientation.
-        /// </summary>
-        public BoundingOrientedBox(Vector3 center, Vector3 halfExtents, Quaternion orientation)
-        {
-            Center = center;
-            HalfExtent = halfExtents;
-            Orientation = orientation;
-        }
-
-        /// <summary>
-        /// Create an oriented box from an axis-aligned box.
-        /// </summary>
-        public static BoundingOrientedBox CreateFromBoundingBox(BoundingBox box)
-        {
-            Vector3 mid = (box.Min + box.Max) * 0.5f;
-            Vector3 halfExtent = (box.Max - box.Min) * 0.5f;
-            return new BoundingOrientedBox(mid, halfExtent, Quaternion.Identity);
-        }
-
-        /// <summary>
-        /// Transform the given bounding box by a rotation around the origin followed by a translation 
-        /// </summary>
-        /// <param name="rotation"></param>
-        /// <param name="translation"></param>
-        /// <returns>A new bounding box, transformed relative to this one</returns>
-        public BoundingOrientedBox Transform(Quaternion rotation, Vector3 translation)
-        {
-            return new BoundingOrientedBox(Vector3.Transform(Center, rotation) + translation,
-                                            HalfExtent,
-                                            Orientation * rotation);
-        }
-
-        /// <summary>
-        /// Transform the given bounding box by a uniform scale and rotation around the origin followed
-        /// by a translation
-        /// </summary>
-        /// <returns>A new bounding box, transformed relative to this one</returns>
-        public BoundingOrientedBox Transform(float scale, Quaternion rotation, Vector3 translation)
-        {
-            return new BoundingOrientedBox(Vector3.Transform(Center * scale, rotation) + translation,
-                                            HalfExtent * scale,
-                                            Orientation * rotation);
-        }
-        
-        #endregion
-
-        #region IEquatable implementation
-
-        public bool Equals(BoundingOrientedBox other)
-        {
-            return (Center == other.Center && HalfExtent == other.HalfExtent && Orientation == other.Orientation);
-        }
-        
-        public override bool Equals(Object obj)
-        {
-            if (obj != null && obj is BoundingOrientedBox)
-            {
-                BoundingOrientedBox other = (BoundingOrientedBox)obj;
-                return (Center == other.Center && HalfExtent == other.HalfExtent && Orientation == other.Orientation);
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        public override int GetHashCode()
-        {
-            return Center.GetHashCode() ^ HalfExtent.GetHashCode() ^ Orientation.GetHashCode();
-        }
-
-        public static bool operator==(BoundingOrientedBox a, BoundingOrientedBox b)
-        {
-            return Equals(a, b);
-        }
-
-        public static bool operator!=(BoundingOrientedBox a, BoundingOrientedBox b)
-        {
-            return !Equals(a, b);
-        }
-
-        public override string ToString()
-        {
-            return "{Center:" + Center.ToString() +
-                   " Extents:" + HalfExtent.ToString() +
-                   " Orientation:" + Orientation.ToString() + "}";
-        }
-
-        #endregion
-
-        #region Test vs. BoundingBox
-
-        /// <summary>
-        /// Determine if box A intersects box B.
-        /// </summary>
-        public bool Intersects(ref BoundingBox box)
-        {
-            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
-            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
-
-            Matrix mb = Matrix.CreateFromQuaternion(Orientation);
-            mb.Translation = Center - boxCenter;
-
-            return ContainsRelativeBox(ref boxHalfExtent, ref HalfExtent, ref mb) != ContainmentType.Disjoint;
-        }
-
-        /// <summary>
-        /// Determine if this box contains, intersects, or is disjoint from the given BoundingBox.
-        /// </summary>
-        public ContainmentType Contains(ref BoundingBox box)
-        {
-            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
-            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
-
-            // Build the 3x3 rotation matrix that defines the orientation of 'other' relative to this box
-            Quaternion relOrient;
-            Quaternion.Conjugate(ref Orientation, out relOrient);
-
-            Matrix relTransform = Matrix.CreateFromQuaternion(relOrient);
-            relTransform.Translation = Vector3.TransformNormal(boxCenter - Center, relTransform);
-
-            return ContainsRelativeBox(ref HalfExtent, ref boxHalfExtent, ref relTransform);
-        }
-
-        /// <summary>
-        /// Determine if box A contains, intersects, or is disjoint from box B.
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingBox boxA, ref BoundingOrientedBox oboxB)
-        {
-            Vector3 boxA_halfExtent = (boxA.Max - boxA.Min) * 0.5f;
-            Vector3 boxA_center = (boxA.Max + boxA.Min) * 0.5f;
-            Matrix mb = Matrix.CreateFromQuaternion(oboxB.Orientation);
-            mb.Translation = oboxB.Center - boxA_center;
-
-            return BoundingOrientedBox.ContainsRelativeBox(ref boxA_halfExtent, ref oboxB.HalfExtent, ref mb);
-        }
-
-        #endregion
-
-        #region Test vs. BoundingOrientedBox
-
-        /// <summary>
-        /// Returns true if this box intersects the given other box.
-        /// </summary>
-        public bool Intersects(ref BoundingOrientedBox other)
-        {
-            return Contains(ref other) != ContainmentType.Disjoint;
-        }
-
-        /// <summary>
-        /// Determine whether this box contains, intersects, or is disjoint from
-        /// the given other box.
-        /// </summary>
-        public ContainmentType Contains(ref BoundingOrientedBox other)
-        {
-            // Build the 3x3 rotation matrix that defines the orientation of 'other' relative to this box
-            Quaternion invOrient;
-            Quaternion.Conjugate(ref Orientation, out invOrient);
-            Quaternion relOrient;
-            Quaternion.Multiply(ref invOrient, ref other.Orientation, out relOrient);
-
-            Matrix relTransform = Matrix.CreateFromQuaternion(relOrient);
-            relTransform.Translation = Vector3.Transform(other.Center - Center, invOrient);
-
-            return ContainsRelativeBox(ref HalfExtent, ref other.HalfExtent, ref relTransform);
-        }
-
-        #endregion
-
-        #region Test vs. BoundingFrustum
-
-        /// <summary>
-        /// Determine whether this box contains, intersects, or is disjoint from
-        /// the given frustum.
-        /// </summary>
-        public ContainmentType Contains(BoundingFrustum frustum)
-        {
-            // Convert this bounding box to an equivalent BoundingFrustum, so we can rely on BoundingFrustum's
-            // implementation. Note that this is very slow, since BoundingFrustum builds various data structures
-            // for this test that it caches internally. To speed it up, you could convert the box to a frustum
-            // just once and re-use that frustum for repeated tests.
-            BoundingFrustum temp = ConvertToFrustum();
-            return temp.Contains(frustum);
-        }
-
-        /// <summary>
-        /// Returns true if this box intersects the given frustum.
-        /// </summary>
-        public bool Intersects(BoundingFrustum frustum)
-        {
-            return (Contains(frustum) != ContainmentType.Disjoint);
-        }
-
-        /// <summary>
-        /// Determine whether the given frustum contains, intersects, or is disjoint from
-        /// the given oriented box.
-        /// </summary>
-        public static ContainmentType Contains(BoundingFrustum frustum, ref BoundingOrientedBox obox)
-        {
-            return frustum.Contains(obox.ConvertToFrustum());
-        }
-
-        #endregion
-
-        #region Test vs. BoundingSphere
-
-        /// <summary>
-        /// Test whether this box contains, intersects, or is disjoint from the given sphere
-        /// </summary>
-        public ContainmentType Contains(ref BoundingSphere sphere)
-        {
-            // Transform the sphere into local box space
-            Quaternion iq = Quaternion.Conjugate(Orientation);
-            Vector3 localCenter = Vector3.Transform(sphere.Center - Center, iq);
-
-            // (dx,dy,dz) = signed distance of center of sphere from edge of box
-            float dx = Math.Abs(localCenter.X) - HalfExtent.X;
-            float dy = Math.Abs(localCenter.Y) - HalfExtent.Y;
-            float dz = Math.Abs(localCenter.Z) - HalfExtent.Z;
-
-            // Check for sphere completely inside box
-            float r = sphere.Radius;
-            if (dx <= -r && dy <= -r && dz <= -r)
-                return ContainmentType.Contains;
-
-            // Compute how far away the sphere is in each dimension
-            dx = Math.Max(dx, 0.0f);
-            dy = Math.Max(dy, 0.0f);
-            dz = Math.Max(dz, 0.0f);
-
-            if(dx*dx + dy*dy + dz*dz >= r*r)
-                return ContainmentType.Disjoint;
-
-            return ContainmentType.Intersects;
-        }
-
-        /// <summary>
-        /// Test whether this box intersects the given sphere
-        /// </summary>
-        public bool Intersects(ref BoundingSphere sphere)
-        {
-            // Transform the sphere into local box space
-            Quaternion iq = Quaternion.Conjugate(Orientation);
-            Vector3 localCenter = Vector3.Transform(sphere.Center - Center, iq);
-
-            // (dx,dy,dz) = signed distance of center of sphere from edge of box
-            float dx = Math.Abs(localCenter.X) - HalfExtent.X;
-            float dy = Math.Abs(localCenter.Y) - HalfExtent.Y;
-            float dz = Math.Abs(localCenter.Z) - HalfExtent.Z;
-
-            // Compute how far away the sphere is in each dimension
-            dx = Math.Max(dx, 0.0f);
-            dy = Math.Max(dy, 0.0f);
-            dz = Math.Max(dz, 0.0f);
-            float r = sphere.Radius;
-
-            return dx * dx + dy * dy + dz * dz < r * r;
-        }
-
-        /// <summary>
-        /// Test whether a BoundingSphere contains, intersects, or is disjoint from a BoundingOrientedBox
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingSphere sphere, ref BoundingOrientedBox box)
-        {
-            // Transform the sphere into local box space
-            Quaternion iq = Quaternion.Conjugate(box.Orientation);
-            Vector3 localCenter = Vector3.Transform(sphere.Center - box.Center, iq);
-            localCenter.X = Math.Abs(localCenter.X);
-            localCenter.Y = Math.Abs(localCenter.Y);
-            localCenter.Z = Math.Abs(localCenter.Z);
-
-            // Check for box completely inside sphere
-            float rSquared = sphere.Radius * sphere.Radius;
-            if ((localCenter + box.HalfExtent).LengthSquared() <= rSquared)
-                return ContainmentType.Contains;
-
-            // (dx,dy,dz) = signed distance of center of sphere from edge of box
-            Vector3 d = localCenter - box.HalfExtent;
-
-            // Compute how far away the sphere is in each dimension
-            d.X = Math.Max(d.X, 0.0f);
-            d.Y = Math.Max(d.Y, 0.0f);
-            d.Z = Math.Max(d.Z, 0.0f);
-
-            if (d.LengthSquared() >= rSquared)
-                return ContainmentType.Disjoint;
-
-            return ContainmentType.Intersects;
-        }
-
-        #endregion
-
-        #region Test vs. 0/1/2d primitives
-
-        /// <summary>
-        /// Returns true if this box contains the given point.
-        /// </summary>
-        public bool Contains(ref Vector3 point)
-        {
-            // Transform the point into box-local space and check against
-            // our extents.
-            Quaternion qinv = Quaternion.Conjugate(Orientation);
-            Vector3 plocal = Vector3.Transform(point - Center, qinv);
-
-            return Math.Abs(plocal.X) <= HalfExtent.X &&
-                   Math.Abs(plocal.Y) <= HalfExtent.Y &&
-                   Math.Abs(plocal.Z) <= HalfExtent.Z;
-        }
-
-        /// <summary>
-        /// Determine whether the given ray intersects this box. If so, returns
-        /// the parametric value of the point of first intersection; otherwise
-        /// returns null.
-        /// </summary>
-        public float? Intersects(ref Ray ray)
-        {
-            Matrix R = Matrix.CreateFromQuaternion(Orientation);
-
-            Vector3 TOrigin = Center - ray.Position;
-
-            float t_min = -float.MaxValue;
-            float t_max = float.MaxValue;
-
-            // X-case
-            float axisDotOrigin = Vector3.Dot(R.Right, TOrigin);
-            float axisDotDir = Vector3.Dot(R.Right, ray.Direction);
-
-            if (axisDotDir >= -RAY_EPSILON && axisDotDir <= RAY_EPSILON)
-            {
-                if ((-axisDotOrigin - HalfExtent.X) > 0.0 || (-axisDotOrigin + HalfExtent.X) > 0.0f)
-                    return null;
-            }
-            else
-            {
-                float t1 = (axisDotOrigin - HalfExtent.X) / axisDotDir;
-                float t2 = (axisDotOrigin + HalfExtent.X) / axisDotDir;
-
-                if (t1 > t2)
-                {
-                    float temp = t1;
-                    t1 = t2;
-                    t2 = temp;
-                }
-
-                if (t1 > t_min)
-                    t_min = t1;
-
-                if (t2 < t_max)
-                    t_max = t2;
-
-                if (t_max < 0.0f || t_min > t_max)
-                    return null;
-            }
-
-            // Y-case
-            axisDotOrigin = Vector3.Dot(R.Up, TOrigin);
-            axisDotDir = Vector3.Dot(R.Up, ray.Direction);
-
-            if (axisDotDir >= -RAY_EPSILON && axisDotDir <= RAY_EPSILON)
-            {
-                if ((-axisDotOrigin - HalfExtent.Y) > 0.0 || (-axisDotOrigin + HalfExtent.Y) > 0.0f)
-                    return null;
-            }
-            else
-            {
-                float t1 = (axisDotOrigin - HalfExtent.Y) / axisDotDir;
-                float t2 = (axisDotOrigin + HalfExtent.Y) / axisDotDir;
-
-                if (t1 > t2)
-                {
-                    float temp = t1;
-                    t1 = t2;
-                    t2 = temp;
-                }
-
-                if (t1 > t_min)
-                    t_min = t1;
-
-                if (t2 < t_max)
-                    t_max = t2;
-
-                if (t_max < 0.0f || t_min > t_max)
-                    return null;
-            }
-
-            // Z-case
-            axisDotOrigin = Vector3.Dot(R.Forward, TOrigin);
-            axisDotDir = Vector3.Dot(R.Forward, ray.Direction);
-
-            if (axisDotDir >= -RAY_EPSILON && axisDotDir <= RAY_EPSILON)
-            {
-                if ((-axisDotOrigin - HalfExtent.Z) > 0.0 || (-axisDotOrigin + HalfExtent.Z) > 0.0f)
-                    return null;
-            }
-            else
-            {
-                float t1 = (axisDotOrigin - HalfExtent.Z) / axisDotDir;
-                float t2 = (axisDotOrigin + HalfExtent.Z) / axisDotDir;
-
-                if (t1 > t2)
-                {
-                    float temp = t1;
-                    t1 = t2;
-                    t2 = temp;
-                }
-
-                if (t1 > t_min)
-                    t_min = t1;
-
-                if (t2 < t_max)
-                    t_max = t2;
-
-                if (t_max < 0.0f || t_min > t_max)
-                    return null;
-            }
-
-            return t_min;
-        }
-
-        /// <summary>
-        /// Classify this bounding box as entirely in front of, in back of, or
-        /// intersecting the given plane.
-        /// </summary>
-        public PlaneIntersectionType Intersects(ref Plane plane)
-        {
-            float dist = plane.DotCoordinate(Center);
-
-            // Transform the plane's normal into this box's space
-            Vector3 localNormal = Vector3.Transform(plane.Normal, Quaternion.Conjugate(Orientation));
-
-            // Project the axes of the box onto the normal of the plane.  Half the
-            // length of the projection (sometime called the "radius") is equal to
-            // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w))
-            // where h(i) are extents of the box, n is the plane normal, and b(i) are the 
-            // axes of the box.
-            float r = Math.Abs(HalfExtent.X*localNormal.X)
-                    + Math.Abs(HalfExtent.Y*localNormal.Y)
-                    + Math.Abs(HalfExtent.Z*localNormal.Z);
-
-            if(dist > r)
-            {
-                return PlaneIntersectionType.Front;
-            }
-            else if(dist < -r)
-            {
-                return PlaneIntersectionType.Back;
-            }
-            else
-            {
-                return PlaneIntersectionType.Intersecting;
-            }
-        }
-
-        #endregion
-
-        #region Helper methods
-
-        /// <summary>
-        /// Return the 8 corner positions of this bounding box.
-        ///
-        ///     ZMax    ZMin
-        ///    0----1  4----5
-        ///    |    |  |    |
-        ///    |    |  |    |
-        ///    3----2  7----6
-        ///
-        /// The ordering of indices is a little strange to match what BoundingBox.GetCorners() does.
-        /// </summary>
-        public Vector3[] GetCorners()
-        {
-            Vector3[] corners = new Vector3[CornerCount];
-            GetCorners(corners, 0);
-            return corners;
-        }
-
-        /// <summary>
-        /// Return the 8 corner positions of this bounding box.
-        ///
-        ///     ZMax    ZMin
-        ///    0----1  4----5
-        ///    |    |  |    |
-        ///    |    |  |    |
-        ///    3----2  7----6
-        ///
-        /// The ordering of indices is a little strange to match what BoundingBox.GetCorners() does.
-        /// </summary>
-        /// <param name="corners">Array to fill with the eight corner positions</param>
-        /// <param name="startIndex">Index within corners array to start writing positions</param>
-        public void GetCorners(Vector3[] corners, int startIndex)
-        {
-            Matrix m = Matrix.CreateFromQuaternion(Orientation);
-            Vector3 hX = m.Left * HalfExtent.X;
-            Vector3 hY = m.Up * HalfExtent.Y;
-            Vector3 hZ = m.Backward * HalfExtent.Z;
-
-            int i = startIndex;
-            corners[i++] = Center - hX + hY + hZ;
-            corners[i++] = Center + hX + hY + hZ;
-            corners[i++] = Center + hX - hY + hZ;
-            corners[i++] = Center - hX - hY + hZ;
-            corners[i++] = Center - hX + hY - hZ;
-            corners[i++] = Center + hX + hY - hZ;
-            corners[i++] = Center + hX - hY - hZ;
-            corners[i++] = Center - hX - hY - hZ;
-        }
-
- 
-        /// <summary>
-        /// Determine whether the box described by half-extents hA, axis-aligned and centered at the origin, contains
-        /// the box described by half-extents hB, whose position and orientation are given by the transform matrix mB.
-        /// The matrix is assumed to contain only rigid motion; if it contains scaling or perpsective the result of
-        /// this method will be incorrect.
-        /// </summary>
-        /// <param name="hA">Half-extents of first box</param>
-        /// <param name="hB">Half-extents of second box</param>
-        /// <param name="mB">Position and orientation of second box relative to first box</param>
-        /// <returns>ContainmentType enum indicating whether the boxes are disjoin, intersecting, or
-        /// whether box A contains box B.</returns>
-        public static ContainmentType ContainsRelativeBox(ref Vector3 hA, ref Vector3 hB, ref Matrix mB)
-        {
-            Vector3 mB_T = mB.Translation;
-            Vector3 mB_TA = new Vector3(Math.Abs(mB_T.X), Math.Abs(mB_T.Y), Math.Abs(mB_T.Z));
-
-            // Transform the extents of B
-            // TODO: check which coords Right/Up/Back refer to and access them directly. This looks dumb.
-            Vector3 bX = mB.Right;      // x-axis of box B
-            Vector3 bY = mB.Up;         // y-axis of box B
-            Vector3 bZ = mB.Backward;   // z-axis of box B
-            Vector3 hx_B = bX * hB.X;   // x extent of box B
-            Vector3 hy_B = bY * hB.Y;   // y extent of box B
-            Vector3 hz_B = bZ * hB.Z;   // z extent of box B
-
-            // Check for containment first.
-            float projx_B = Math.Abs(hx_B.X) + Math.Abs(hy_B.X) + Math.Abs(hz_B.X);
-            float projy_B = Math.Abs(hx_B.Y) + Math.Abs(hy_B.Y) + Math.Abs(hz_B.Y);
-            float projz_B = Math.Abs(hx_B.Z) + Math.Abs(hy_B.Z) + Math.Abs(hz_B.Z);
-            if (mB_TA.X + projx_B <= hA.X && mB_TA.Y + projy_B <= hA.Y && mB_TA.Z + projz_B <= hA.Z)
-                return ContainmentType.Contains;
-
-            // Check for separation along the faces of the other box,
-            // by projecting each local axis onto the other boxes' axes
-            // http://www.cs.unc.edu/~geom/theses/gottschalk/main.pdf
-            //
-            // The general test form, given a choice of separating axis, is:
-            //      sizeA = abs(dot(A.e1,axis)) + abs(dot(A.e2,axis)) + abs(dot(A.e3,axis))
-            //      sizeB = abs(dot(B.e1,axis)) + abs(dot(B.e2,axis)) + abs(dot(B.e3,axis))
-            //      distance = abs(dot(B.center - A.center),axis))
-            //      if distance >= sizeA+sizeB, the boxes are disjoint
-            //
-            // We need to do this test on 15 axes:
-            //      x, y, z axis of box A
-            //      x, y, z axis of box B
-            //      (v1 cross v2) for each v1 in A's axes, for each v2 in B's axes
-            //
-            // Since we're working in a space where A is axis-aligned and A.center=0, many
-            // of the tests and products simplify away.
-
-            // Check for separation along the axes of box A
-            if (mB_TA.X >= hA.X + Math.Abs(hx_B.X) + Math.Abs(hy_B.X) + Math.Abs(hz_B.X))
-                return ContainmentType.Disjoint;
-
-            if (mB_TA.Y >= hA.Y + Math.Abs(hx_B.Y) + Math.Abs(hy_B.Y) + Math.Abs(hz_B.Y))
-                return ContainmentType.Disjoint;
-
-            if (mB_TA.Z >= hA.Z + Math.Abs(hx_B.Z) + Math.Abs(hy_B.Z) + Math.Abs(hz_B.Z))
-                return ContainmentType.Disjoint;
-
-            // Check for separation along the axes box B, hx_B/hy_B/hz_B
-            if (Math.Abs(Vector3.Dot(mB_T, bX)) >= Math.Abs(hA.X * bX.X) + Math.Abs(hA.Y * bX.Y) + Math.Abs(hA.Z * bX.Z) + hB.X)
-                return ContainmentType.Disjoint;
-
-            if (Math.Abs(Vector3.Dot(mB_T, bY)) >= Math.Abs(hA.X * bY.X) + Math.Abs(hA.Y * bY.Y) + Math.Abs(hA.Z * bY.Z) + hB.Y)
-                return ContainmentType.Disjoint;
-
-            if (Math.Abs(Vector3.Dot(mB_T, bZ)) >= Math.Abs(hA.X * bZ.X) + Math.Abs(hA.Y * bZ.Y) + Math.Abs(hA.Z * bZ.Z) + hB.Z)
-                return ContainmentType.Disjoint;
-
-            // Check for separation in plane containing an axis of box A and and axis of box B
-            //
-            // We need to compute all 9 cross products to find them, but a lot of terms drop out
-            // since we're working in A's local space. Also, since each such plane is parallel
-            // to the defining axis in each box, we know those dot products will be 0 and can
-            // omit them.
-            Vector3 axis;
-
-            // a.X ^ b.X = (1,0,0) ^ bX
-            axis = new Vector3(0, -bX.Z, bX.Y);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Y * axis.Y) + Math.Abs(hA.Z * axis.Z) + Math.Abs(Vector3.Dot(axis, hy_B)) + Math.Abs(Vector3.Dot(axis, hz_B)))
-                return ContainmentType.Disjoint;
-
-            // a.X ^ b.Y = (1,0,0) ^ bY
-            axis = new Vector3(0, -bY.Z, bY.Y);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Y * axis.Y) + Math.Abs(hA.Z * axis.Z) + Math.Abs(Vector3.Dot(axis, hz_B)) + Math.Abs(Vector3.Dot(axis, hx_B)))
-                return ContainmentType.Disjoint;
-
-            // a.X ^ b.Z = (1,0,0) ^ bZ
-            axis = new Vector3(0, -bZ.Z, bZ.Y);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Y * axis.Y) + Math.Abs(hA.Z * axis.Z) + Math.Abs(Vector3.Dot(axis, hx_B)) + Math.Abs(Vector3.Dot(axis, hy_B)))
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.X = (0,1,0) ^ bX
-            axis = new Vector3(bX.Z, 0, -bX.X);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Z * axis.Z) + Math.Abs(hA.X * axis.X) + Math.Abs(Vector3.Dot(axis, hy_B)) + Math.Abs(Vector3.Dot(axis, hz_B)))
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.Y = (0,1,0) ^ bY
-            axis = new Vector3(bY.Z, 0, -bY.X);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Z * axis.Z) + Math.Abs(hA.X * axis.X) + Math.Abs(Vector3.Dot(axis, hz_B)) + Math.Abs(Vector3.Dot(axis, hx_B)))
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.Z = (0,1,0) ^ bZ
-            axis = new Vector3(bZ.Z, 0, -bZ.X);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Z * axis.Z) + Math.Abs(hA.X * axis.X) + Math.Abs(Vector3.Dot(axis, hx_B)) + Math.Abs(Vector3.Dot(axis, hy_B)))
-                return ContainmentType.Disjoint;
-
-            // a.Z ^ b.X = (0,0,1) ^ bX
-            axis = new Vector3(-bX.Y, bX.X, 0);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.X * axis.X) + Math.Abs(hA.Y * axis.Y) + Math.Abs(Vector3.Dot(axis, hy_B)) + Math.Abs(Vector3.Dot(axis, hz_B)))
-                return ContainmentType.Disjoint;
-
-            // a.Z ^ b.Y = (0,0,1) ^ bY
-            axis = new Vector3(-bY.Y, bY.X, 0);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.X * axis.X) + Math.Abs(hA.Y * axis.Y) + Math.Abs(Vector3.Dot(axis, hz_B)) + Math.Abs(Vector3.Dot(axis, hx_B)))
-                return ContainmentType.Disjoint;
-
-            // a.Z ^ b.Z = (0,0,1) ^ bZ
-            axis = new Vector3(-bZ.Y, bZ.X, 0);
-            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.X * axis.X) + Math.Abs(hA.Y * axis.Y) + Math.Abs(Vector3.Dot(axis, hx_B)) + Math.Abs(Vector3.Dot(axis, hy_B)))
-                return ContainmentType.Disjoint;
-
-            return ContainmentType.Intersects;
-        }
-
-        /// <summary>
-        /// Convert this BoundingOrientedBox to a BoundingFrustum describing the same volume.
-        ///
-        /// A BoundingFrustum is defined by the matrix that carries its volume to the
-        /// box from (-1,-1,0) to (1,1,1), so we just need a matrix that carries our box there.
-        /// </summary>
-        public BoundingFrustum ConvertToFrustum()
-        {
-            Quaternion invOrientation;
-            Quaternion.Conjugate(ref Orientation, out invOrientation);
-            float sx = 1.0f / HalfExtent.X;
-            float sy = 1.0f / HalfExtent.Y;
-            float sz = .5f / HalfExtent.Z;
-            Matrix temp;
-            Matrix.CreateFromQuaternion(ref invOrientation, out temp);
-            temp.M11 *= sx; temp.M21 *= sx; temp.M31 *= sx;
-            temp.M12 *= sy; temp.M22 *= sy; temp.M32 *= sy;
-            temp.M13 *= sz; temp.M23 *= sz; temp.M33 *= sz;
-            temp.Translation = Vector3.UnitZ*0.5f + Vector3.TransformNormal(-Center, temp);
-
-            return new BoundingFrustum(temp);
-        }
-        #endregion
-    }
-}
+//-----------------------------------------------------------------------------
+// BoundingOrientedBox.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+
+namespace CollisionSample
+{
+    /// <summary>
+    /// Bounding volume using an oriented bounding box.
+    /// </summary>
+    public struct BoundingOrientedBox : IEquatable<BoundingOrientedBox>
+    {
+        public const int CornerCount = 8;
+
+        // Epsilon value used in ray tests, where a ray might hit the box almost edge-on.
+        const float RAY_EPSILON = 1e-20F;
+
+        public Vector3 Center;
+        public Vector3 HalfExtent;
+        public Quaternion Orientation;
+
+
+        /// <summary>
+        /// Create an oriented box with the given center, half-extents, and orientation.
+        /// </summary>
+        public BoundingOrientedBox(Vector3 center, Vector3 halfExtents, Quaternion orientation)
+        {
+            Center = center;
+            HalfExtent = halfExtents;
+            Orientation = orientation;
+        }
+
+        /// <summary>
+        /// Create an oriented box from an axis-aligned box.
+        /// </summary>
+        public static BoundingOrientedBox CreateFromBoundingBox(BoundingBox box)
+        {
+            Vector3 mid = (box.Min + box.Max) * 0.5f;
+            Vector3 halfExtent = (box.Max - box.Min) * 0.5f;
+            return new BoundingOrientedBox(mid, halfExtent, Quaternion.Identity);
+        }
+
+        /// <summary>
+        /// Transform the given bounding box by a rotation around the origin followed by a translation 
+        /// </summary>
+        /// <param name="rotation"></param>
+        /// <param name="translation"></param>
+        /// <returns>A new bounding box, transformed relative to this one</returns>
+        public BoundingOrientedBox Transform(Quaternion rotation, Vector3 translation)
+        {
+            return new BoundingOrientedBox(Vector3.Transform(Center, rotation) + translation,
+                                            HalfExtent,
+                                            Orientation * rotation);
+        }
+
+        /// <summary>
+        /// Transform the given bounding box by a uniform scale and rotation around the origin followed
+        /// by a translation
+        /// </summary>
+        /// <returns>A new bounding box, transformed relative to this one</returns>
+        public BoundingOrientedBox Transform(float scale, Quaternion rotation, Vector3 translation)
+        {
+            return new BoundingOrientedBox(Vector3.Transform(Center * scale, rotation) + translation,
+                                            HalfExtent * scale,
+                                            Orientation * rotation);
+        }
+        
+
+
+        public bool Equals(BoundingOrientedBox other)
+        {
+            return (Center == other.Center && HalfExtent == other.HalfExtent && Orientation == other.Orientation);
+        }
+        
+        public override bool Equals(Object obj)
+        {
+            if (obj != null && obj is BoundingOrientedBox)
+            {
+                BoundingOrientedBox other = (BoundingOrientedBox)obj;
+                return (Center == other.Center && HalfExtent == other.HalfExtent && Orientation == other.Orientation);
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public override int GetHashCode()
+        {
+            return Center.GetHashCode() ^ HalfExtent.GetHashCode() ^ Orientation.GetHashCode();
+        }
+
+        public static bool operator==(BoundingOrientedBox a, BoundingOrientedBox b)
+        {
+            return Equals(a, b);
+        }
+
+        public static bool operator!=(BoundingOrientedBox a, BoundingOrientedBox b)
+        {
+            return !Equals(a, b);
+        }
+
+        public override string ToString()
+        {
+            return "{Center:" + Center.ToString() +
+                   " Extents:" + HalfExtent.ToString() +
+                   " Orientation:" + Orientation.ToString() + "}";
+        }
+
+
+
+        /// <summary>
+        /// Determine if box A intersects box B.
+        /// </summary>
+        public bool Intersects(ref BoundingBox box)
+        {
+            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
+            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
+
+            Matrix mb = Matrix.CreateFromQuaternion(Orientation);
+            mb.Translation = Center - boxCenter;
+
+            return ContainsRelativeBox(ref boxHalfExtent, ref HalfExtent, ref mb) != ContainmentType.Disjoint;
+        }
+
+        /// <summary>
+        /// Determine if this box contains, intersects, or is disjoint from the given BoundingBox.
+        /// </summary>
+        public ContainmentType Contains(ref BoundingBox box)
+        {
+            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
+            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
+
+            // Build the 3x3 rotation matrix that defines the orientation of 'other' relative to this box
+            Quaternion relOrient;
+            Quaternion.Conjugate(ref Orientation, out relOrient);
+
+            Matrix relTransform = Matrix.CreateFromQuaternion(relOrient);
+            relTransform.Translation = Vector3.TransformNormal(boxCenter - Center, relTransform);
+
+            return ContainsRelativeBox(ref HalfExtent, ref boxHalfExtent, ref relTransform);
+        }
+
+        /// <summary>
+        /// Determine if box A contains, intersects, or is disjoint from box B.
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingBox boxA, ref BoundingOrientedBox oboxB)
+        {
+            Vector3 boxA_halfExtent = (boxA.Max - boxA.Min) * 0.5f;
+            Vector3 boxA_center = (boxA.Max + boxA.Min) * 0.5f;
+            Matrix mb = Matrix.CreateFromQuaternion(oboxB.Orientation);
+            mb.Translation = oboxB.Center - boxA_center;
+
+            return BoundingOrientedBox.ContainsRelativeBox(ref boxA_halfExtent, ref oboxB.HalfExtent, ref mb);
+        }
+
+
+
+        /// <summary>
+        /// Returns true if this box intersects the given other box.
+        /// </summary>
+        public bool Intersects(ref BoundingOrientedBox other)
+        {
+            return Contains(ref other) != ContainmentType.Disjoint;
+        }
+
+        /// <summary>
+        /// Determine whether this box contains, intersects, or is disjoint from
+        /// the given other box.
+        /// </summary>
+        public ContainmentType Contains(ref BoundingOrientedBox other)
+        {
+            // Build the 3x3 rotation matrix that defines the orientation of 'other' relative to this box
+            Quaternion invOrient;
+            Quaternion.Conjugate(ref Orientation, out invOrient);
+            Quaternion relOrient;
+            Quaternion.Multiply(ref invOrient, ref other.Orientation, out relOrient);
+
+            Matrix relTransform = Matrix.CreateFromQuaternion(relOrient);
+            relTransform.Translation = Vector3.Transform(other.Center - Center, invOrient);
+
+            return ContainsRelativeBox(ref HalfExtent, ref other.HalfExtent, ref relTransform);
+        }
+
+
+
+        /// <summary>
+        /// Determine whether this box contains, intersects, or is disjoint from
+        /// the given frustum.
+        /// </summary>
+        public ContainmentType Contains(BoundingFrustum frustum)
+        {
+            // Convert this bounding box to an equivalent BoundingFrustum, so we can rely on BoundingFrustum's
+            // implementation. Note that this is very slow, since BoundingFrustum builds various data structures
+            // for this test that it caches internally. To speed it up, you could convert the box to a frustum
+            // just once and re-use that frustum for repeated tests.
+            BoundingFrustum temp = ConvertToFrustum();
+            return temp.Contains(frustum);
+        }
+
+        /// <summary>
+        /// Returns true if this box intersects the given frustum.
+        /// </summary>
+        public bool Intersects(BoundingFrustum frustum)
+        {
+            return (Contains(frustum) != ContainmentType.Disjoint);
+        }
+
+        /// <summary>
+        /// Determine whether the given frustum contains, intersects, or is disjoint from
+        /// the given oriented box.
+        /// </summary>
+        public static ContainmentType Contains(BoundingFrustum frustum, ref BoundingOrientedBox obox)
+        {
+            return frustum.Contains(obox.ConvertToFrustum());
+        }
+
+
+
+        /// <summary>
+        /// Test whether this box contains, intersects, or is disjoint from the given sphere
+        /// </summary>
+        public ContainmentType Contains(ref BoundingSphere sphere)
+        {
+            // Transform the sphere into local box space
+            Quaternion iq = Quaternion.Conjugate(Orientation);
+            Vector3 localCenter = Vector3.Transform(sphere.Center - Center, iq);
+
+            // (dx,dy,dz) = signed distance of center of sphere from edge of box
+            float dx = Math.Abs(localCenter.X) - HalfExtent.X;
+            float dy = Math.Abs(localCenter.Y) - HalfExtent.Y;
+            float dz = Math.Abs(localCenter.Z) - HalfExtent.Z;
+
+            // Check for sphere completely inside box
+            float r = sphere.Radius;
+            if (dx <= -r && dy <= -r && dz <= -r)
+                return ContainmentType.Contains;
+
+            // Compute how far away the sphere is in each dimension
+            dx = Math.Max(dx, 0.0f);
+            dy = Math.Max(dy, 0.0f);
+            dz = Math.Max(dz, 0.0f);
+
+            if(dx*dx + dy*dy + dz*dz >= r*r)
+                return ContainmentType.Disjoint;
+
+            return ContainmentType.Intersects;
+        }
+
+        /// <summary>
+        /// Test whether this box intersects the given sphere
+        /// </summary>
+        public bool Intersects(ref BoundingSphere sphere)
+        {
+            // Transform the sphere into local box space
+            Quaternion iq = Quaternion.Conjugate(Orientation);
+            Vector3 localCenter = Vector3.Transform(sphere.Center - Center, iq);
+
+            // (dx,dy,dz) = signed distance of center of sphere from edge of box
+            float dx = Math.Abs(localCenter.X) - HalfExtent.X;
+            float dy = Math.Abs(localCenter.Y) - HalfExtent.Y;
+            float dz = Math.Abs(localCenter.Z) - HalfExtent.Z;
+
+            // Compute how far away the sphere is in each dimension
+            dx = Math.Max(dx, 0.0f);
+            dy = Math.Max(dy, 0.0f);
+            dz = Math.Max(dz, 0.0f);
+            float r = sphere.Radius;
+
+            return dx * dx + dy * dy + dz * dz < r * r;
+        }
+
+        /// <summary>
+        /// Test whether a BoundingSphere contains, intersects, or is disjoint from a BoundingOrientedBox
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingSphere sphere, ref BoundingOrientedBox box)
+        {
+            // Transform the sphere into local box space
+            Quaternion iq = Quaternion.Conjugate(box.Orientation);
+            Vector3 localCenter = Vector3.Transform(sphere.Center - box.Center, iq);
+            localCenter.X = Math.Abs(localCenter.X);
+            localCenter.Y = Math.Abs(localCenter.Y);
+            localCenter.Z = Math.Abs(localCenter.Z);
+
+            // Check for box completely inside sphere
+            float rSquared = sphere.Radius * sphere.Radius;
+            if ((localCenter + box.HalfExtent).LengthSquared() <= rSquared)
+                return ContainmentType.Contains;
+
+            // (dx,dy,dz) = signed distance of center of sphere from edge of box
+            Vector3 d = localCenter - box.HalfExtent;
+
+            // Compute how far away the sphere is in each dimension
+            d.X = Math.Max(d.X, 0.0f);
+            d.Y = Math.Max(d.Y, 0.0f);
+            d.Z = Math.Max(d.Z, 0.0f);
+
+            if (d.LengthSquared() >= rSquared)
+                return ContainmentType.Disjoint;
+
+            return ContainmentType.Intersects;
+        }
+
+
+
+        /// <summary>
+        /// Returns true if this box contains the given point.
+        /// </summary>
+        public bool Contains(ref Vector3 point)
+        {
+            // Transform the point into box-local space and check against
+            // our extents.
+            Quaternion qinv = Quaternion.Conjugate(Orientation);
+            Vector3 plocal = Vector3.Transform(point - Center, qinv);
+
+            return Math.Abs(plocal.X) <= HalfExtent.X &&
+                   Math.Abs(plocal.Y) <= HalfExtent.Y &&
+                   Math.Abs(plocal.Z) <= HalfExtent.Z;
+        }
+
+        /// <summary>
+        /// Determine whether the given ray intersects this box. If so, returns
+        /// the parametric value of the point of first intersection; otherwise
+        /// returns null.
+        /// </summary>
+        public float? Intersects(ref Ray ray)
+        {
+            Matrix R = Matrix.CreateFromQuaternion(Orientation);
+
+            Vector3 TOrigin = Center - ray.Position;
+
+            float t_min = -float.MaxValue;
+            float t_max = float.MaxValue;
+
+            // X-case
+            float axisDotOrigin = Vector3.Dot(R.Right, TOrigin);
+            float axisDotDir = Vector3.Dot(R.Right, ray.Direction);
+
+            if (axisDotDir >= -RAY_EPSILON && axisDotDir <= RAY_EPSILON)
+            {
+                if ((-axisDotOrigin - HalfExtent.X) > 0.0 || (-axisDotOrigin + HalfExtent.X) > 0.0f)
+                    return null;
+            }
+            else
+            {
+                float t1 = (axisDotOrigin - HalfExtent.X) / axisDotDir;
+                float t2 = (axisDotOrigin + HalfExtent.X) / axisDotDir;
+
+                if (t1 > t2)
+                {
+                    float temp = t1;
+                    t1 = t2;
+                    t2 = temp;
+                }
+
+                if (t1 > t_min)
+                    t_min = t1;
+
+                if (t2 < t_max)
+                    t_max = t2;
+
+                if (t_max < 0.0f || t_min > t_max)
+                    return null;
+            }
+
+            // Y-case
+            axisDotOrigin = Vector3.Dot(R.Up, TOrigin);
+            axisDotDir = Vector3.Dot(R.Up, ray.Direction);
+
+            if (axisDotDir >= -RAY_EPSILON && axisDotDir <= RAY_EPSILON)
+            {
+                if ((-axisDotOrigin - HalfExtent.Y) > 0.0 || (-axisDotOrigin + HalfExtent.Y) > 0.0f)
+                    return null;
+            }
+            else
+            {
+                float t1 = (axisDotOrigin - HalfExtent.Y) / axisDotDir;
+                float t2 = (axisDotOrigin + HalfExtent.Y) / axisDotDir;
+
+                if (t1 > t2)
+                {
+                    float temp = t1;
+                    t1 = t2;
+                    t2 = temp;
+                }
+
+                if (t1 > t_min)
+                    t_min = t1;
+
+                if (t2 < t_max)
+                    t_max = t2;
+
+                if (t_max < 0.0f || t_min > t_max)
+                    return null;
+            }
+
+            // Z-case
+            axisDotOrigin = Vector3.Dot(R.Forward, TOrigin);
+            axisDotDir = Vector3.Dot(R.Forward, ray.Direction);
+
+            if (axisDotDir >= -RAY_EPSILON && axisDotDir <= RAY_EPSILON)
+            {
+                if ((-axisDotOrigin - HalfExtent.Z) > 0.0 || (-axisDotOrigin + HalfExtent.Z) > 0.0f)
+                    return null;
+            }
+            else
+            {
+                float t1 = (axisDotOrigin - HalfExtent.Z) / axisDotDir;
+                float t2 = (axisDotOrigin + HalfExtent.Z) / axisDotDir;
+
+                if (t1 > t2)
+                {
+                    float temp = t1;
+                    t1 = t2;
+                    t2 = temp;
+                }
+
+                if (t1 > t_min)
+                    t_min = t1;
+
+                if (t2 < t_max)
+                    t_max = t2;
+
+                if (t_max < 0.0f || t_min > t_max)
+                    return null;
+            }
+
+            return t_min;
+        }
+
+        /// <summary>
+        /// Classify this bounding box as entirely in front of, in back of, or
+        /// intersecting the given plane.
+        /// </summary>
+        public PlaneIntersectionType Intersects(ref Plane plane)
+        {
+            float dist = plane.DotCoordinate(Center);
+
+            // Transform the plane's normal into this box's space
+            Vector3 localNormal = Vector3.Transform(plane.Normal, Quaternion.Conjugate(Orientation));
+
+            // Project the axes of the box onto the normal of the plane.  Half the
+            // length of the projection (sometime called the "radius") is equal to
+            // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w))
+            // where h(i) are extents of the box, n is the plane normal, and b(i) are the 
+            // axes of the box.
+            float r = Math.Abs(HalfExtent.X*localNormal.X)
+                    + Math.Abs(HalfExtent.Y*localNormal.Y)
+                    + Math.Abs(HalfExtent.Z*localNormal.Z);
+
+            if(dist > r)
+            {
+                return PlaneIntersectionType.Front;
+            }
+            else if(dist < -r)
+            {
+                return PlaneIntersectionType.Back;
+            }
+            else
+            {
+                return PlaneIntersectionType.Intersecting;
+            }
+        }
+
+
+
+        /// <summary>
+        /// Return the 8 corner positions of this bounding box.
+        ///
+        ///     ZMax    ZMin
+        ///    0----1  4----5
+        ///    |    |  |    |
+        ///    |    |  |    |
+        ///    3----2  7----6
+        ///
+        /// The ordering of indices is a little strange to match what BoundingBox.GetCorners() does.
+        /// </summary>
+        public Vector3[] GetCorners()
+        {
+            Vector3[] corners = new Vector3[CornerCount];
+            GetCorners(corners, 0);
+            return corners;
+        }
+
+        /// <summary>
+        /// Return the 8 corner positions of this bounding box.
+        ///
+        ///     ZMax    ZMin
+        ///    0----1  4----5
+        ///    |    |  |    |
+        ///    |    |  |    |
+        ///    3----2  7----6
+        ///
+        /// The ordering of indices is a little strange to match what BoundingBox.GetCorners() does.
+        /// </summary>
+        /// <param name="corners">Array to fill with the eight corner positions</param>
+        /// <param name="startIndex">Index within corners array to start writing positions</param>
+        public void GetCorners(Vector3[] corners, int startIndex)
+        {
+            Matrix m = Matrix.CreateFromQuaternion(Orientation);
+            Vector3 hX = m.Left * HalfExtent.X;
+            Vector3 hY = m.Up * HalfExtent.Y;
+            Vector3 hZ = m.Backward * HalfExtent.Z;
+
+            int i = startIndex;
+            corners[i++] = Center - hX + hY + hZ;
+            corners[i++] = Center + hX + hY + hZ;
+            corners[i++] = Center + hX - hY + hZ;
+            corners[i++] = Center - hX - hY + hZ;
+            corners[i++] = Center - hX + hY - hZ;
+            corners[i++] = Center + hX + hY - hZ;
+            corners[i++] = Center + hX - hY - hZ;
+            corners[i++] = Center - hX - hY - hZ;
+        }
+
+ 
+        /// <summary>
+        /// Determine whether the box described by half-extents hA, axis-aligned and centered at the origin, contains
+        /// the box described by half-extents hB, whose position and orientation are given by the transform matrix mB.
+        /// The matrix is assumed to contain only rigid motion; if it contains scaling or perpsective the result of
+        /// this method will be incorrect.
+        /// </summary>
+        /// <param name="hA">Half-extents of first box</param>
+        /// <param name="hB">Half-extents of second box</param>
+        /// <param name="mB">Position and orientation of second box relative to first box</param>
+        /// <returns>ContainmentType enum indicating whether the boxes are disjoin, intersecting, or
+        /// whether box A contains box B.</returns>
+        public static ContainmentType ContainsRelativeBox(ref Vector3 hA, ref Vector3 hB, ref Matrix mB)
+        {
+            Vector3 mB_T = mB.Translation;
+            Vector3 mB_TA = new Vector3(Math.Abs(mB_T.X), Math.Abs(mB_T.Y), Math.Abs(mB_T.Z));
+
+            // Transform the extents of B
+            // TODO: check which coords Right/Up/Back refer to and access them directly. This looks dumb.
+            Vector3 bX = mB.Right;      // x-axis of box B
+            Vector3 bY = mB.Up;         // y-axis of box B
+            Vector3 bZ = mB.Backward;   // z-axis of box B
+            Vector3 hx_B = bX * hB.X;   // x extent of box B
+            Vector3 hy_B = bY * hB.Y;   // y extent of box B
+            Vector3 hz_B = bZ * hB.Z;   // z extent of box B
+
+            // Check for containment first.
+            float projx_B = Math.Abs(hx_B.X) + Math.Abs(hy_B.X) + Math.Abs(hz_B.X);
+            float projy_B = Math.Abs(hx_B.Y) + Math.Abs(hy_B.Y) + Math.Abs(hz_B.Y);
+            float projz_B = Math.Abs(hx_B.Z) + Math.Abs(hy_B.Z) + Math.Abs(hz_B.Z);
+            if (mB_TA.X + projx_B <= hA.X && mB_TA.Y + projy_B <= hA.Y && mB_TA.Z + projz_B <= hA.Z)
+                return ContainmentType.Contains;
+
+            // Check for separation along the faces of the other box,
+            // by projecting each local axis onto the other boxes' axes
+            // http://www.cs.unc.edu/~geom/theses/gottschalk/main.pdf
+            //
+            // The general test form, given a choice of separating axis, is:
+            //      sizeA = abs(dot(A.e1,axis)) + abs(dot(A.e2,axis)) + abs(dot(A.e3,axis))
+            //      sizeB = abs(dot(B.e1,axis)) + abs(dot(B.e2,axis)) + abs(dot(B.e3,axis))
+            //      distance = abs(dot(B.center - A.center),axis))
+            //      if distance >= sizeA+sizeB, the boxes are disjoint
+            //
+            // We need to do this test on 15 axes:
+            //      x, y, z axis of box A
+            //      x, y, z axis of box B
+            //      (v1 cross v2) for each v1 in A's axes, for each v2 in B's axes
+            //
+            // Since we're working in a space where A is axis-aligned and A.center=0, many
+            // of the tests and products simplify away.
+
+            // Check for separation along the axes of box A
+            if (mB_TA.X >= hA.X + Math.Abs(hx_B.X) + Math.Abs(hy_B.X) + Math.Abs(hz_B.X))
+                return ContainmentType.Disjoint;
+
+            if (mB_TA.Y >= hA.Y + Math.Abs(hx_B.Y) + Math.Abs(hy_B.Y) + Math.Abs(hz_B.Y))
+                return ContainmentType.Disjoint;
+
+            if (mB_TA.Z >= hA.Z + Math.Abs(hx_B.Z) + Math.Abs(hy_B.Z) + Math.Abs(hz_B.Z))
+                return ContainmentType.Disjoint;
+
+            // Check for separation along the axes box B, hx_B/hy_B/hz_B
+            if (Math.Abs(Vector3.Dot(mB_T, bX)) >= Math.Abs(hA.X * bX.X) + Math.Abs(hA.Y * bX.Y) + Math.Abs(hA.Z * bX.Z) + hB.X)
+                return ContainmentType.Disjoint;
+
+            if (Math.Abs(Vector3.Dot(mB_T, bY)) >= Math.Abs(hA.X * bY.X) + Math.Abs(hA.Y * bY.Y) + Math.Abs(hA.Z * bY.Z) + hB.Y)
+                return ContainmentType.Disjoint;
+
+            if (Math.Abs(Vector3.Dot(mB_T, bZ)) >= Math.Abs(hA.X * bZ.X) + Math.Abs(hA.Y * bZ.Y) + Math.Abs(hA.Z * bZ.Z) + hB.Z)
+                return ContainmentType.Disjoint;
+
+            // Check for separation in plane containing an axis of box A and and axis of box B
+            //
+            // We need to compute all 9 cross products to find them, but a lot of terms drop out
+            // since we're working in A's local space. Also, since each such plane is parallel
+            // to the defining axis in each box, we know those dot products will be 0 and can
+            // omit them.
+            Vector3 axis;
+
+            // a.X ^ b.X = (1,0,0) ^ bX
+            axis = new Vector3(0, -bX.Z, bX.Y);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Y * axis.Y) + Math.Abs(hA.Z * axis.Z) + Math.Abs(Vector3.Dot(axis, hy_B)) + Math.Abs(Vector3.Dot(axis, hz_B)))
+                return ContainmentType.Disjoint;
+
+            // a.X ^ b.Y = (1,0,0) ^ bY
+            axis = new Vector3(0, -bY.Z, bY.Y);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Y * axis.Y) + Math.Abs(hA.Z * axis.Z) + Math.Abs(Vector3.Dot(axis, hz_B)) + Math.Abs(Vector3.Dot(axis, hx_B)))
+                return ContainmentType.Disjoint;
+
+            // a.X ^ b.Z = (1,0,0) ^ bZ
+            axis = new Vector3(0, -bZ.Z, bZ.Y);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Y * axis.Y) + Math.Abs(hA.Z * axis.Z) + Math.Abs(Vector3.Dot(axis, hx_B)) + Math.Abs(Vector3.Dot(axis, hy_B)))
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.X = (0,1,0) ^ bX
+            axis = new Vector3(bX.Z, 0, -bX.X);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Z * axis.Z) + Math.Abs(hA.X * axis.X) + Math.Abs(Vector3.Dot(axis, hy_B)) + Math.Abs(Vector3.Dot(axis, hz_B)))
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.Y = (0,1,0) ^ bY
+            axis = new Vector3(bY.Z, 0, -bY.X);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Z * axis.Z) + Math.Abs(hA.X * axis.X) + Math.Abs(Vector3.Dot(axis, hz_B)) + Math.Abs(Vector3.Dot(axis, hx_B)))
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.Z = (0,1,0) ^ bZ
+            axis = new Vector3(bZ.Z, 0, -bZ.X);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.Z * axis.Z) + Math.Abs(hA.X * axis.X) + Math.Abs(Vector3.Dot(axis, hx_B)) + Math.Abs(Vector3.Dot(axis, hy_B)))
+                return ContainmentType.Disjoint;
+
+            // a.Z ^ b.X = (0,0,1) ^ bX
+            axis = new Vector3(-bX.Y, bX.X, 0);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.X * axis.X) + Math.Abs(hA.Y * axis.Y) + Math.Abs(Vector3.Dot(axis, hy_B)) + Math.Abs(Vector3.Dot(axis, hz_B)))
+                return ContainmentType.Disjoint;
+
+            // a.Z ^ b.Y = (0,0,1) ^ bY
+            axis = new Vector3(-bY.Y, bY.X, 0);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.X * axis.X) + Math.Abs(hA.Y * axis.Y) + Math.Abs(Vector3.Dot(axis, hz_B)) + Math.Abs(Vector3.Dot(axis, hx_B)))
+                return ContainmentType.Disjoint;
+
+            // a.Z ^ b.Z = (0,0,1) ^ bZ
+            axis = new Vector3(-bZ.Y, bZ.X, 0);
+            if (Math.Abs(Vector3.Dot(mB_T, axis)) >= Math.Abs(hA.X * axis.X) + Math.Abs(hA.Y * axis.Y) + Math.Abs(Vector3.Dot(axis, hx_B)) + Math.Abs(Vector3.Dot(axis, hy_B)))
+                return ContainmentType.Disjoint;
+
+            return ContainmentType.Intersects;
+        }
+
+        /// <summary>
+        /// Convert this BoundingOrientedBox to a BoundingFrustum describing the same volume.
+        ///
+        /// A BoundingFrustum is defined by the matrix that carries its volume to the
+        /// box from (-1,-1,0) to (1,1,1), so we just need a matrix that carries our box there.
+        /// </summary>
+        public BoundingFrustum ConvertToFrustum()
+        {
+            Quaternion invOrientation;
+            Quaternion.Conjugate(ref Orientation, out invOrientation);
+            float sx = 1.0f / HalfExtent.X;
+            float sy = 1.0f / HalfExtent.Y;
+            float sz = .5f / HalfExtent.Z;
+            Matrix temp;
+            Matrix.CreateFromQuaternion(ref invOrientation, out temp);
+            temp.M11 *= sx; temp.M21 *= sx; temp.M31 *= sx;
+            temp.M12 *= sy; temp.M22 *= sy; temp.M32 *= sy;
+            temp.M13 *= sz; temp.M23 *= sz; temp.M33 *= sz;
+            temp.Translation = Vector3.UnitZ*0.5f + Vector3.TransformNormal(-Center, temp);
+
+            return new BoundingFrustum(temp);
+        }
+    }
+}

+ 14 - 0
CollisionSample/Core/CollisionSample.Core.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <AssemblyName>CollisionSample.Core</AssemblyName>
+    <RootNamespace>CollisionSample.Core</RootNamespace>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+  </ItemGroup>
+
+</Project>

+ 558 - 565
CollisionSample/CollisionSample.cs → CollisionSample/Core/CollisionSample.cs

@@ -1,565 +1,558 @@
-//-----------------------------------------------------------------------------
-// CollisionSample.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Audio;
-using Microsoft.Xna.Framework.GamerServices;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Input;
-using Microsoft.Xna.Framework.Input.Touch;
-using Microsoft.Xna.Framework.Media;
-
-namespace CollisionSample
-{
-    /// <summary>
-    /// This sample demonstrates various forms of collision detection for primitives
-    /// supplied in the framework, plus oriented bounding boxes and triangles.
-    /// </summary>
-    public class CollisionSample : Microsoft.Xna.Framework.Game
-    {
-        #region Constants
-
-        public const int FrustumGroupIndex = 0;
-        public const int AABoxGroupIndex = 1;
-        public const int OBoxGroupIndex = 2;
-        public const int SphereGroupIndex = 3;
-        public const int RayGroupIndex = 4;
-        public const int NumGroups = 5;
-
-        public const int TriIndex = 0;
-        public const int SphereIndex = 1;
-        public const int AABoxIndex = 2;
-        public const int OBoxIndex = 3;
-        public const int NumSecondaryShapes = 4;
-
-        public const float CAMERA_SPACING = 50.0F;
-
-        public const float YAW_RATE = 1;            // radians per second for keyboard controls
-        public const float PITCH_RATE = 0.75f;      // radians per second for keyboard controls
-        public const float YAW_DRAG_RATE = .01f;     // radians per pixel for drag control
-        public const float PITCH_DRAG_RATE = .01f;   // radians per pixel for drag control
-        public const float PINCH_ZOOM_RATE = .01f;  // scale factor for pinch-zoom rate
-        public const float DISTANCE_RATE = 10;
-
-        #endregion
-
-        #region Fields
-
-        // Rendering helpers
-        GraphicsDeviceManager graphics;
-        DebugDraw debugDraw;
-
-        // Primary shapes 
-        BoundingFrustum primaryFrustum;
-        BoundingBox primaryAABox;
-        BoundingOrientedBox primaryOBox;
-        BoundingSphere primarySphere;
-        Ray primaryRay;
-
-        // Secondary shapes.
-        Triangle[] secondaryTris = new Triangle[NumGroups];
-        BoundingSphere[] secondarySpheres = new BoundingSphere[NumGroups];
-        BoundingBox[] secondaryAABoxes = new BoundingBox[NumGroups];
-        BoundingOrientedBox[] secondaryOBoxes = new BoundingOrientedBox[NumGroups];
-
-        // Collision results
-        ContainmentType[,] collideResults = new ContainmentType[NumGroups, NumSecondaryShapes];
-        Vector3? rayHitResult;
-
-        // Camera state
-        Vector3[] cameraOrigins = new Vector3[NumGroups];
-        int currentCamera;
-
-        bool cameraOrtho;
-        float cameraYaw;
-        float cameraPitch;
-        float cameraDistance;
-        Vector3 cameraTarget;
-
-        KeyboardState currentKeyboardState = new KeyboardState();
-        GamePadState currentGamePadState = new GamePadState();
-        List<GestureSample> currentGestures = new List<GestureSample>();
-
-        KeyboardState previousKeyboardState = new KeyboardState();
-        GamePadState previousGamePadState = new GamePadState();
-
-        TimeSpan unpausedClock = new TimeSpan();
-        bool paused;
-
-        #endregion
-
-        #region Initialization
-
-        public CollisionSample()
-        {
-            graphics = new GraphicsDeviceManager(this);
-            TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Pinch | GestureType.FreeDrag;
-#if WINDOWS_PHONE
-            TargetElapsedTime = TimeSpan.FromTicks(333333);
-            graphics.IsFullScreen = true;
-#endif
-#if WINDOWS || XBOX
-            graphics.PreferredBackBufferWidth = 853;
-            graphics.PreferredBackBufferHeight = 480;
-#endif
-        }
-
-        // Set up initial bounding shapes for the primary (static) and secondary (moving)
-        // bounding shapes along with relevant camera position information.
-        protected override void Initialize()
-        {
-            Console.WriteLine("DEBUG - Game Initialize!");
-
-            debugDraw = new DebugDraw(GraphicsDevice);
-
-            Components.Add(new FrameRateCounter(this));
-
-            // Primary frustum
-            Matrix m1 = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 1.77778F, 0.5f, 10.0f);
-            Matrix m2 = Matrix.CreateTranslation(new Vector3(0, 0, -7));
-            primaryFrustum = new BoundingFrustum(Matrix.Multiply(m2, m1));
-            cameraOrigins[FrustumGroupIndex] = Vector3.Zero;
-
-            // Primary axis-aligned box
-            primaryAABox.Min = new Vector3(CAMERA_SPACING - 3, -4, -5);
-            primaryAABox.Max = new Vector3(CAMERA_SPACING + 3, 4, 5);
-            cameraOrigins[AABoxGroupIndex] = new Vector3(CAMERA_SPACING, 0, 0);
-
-            // Primary oriented box
-            primaryOBox.Center = new Vector3(-CAMERA_SPACING, 0, 0);
-            primaryOBox.HalfExtent = new Vector3(3, 4, 5);
-            primaryOBox.Orientation = Quaternion.CreateFromYawPitchRoll(0.8f, 0.7f, 0);
-            cameraOrigins[OBoxGroupIndex] = primaryOBox.Center;
-
-            // Primary sphere
-            primarySphere.Center = new Vector3(0, 0, -CAMERA_SPACING);
-            primarySphere.Radius = 5;
-            cameraOrigins[SphereGroupIndex] = primarySphere.Center;
-
-            // Primary ray
-            primaryRay.Position = new Vector3(0, 0, CAMERA_SPACING);
-            primaryRay.Direction = Vector3.UnitZ;
-            cameraOrigins[RayGroupIndex] = primaryRay.Position;
-
-            // Initialize all of the secondary objects with default values
-            Vector3 half = new Vector3(0.5F, 0.5F, 0.5F);
-            for (int i = 0; i < NumGroups; i++)
-            {
-                secondarySpheres[i] = new BoundingSphere(Vector3.Zero, 1.0f);
-                secondaryOBoxes[i] = new BoundingOrientedBox(Vector3.Zero, half, Quaternion.Identity);
-                secondaryAABoxes[i] = new BoundingBox(-half, half);
-                secondaryTris[i] = new Triangle();
-            }
-
-            rayHitResult = null;
-
-            currentCamera = 3;
-            cameraOrtho = false;
-            cameraYaw = (float)Math.PI * 0.75F;
-            cameraPitch = MathHelper.PiOver4;
-            cameraDistance = 20;
-            cameraTarget = cameraOrigins[0];
-
-            paused = false;
-            
-            base.Initialize();
-        }
-        #endregion
-
-        #region Update
-
-        protected override void Update(GameTime gameTime)
-        {
-            ReadInputDevices();
-
-            if (currentKeyboardState.IsKeyDown(Keys.Escape) || currentGamePadState.IsButtonDown(Buttons.Back))
-                this.Exit();
-
-            if (!paused)
-            {
-                unpausedClock += gameTime.ElapsedGameTime;
-            }
-
-            // Run collision even when paused, for timing and debugging, and so the step
-            // forward/backward keys work.
-            Animate();
-            Collide();
-            HandleInput(gameTime);
-
-            base.Update(gameTime);
-        }
-
-        private void ReadInputDevices()
-        {
-            previousKeyboardState = currentKeyboardState;
-            previousGamePadState = currentGamePadState;
-
-            currentKeyboardState = Keyboard.GetState();
-            currentGamePadState = GamePad.GetState(PlayerIndex.One);
-            currentGestures.Clear();
-            while (TouchPanel.IsGestureAvailable)
-            {
-                currentGestures.Add(TouchPanel.ReadGesture());
-            }
-        }
-
-        /// <summary>
-        /// Move all the secondary shapes around, and move the ray so it
-        /// sweeps back and forth across its secondry shapes.
-        /// </summary>
-        private void Animate()
-        {
-            float t = (float)unpausedClock.TotalSeconds / 2.0F;
-
-            // Rates at which x,y,z cycle
-            const float xRate = 1.1f;
-            const float yRate = 3.6f;
-            const float zRate = 1.9f;
-            const float pathSize = 6;
-
-            // how far apart following shapes are, in seconds
-            const float gap = 0.25f;
-
-            // orientation for rotatable shapes
-            Quaternion orientation = Quaternion.CreateFromYawPitchRoll(t * 0.2f, t * 1.4f, t);
-
-            for (int g = 0; g < NumGroups; g++)
-            {
-                // Animate spheres
-                secondarySpheres[g].Center = cameraOrigins[g];
-                secondarySpheres[g].Center.X += pathSize * (float)Math.Sin(xRate * t);
-                secondarySpheres[g].Center.Y += pathSize * (float)Math.Sin(yRate * t);
-                secondarySpheres[g].Center.Z += pathSize * (float)Math.Sin(zRate * t);
-
-                // Animate oriented boxes
-                secondaryOBoxes[g].Center = cameraOrigins[g];
-                secondaryOBoxes[g].Orientation = orientation;
-                secondaryOBoxes[g].Center.X += pathSize * (float)Math.Sin(xRate * (t - gap));
-                secondaryOBoxes[g].Center.Y += pathSize * (float)Math.Sin(yRate * (t - gap));
-                secondaryOBoxes[g].Center.Z += pathSize * (float)Math.Sin(zRate * (t - gap));
-
-                // Animate axis-aligned boxes
-                Vector3 boxsize = new Vector3(1.0f, 1.3f, 1.9f);
-                secondaryAABoxes[g].Min = cameraOrigins[g] - boxsize * 0.5f;
-                secondaryAABoxes[g].Min.X += pathSize * (float)Math.Sin(xRate * (t - 2 * gap));
-                secondaryAABoxes[g].Min.Y += pathSize * (float)Math.Sin(yRate * (t - 2 * gap));
-                secondaryAABoxes[g].Min.Z += pathSize * (float)Math.Sin(zRate * (t - 2 * gap));
-                secondaryAABoxes[g].Max = secondaryAABoxes[g].Min + boxsize;
-
-                // Animate triangles
-                Vector3 trianglePos = cameraOrigins[g];
-                trianglePos.X += pathSize * (float)Math.Sin(xRate * (t - 3 * gap));
-                trianglePos.Y += pathSize * (float)Math.Sin(yRate * (t - 3 * gap));
-                trianglePos.Z += pathSize * (float)Math.Sin(zRate * (t - 3 * gap));
-
-                // triangle points in local space - equilateral triangle with radius of 2
-                secondaryTris[g].V0 = trianglePos + Vector3.Transform(new Vector3(0, 2, 0), orientation);
-                secondaryTris[g].V1 = trianglePos + Vector3.Transform(new Vector3(1.73f, -1, 0), orientation);
-                secondaryTris[g].V2 = trianglePos + Vector3.Transform(new Vector3(-1.73f, -1, 0), orientation);
-            }
-            //int index = (int)(t*5);
-            //secondaryOBoxes[OBoxGroupIndex] = BoundingOrientedBox.iboxes[index % BoundingOrientedBox.iboxes.Length];
-
-            // Animate primary ray (this is the only animated primary object)
-            // It sweeps back and forth across the secondary objects
-            const float sweepTime = 3.1f;
-            float rayDt = (-Math.Abs((t/sweepTime) % 2.0f - 1.0f) * NumSecondaryShapes + 0.5f) * gap;
-            primaryRay.Direction.X = (float)Math.Sin(xRate * (t + rayDt));
-            primaryRay.Direction.Y = (float)Math.Sin(yRate * (t + rayDt));
-            primaryRay.Direction.Z = (float)Math.Sin(zRate * (t + rayDt));
-            primaryRay.Direction.Normalize();
-        }
-
-        /// <summary>
-        /// Check each pair of objects for collision/containment and store the results for
-        /// coloring them at render time.
-        /// </summary>
-        private void Collide()
-        {
-            // test collisions between objects and frustum
-            collideResults[FrustumGroupIndex, SphereIndex] = primaryFrustum.Contains(secondarySpheres[FrustumGroupIndex]);
-            collideResults[FrustumGroupIndex, OBoxIndex] = BoundingOrientedBox.Contains(primaryFrustum, ref secondaryOBoxes[FrustumGroupIndex]);
-            collideResults[FrustumGroupIndex, AABoxIndex] = primaryFrustum.Contains(secondaryAABoxes[FrustumGroupIndex]);
-            collideResults[FrustumGroupIndex, TriIndex] = TriangleTest.Contains(primaryFrustum, ref secondaryTris[FrustumGroupIndex]);
-
-            // test collisions between objects and aligned box
-            collideResults[AABoxGroupIndex, SphereIndex] = primaryAABox.Contains(secondarySpheres[AABoxGroupIndex]);
-            collideResults[AABoxGroupIndex, OBoxIndex] = BoundingOrientedBox.Contains(ref primaryAABox, ref secondaryOBoxes[AABoxGroupIndex]);
-            collideResults[AABoxGroupIndex, AABoxIndex] = primaryAABox.Contains(secondaryAABoxes[AABoxGroupIndex]);
-            collideResults[AABoxGroupIndex, TriIndex] = TriangleTest.Contains(ref primaryAABox, ref secondaryTris[AABoxGroupIndex]);
-
-            // test collisions between objects and oriented box
-            collideResults[OBoxGroupIndex, SphereIndex] = primaryOBox.Contains(ref secondarySpheres[OBoxGroupIndex]);
-            collideResults[OBoxGroupIndex, OBoxIndex] = primaryOBox.Contains(ref secondaryOBoxes[OBoxGroupIndex]);
-            collideResults[OBoxGroupIndex, AABoxIndex] = primaryOBox.Contains(ref secondaryAABoxes[OBoxGroupIndex]);
-            collideResults[OBoxGroupIndex, TriIndex] = TriangleTest.Contains(ref primaryOBox, ref secondaryTris[OBoxGroupIndex]);
-
-            // test collisions between objects and sphere
-            collideResults[SphereGroupIndex, SphereIndex] = primarySphere.Contains(secondarySpheres[SphereGroupIndex]);
-            collideResults[SphereGroupIndex, OBoxIndex] = BoundingOrientedBox.Contains(ref primarySphere, ref secondaryOBoxes[SphereGroupIndex]);
-            collideResults[SphereGroupIndex, AABoxIndex] = primarySphere.Contains(secondaryAABoxes[SphereGroupIndex]);
-            collideResults[SphereGroupIndex, TriIndex] = TriangleTest.Contains(ref primarySphere, ref secondaryTris[SphereGroupIndex]);
-
-            // test collisions between objects and ray
-            float dist = -1;
-            collideResults[RayGroupIndex, SphereIndex] =
-            collideResults[RayGroupIndex, OBoxIndex] =
-            collideResults[RayGroupIndex, AABoxIndex] =
-            collideResults[RayGroupIndex, TriIndex] = ContainmentType.Disjoint;
-            rayHitResult = null;
-
-            float? r = primaryRay.Intersects(secondarySpheres[RayGroupIndex]);
-            if (r.HasValue)
-            {
-                collideResults[RayGroupIndex, SphereIndex] = ContainmentType.Intersects;
-                dist = r.Value;
-            }
-
-            r = secondaryOBoxes[RayGroupIndex].Intersects(ref primaryRay);
-            if (r.HasValue)
-            {
-                collideResults[RayGroupIndex, OBoxIndex] = ContainmentType.Intersects;
-                dist = r.Value;
-            }
-
-            r = primaryRay.Intersects(secondaryAABoxes[RayGroupIndex]);
-            if (r.HasValue)
-            {
-                collideResults[RayGroupIndex, AABoxIndex] = ContainmentType.Intersects;
-                dist = r.Value;
-            }
-
-            r = TriangleTest.Intersects(ref primaryRay, ref secondaryTris[RayGroupIndex]);
-            if (r.HasValue)
-            {
-                collideResults[RayGroupIndex, TriIndex] = ContainmentType.Intersects;
-                dist = r.Value;
-            }
-
-            // If one of the ray intersection tests was successful, fDistance will be positive.
-            // If so, compute the intersection location and store it in g_RayHitResultBox.
-            if (dist > 0)
-            {
-                rayHitResult = primaryRay.Position + primaryRay.Direction * dist;
-            }
-        }
-
-        private void HandleInput(GameTime gameTime)
-        {
-            // Rely on the fact that we are using fixed time-step update
-            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
-
-            // Allow single-stepping through time
-            if (paused)
-            {
-                if (currentKeyboardState.IsKeyDown(Keys.OemOpenBrackets))
-                    unpausedClock -= TimeSpan.FromSeconds(dt);
-                if (currentKeyboardState.IsKeyDown(Keys.OemCloseBrackets))
-                    unpausedClock += TimeSpan.FromSeconds(dt);
-            }
-
-            // Change yaw/pitch based on right thumbstickb
-            cameraYaw += currentGamePadState.ThumbSticks.Right.X * dt * YAW_RATE;
-            cameraPitch += currentGamePadState.ThumbSticks.Right.Y * dt * PITCH_RATE;
-
-            if (currentKeyboardState.IsKeyDown(Keys.Right))
-                cameraYaw += dt * YAW_RATE;
-            if (currentKeyboardState.IsKeyDown(Keys.Left))
-                cameraYaw -= dt * YAW_RATE;
-
-            if (currentKeyboardState.IsKeyDown(Keys.Up))
-                cameraPitch += dt * PITCH_RATE;
-            if (currentKeyboardState.IsKeyDown(Keys.Down))
-                cameraPitch -= dt * PITCH_RATE;
-
-            // Change distance based on right/left trigger
-            if (currentGamePadState.IsButtonDown(Buttons.LeftTrigger)
-                || currentKeyboardState.IsKeyDown(Keys.Subtract)
-                || currentKeyboardState.IsKeyDown(Keys.OemMinus))
-            {
-                cameraDistance += dt * DISTANCE_RATE;
-            }
-
-            if (currentGamePadState.IsButtonDown(Buttons.RightTrigger)
-                || currentKeyboardState.IsKeyDown(Keys.Add)
-                || currentKeyboardState.IsKeyDown(Keys.OemPlus))
-            {
-                cameraDistance -= dt * DISTANCE_RATE;
-            }
-
-            // Group cycle
-            if ( (currentKeyboardState.IsKeyDown(Keys.G) && previousKeyboardState.IsKeyUp(Keys.G)) ||
-                 (currentGamePadState.IsButtonDown(Buttons.A) && previousGamePadState.IsButtonUp(Buttons.A)))
-            {
-                currentCamera = (currentCamera + 1) % NumGroups;
-            }
-
-            // Camera reset
-            if ((currentKeyboardState.IsKeyDown(Keys.Home) && previousKeyboardState.IsKeyUp(Keys.Home))
-                || (currentGamePadState.IsButtonDown(Buttons.Y) && previousGamePadState.IsButtonUp(Buttons.Y)))
-            {
-                cameraYaw = (float)Math.PI * 0.75F;
-                cameraPitch = MathHelper.PiOver4;
-                cameraDistance = 40;
-            }
-
-            // Orthographic vs. perpsective projection toggle
-            if ((currentKeyboardState.IsKeyDown(Keys.B) && previousKeyboardState.IsKeyUp(Keys.B))
-                || (currentGamePadState.IsButtonDown(Buttons.B) && previousGamePadState.IsButtonUp(Buttons.B)))
-            {
-                cameraOrtho = !cameraOrtho;
-            }
-
-            if (currentKeyboardState.IsKeyDown(Keys.O))
-            {
-                cameraOrtho = true;
-            }
-
-            if (currentKeyboardState.IsKeyDown(Keys.P))
-            {
-                cameraOrtho = false;
-            }
-
-            // Pause animation
-            if ((currentKeyboardState.IsKeyDown(Keys.Space) && previousKeyboardState.IsKeyUp(Keys.Space))
-                || (currentGamePadState.IsButtonDown(Buttons.X) && previousGamePadState.IsButtonUp(Buttons.X)))
-            {
-                paused = !paused;
-            }
-
-            // Handle tap, drag, and pinch gestures
-            foreach (GestureSample sample in currentGestures)
-            {
-                switch (sample.GestureType)
-                {
-                    case GestureType.Tap:
-                        currentCamera = (currentCamera + 1) % NumGroups;
-                        break;
-
-                    case GestureType.FreeDrag:
-                        cameraYaw += sample.Delta.X * -YAW_DRAG_RATE;
-                        cameraPitch += sample.Delta.Y * PITCH_DRAG_RATE;
-                        break;
-
-                    case GestureType.Pinch:
-                        float dOld = Vector2.Distance(sample.Position - sample.Delta, sample.Position2 - sample.Delta2);
-                        float dNew = Vector2.Distance(sample.Position, sample.Position2);
-                        cameraDistance *= (float)Math.Exp((dOld - dNew) * PINCH_ZOOM_RATE);
-                        break;
-                }
-            }
-
-            // Clamp camera to safe values
-            cameraYaw = MathHelper.WrapAngle(cameraYaw);
-            cameraPitch = MathHelper.Clamp(cameraPitch, -MathHelper.PiOver2, MathHelper.PiOver2);
-            cameraDistance = MathHelper.Clamp(cameraDistance, 2, 80);
-
-            // Handle time-based lerp for group transition
-            float lerp = Math.Min(4.0F * dt, 1.0F);
-            cameraTarget = (lerp * cameraOrigins[currentCamera]) + ((1.0F - lerp) * cameraTarget);
-        }
-
-        #endregion
-
-        #region Draw
-
-        /// <summary>
-        /// This draws the bounding shapes and HUD for the sample.
-        /// </summary>
-        /// <param name="gameTime">Provides a snapshot of timing values.</param>
-        protected override void Draw(GameTime gameTime)
-        {
-            GraphicsDevice.Clear(Color.CornflowerBlue);
-
-            float aspect = GraphicsDevice.Viewport.AspectRatio;
-
-            float yawCos = (float)Math.Cos(cameraYaw);
-            float yawSin = (float)Math.Sin(cameraYaw);
-
-            float pitchCos = (float)Math.Cos(cameraPitch);
-            float pitchSin = (float)Math.Sin(cameraPitch);
-
-            Vector3 eye = new Vector3(cameraDistance * pitchCos * yawSin + cameraTarget.X,
-                                       cameraDistance * pitchSin + cameraTarget.Y,
-                                       cameraDistance * pitchCos * yawCos + cameraTarget.Z);
-            Matrix view = Matrix.CreateLookAt(eye, cameraTarget, Vector3.Up);
-            Matrix projection = (cameraOrtho)
-                                ? Matrix.CreateOrthographic(aspect * cameraDistance, cameraDistance, 1.0F, 1000.0F)
-                                : Matrix.CreatePerspectiveFieldOfView((float)(System.Math.PI) / 4.0F, aspect, 1.0F, 1000.0F);
-
-            debugDraw.Begin(view, projection);
-
-            // Draw ground planes
-            for (int g = 0; g < NumGroups; ++g)
-            {
-                Vector3 origin = new Vector3(cameraOrigins[g].X - 20, cameraOrigins[g].Y - 10, cameraOrigins[g].Z - 20);
-                debugDraw.DrawWireGrid(Vector3.UnitX*40, Vector3.UnitZ*40, origin, 20, 20, Color.Black);
-            }
-
-            DrawPrimaryShapes();
-
-            // Draw secondary shapes
-            for (int g = 0; g < NumGroups; g++)
-            {
-                debugDraw.DrawWireSphere(secondarySpheres[g], GetCollideColor(g, SphereIndex));
-                debugDraw.DrawWireBox(secondaryAABoxes[g], GetCollideColor(g, AABoxIndex));
-                debugDraw.DrawWireBox(secondaryOBoxes[g], GetCollideColor(g, OBoxIndex));
-                debugDraw.DrawWireTriangle(secondaryTris[g], GetCollideColor(g, TriIndex));
-            }
-
-            // Draw results of ray-object intersection, if there was a hit this frame
-            if (rayHitResult.HasValue)
-            {
-                Vector3 size = new Vector3(0.05f, 0.05f, 0.05f);
-                BoundingBox weeBox = new BoundingBox(rayHitResult.Value - size, rayHitResult.Value + size);
-                debugDraw.DrawWireBox(weeBox, Color.Yellow);
-            }
-
-            debugDraw.End();
-
-            // Draw overlay text.
-            //     string text = "A = (G)roup\nY = Reset (Home)\nB = (O)rtho/(P)erspective\nX = Pause (Space)";
-
-            // spriteBatch.Begin();
-            // spriteBatch.DrawString(spriteFont, text, new Vector2(86, 48), Color.White);
-            // spriteBatch.End();
-
-            base.Draw(gameTime);
-        }
-
-        void DrawPrimaryShapes()
-        {
-            debugDraw.DrawWireBox(primaryAABox, Color.White);
-            debugDraw.DrawWireBox(primaryOBox, Color.White);
-            debugDraw.DrawWireFrustum(primaryFrustum, Color.White);
-            debugDraw.DrawWireSphere(primarySphere, Color.White);
-            debugDraw.DrawRay(primaryRay, Color.Red, 10.0f);
-        }
-
-        private Color GetCollideColor(int group, int shape)
-        {
-            ContainmentType cr = collideResults[group, shape];
-            switch (cr)
-            {
-                case ContainmentType.Contains:
-                    return Color.Red;
-                case ContainmentType.Disjoint:
-                    return Color.LightGray;
-                case ContainmentType.Intersects:
-                    return Color.Yellow;
-                default:
-                    return Color.Black;
-            }
-        }
-
-        #endregion
-    }
-
-}
+//-----------------------------------------------------------------------------
+// CollisionSample.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Media;
+
+namespace CollisionSample
+{
+    /// <summary>
+    /// This sample demonstrates various forms of collision detection for primitives
+    /// supplied in the framework, plus oriented bounding boxes and triangles.
+    /// </summary>
+    public class CollisionGame : Game
+    {
+
+        public const int FrustumGroupIndex = 0;
+        public const int AABoxGroupIndex = 1;
+        public const int OBoxGroupIndex = 2;
+        public const int SphereGroupIndex = 3;
+        public const int RayGroupIndex = 4;
+        public const int NumGroups = 5;
+
+        public const int TriIndex = 0;
+        public const int SphereIndex = 1;
+        public const int AABoxIndex = 2;
+        public const int OBoxIndex = 3;
+        public const int NumSecondaryShapes = 4;
+
+        public const float CAMERA_SPACING = 50.0F;
+
+        public const float YAW_RATE = 1;            // radians per second for keyboard controls
+        public const float PITCH_RATE = 0.75f;      // radians per second for keyboard controls
+        public const float YAW_DRAG_RATE = .01f;     // radians per pixel for drag control
+        public const float PITCH_DRAG_RATE = .01f;   // radians per pixel for drag control
+        public const float PINCH_ZOOM_RATE = .01f;  // scale factor for pinch-zoom rate
+        public const float DISTANCE_RATE = 10;
+
+
+
+        // Rendering helpers
+        GraphicsDeviceManager graphics;
+        DebugDraw debugDraw;
+
+        // Primary shapes 
+        BoundingFrustum primaryFrustum;
+        BoundingBox primaryAABox;
+        BoundingOrientedBox primaryOBox;
+        BoundingSphere primarySphere;
+        Ray primaryRay;
+
+        // Secondary shapes.
+        Triangle[] secondaryTris = new Triangle[NumGroups];
+        BoundingSphere[] secondarySpheres = new BoundingSphere[NumGroups];
+        BoundingBox[] secondaryAABoxes = new BoundingBox[NumGroups];
+        BoundingOrientedBox[] secondaryOBoxes = new BoundingOrientedBox[NumGroups];
+
+        // Collision results
+        ContainmentType[,] collideResults = new ContainmentType[NumGroups, NumSecondaryShapes];
+        Vector3? rayHitResult;
+
+        // Camera state
+        Vector3[] cameraOrigins = new Vector3[NumGroups];
+        int currentCamera;
+
+        bool cameraOrtho;
+        float cameraYaw;
+        float cameraPitch;
+        float cameraDistance;
+        Vector3 cameraTarget;
+
+        KeyboardState currentKeyboardState = new KeyboardState();
+        GamePadState currentGamePadState = new GamePadState();
+        List<GestureSample> currentGestures = new List<GestureSample>();
+
+        KeyboardState previousKeyboardState = new KeyboardState();
+        GamePadState previousGamePadState = new GamePadState();
+
+        TimeSpan unpausedClock = new TimeSpan();
+        bool paused;
+
+
+
+        public CollisionGame()
+        {
+            graphics = new GraphicsDeviceManager(this);
+
+            Content.RootDirectory = "Content";
+
+            TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Pinch | GestureType.FreeDrag;
+
+            graphics.PreferredBackBufferWidth = 480;
+            graphics.PreferredBackBufferHeight = 640;
+
+            IsMouseVisible = true;
+
+#if ____MOBILE___
+            graphics.IsFullScreen = true;
+#endif
+        }
+
+        // Set up initial bounding shapes for the primary (static) and secondary (moving)
+        // bounding shapes along with relevant camera position information.
+        protected override void Initialize()
+        {
+            Console.WriteLine("DEBUG - Game Initialize!");
+
+            debugDraw = new DebugDraw(GraphicsDevice);
+
+            Components.Add(new FrameRateCounter(this));
+
+            // Primary frustum
+            Matrix m1 = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 1.77778F, 0.5f, 10.0f);
+            Matrix m2 = Matrix.CreateTranslation(new Vector3(0, 0, -7));
+            primaryFrustum = new BoundingFrustum(Matrix.Multiply(m2, m1));
+            cameraOrigins[FrustumGroupIndex] = Vector3.Zero;
+
+            // Primary axis-aligned box
+            primaryAABox.Min = new Vector3(CAMERA_SPACING - 3, -4, -5);
+            primaryAABox.Max = new Vector3(CAMERA_SPACING + 3, 4, 5);
+            cameraOrigins[AABoxGroupIndex] = new Vector3(CAMERA_SPACING, 0, 0);
+
+            // Primary oriented box
+            primaryOBox.Center = new Vector3(-CAMERA_SPACING, 0, 0);
+            primaryOBox.HalfExtent = new Vector3(3, 4, 5);
+            primaryOBox.Orientation = Quaternion.CreateFromYawPitchRoll(0.8f, 0.7f, 0);
+            cameraOrigins[OBoxGroupIndex] = primaryOBox.Center;
+
+            // Primary sphere
+            primarySphere.Center = new Vector3(0, 0, -CAMERA_SPACING);
+            primarySphere.Radius = 5;
+            cameraOrigins[SphereGroupIndex] = primarySphere.Center;
+
+            // Primary ray
+            primaryRay.Position = new Vector3(0, 0, CAMERA_SPACING);
+            primaryRay.Direction = Vector3.UnitZ;
+            cameraOrigins[RayGroupIndex] = primaryRay.Position;
+
+            // Initialize all of the secondary objects with default values
+            Vector3 half = new Vector3(0.5F, 0.5F, 0.5F);
+            for (int i = 0; i < NumGroups; i++)
+            {
+                secondarySpheres[i] = new BoundingSphere(Vector3.Zero, 1.0f);
+                secondaryOBoxes[i] = new BoundingOrientedBox(Vector3.Zero, half, Quaternion.Identity);
+                secondaryAABoxes[i] = new BoundingBox(-half, half);
+                secondaryTris[i] = new Triangle();
+            }
+
+            rayHitResult = null;
+
+            currentCamera = 3;
+            cameraOrtho = false;
+            cameraYaw = (float)Math.PI * 0.75F;
+            cameraPitch = MathHelper.PiOver4;
+            cameraDistance = 20;
+            cameraTarget = cameraOrigins[0];
+
+            paused = false;
+            
+            base.Initialize();
+        }
+
+
+        protected override void Update(GameTime gameTime)
+        {
+            ReadInputDevices();
+
+            if (currentKeyboardState.IsKeyDown(Keys.Escape) || currentGamePadState.IsButtonDown(Buttons.Back))
+                this.Exit();
+
+            if (!paused)
+            {
+                unpausedClock += gameTime.ElapsedGameTime;
+            }
+
+            // Run collision even when paused, for timing and debugging, and so the step
+            // forward/backward keys work.
+            Animate();
+            Collide();
+            HandleInput(gameTime);
+
+            base.Update(gameTime);
+        }
+
+        private void ReadInputDevices()
+        {
+            previousKeyboardState = currentKeyboardState;
+            previousGamePadState = currentGamePadState;
+
+            currentKeyboardState = Keyboard.GetState();
+            currentGamePadState = GamePad.GetState(PlayerIndex.One);
+            currentGestures.Clear();
+            while (TouchPanel.IsGestureAvailable)
+            {
+                currentGestures.Add(TouchPanel.ReadGesture());
+            }
+        }
+
+        /// <summary>
+        /// Move all the secondary shapes around, and move the ray so it
+        /// sweeps back and forth across its secondry shapes.
+        /// </summary>
+        private void Animate()
+        {
+            float t = (float)unpausedClock.TotalSeconds / 2.0F;
+
+            // Rates at which x,y,z cycle
+            const float xRate = 1.1f;
+            const float yRate = 3.6f;
+            const float zRate = 1.9f;
+            const float pathSize = 6;
+
+            // how far apart following shapes are, in seconds
+            const float gap = 0.25f;
+
+            // orientation for rotatable shapes
+            Quaternion orientation = Quaternion.CreateFromYawPitchRoll(t * 0.2f, t * 1.4f, t);
+
+            for (int g = 0; g < NumGroups; g++)
+            {
+                // Animate spheres
+                secondarySpheres[g].Center = cameraOrigins[g];
+                secondarySpheres[g].Center.X += pathSize * (float)Math.Sin(xRate * t);
+                secondarySpheres[g].Center.Y += pathSize * (float)Math.Sin(yRate * t);
+                secondarySpheres[g].Center.Z += pathSize * (float)Math.Sin(zRate * t);
+
+                // Animate oriented boxes
+                secondaryOBoxes[g].Center = cameraOrigins[g];
+                secondaryOBoxes[g].Orientation = orientation;
+                secondaryOBoxes[g].Center.X += pathSize * (float)Math.Sin(xRate * (t - gap));
+                secondaryOBoxes[g].Center.Y += pathSize * (float)Math.Sin(yRate * (t - gap));
+                secondaryOBoxes[g].Center.Z += pathSize * (float)Math.Sin(zRate * (t - gap));
+
+                // Animate axis-aligned boxes
+                Vector3 boxsize = new Vector3(1.0f, 1.3f, 1.9f);
+                secondaryAABoxes[g].Min = cameraOrigins[g] - boxsize * 0.5f;
+                secondaryAABoxes[g].Min.X += pathSize * (float)Math.Sin(xRate * (t - 2 * gap));
+                secondaryAABoxes[g].Min.Y += pathSize * (float)Math.Sin(yRate * (t - 2 * gap));
+                secondaryAABoxes[g].Min.Z += pathSize * (float)Math.Sin(zRate * (t - 2 * gap));
+                secondaryAABoxes[g].Max = secondaryAABoxes[g].Min + boxsize;
+
+                // Animate triangles
+                Vector3 trianglePos = cameraOrigins[g];
+                trianglePos.X += pathSize * (float)Math.Sin(xRate * (t - 3 * gap));
+                trianglePos.Y += pathSize * (float)Math.Sin(yRate * (t - 3 * gap));
+                trianglePos.Z += pathSize * (float)Math.Sin(zRate * (t - 3 * gap));
+
+                // triangle points in local space - equilateral triangle with radius of 2
+                secondaryTris[g].V0 = trianglePos + Vector3.Transform(new Vector3(0, 2, 0), orientation);
+                secondaryTris[g].V1 = trianglePos + Vector3.Transform(new Vector3(1.73f, -1, 0), orientation);
+                secondaryTris[g].V2 = trianglePos + Vector3.Transform(new Vector3(-1.73f, -1, 0), orientation);
+            }
+            //int index = (int)(t*5);
+            //secondaryOBoxes[OBoxGroupIndex] = BoundingOrientedBox.iboxes[index % BoundingOrientedBox.iboxes.Length];
+
+            // Animate primary ray (this is the only animated primary object)
+            // It sweeps back and forth across the secondary objects
+            const float sweepTime = 3.1f;
+            float rayDt = (-Math.Abs((t/sweepTime) % 2.0f - 1.0f) * NumSecondaryShapes + 0.5f) * gap;
+            primaryRay.Direction.X = (float)Math.Sin(xRate * (t + rayDt));
+            primaryRay.Direction.Y = (float)Math.Sin(yRate * (t + rayDt));
+            primaryRay.Direction.Z = (float)Math.Sin(zRate * (t + rayDt));
+            primaryRay.Direction.Normalize();
+        }
+
+        /// <summary>
+        /// Check each pair of objects for collision/containment and store the results for
+        /// coloring them at render time.
+        /// </summary>
+        private void Collide()
+        {
+            // test collisions between objects and frustum
+            collideResults[FrustumGroupIndex, SphereIndex] = primaryFrustum.Contains(secondarySpheres[FrustumGroupIndex]);
+            collideResults[FrustumGroupIndex, OBoxIndex] = BoundingOrientedBox.Contains(primaryFrustum, ref secondaryOBoxes[FrustumGroupIndex]);
+            collideResults[FrustumGroupIndex, AABoxIndex] = primaryFrustum.Contains(secondaryAABoxes[FrustumGroupIndex]);
+            collideResults[FrustumGroupIndex, TriIndex] = TriangleTest.Contains(primaryFrustum, ref secondaryTris[FrustumGroupIndex]);
+
+            // test collisions between objects and aligned box
+            collideResults[AABoxGroupIndex, SphereIndex] = primaryAABox.Contains(secondarySpheres[AABoxGroupIndex]);
+            collideResults[AABoxGroupIndex, OBoxIndex] = BoundingOrientedBox.Contains(ref primaryAABox, ref secondaryOBoxes[AABoxGroupIndex]);
+            collideResults[AABoxGroupIndex, AABoxIndex] = primaryAABox.Contains(secondaryAABoxes[AABoxGroupIndex]);
+            collideResults[AABoxGroupIndex, TriIndex] = TriangleTest.Contains(ref primaryAABox, ref secondaryTris[AABoxGroupIndex]);
+
+            // test collisions between objects and oriented box
+            collideResults[OBoxGroupIndex, SphereIndex] = primaryOBox.Contains(ref secondarySpheres[OBoxGroupIndex]);
+            collideResults[OBoxGroupIndex, OBoxIndex] = primaryOBox.Contains(ref secondaryOBoxes[OBoxGroupIndex]);
+            collideResults[OBoxGroupIndex, AABoxIndex] = primaryOBox.Contains(ref secondaryAABoxes[OBoxGroupIndex]);
+            collideResults[OBoxGroupIndex, TriIndex] = TriangleTest.Contains(ref primaryOBox, ref secondaryTris[OBoxGroupIndex]);
+
+            // test collisions between objects and sphere
+            collideResults[SphereGroupIndex, SphereIndex] = primarySphere.Contains(secondarySpheres[SphereGroupIndex]);
+            collideResults[SphereGroupIndex, OBoxIndex] = BoundingOrientedBox.Contains(ref primarySphere, ref secondaryOBoxes[SphereGroupIndex]);
+            collideResults[SphereGroupIndex, AABoxIndex] = primarySphere.Contains(secondaryAABoxes[SphereGroupIndex]);
+            collideResults[SphereGroupIndex, TriIndex] = TriangleTest.Contains(ref primarySphere, ref secondaryTris[SphereGroupIndex]);
+
+            // test collisions between objects and ray
+            float dist = -1;
+            collideResults[RayGroupIndex, SphereIndex] =
+            collideResults[RayGroupIndex, OBoxIndex] =
+            collideResults[RayGroupIndex, AABoxIndex] =
+            collideResults[RayGroupIndex, TriIndex] = ContainmentType.Disjoint;
+            rayHitResult = null;
+
+            float? r = primaryRay.Intersects(secondarySpheres[RayGroupIndex]);
+            if (r.HasValue)
+            {
+                collideResults[RayGroupIndex, SphereIndex] = ContainmentType.Intersects;
+                dist = r.Value;
+            }
+
+            r = secondaryOBoxes[RayGroupIndex].Intersects(ref primaryRay);
+            if (r.HasValue)
+            {
+                collideResults[RayGroupIndex, OBoxIndex] = ContainmentType.Intersects;
+                dist = r.Value;
+            }
+
+            r = primaryRay.Intersects(secondaryAABoxes[RayGroupIndex]);
+            if (r.HasValue)
+            {
+                collideResults[RayGroupIndex, AABoxIndex] = ContainmentType.Intersects;
+                dist = r.Value;
+            }
+
+            r = TriangleTest.Intersects(ref primaryRay, ref secondaryTris[RayGroupIndex]);
+            if (r.HasValue)
+            {
+                collideResults[RayGroupIndex, TriIndex] = ContainmentType.Intersects;
+                dist = r.Value;
+            }
+
+            // If one of the ray intersection tests was successful, fDistance will be positive.
+            // If so, compute the intersection location and store it in g_RayHitResultBox.
+            if (dist > 0)
+            {
+                rayHitResult = primaryRay.Position + primaryRay.Direction * dist;
+            }
+        }
+
+        private void HandleInput(GameTime gameTime)
+        {
+            // Rely on the fact that we are using fixed time-step update
+            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            // Allow single-stepping through time
+            if (paused)
+            {
+                if (currentKeyboardState.IsKeyDown(Keys.OemOpenBrackets))
+                    unpausedClock -= TimeSpan.FromSeconds(dt);
+                if (currentKeyboardState.IsKeyDown(Keys.OemCloseBrackets))
+                    unpausedClock += TimeSpan.FromSeconds(dt);
+            }
+
+            // Change yaw/pitch based on right thumbstickb
+            cameraYaw += currentGamePadState.ThumbSticks.Right.X * dt * YAW_RATE;
+            cameraPitch += currentGamePadState.ThumbSticks.Right.Y * dt * PITCH_RATE;
+
+            if (currentKeyboardState.IsKeyDown(Keys.Right))
+                cameraYaw += dt * YAW_RATE;
+            if (currentKeyboardState.IsKeyDown(Keys.Left))
+                cameraYaw -= dt * YAW_RATE;
+
+            if (currentKeyboardState.IsKeyDown(Keys.Up))
+                cameraPitch += dt * PITCH_RATE;
+            if (currentKeyboardState.IsKeyDown(Keys.Down))
+                cameraPitch -= dt * PITCH_RATE;
+
+            // Change distance based on right/left trigger
+            if (currentGamePadState.IsButtonDown(Buttons.LeftTrigger)
+                || currentKeyboardState.IsKeyDown(Keys.Subtract)
+                || currentKeyboardState.IsKeyDown(Keys.OemMinus))
+            {
+                cameraDistance += dt * DISTANCE_RATE;
+            }
+
+            if (currentGamePadState.IsButtonDown(Buttons.RightTrigger)
+                || currentKeyboardState.IsKeyDown(Keys.Add)
+                || currentKeyboardState.IsKeyDown(Keys.OemPlus))
+            {
+                cameraDistance -= dt * DISTANCE_RATE;
+            }
+
+            // Group cycle
+            if ( (currentKeyboardState.IsKeyDown(Keys.G) && previousKeyboardState.IsKeyUp(Keys.G)) ||
+                 (currentGamePadState.IsButtonDown(Buttons.A) && previousGamePadState.IsButtonUp(Buttons.A)))
+            {
+                currentCamera = (currentCamera + 1) % NumGroups;
+            }
+
+            // Camera reset
+            if ((currentKeyboardState.IsKeyDown(Keys.Home) && previousKeyboardState.IsKeyUp(Keys.Home))
+                || (currentGamePadState.IsButtonDown(Buttons.Y) && previousGamePadState.IsButtonUp(Buttons.Y)))
+            {
+                cameraYaw = (float)Math.PI * 0.75F;
+                cameraPitch = MathHelper.PiOver4;
+                cameraDistance = 40;
+            }
+
+            // Orthographic vs. perpsective projection toggle
+            if ((currentKeyboardState.IsKeyDown(Keys.B) && previousKeyboardState.IsKeyUp(Keys.B))
+                || (currentGamePadState.IsButtonDown(Buttons.B) && previousGamePadState.IsButtonUp(Buttons.B)))
+            {
+                cameraOrtho = !cameraOrtho;
+            }
+
+            if (currentKeyboardState.IsKeyDown(Keys.O))
+            {
+                cameraOrtho = true;
+            }
+
+            if (currentKeyboardState.IsKeyDown(Keys.P))
+            {
+                cameraOrtho = false;
+            }
+
+            // Pause animation
+            if ((currentKeyboardState.IsKeyDown(Keys.Space) && previousKeyboardState.IsKeyUp(Keys.Space))
+                || (currentGamePadState.IsButtonDown(Buttons.X) && previousGamePadState.IsButtonUp(Buttons.X)))
+            {
+                paused = !paused;
+            }
+
+            // Handle tap, drag, and pinch gestures
+            foreach (GestureSample sample in currentGestures)
+            {
+                switch (sample.GestureType)
+                {
+                    case GestureType.Tap:
+                        currentCamera = (currentCamera + 1) % NumGroups;
+                        break;
+
+                    case GestureType.FreeDrag:
+                        cameraYaw += sample.Delta.X * -YAW_DRAG_RATE;
+                        cameraPitch += sample.Delta.Y * PITCH_DRAG_RATE;
+                        break;
+
+                    case GestureType.Pinch:
+                        float dOld = Vector2.Distance(sample.Position - sample.Delta, sample.Position2 - sample.Delta2);
+                        float dNew = Vector2.Distance(sample.Position, sample.Position2);
+                        cameraDistance *= (float)Math.Exp((dOld - dNew) * PINCH_ZOOM_RATE);
+                        break;
+                }
+            }
+
+            // Clamp camera to safe values
+            cameraYaw = MathHelper.WrapAngle(cameraYaw);
+            cameraPitch = MathHelper.Clamp(cameraPitch, -MathHelper.PiOver2, MathHelper.PiOver2);
+            cameraDistance = MathHelper.Clamp(cameraDistance, 2, 80);
+
+            // Handle time-based lerp for group transition
+            float lerp = Math.Min(4.0F * dt, 1.0F);
+            cameraTarget = (lerp * cameraOrigins[currentCamera]) + ((1.0F - lerp) * cameraTarget);
+        }
+
+
+
+        /// <summary>
+        /// This draws the bounding shapes and HUD for the sample.
+        /// </summary>
+        /// <param name="gameTime">Provides a snapshot of timing values.</param>
+        protected override void Draw(GameTime gameTime)
+        {
+            GraphicsDevice.Clear(Color.CornflowerBlue);
+
+            float aspect = GraphicsDevice.Viewport.AspectRatio;
+
+            float yawCos = (float)Math.Cos(cameraYaw);
+            float yawSin = (float)Math.Sin(cameraYaw);
+
+            float pitchCos = (float)Math.Cos(cameraPitch);
+            float pitchSin = (float)Math.Sin(cameraPitch);
+
+            Vector3 eye = new Vector3(cameraDistance * pitchCos * yawSin + cameraTarget.X,
+                                       cameraDistance * pitchSin + cameraTarget.Y,
+                                       cameraDistance * pitchCos * yawCos + cameraTarget.Z);
+            Matrix view = Matrix.CreateLookAt(eye, cameraTarget, Vector3.Up);
+            Matrix projection = (cameraOrtho)
+                                ? Matrix.CreateOrthographic(aspect * cameraDistance, cameraDistance, 1.0F, 1000.0F)
+                                : Matrix.CreatePerspectiveFieldOfView((float)(System.Math.PI) / 4.0F, aspect, 1.0F, 1000.0F);
+
+            debugDraw.Begin(view, projection);
+
+            // Draw ground planes
+            for (int g = 0; g < NumGroups; ++g)
+            {
+                Vector3 origin = new Vector3(cameraOrigins[g].X - 20, cameraOrigins[g].Y - 10, cameraOrigins[g].Z - 20);
+                debugDraw.DrawWireGrid(Vector3.UnitX*40, Vector3.UnitZ*40, origin, 20, 20, Color.Black);
+            }
+
+            DrawPrimaryShapes();
+
+            // Draw secondary shapes
+            for (int g = 0; g < NumGroups; g++)
+            {
+                debugDraw.DrawWireSphere(secondarySpheres[g], GetCollideColor(g, SphereIndex));
+                debugDraw.DrawWireBox(secondaryAABoxes[g], GetCollideColor(g, AABoxIndex));
+                debugDraw.DrawWireBox(secondaryOBoxes[g], GetCollideColor(g, OBoxIndex));
+                debugDraw.DrawWireTriangle(secondaryTris[g], GetCollideColor(g, TriIndex));
+            }
+
+            // Draw results of ray-object intersection, if there was a hit this frame
+            if (rayHitResult.HasValue)
+            {
+                Vector3 size = new Vector3(0.05f, 0.05f, 0.05f);
+                BoundingBox weeBox = new BoundingBox(rayHitResult.Value - size, rayHitResult.Value + size);
+                debugDraw.DrawWireBox(weeBox, Color.Yellow);
+            }
+
+            debugDraw.End();
+
+            // Draw overlay text.
+            //     string text = "A = (G)roup\nY = Reset (Home)\nB = (O)rtho/(P)erspective\nX = Pause (Space)";
+
+            // spriteBatch.Begin();
+            // spriteBatch.DrawString(spriteFont, text, new Vector2(86, 48), Color.White);
+            // spriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+
+        void DrawPrimaryShapes()
+        {
+            debugDraw.DrawWireBox(primaryAABox, Color.White);
+            debugDraw.DrawWireBox(primaryOBox, Color.White);
+            debugDraw.DrawWireFrustum(primaryFrustum, Color.White);
+            debugDraw.DrawWireSphere(primarySphere, Color.White);
+            debugDraw.DrawRay(primaryRay, Color.Red, 10.0f);
+        }
+
+        private Color GetCollideColor(int group, int shape)
+        {
+            ContainmentType cr = collideResults[group, shape];
+            switch (cr)
+            {
+                case ContainmentType.Contains:
+                    return Color.Red;
+                case ContainmentType.Disjoint:
+                    return Color.LightGray;
+                case ContainmentType.Intersects:
+                    return Color.Yellow;
+                default:
+                    return Color.Black;
+            }
+        }
+
+    }
+
+}

+ 0 - 0
CollisionSample/Background.png → CollisionSample/Core/Content/Background.png


+ 0 - 0
CollisionSample/Game.ico → CollisionSample/Core/Content/Game.ico


+ 0 - 0
CollisionSample/GameThumbnail.png → CollisionSample/Core/Content/GameThumbnail.png


+ 16 - 0
CollisionSample/Core/Content/app.manifest

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="CollisionSample.app"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
+    </dependentAssembly>
+  </dependency>
+</assembly>

+ 325 - 335
CollisionSample/DebugDraw.cs → CollisionSample/Core/DebugDraw.cs

@@ -1,335 +1,325 @@
-//-----------------------------------------------------------------------------
-// DebugDraw.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-
-using System;
-using System.Diagnostics;
-using System.Collections.Generic;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-
-
-namespace CollisionSample
-{
-    /// <summary>
-    /// Debug drawing routines for common collision shapes. These are not designed to be the most
-    /// efficent way to submit geometry to the graphics device as they are intended for use in
-    /// visualizing collision for debugging purposes.
-    /// </summary>
-    public class DebugDraw : IDisposable
-    {
-        #region Constants
-
-        public const int MAX_VERTS = 2000;
-        public const int MAX_INDICES = 2000;
-
-        // Indices for drawing the edges of a cube, given the vertex ordering
-        // used by Bounding(Frustum|Box|OrientedBox).GetCorners()
-        static ushort[] cubeIndices = new ushort[] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 };
-        
-        #endregion
-
-        #region Fields
-
-        BasicEffect basicEffect;
-        DynamicVertexBuffer vertexBuffer;
-        DynamicIndexBuffer indexBuffer;
-
-        ushort[] Indices = new ushort[MAX_INDICES];
-        VertexPositionColor[] Vertices = new VertexPositionColor[MAX_VERTS];
-        int IndexCount;
-        int VertexCount;
-
-        #endregion
-
-        #region Initialization
-
-        public DebugDraw(GraphicsDevice device)
-        {
-            vertexBuffer = new DynamicVertexBuffer(device, typeof(VertexPositionColor), MAX_VERTS, BufferUsage.WriteOnly);
-            indexBuffer = new DynamicIndexBuffer(device, typeof(ushort), MAX_INDICES, BufferUsage.WriteOnly);
-
-            basicEffect = new BasicEffect(device); //(device, null);
-            basicEffect.LightingEnabled = false;
-            basicEffect.VertexColorEnabled = true;
-            basicEffect.TextureEnabled = false;
-        }
-
-        #endregion
-
-        #region Dispose
-
-        ~DebugDraw()
-        {
-            Dispose(false);
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                if (vertexBuffer != null)
-                    vertexBuffer.Dispose();
-
-                if (indexBuffer != null)
-                    indexBuffer.Dispose();
-
-                if (basicEffect != null)
-                    basicEffect.Dispose();
-            }
-        }
-
-        #endregion
-
-        #region Draw
-
-        /// <summary>
-        /// Starts debug drawing by setting the required render states and camera information
-        /// </summary>
-        public void Begin(Matrix view, Matrix projection)
-        {
-            basicEffect.World = Matrix.Identity;
-            basicEffect.View = view;
-            basicEffect.Projection = projection;
-
-            VertexCount = 0;
-            IndexCount = 0;
-        }
-
-        /// <summary>
-        /// Ends debug drawing and restores standard render states
-        /// </summary>
-        public void End()
-        {
-            FlushDrawing();
-        }
-
-        public void DrawWireShape(Vector3[] positionArray, ushort[] indexArray, Color color)
-        {
-            if (Reserve(positionArray.Length, indexArray.Length))
-            {
-                for (int i = 0; i < indexArray.Length; i++)
-                    Indices[IndexCount++] = (ushort)(VertexCount + indexArray[i]);
-
-                for (int i = 0; i < positionArray.Length; i++)
-                    Vertices[VertexCount++] = new VertexPositionColor(positionArray[i], color);
-            }
-        }
-
-        // Draw any queued objects and reset our line buffers
-        private void FlushDrawing()
-        {
-            if (IndexCount > 0)
-            {
-                vertexBuffer.SetData(Vertices, 0, VertexCount, SetDataOptions.Discard);
-                indexBuffer.SetData(Indices, 0, IndexCount, SetDataOptions.Discard);
-
-                GraphicsDevice device = basicEffect.GraphicsDevice;
-                device.SetVertexBuffer(vertexBuffer);
-                device.Indices = indexBuffer;
-
-                foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
-                {
-                    pass.Apply();
-                    device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, VertexCount, 0, IndexCount / 2);
-                }
-
-                device.SetVertexBuffer(null);
-                device.Indices = null;
-            }
-            IndexCount = 0;
-            VertexCount = 0;
-        }
-
-        // Check if there's enough space to draw an object with the given vertex/index counts.
-        // If necessary, call FlushDrawing() to make room.
-        private bool Reserve(int numVerts, int numIndices)
-        {
-            if(numVerts > MAX_VERTS || numIndices > MAX_INDICES)
-            {
-                // Whatever it is, we can't draw it
-                return false;
-            }
-            if (VertexCount + numVerts > MAX_VERTS || IndexCount + numIndices >= MAX_INDICES)
-            {
-                // We can draw it, but we need to make room first
-                FlushDrawing();
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// Renders a 2D grid (must be called within a Begin/End pair)
-        /// </summary>
-        /// <param name="xAxis">Vector direction for the local X-axis direction of the grid</param>
-        /// <param name="yAxis">Vector direction for the local Y-axis of the grid</param>
-        /// <param name="origin">3D starting anchor point for the grid</param>
-        /// <param name="iXDivisions">Number of divisions in the local X-axis direction</param>
-        /// <param name="iYDivisions">Number of divisions in the local Y-axis direction</param>
-        /// <param name="color">Color of the grid lines</param>
-        public void DrawWireGrid(Vector3 xAxis, Vector3 yAxis, Vector3 origin, int iXDivisions, int iYDivisions, Color color)
-        {
-            Vector3 pos, step;
-
-            pos = origin;
-            step = xAxis / iXDivisions;
-            for (int i = 0; i <= iXDivisions; i++)
-            {
-                DrawLine(pos, pos + yAxis, color);
-                pos += step;
-            }
-
-            pos = origin;
-            step = yAxis / iYDivisions;
-            for (int i = 0; i <= iYDivisions; i++)
-            {
-                DrawLine(pos, pos + xAxis, color);
-                pos += step;
-            }
-        }
-
-        /// <summary>
-        /// Renders the outline of a bounding frustum
-        /// </summary>
-        /// <param name="frustum">Bounding frustum to render</param>
-        /// <param name="color">Color of the frustum lines</param>
-        public void DrawWireFrustum(BoundingFrustum frustum, Color color)
-        {
-            DrawWireShape(frustum.GetCorners(), cubeIndices, color);
-        }
-
-        /// <summary>
-        /// Renders the outline of an axis-aligned bounding box
-        /// </summary>
-        /// <param name="box">Bounding box to render</param>
-        /// <param name="color">Color of the box lines</param>
-        public void DrawWireBox(BoundingBox box, Color color)
-        {
-            DrawWireShape(box.GetCorners(), cubeIndices, color);
-        }
-
-        /// <summary>
-        /// Renders the outline of an oriented bounding box
-        /// </summary>
-        /// <param name="box">Oriented bounding box to render</param>
-        /// <param name="color">Color of the box lines</param>
-        public void DrawWireBox(BoundingOrientedBox box, Color color)
-        {
-            DrawWireShape(box.GetCorners(), cubeIndices, color);
-        }
-
-        /// <summary>
-        /// Renders a circular ring (tessellated circle)
-        /// </summary>
-        /// <param name="origin">Center point for the ring</param>
-        /// <param name="majorAxis">Direction of the major-axis of the circle</param>
-        /// <param name="minorAxis">Direction of hte minor-axis of the circle</param>
-        /// <param name="color">Color of the ring lines</param>
-        public void DrawRing(Vector3 origin, Vector3 majorAxis, Vector3 minorAxis, Color color)
-        {
-            const int RING_SEGMENTS = 32;
-            const float fAngleDelta = 2.0F * (float)Math.PI / RING_SEGMENTS;
-
-            if (Reserve(RING_SEGMENTS, RING_SEGMENTS * 2))
-            {
-                for (int i = 0; i < RING_SEGMENTS; i++)
-                {
-                    Indices[IndexCount++] = (ushort)(VertexCount + i);
-                    Indices[IndexCount++] = (ushort)(VertexCount + (i + 1) % RING_SEGMENTS);
-                }
-                float cosDelta = (float)Math.Cos(fAngleDelta);
-                float sinDelta = (float)Math.Sin(fAngleDelta);
-
-                float cosAcc = 1;
-                float sinAcc = 0;
-
-                for (int i = 0; i < RING_SEGMENTS; ++i)
-                {
-                    Vector3 pos = new Vector3(majorAxis.X * cosAcc + minorAxis.X * sinAcc + origin.X,
-                                              majorAxis.Y * cosAcc + minorAxis.Y * sinAcc + origin.Y,
-                                              majorAxis.Z * cosAcc + minorAxis.Z * sinAcc + origin.Z);
-
-                    Vertices[VertexCount++] = new VertexPositionColor(pos, color);
-
-                    float newCos = cosAcc * cosDelta - sinAcc * sinDelta;
-                    float newSin = cosAcc * sinDelta + sinAcc * cosDelta;
-
-                    cosAcc = newCos;
-                    sinAcc = newSin;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Renders the outline of a bounding sphere.
-        /// 
-        /// This code assumes that the model and view matrices contain only rigid motion.
-        /// </summary>
-        /// <param name="sphere">Bounding sphere to render</param>
-        /// <param name="color">Color of the outline lines</param>
-        public void DrawWireSphere(BoundingSphere sphere, Color color)
-        {
-            // Invert the modelview matrix to get direction vectors
-            // in screen space, so we can draw a circle that always
-            // faces the camera.
-            Matrix view = basicEffect.World * basicEffect.View;
-            Matrix.Transpose(ref view, out view);
-            DrawRing(sphere.Center, view.Right * sphere.Radius, view.Up * sphere.Radius, color);
-        }
-
-        /// <summary>
-        /// Draw a ray of the given length
-        /// </summary>
-        /// <param name="ray"></param>
-        /// <param name="color"></param>
-        /// <param name="length"></param>
-        public void DrawRay(Ray ray, Color color, float length)
-        {
-            DrawLine(ray.Position, ray.Position + ray.Direction * length, color);
-        }
-
-        public void DrawLine(Vector3 v0, Vector3 v1, Color color)
-        {
-            if(Reserve(2, 2))
-            {
-                Indices[IndexCount++] = (ushort)VertexCount;
-                Indices[IndexCount++] = (ushort)(VertexCount+1);
-                Vertices[VertexCount++] = new VertexPositionColor(v0, color);
-                Vertices[VertexCount++] = new VertexPositionColor(v1, color);
-            }
-        }
-
-        public void DrawWireTriangle(Vector3 v0, Vector3 v1, Vector3 v2, Color color)
-        {
-            if(Reserve(3, 6))
-            {
-                Indices[IndexCount++] = (ushort)(VertexCount+0);
-                Indices[IndexCount++] = (ushort)(VertexCount+1);
-                Indices[IndexCount++] = (ushort)(VertexCount+1);
-                Indices[IndexCount++] = (ushort)(VertexCount+2);
-                Indices[IndexCount++] = (ushort)(VertexCount+2);
-                Indices[IndexCount++] = (ushort)(VertexCount+0);
-
-                Vertices[VertexCount++] = new VertexPositionColor(v0, color);
-                Vertices[VertexCount++] = new VertexPositionColor(v1, color);
-                Vertices[VertexCount++] = new VertexPositionColor(v2, color);
-            }
-        }
-
-        public void DrawWireTriangle(Triangle t, Color color)
-        {
-            DrawWireTriangle(t.V0, t.V1, t.V2, color);
-        }
-
-        #endregion
-    }
-}
+//-----------------------------------------------------------------------------
+// DebugDraw.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+
+namespace CollisionSample
+{
+    /// <summary>
+    /// Debug drawing routines for common collision shapes. These are not designed to be the most
+    /// efficent way to submit geometry to the graphics device as they are intended for use in
+    /// visualizing collision for debugging purposes.
+    /// </summary>
+    public class DebugDraw : IDisposable
+    {
+
+        public const int MAX_VERTS = 2000;
+        public const int MAX_INDICES = 2000;
+
+        // Indices for drawing the edges of a cube, given the vertex ordering
+        // used by Bounding(Frustum|Box|OrientedBox).GetCorners()
+        static ushort[] cubeIndices = new ushort[] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 };
+        
+
+
+        BasicEffect basicEffect;
+        DynamicVertexBuffer vertexBuffer;
+        DynamicIndexBuffer indexBuffer;
+
+        ushort[] Indices = new ushort[MAX_INDICES];
+        VertexPositionColor[] Vertices = new VertexPositionColor[MAX_VERTS];
+        int IndexCount;
+        int VertexCount;
+
+
+
+        public DebugDraw(GraphicsDevice device)
+        {
+            vertexBuffer = new DynamicVertexBuffer(device, typeof(VertexPositionColor), MAX_VERTS, BufferUsage.WriteOnly);
+            indexBuffer = new DynamicIndexBuffer(device, typeof(ushort), MAX_INDICES, BufferUsage.WriteOnly);
+
+            basicEffect = new BasicEffect(device); //(device, null);
+            basicEffect.LightingEnabled = false;
+            basicEffect.VertexColorEnabled = true;
+            basicEffect.TextureEnabled = false;
+        }
+
+
+
+        ~DebugDraw()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                if (vertexBuffer != null)
+                    vertexBuffer.Dispose();
+
+                if (indexBuffer != null)
+                    indexBuffer.Dispose();
+
+                if (basicEffect != null)
+                    basicEffect.Dispose();
+            }
+        }
+
+
+
+        /// <summary>
+        /// Starts debug drawing by setting the required render states and camera information
+        /// </summary>
+        public void Begin(Matrix view, Matrix projection)
+        {
+            basicEffect.World = Matrix.Identity;
+            basicEffect.View = view;
+            basicEffect.Projection = projection;
+
+            VertexCount = 0;
+            IndexCount = 0;
+        }
+
+        /// <summary>
+        /// Ends debug drawing and restores standard render states
+        /// </summary>
+        public void End()
+        {
+            FlushDrawing();
+        }
+
+        public void DrawWireShape(Vector3[] positionArray, ushort[] indexArray, Color color)
+        {
+            if (Reserve(positionArray.Length, indexArray.Length))
+            {
+                for (int i = 0; i < indexArray.Length; i++)
+                    Indices[IndexCount++] = (ushort)(VertexCount + indexArray[i]);
+
+                for (int i = 0; i < positionArray.Length; i++)
+                    Vertices[VertexCount++] = new VertexPositionColor(positionArray[i], color);
+            }
+        }
+
+        // Draw any queued objects and reset our line buffers
+        private void FlushDrawing()
+        {
+            if (IndexCount > 0)
+            {
+                vertexBuffer.SetData(Vertices, 0, VertexCount, SetDataOptions.Discard);
+                indexBuffer.SetData(Indices, 0, IndexCount, SetDataOptions.Discard);
+
+                GraphicsDevice device = basicEffect.GraphicsDevice;
+                device.SetVertexBuffer(vertexBuffer);
+                device.Indices = indexBuffer;
+
+                foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
+                {
+                    pass.Apply();
+                    device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, VertexCount, 0, IndexCount / 2);
+                }
+
+                device.SetVertexBuffer(null);
+                device.Indices = null;
+            }
+            IndexCount = 0;
+            VertexCount = 0;
+        }
+
+        // Check if there's enough space to draw an object with the given vertex/index counts.
+        // If necessary, call FlushDrawing() to make room.
+        private bool Reserve(int numVerts, int numIndices)
+        {
+            if(numVerts > MAX_VERTS || numIndices > MAX_INDICES)
+            {
+                // Whatever it is, we can't draw it
+                return false;
+            }
+            if (VertexCount + numVerts > MAX_VERTS || IndexCount + numIndices >= MAX_INDICES)
+            {
+                // We can draw it, but we need to make room first
+                FlushDrawing();
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Renders a 2D grid (must be called within a Begin/End pair)
+        /// </summary>
+        /// <param name="xAxis">Vector direction for the local X-axis direction of the grid</param>
+        /// <param name="yAxis">Vector direction for the local Y-axis of the grid</param>
+        /// <param name="origin">3D starting anchor point for the grid</param>
+        /// <param name="iXDivisions">Number of divisions in the local X-axis direction</param>
+        /// <param name="iYDivisions">Number of divisions in the local Y-axis direction</param>
+        /// <param name="color">Color of the grid lines</param>
+        public void DrawWireGrid(Vector3 xAxis, Vector3 yAxis, Vector3 origin, int iXDivisions, int iYDivisions, Color color)
+        {
+            Vector3 pos, step;
+
+            pos = origin;
+            step = xAxis / iXDivisions;
+            for (int i = 0; i <= iXDivisions; i++)
+            {
+                DrawLine(pos, pos + yAxis, color);
+                pos += step;
+            }
+
+            pos = origin;
+            step = yAxis / iYDivisions;
+            for (int i = 0; i <= iYDivisions; i++)
+            {
+                DrawLine(pos, pos + xAxis, color);
+                pos += step;
+            }
+        }
+
+        /// <summary>
+        /// Renders the outline of a bounding frustum
+        /// </summary>
+        /// <param name="frustum">Bounding frustum to render</param>
+        /// <param name="color">Color of the frustum lines</param>
+        public void DrawWireFrustum(BoundingFrustum frustum, Color color)
+        {
+            DrawWireShape(frustum.GetCorners(), cubeIndices, color);
+        }
+
+        /// <summary>
+        /// Renders the outline of an axis-aligned bounding box
+        /// </summary>
+        /// <param name="box">Bounding box to render</param>
+        /// <param name="color">Color of the box lines</param>
+        public void DrawWireBox(BoundingBox box, Color color)
+        {
+            DrawWireShape(box.GetCorners(), cubeIndices, color);
+        }
+
+        /// <summary>
+        /// Renders the outline of an oriented bounding box
+        /// </summary>
+        /// <param name="box">Oriented bounding box to render</param>
+        /// <param name="color">Color of the box lines</param>
+        public void DrawWireBox(BoundingOrientedBox box, Color color)
+        {
+            DrawWireShape(box.GetCorners(), cubeIndices, color);
+        }
+
+        /// <summary>
+        /// Renders a circular ring (tessellated circle)
+        /// </summary>
+        /// <param name="origin">Center point for the ring</param>
+        /// <param name="majorAxis">Direction of the major-axis of the circle</param>
+        /// <param name="minorAxis">Direction of hte minor-axis of the circle</param>
+        /// <param name="color">Color of the ring lines</param>
+        public void DrawRing(Vector3 origin, Vector3 majorAxis, Vector3 minorAxis, Color color)
+        {
+            const int RING_SEGMENTS = 32;
+            const float fAngleDelta = 2.0F * (float)Math.PI / RING_SEGMENTS;
+
+            if (Reserve(RING_SEGMENTS, RING_SEGMENTS * 2))
+            {
+                for (int i = 0; i < RING_SEGMENTS; i++)
+                {
+                    Indices[IndexCount++] = (ushort)(VertexCount + i);
+                    Indices[IndexCount++] = (ushort)(VertexCount + (i + 1) % RING_SEGMENTS);
+                }
+                float cosDelta = (float)Math.Cos(fAngleDelta);
+                float sinDelta = (float)Math.Sin(fAngleDelta);
+
+                float cosAcc = 1;
+                float sinAcc = 0;
+
+                for (int i = 0; i < RING_SEGMENTS; ++i)
+                {
+                    Vector3 pos = new Vector3(majorAxis.X * cosAcc + minorAxis.X * sinAcc + origin.X,
+                                              majorAxis.Y * cosAcc + minorAxis.Y * sinAcc + origin.Y,
+                                              majorAxis.Z * cosAcc + minorAxis.Z * sinAcc + origin.Z);
+
+                    Vertices[VertexCount++] = new VertexPositionColor(pos, color);
+
+                    float newCos = cosAcc * cosDelta - sinAcc * sinDelta;
+                    float newSin = cosAcc * sinDelta + sinAcc * cosDelta;
+
+                    cosAcc = newCos;
+                    sinAcc = newSin;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Renders the outline of a bounding sphere.
+        /// 
+        /// This code assumes that the model and view matrices contain only rigid motion.
+        /// </summary>
+        /// <param name="sphere">Bounding sphere to render</param>
+        /// <param name="color">Color of the outline lines</param>
+        public void DrawWireSphere(BoundingSphere sphere, Color color)
+        {
+            // Invert the modelview matrix to get direction vectors
+            // in screen space, so we can draw a circle that always
+            // faces the camera.
+            Matrix view = basicEffect.World * basicEffect.View;
+            Matrix.Transpose(ref view, out view);
+            DrawRing(sphere.Center, view.Right * sphere.Radius, view.Up * sphere.Radius, color);
+        }
+
+        /// <summary>
+        /// Draw a ray of the given length
+        /// </summary>
+        /// <param name="ray"></param>
+        /// <param name="color"></param>
+        /// <param name="length"></param>
+        public void DrawRay(Ray ray, Color color, float length)
+        {
+            DrawLine(ray.Position, ray.Position + ray.Direction * length, color);
+        }
+
+        public void DrawLine(Vector3 v0, Vector3 v1, Color color)
+        {
+            if(Reserve(2, 2))
+            {
+                Indices[IndexCount++] = (ushort)VertexCount;
+                Indices[IndexCount++] = (ushort)(VertexCount+1);
+                Vertices[VertexCount++] = new VertexPositionColor(v0, color);
+                Vertices[VertexCount++] = new VertexPositionColor(v1, color);
+            }
+        }
+
+        public void DrawWireTriangle(Vector3 v0, Vector3 v1, Vector3 v2, Color color)
+        {
+            if(Reserve(3, 6))
+            {
+                Indices[IndexCount++] = (ushort)(VertexCount+0);
+                Indices[IndexCount++] = (ushort)(VertexCount+1);
+                Indices[IndexCount++] = (ushort)(VertexCount+1);
+                Indices[IndexCount++] = (ushort)(VertexCount+2);
+                Indices[IndexCount++] = (ushort)(VertexCount+2);
+                Indices[IndexCount++] = (ushort)(VertexCount+0);
+
+                Vertices[VertexCount++] = new VertexPositionColor(v0, color);
+                Vertices[VertexCount++] = new VertexPositionColor(v1, color);
+                Vertices[VertexCount++] = new VertexPositionColor(v2, color);
+            }
+        }
+
+        public void DrawWireTriangle(Triangle t, Color color)
+        {
+            DrawWireTriangle(t.V0, t.V1, t.V2, color);
+        }
+
+    }
+}

+ 89 - 95
CollisionSample/FrameRateCounter.cs → CollisionSample/Core/FrameRateCounter.cs

@@ -1,95 +1,89 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// FrameRateCounter.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-#region Using Statements
-using System;
-using System.Collections.Generic;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Content;
-using Microsoft.Xna.Framework.Graphics;
-#endregion
-
-namespace CollisionSample
-{
-    /// <summary>
-    /// General Timing and Frame Rate Display Component.
-    /// Add this to the GameComponentCollection to display the frame rate
-    /// </summary>
-    public class FrameRateCounter : DrawableGameComponent
-    {
-        ContentManager  content;
-        SpriteBatch     spriteBatch;
-        SpriteFont      spriteFont;
-
-        int frameRate = 0;
-        int frameCounter = 0;
-        TimeSpan elapsedTime = TimeSpan.Zero;
-
-        /// <summary>
-        /// Constructor which initializes the Content Manager which is used later for loading the font for display.
-        /// </summary>
-        /// <param name="game"></param>
-        public FrameRateCounter(Game game)
-            : base(game)
-        {
-            content = new ContentManager(game.Services);
-        }
-
-        /// <summary>
-        /// Graphics device objects are created here including the font.
-        /// </summary>
-        protected override void LoadContent()
-        {
-            spriteBatch = new SpriteBatch(GraphicsDevice);
-            spriteFont = content.Load<SpriteFont>("content\\Font");
-        }
-
-        /// <summary>
-        /// 
-        /// </summary>
-        protected override void UnloadContent()
-        {
-            content.Unload();
-        }
-
-        /// <summary>
-        /// The Update function is where the timing is measured and frame rate is computed.
-        /// </summary>
-        /// <param name="gameTime"></param>
-        public override void Update(GameTime gameTime)
-        {
-            elapsedTime += gameTime.ElapsedGameTime;
-
-            if (elapsedTime > TimeSpan.FromSeconds(1))
-            {
-                elapsedTime -= TimeSpan.FromSeconds(1);
-                frameRate = frameCounter;
-                frameCounter = 0;
-            }
-        }
-
-        #region Draw
-        /// <summary>
-        /// Frame rate display occurs during the Draw method and uses the Font and Sprite batch to render text.
-        /// </summary>
-        /// <param name="gameTime"></param>
-        public override void Draw(GameTime gameTime)
-        {
-            frameCounter++;
-
-            string fps = string.Format("fps: {0}", frameRate);
-
-            spriteBatch.Begin();
-            spriteBatch.DrawString(spriteFont, fps, new Vector2(32, 32), Color.White);
-            spriteBatch.End();
-        }
-        #endregion
-    }
-
-}
+//-----------------------------------------------------------------------------
+// FrameRateCounter.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace CollisionSample
+{
+    /// <summary>
+    /// General Timing and Frame Rate Display Component.
+    /// Add this to the GameComponentCollection to display the frame rate
+    /// </summary>
+    public class FrameRateCounter : DrawableGameComponent
+    {
+        ContentManager  content;
+        SpriteBatch     spriteBatch;
+        SpriteFont      spriteFont;
+
+        int frameRate = 0;
+        int frameCounter = 0;
+        TimeSpan elapsedTime = TimeSpan.Zero;
+
+        /// <summary>
+        /// Constructor which initializes the Content Manager which is used later for loading the font for display.
+        /// </summary>
+        /// <param name="game"></param>
+        public FrameRateCounter(Game game)
+            : base(game)
+        {
+            content = game.Content;
+        }
+
+        /// <summary>
+        /// Graphics device objects are created here including the font.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+            spriteFont = content.Load<SpriteFont>("Arial");
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        protected override void UnloadContent()
+        {
+            content.Unload();
+        }
+
+        /// <summary>
+        /// The Update function is where the timing is measured and frame rate is computed.
+        /// </summary>
+        /// <param name="gameTime"></param>
+        public override void Update(GameTime gameTime)
+        {
+            elapsedTime += gameTime.ElapsedGameTime;
+
+            if (elapsedTime > TimeSpan.FromSeconds(1))
+            {
+                elapsedTime -= TimeSpan.FromSeconds(1);
+                frameRate = frameCounter;
+                frameCounter = 0;
+            }
+        }
+
+        /// <summary>
+        /// Frame rate display occurs during the Draw method and uses the Font and Sprite batch to render text.
+        /// </summary>
+        /// <param name="gameTime"></param>
+        public override void Draw(GameTime gameTime)
+        {
+            frameCounter++;
+
+            string fps = string.Format("fps: {0}", frameRate);
+
+            spriteBatch.Begin();
+            spriteBatch.DrawString(spriteFont, fps, new Vector2(32, 32), Color.White);
+            spriteBatch.End();
+        }
+    }
+
+}

+ 40 - 40
CollisionSample/GeomUtil.cs → CollisionSample/Core/GeomUtil.cs

@@ -1,40 +1,40 @@
-//-----------------------------------------------------------------------------
-// TriangleTest.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-using System;
-using Microsoft.Xna.Framework;
-
-
-namespace CollisionSample
-{
-    /// <summary>
-    /// Contains miscellaneous utilities augmenting the framework math library.
-    /// </summary>
-    public static class GeomUtil
-    {
-        /// <summary>
-        /// Do a full perspective transform of the given vector by the given matrix,
-        /// dividing out the w coordinate to return a Vector3 result.
-        /// </summary>
-        /// <param name="position">Vector3 of a point in space</param>
-        /// <param name="matrix">4x4 matrix</param>
-        /// <param name="result">Transformed vector after perspective divide</param>
-        public static void PerspectiveTransform(ref Vector3 position, ref Matrix matrix, out Vector3 result)
-        {
-            float w = position.X * matrix.M14 + position.Y * matrix.M24 + position.Z * matrix.M34 + matrix.M44;
-            float winv = 1.0f / w;
-
-            float x = position.X * matrix.M11 + position.Y * matrix.M21 + position.Z * matrix.M31 + matrix.M41;
-            float y = position.X * matrix.M12 + position.Y * matrix.M22 + position.Z * matrix.M32 + matrix.M42;
-            float z = position.X * matrix.M13 + position.Y * matrix.M23 + position.Z * matrix.M33 + matrix.M43;
-
-            result = new Vector3();
-            result.X = x * winv;
-            result.Y = y * winv;
-            result.Z = z * winv;
-        }
-    }
-}
+//-----------------------------------------------------------------------------
+// TriangleTest.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+using System;
+using Microsoft.Xna.Framework;
+
+
+namespace CollisionSample
+{
+    /// <summary>
+    /// Contains miscellaneous utilities augmenting the framework math library.
+    /// </summary>
+    public static class GeomUtil
+    {
+        /// <summary>
+        /// Do a full perspective transform of the given vector by the given matrix,
+        /// dividing out the w coordinate to return a Vector3 result.
+        /// </summary>
+        /// <param name="position">Vector3 of a point in space</param>
+        /// <param name="matrix">4x4 matrix</param>
+        /// <param name="result">Transformed vector after perspective divide</param>
+        public static void PerspectiveTransform(ref Vector3 position, ref Matrix matrix, out Vector3 result)
+        {
+            float w = position.X * matrix.M14 + position.Y * matrix.M24 + position.Z * matrix.M34 + matrix.M44;
+            float winv = 1.0f / w;
+
+            float x = position.X * matrix.M11 + position.Y * matrix.M21 + position.Z * matrix.M31 + matrix.M41;
+            float y = position.X * matrix.M12 + position.Y * matrix.M22 + position.Z * matrix.M32 + matrix.M42;
+            float z = position.X * matrix.M13 + position.Y * matrix.M23 + position.Z * matrix.M33 + matrix.M43;
+
+            result = new Vector3();
+            result.X = x * winv;
+            result.Y = y * winv;
+            result.Z = z * winv;
+        }
+    }
+}

+ 564 - 578
CollisionSample/TriangleTest.cs → CollisionSample/Core/TriangleTest.cs

@@ -1,578 +1,564 @@
-//---------------------------------------------------------------------------------------------------------------------
-// TriangleTest.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//---------------------------------------------------------------------------------------------------------------------
-
-using System;
-using Microsoft.Xna.Framework;
-
-namespace CollisionSample
-{
-    /// <summary>
-    /// Represents a simple triangle by the vertices at each corner.
-    /// </summary>
-    public struct Triangle
-    {
-        public Vector3 V0;
-        public Vector3 V1;
-        public Vector3 V2;
-
-        public Triangle(Vector3 v0, Vector3 v1, Vector3 v2)
-        {
-            V0 = v0;
-            V1 = v1;
-            V2 = v2;
-        }
-    }
-
-    /// <summary>
-    /// Triangle-based collision tests
-    /// </summary>
-    public static class TriangleTest
-    {
-        const float EPSILON = 1e-20F;
-
-        #region Triangle-BoundingBox
-
-        /// <summary>
-        /// Returns true if the given box intersects the triangle (v0,v1,v2).
-        /// </summary>
-        public static bool Intersects(ref BoundingBox box, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
-            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
-
-            // Transform the triangle into the local space with the box center at the origin
-            Triangle localTri = new Triangle();
-            Vector3.Subtract(ref v0, ref boxCenter, out localTri.V0);
-            Vector3.Subtract(ref v1, ref boxCenter, out localTri.V1);
-            Vector3.Subtract(ref v2, ref boxCenter, out localTri.V2);
-
-            return OriginBoxContains(ref boxHalfExtent, ref localTri) != ContainmentType.Disjoint;
-        }
-
-        /// <summary>
-        /// Tests whether the given box contains, intersects, or is disjoint from the triangle (v0,v1,v2).
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingBox box, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
-            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
-
-            // Transform the triangle into the local space with the box center at the origin
-            Triangle localTri;
-            Vector3.Subtract(ref v0, ref boxCenter, out localTri.V0);
-            Vector3.Subtract(ref v1, ref boxCenter, out localTri.V1);
-            Vector3.Subtract(ref v2, ref boxCenter, out localTri.V2);
-
-            return OriginBoxContains(ref boxHalfExtent, ref localTri);
-        }
-
-        /// <summary>
-        /// Tests whether the given box contains, intersects, or is disjoint from the given triangle.
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingBox box, ref Triangle triangle)
-        {
-            return Contains(ref box, ref triangle.V0, ref triangle.V1, ref triangle.V2);
-        }
-        #endregion
-
-        #region Triangle-BoundingOrientedBox
-
-        /// <summary>
-        /// Returns true if the given BoundingOrientedBox intersects the triangle (v0,v1,v2)
-        /// </summary>
-        public static bool Intersects(ref BoundingOrientedBox obox, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            // Transform the triangle into the local space of the box, so we can use a
-            // faster axis-aligned box test.
-            // Note than when transforming more than one point, using an intermediate matrix
-            // is faster than doing multiple quaternion transforms directly.
-            Quaternion qinv;
-            Quaternion.Conjugate(ref obox.Orientation, out qinv);
-
-            Matrix minv = Matrix.CreateFromQuaternion(qinv);
-            Triangle localTri = new Triangle();
-            localTri.V0 = Vector3.TransformNormal(v0 - obox.Center, minv);
-            localTri.V1 = Vector3.TransformNormal(v1 - obox.Center, minv);
-            localTri.V2 = Vector3.TransformNormal(v2 - obox.Center, minv);
-
-            return OriginBoxContains(ref obox.HalfExtent, ref localTri) != ContainmentType.Disjoint;
-        }
-
-        /// <summary>
-        /// Determines whether the given BoundingOrientedBox contains/intersects/is disjoint from the triangle
-        /// (v0,v1,v2)
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingOrientedBox obox, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            // Transform the triangle into the local space of the box, so we can use a
-            // faster axis-aligned box test.
-            // Note than when transforming more than one point, using an intermediate matrix
-            // is faster than doing multiple quaternion transforms directly.
-            Quaternion qinv;
-            Quaternion.Conjugate(ref obox.Orientation, out qinv);
-
-            Matrix minv;
-            Matrix.CreateFromQuaternion(ref qinv, out minv);
-
-            Triangle localTri = new Triangle();
-            localTri.V0 = Vector3.TransformNormal(v0 - obox.Center, minv);
-            localTri.V1 = Vector3.TransformNormal(v1 - obox.Center, minv);
-            localTri.V2 = Vector3.TransformNormal(v2 - obox.Center, minv);
-
-            return OriginBoxContains(ref obox.HalfExtent, ref localTri);
-        }
-
-        /// <summary>
-        /// Determines whether the given BoundingOrientedBox contains/intersects/is disjoint from the
-        /// given triangle.
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingOrientedBox obox, ref Triangle triangle)
-        {
-            return Contains(ref obox, ref triangle.V0, ref triangle.V1, ref triangle.V2);
-        }
-        #endregion
-
-        #region Triangle-Sphere
-
-        /// <summary>
-        /// Returns true if the given sphere intersects the triangle (v0,v1,v2).
-        /// </summary>
-        public static bool Intersects(ref BoundingSphere sphere, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            Vector3 p = NearestPointOnTriangle(ref sphere.Center, ref v0, ref v1, ref v2);
-            return Vector3.DistanceSquared(sphere.Center, p) < sphere.Radius * sphere.Radius;
-        }
-
-        /// <summary>
-        /// Returns true if the given sphere intersects the given triangle.
-        /// </summary>
-        public static bool Intersects(ref BoundingSphere sphere, ref Triangle t)
-        {
-            Vector3 p = NearestPointOnTriangle(ref sphere.Center, ref t.V0, ref t.V1, ref t.V2);
-            return Vector3.DistanceSquared(sphere.Center, p) < sphere.Radius * sphere.Radius;
-        }
-
-        /// <summary>
-        /// Determines whether the given sphere contains/intersects/is disjoint from the triangle
-        /// (v0,v1,v2)
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingSphere sphere, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            float r2 = sphere.Radius * sphere.Radius;
-            if (Vector3.DistanceSquared(v0, sphere.Center) <= r2 &&
-                Vector3.DistanceSquared(v1, sphere.Center) <= r2 &&
-                Vector3.DistanceSquared(v2, sphere.Center) <= r2)
-                return ContainmentType.Contains;
-
-            return Intersects(ref sphere, ref v0, ref v1, ref v2)
-                   ? ContainmentType.Intersects : ContainmentType.Disjoint;
-        }
-
-        /// <summary>
-        /// Determines whether the given sphere contains/intersects/is disjoint from the
-        /// given triangle.
-        /// </summary>
-        public static ContainmentType Contains(ref BoundingSphere sphere, ref Triangle triangle)
-        {
-            return Contains(ref sphere, ref triangle.V0, ref triangle.V1, ref triangle.V2);
-        }
-        #endregion
-
-        #region Triangle-Frustum
-
-        /// <summary>
-        /// Returns true if the given frustum intersects the triangle (v0,v1,v2).
-        /// </summary>
-        public static bool Intersects(BoundingFrustum frustum, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            // A BoundingFrustum is defined by a matrix that projects the frustum shape
-            // into the box from (-1,-1,0) to (1,1,1). We will project the triangle
-            // through this matrix, and then do a simpler box-triangle test.
-            Matrix m = frustum.Matrix;
-            Triangle localTri;
-            GeomUtil.PerspectiveTransform(ref v0, ref m, out localTri.V0);
-            GeomUtil.PerspectiveTransform(ref v1, ref m, out localTri.V1);
-            GeomUtil.PerspectiveTransform(ref v2, ref m, out localTri.V2);
-
-            BoundingBox box;
-            box.Min = new Vector3(-1, -1, 0);
-            box.Max = new Vector3(1, 1, 1);
-
-            return Intersects(ref box, ref localTri.V0, ref localTri.V1, ref localTri.V2);
-        }
-
-        /// <summary>
-        /// Determines whether the given frustum contains/intersects/is disjoint from the triangle
-        /// (v0,v1,v2)
-        /// </summary>
-        public static ContainmentType Contains(BoundingFrustum frustum, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            // A BoundingFrustum is defined by a matrix that projects the frustum shape
-            // into the box from (-1,-1,0) to (1,1,1). We will project the triangle
-            // through this matrix, and then do a simpler box-triangle test.
-            Matrix m = frustum.Matrix;
-            Triangle localTri;
-            GeomUtil.PerspectiveTransform(ref v0, ref m, out localTri.V0);
-            GeomUtil.PerspectiveTransform(ref v1, ref m, out localTri.V1);
-            GeomUtil.PerspectiveTransform(ref v2, ref m, out localTri.V2);
-
-            // Center the projected box at the origin
-            Vector3 halfExtent = new Vector3(1, 1, 0.5f);
-            localTri.V0.Z -= 0.5f;
-            localTri.V1.Z -= 0.5f;
-            localTri.V2.Z -= 0.5f;
-            return OriginBoxContains(ref halfExtent, ref localTri);
-        }
-
-        /// <summary>
-        /// Determines whether the given frustum contains/intersects/is disjoint from the
-        /// given triangle.
-        /// </summary>
-        public static ContainmentType Contains(BoundingFrustum frustum, ref Triangle triangle)
-        {
-            return Contains(frustum, ref triangle.V0, ref triangle.V1, ref triangle.V2);
-        }
-        #endregion
-
-        #region Triangle-Plane
-
-        /// <summary>
-        /// Classify the triangle (v0,v1,v2) with respect to the given plane.
-        /// </summary>
-        public static PlaneIntersectionType Intersects(ref Plane plane, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            float dV0 = plane.DotCoordinate(v0);
-            float dV1 = plane.DotCoordinate(v1);
-            float dV2 = plane.DotCoordinate(v2);
-
-            if (Math.Min(dV0, Math.Min(dV1, dV2)) >= 0)
-            {
-                return PlaneIntersectionType.Front;
-            }
-            if (Math.Max(dV0, Math.Max(dV1, dV2)) <= 0)
-            {
-                return PlaneIntersectionType.Back;
-            }
-            return PlaneIntersectionType.Intersecting;
-        }
-        #endregion
-
-        #region Triangle-Ray
-
-        /// <summary>
-        /// Determine whether the triangle (v0,v1,v2) intersects the given ray. If there is intersection,
-        /// returns the parametric value of the intersection point on the ray. Otherwise returns null.
-        /// </summary>
-        public static float? Intersects(ref Ray ray, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            // The algorithm is based on Moller, Tomas and Trumbore, "Fast, Minimum Storage 
-            // Ray-Triangle Intersection", Journal of Graphics Tools, vol. 2, no. 1, 
-            // pp 21-28, 1997.
-
-            Vector3 e1 = v1 - v0;
-            Vector3 e2 = v2 - v0;
-
-            Vector3 p = Vector3.Cross(ray.Direction, e2);
-
-            float det = Vector3.Dot(e1, p);
-
-            float t;
-            if (det >= EPSILON)
-            {
-                // Determinate is positive (front side of the triangle).
-                Vector3 s = ray.Position - v0;
-                float u = Vector3.Dot(s, p);
-                if (u < 0 || u > det)
-                    return null;
-
-                Vector3 q = Vector3.Cross(s, e1);
-                float v = Vector3.Dot(ray.Direction, q);
-                if (v < 0 || ((u + v) > det))
-                    return null;
-
-                t = Vector3.Dot(e2, q);
-                if (t < 0)
-                    return null;
-            }
-            else if (det <= -EPSILON)
-            {
-                // Determinate is negative (back side of the triangle).
-                Vector3 s = ray.Position - v0;
-                float u = Vector3.Dot(s, p);
-                if (u > 0 || u < det)
-                    return null;
-
-                Vector3 q = Vector3.Cross(s, e1);
-                float v = Vector3.Dot(ray.Direction, q);
-                if (v > 0 || ((u + v) < det))
-                    return null;
-
-                t = Vector3.Dot(e2, q);
-                if (t > 0)
-                    return null;
-            }
-            else
-            {
-                // Parallel ray.
-                return null;
-            }
-
-            return t / det;
-        }
-
-        /// <summary>
-        /// Determine whether the given triangle intersects the given ray. If there is intersection,
-        /// returns the parametric value of the intersection point on the ray. Otherwise returns null.
-        /// </summary>
-        public static float? Intersects(ref Ray ray, ref Triangle tri)
-        {
-            return Intersects(ref ray, ref tri.V0, ref tri.V1, ref tri.V2);
-        }
-
-        #endregion
-
-        #region Common utility methods
-
-        /// <summary>
-        /// Return the point on triangle (v0,v1,v2) closest to point p.
-        /// </summary>
-        public static Vector3 NearestPointOnTriangle(ref Vector3 p, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
-        {
-            // We'll work in a space where v0 is the origin.
-            // Let D=p-v0 be the local position of p, E1=v1-v0 and E2=v2-v0 be the
-            // local positions of v1 and v2.
-            //
-            // Points on the triangle are defined by
-            //      P=v0 + s*E1 + t*E2 
-            //      for s >= 0, t >= 0, s+t <= 1
-            //
-            // To compute (s,t) for p, note that s=the ratio of the components of d and e1 which
-            // are perpendicular to e2 in the plane of the triangle.
-            //
-            // s = project(perp(D,E2),E1) / project(perp(E1,E2),E1)
-            // where project(A,B) = B*(A . B)/(B . B)
-            //       perp(A,B) = A - project(A,B)
-            //
-            // expanding and rearranging terms a bit gives:
-            //
-            //     (D . E1)*(E2 . E2) - (D . E2)*(E1 . E2)
-            // s = ---------------------------------------
-            //     (E1 . E1)*(E2 . E2) - (E1 . E2)^2
-            //
-            // t = [same thing with E1/E2 swapped]
-            //
-            // Note that the denominator is the same for s and t, so we only need to compute it
-            // once, and that the denominator is never negative. So we can compute the numerator
-            // and denominator separately, and only do the division in case we actually need to
-            // produce s and/or t.
-            //
-            // We also need the parametric projections of p onto each edge:
-            //      u1 onto E1, u2 onto E2, u12 onto the (v2-v1) edge.
-            //      u1 = (D . E1)/(E1 . E1)
-            //      u2 = (D . E2)/(E2 . E2)
-            Vector3 D = p - v0;
-            Vector3 E1 = (v1 - v0);
-            Vector3 E2 = (v2 - v0);
-            float dot11 = E1.LengthSquared();
-            float dot12 = Vector3.Dot(E1, E2);
-            float dot22 = E2.LengthSquared();
-            float dot1d = Vector3.Dot(E1, D);
-            float dot2d = Vector3.Dot(E2, D);
-            float dotdd = D.LengthSquared();
-
-            float s = dot1d * dot22 - dot2d * dot12;
-            float t = dot2d * dot11 - dot1d * dot12;
-            float d = dot11 * dot22 - dot12 * dot12;
-
-            if (dot1d <= 0 && dot2d <= 0)
-            {
-                // nearest point is V0
-                return v0;
-            }
-            if (s <= 0 && dot2d >= 0 && dot2d <= dot22)
-            {
-                // nearest point is on E2
-                return v0 + E2 * (dot2d / dot22);
-            }
-            if (t <= 0 && dot1d >= 0 && dot1d <= dot11)
-            {
-                // nearest point is on E1
-                return v0 + E1 * (dot1d / dot11);
-            }
-            if (s >= 0 && t >= 0 && s + t <= d)
-            {
-                // nearest point is inside the triangle
-                float dr = 1.0f / d;
-                return v0 + (s * dr) * E1 + (t * dr) * E2;
-            }
-
-            // we need to compute u12. This is hairier than
-            // u1 or u2 because we're not in a convenient
-            // basis any more.
-            float u12_num = dot2d - dot1d - dot12 + dot11;
-            float u12_den = dot22 + dot11 - 2 * dot12;
-            if (u12_num <= 0)
-            {
-                return v1;
-            }
-            if (u12_num >= u12_den)
-            {
-                return v2;
-            }
-            return v1 + (v2 - v1) * (u12_num / u12_den);
-        }
-
-        /// <summary>
-        /// Check if an origin-centered, axis-aligned box with the given half extents contains,
-        /// intersects, or is disjoint from the given triangle. This is used for the box and
-        /// frustum vs. triangle tests.
-        /// </summary>
-        public static ContainmentType OriginBoxContains(ref Vector3 halfExtent, ref Triangle tri)
-        {
-            BoundingBox triBounds = new BoundingBox(); // 'new' to work around NetCF bug
-            triBounds.Min.X = Math.Min(tri.V0.X, Math.Min(tri.V1.X, tri.V2.X));
-            triBounds.Min.Y = Math.Min(tri.V0.Y, Math.Min(tri.V1.Y, tri.V2.Y));
-            triBounds.Min.Z = Math.Min(tri.V0.Z, Math.Min(tri.V1.Z, tri.V2.Z));
-
-            triBounds.Max.X = Math.Max(tri.V0.X, Math.Max(tri.V1.X, tri.V2.X));
-            triBounds.Max.Y = Math.Max(tri.V0.Y, Math.Max(tri.V1.Y, tri.V2.Y));
-            triBounds.Max.Z = Math.Max(tri.V0.Z, Math.Max(tri.V1.Z, tri.V2.Z));
-
-            Vector3 triBoundhalfExtent;
-            triBoundhalfExtent.X = (triBounds.Max.X - triBounds.Min.X) * 0.5f;
-            triBoundhalfExtent.Y = (triBounds.Max.Y - triBounds.Min.Y) * 0.5f;
-            triBoundhalfExtent.Z = (triBounds.Max.Z - triBounds.Min.Z) * 0.5f;
-
-            Vector3 triBoundCenter;
-            triBoundCenter.X = (triBounds.Max.X + triBounds.Min.X) * 0.5f;
-            triBoundCenter.Y = (triBounds.Max.Y + triBounds.Min.Y) * 0.5f;
-            triBoundCenter.Z = (triBounds.Max.Z + triBounds.Min.Z) * 0.5f;
-
-            if (triBoundhalfExtent.X + halfExtent.X <= Math.Abs(triBoundCenter.X) ||
-                triBoundhalfExtent.Y + halfExtent.Y <= Math.Abs(triBoundCenter.Y) ||
-                triBoundhalfExtent.Z + halfExtent.Z <= Math.Abs(triBoundCenter.Z))
-            {
-                return ContainmentType.Disjoint;
-            }
-
-            if (triBoundhalfExtent.X + Math.Abs(triBoundCenter.X) <= halfExtent.X &&
-                triBoundhalfExtent.Y + Math.Abs(triBoundCenter.Y) <= halfExtent.Y &&
-                triBoundhalfExtent.Z + Math.Abs(triBoundCenter.Z) <= halfExtent.Z)
-            {
-                return ContainmentType.Contains;
-            }
-
-            Vector3 edge1, edge2, edge3;
-            Vector3.Subtract(ref tri.V1, ref tri.V0, out edge1);
-            Vector3.Subtract(ref tri.V2, ref tri.V0, out edge2);
-
-            Vector3 normal;
-            Vector3.Cross(ref edge1, ref edge2, out normal);
-            float triangleDist = Vector3.Dot(tri.V0, normal);
-            if(Math.Abs(normal.X*halfExtent.X) + Math.Abs(normal.Y*halfExtent.Y) + Math.Abs(normal.Z*halfExtent.Z) <= Math.Abs(triangleDist))
-            {
-                return ContainmentType.Disjoint;
-            }
-
-            // Worst case: we need to check all 9 possible separating planes
-            // defined by Cross(box edge,triangle edge)
-            // Check for separation in plane containing an axis of box A and and axis of box B
-            //
-            // We need to compute all 9 cross products to find them, but a lot of terms drop out
-            // since we're working in A's local space. Also, since each such plane is parallel
-            // to the defining axis in each box, we know those dot products will be 0 and can
-            // omit them.
-            Vector3.Subtract(ref tri.V1, ref tri.V2, out edge3);
-            float dv0, dv1, dv2, dhalf;
-
-            // a.X ^ b.X = (1,0,0) ^ edge1
-            // axis = Vector3(0, -edge1.Z, edge1.Y);
-            dv0 = tri.V0.Z * edge1.Y - tri.V0.Y * edge1.Z;
-            dv1 = tri.V1.Z * edge1.Y - tri.V1.Y * edge1.Z;
-            dv2 = tri.V2.Z * edge1.Y - tri.V2.Y * edge1.Z;
-            dhalf = Math.Abs(halfExtent.Y * edge1.Z) + Math.Abs(halfExtent.Z * edge1.Y);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.X ^ b.Y = (1,0,0) ^ edge2
-            // axis = Vector3(0, -edge2.Z, edge2.Y);
-            dv0 = tri.V0.Z * edge2.Y - tri.V0.Y * edge2.Z;
-            dv1 = tri.V1.Z * edge2.Y - tri.V1.Y * edge2.Z;
-            dv2 = tri.V2.Z * edge2.Y - tri.V2.Y * edge2.Z;
-            dhalf = Math.Abs(halfExtent.Y * edge2.Z) + Math.Abs(halfExtent.Z * edge2.Y);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.X ^ b.Y = (1,0,0) ^ edge3
-            // axis = Vector3(0, -edge3.Z, edge3.Y);
-            dv0 = tri.V0.Z * edge3.Y - tri.V0.Y * edge3.Z;
-            dv1 = tri.V1.Z * edge3.Y - tri.V1.Y * edge3.Z;
-            dv2 = tri.V2.Z * edge3.Y - tri.V2.Y * edge3.Z;
-            dhalf = Math.Abs(halfExtent.Y * edge3.Z) + Math.Abs(halfExtent.Z * edge3.Y);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.X = (0,1,0) ^ edge1
-            // axis = Vector3(edge1.Z, 0, -edge1.X);
-            dv0 = tri.V0.X * edge1.Z - tri.V0.Z * edge1.X;
-            dv1 = tri.V1.X * edge1.Z - tri.V1.Z * edge1.X;
-            dv2 = tri.V2.X * edge1.Z - tri.V2.Z * edge1.X;
-            dhalf = Math.Abs(halfExtent.X * edge1.Z) + Math.Abs(halfExtent.Z * edge1.X);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.X = (0,1,0) ^ edge2
-            // axis = Vector3(edge2.Z, 0, -edge2.X);
-            dv0 = tri.V0.X * edge2.Z - tri.V0.Z * edge2.X;
-            dv1 = tri.V1.X * edge2.Z - tri.V1.Z * edge2.X;
-            dv2 = tri.V2.X * edge2.Z - tri.V2.Z * edge2.X;
-            dhalf = Math.Abs(halfExtent.X * edge2.Z) + Math.Abs(halfExtent.Z * edge2.X);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.X = (0,1,0) ^ bX
-            // axis = Vector3(edge3.Z, 0, -edge3.X);
-            dv0 = tri.V0.X * edge3.Z - tri.V0.Z * edge3.X;
-            dv1 = tri.V1.X * edge3.Z - tri.V1.Z * edge3.X;
-            dv2 = tri.V2.X * edge3.Z - tri.V2.Z * edge3.X;
-            dhalf = Math.Abs(halfExtent.X * edge3.Z) + Math.Abs(halfExtent.Z * edge3.X);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.X = (0,0,1) ^ edge1
-            // axis = Vector3(-edge1.Y, edge1.X, 0);
-            dv0 = tri.V0.Y * edge1.X - tri.V0.X * edge1.Y;
-            dv1 = tri.V1.Y * edge1.X - tri.V1.X * edge1.Y;
-            dv2 = tri.V2.Y * edge1.X - tri.V2.X * edge1.Y;
-            dhalf = Math.Abs(halfExtent.Y * edge1.X) + Math.Abs(halfExtent.X * edge1.Y);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.X = (0,0,1) ^ edge2
-            // axis = Vector3(-edge2.Y, edge2.X, 0);
-            dv0 = tri.V0.Y * edge2.X - tri.V0.X * edge2.Y;
-            dv1 = tri.V1.Y * edge2.X - tri.V1.X * edge2.Y;
-            dv2 = tri.V2.Y * edge2.X - tri.V2.X * edge2.Y;
-            dhalf = Math.Abs(halfExtent.Y * edge2.X) + Math.Abs(halfExtent.X * edge2.Y);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            // a.Y ^ b.X = (0,0,1) ^ edge3
-            // axis = Vector3(-edge3.Y, edge3.X, 0);
-            dv0 = tri.V0.Y * edge3.X - tri.V0.X * edge3.Y;
-            dv1 = tri.V1.Y * edge3.X - tri.V1.X * edge3.Y;
-            dv2 = tri.V2.Y * edge3.X - tri.V2.X * edge3.Y;
-            dhalf = Math.Abs(halfExtent.Y * edge3.X) + Math.Abs(halfExtent.X * edge3.Y);
-            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
-                return ContainmentType.Disjoint;
-
-            return ContainmentType.Intersects;
-        }
-
-        #endregion
-    }
-}
+//---------------------------------------------------------------------------------------------------------------------
+// TriangleTest.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//---------------------------------------------------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+
+namespace CollisionSample
+{
+    /// <summary>
+    /// Represents a simple triangle by the vertices at each corner.
+    /// </summary>
+    public struct Triangle
+    {
+        public Vector3 V0;
+        public Vector3 V1;
+        public Vector3 V2;
+
+        public Triangle(Vector3 v0, Vector3 v1, Vector3 v2)
+        {
+            V0 = v0;
+            V1 = v1;
+            V2 = v2;
+        }
+    }
+
+    /// <summary>
+    /// Triangle-based collision tests
+    /// </summary>
+    public static class TriangleTest
+    {
+        const float EPSILON = 1e-20F;
+
+
+        /// <summary>
+        /// Returns true if the given box intersects the triangle (v0,v1,v2).
+        /// </summary>
+        public static bool Intersects(ref BoundingBox box, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
+            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
+
+            // Transform the triangle into the local space with the box center at the origin
+            Triangle localTri = new Triangle();
+            Vector3.Subtract(ref v0, ref boxCenter, out localTri.V0);
+            Vector3.Subtract(ref v1, ref boxCenter, out localTri.V1);
+            Vector3.Subtract(ref v2, ref boxCenter, out localTri.V2);
+
+            return OriginBoxContains(ref boxHalfExtent, ref localTri) != ContainmentType.Disjoint;
+        }
+
+        /// <summary>
+        /// Tests whether the given box contains, intersects, or is disjoint from the triangle (v0,v1,v2).
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingBox box, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
+            Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
+
+            // Transform the triangle into the local space with the box center at the origin
+            Triangle localTri;
+            Vector3.Subtract(ref v0, ref boxCenter, out localTri.V0);
+            Vector3.Subtract(ref v1, ref boxCenter, out localTri.V1);
+            Vector3.Subtract(ref v2, ref boxCenter, out localTri.V2);
+
+            return OriginBoxContains(ref boxHalfExtent, ref localTri);
+        }
+
+        /// <summary>
+        /// Tests whether the given box contains, intersects, or is disjoint from the given triangle.
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingBox box, ref Triangle triangle)
+        {
+            return Contains(ref box, ref triangle.V0, ref triangle.V1, ref triangle.V2);
+        }
+
+
+        /// <summary>
+        /// Returns true if the given BoundingOrientedBox intersects the triangle (v0,v1,v2)
+        /// </summary>
+        public static bool Intersects(ref BoundingOrientedBox obox, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            // Transform the triangle into the local space of the box, so we can use a
+            // faster axis-aligned box test.
+            // Note than when transforming more than one point, using an intermediate matrix
+            // is faster than doing multiple quaternion transforms directly.
+            Quaternion qinv;
+            Quaternion.Conjugate(ref obox.Orientation, out qinv);
+
+            Matrix minv = Matrix.CreateFromQuaternion(qinv);
+            Triangle localTri = new Triangle();
+            localTri.V0 = Vector3.TransformNormal(v0 - obox.Center, minv);
+            localTri.V1 = Vector3.TransformNormal(v1 - obox.Center, minv);
+            localTri.V2 = Vector3.TransformNormal(v2 - obox.Center, minv);
+
+            return OriginBoxContains(ref obox.HalfExtent, ref localTri) != ContainmentType.Disjoint;
+        }
+
+        /// <summary>
+        /// Determines whether the given BoundingOrientedBox contains/intersects/is disjoint from the triangle
+        /// (v0,v1,v2)
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingOrientedBox obox, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            // Transform the triangle into the local space of the box, so we can use a
+            // faster axis-aligned box test.
+            // Note than when transforming more than one point, using an intermediate matrix
+            // is faster than doing multiple quaternion transforms directly.
+            Quaternion qinv;
+            Quaternion.Conjugate(ref obox.Orientation, out qinv);
+
+            Matrix minv;
+            Matrix.CreateFromQuaternion(ref qinv, out minv);
+
+            Triangle localTri = new Triangle();
+            localTri.V0 = Vector3.TransformNormal(v0 - obox.Center, minv);
+            localTri.V1 = Vector3.TransformNormal(v1 - obox.Center, minv);
+            localTri.V2 = Vector3.TransformNormal(v2 - obox.Center, minv);
+
+            return OriginBoxContains(ref obox.HalfExtent, ref localTri);
+        }
+
+        /// <summary>
+        /// Determines whether the given BoundingOrientedBox contains/intersects/is disjoint from the
+        /// given triangle.
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingOrientedBox obox, ref Triangle triangle)
+        {
+            return Contains(ref obox, ref triangle.V0, ref triangle.V1, ref triangle.V2);
+        }
+
+
+        /// <summary>
+        /// Returns true if the given sphere intersects the triangle (v0,v1,v2).
+        /// </summary>
+        public static bool Intersects(ref BoundingSphere sphere, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            Vector3 p = NearestPointOnTriangle(ref sphere.Center, ref v0, ref v1, ref v2);
+            return Vector3.DistanceSquared(sphere.Center, p) < sphere.Radius * sphere.Radius;
+        }
+
+        /// <summary>
+        /// Returns true if the given sphere intersects the given triangle.
+        /// </summary>
+        public static bool Intersects(ref BoundingSphere sphere, ref Triangle t)
+        {
+            Vector3 p = NearestPointOnTriangle(ref sphere.Center, ref t.V0, ref t.V1, ref t.V2);
+            return Vector3.DistanceSquared(sphere.Center, p) < sphere.Radius * sphere.Radius;
+        }
+
+        /// <summary>
+        /// Determines whether the given sphere contains/intersects/is disjoint from the triangle
+        /// (v0,v1,v2)
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingSphere sphere, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            float r2 = sphere.Radius * sphere.Radius;
+            if (Vector3.DistanceSquared(v0, sphere.Center) <= r2 &&
+                Vector3.DistanceSquared(v1, sphere.Center) <= r2 &&
+                Vector3.DistanceSquared(v2, sphere.Center) <= r2)
+                return ContainmentType.Contains;
+
+            return Intersects(ref sphere, ref v0, ref v1, ref v2)
+                   ? ContainmentType.Intersects : ContainmentType.Disjoint;
+        }
+
+        /// <summary>
+        /// Determines whether the given sphere contains/intersects/is disjoint from the
+        /// given triangle.
+        /// </summary>
+        public static ContainmentType Contains(ref BoundingSphere sphere, ref Triangle triangle)
+        {
+            return Contains(ref sphere, ref triangle.V0, ref triangle.V1, ref triangle.V2);
+        }
+
+
+        /// <summary>
+        /// Returns true if the given frustum intersects the triangle (v0,v1,v2).
+        /// </summary>
+        public static bool Intersects(BoundingFrustum frustum, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            // A BoundingFrustum is defined by a matrix that projects the frustum shape
+            // into the box from (-1,-1,0) to (1,1,1). We will project the triangle
+            // through this matrix, and then do a simpler box-triangle test.
+            Matrix m = frustum.Matrix;
+            Triangle localTri;
+            GeomUtil.PerspectiveTransform(ref v0, ref m, out localTri.V0);
+            GeomUtil.PerspectiveTransform(ref v1, ref m, out localTri.V1);
+            GeomUtil.PerspectiveTransform(ref v2, ref m, out localTri.V2);
+
+            BoundingBox box;
+            box.Min = new Vector3(-1, -1, 0);
+            box.Max = new Vector3(1, 1, 1);
+
+            return Intersects(ref box, ref localTri.V0, ref localTri.V1, ref localTri.V2);
+        }
+
+        /// <summary>
+        /// Determines whether the given frustum contains/intersects/is disjoint from the triangle
+        /// (v0,v1,v2)
+        /// </summary>
+        public static ContainmentType Contains(BoundingFrustum frustum, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            // A BoundingFrustum is defined by a matrix that projects the frustum shape
+            // into the box from (-1,-1,0) to (1,1,1). We will project the triangle
+            // through this matrix, and then do a simpler box-triangle test.
+            Matrix m = frustum.Matrix;
+            Triangle localTri;
+            GeomUtil.PerspectiveTransform(ref v0, ref m, out localTri.V0);
+            GeomUtil.PerspectiveTransform(ref v1, ref m, out localTri.V1);
+            GeomUtil.PerspectiveTransform(ref v2, ref m, out localTri.V2);
+
+            // Center the projected box at the origin
+            Vector3 halfExtent = new Vector3(1, 1, 0.5f);
+            localTri.V0.Z -= 0.5f;
+            localTri.V1.Z -= 0.5f;
+            localTri.V2.Z -= 0.5f;
+            return OriginBoxContains(ref halfExtent, ref localTri);
+        }
+
+        /// <summary>
+        /// Determines whether the given frustum contains/intersects/is disjoint from the
+        /// given triangle.
+        /// </summary>
+        public static ContainmentType Contains(BoundingFrustum frustum, ref Triangle triangle)
+        {
+            return Contains(frustum, ref triangle.V0, ref triangle.V1, ref triangle.V2);
+        }
+
+
+        /// <summary>
+        /// Classify the triangle (v0,v1,v2) with respect to the given plane.
+        /// </summary>
+        public static PlaneIntersectionType Intersects(ref Plane plane, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            float dV0 = plane.DotCoordinate(v0);
+            float dV1 = plane.DotCoordinate(v1);
+            float dV2 = plane.DotCoordinate(v2);
+
+            if (Math.Min(dV0, Math.Min(dV1, dV2)) >= 0)
+            {
+                return PlaneIntersectionType.Front;
+            }
+            if (Math.Max(dV0, Math.Max(dV1, dV2)) <= 0)
+            {
+                return PlaneIntersectionType.Back;
+            }
+            return PlaneIntersectionType.Intersecting;
+        }
+
+
+        /// <summary>
+        /// Determine whether the triangle (v0,v1,v2) intersects the given ray. If there is intersection,
+        /// returns the parametric value of the intersection point on the ray. Otherwise returns null.
+        /// </summary>
+        public static float? Intersects(ref Ray ray, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            // The algorithm is based on Moller, Tomas and Trumbore, "Fast, Minimum Storage 
+            // Ray-Triangle Intersection", Journal of Graphics Tools, vol. 2, no. 1, 
+            // pp 21-28, 1997.
+
+            Vector3 e1 = v1 - v0;
+            Vector3 e2 = v2 - v0;
+
+            Vector3 p = Vector3.Cross(ray.Direction, e2);
+
+            float det = Vector3.Dot(e1, p);
+
+            float t;
+            if (det >= EPSILON)
+            {
+                // Determinate is positive (front side of the triangle).
+                Vector3 s = ray.Position - v0;
+                float u = Vector3.Dot(s, p);
+                if (u < 0 || u > det)
+                    return null;
+
+                Vector3 q = Vector3.Cross(s, e1);
+                float v = Vector3.Dot(ray.Direction, q);
+                if (v < 0 || ((u + v) > det))
+                    return null;
+
+                t = Vector3.Dot(e2, q);
+                if (t < 0)
+                    return null;
+            }
+            else if (det <= -EPSILON)
+            {
+                // Determinate is negative (back side of the triangle).
+                Vector3 s = ray.Position - v0;
+                float u = Vector3.Dot(s, p);
+                if (u > 0 || u < det)
+                    return null;
+
+                Vector3 q = Vector3.Cross(s, e1);
+                float v = Vector3.Dot(ray.Direction, q);
+                if (v > 0 || ((u + v) < det))
+                    return null;
+
+                t = Vector3.Dot(e2, q);
+                if (t > 0)
+                    return null;
+            }
+            else
+            {
+                // Parallel ray.
+                return null;
+            }
+
+            return t / det;
+        }
+
+        /// <summary>
+        /// Determine whether the given triangle intersects the given ray. If there is intersection,
+        /// returns the parametric value of the intersection point on the ray. Otherwise returns null.
+        /// </summary>
+        public static float? Intersects(ref Ray ray, ref Triangle tri)
+        {
+            return Intersects(ref ray, ref tri.V0, ref tri.V1, ref tri.V2);
+        }
+
+
+
+        /// <summary>
+        /// Return the point on triangle (v0,v1,v2) closest to point p.
+        /// </summary>
+        public static Vector3 NearestPointOnTriangle(ref Vector3 p, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
+        {
+            // We'll work in a space where v0 is the origin.
+            // Let D=p-v0 be the local position of p, E1=v1-v0 and E2=v2-v0 be the
+            // local positions of v1 and v2.
+            //
+            // Points on the triangle are defined by
+            //      P=v0 + s*E1 + t*E2 
+            //      for s >= 0, t >= 0, s+t <= 1
+            //
+            // To compute (s,t) for p, note that s=the ratio of the components of d and e1 which
+            // are perpendicular to e2 in the plane of the triangle.
+            //
+            // s = project(perp(D,E2),E1) / project(perp(E1,E2),E1)
+            // where project(A,B) = B*(A . B)/(B . B)
+            //       perp(A,B) = A - project(A,B)
+            //
+            // expanding and rearranging terms a bit gives:
+            //
+            //     (D . E1)*(E2 . E2) - (D . E2)*(E1 . E2)
+            // s = ---------------------------------------
+            //     (E1 . E1)*(E2 . E2) - (E1 . E2)^2
+            //
+            // t = [same thing with E1/E2 swapped]
+            //
+            // Note that the denominator is the same for s and t, so we only need to compute it
+            // once, and that the denominator is never negative. So we can compute the numerator
+            // and denominator separately, and only do the division in case we actually need to
+            // produce s and/or t.
+            //
+            // We also need the parametric projections of p onto each edge:
+            //      u1 onto E1, u2 onto E2, u12 onto the (v2-v1) edge.
+            //      u1 = (D . E1)/(E1 . E1)
+            //      u2 = (D . E2)/(E2 . E2)
+            Vector3 D = p - v0;
+            Vector3 E1 = (v1 - v0);
+            Vector3 E2 = (v2 - v0);
+            float dot11 = E1.LengthSquared();
+            float dot12 = Vector3.Dot(E1, E2);
+            float dot22 = E2.LengthSquared();
+            float dot1d = Vector3.Dot(E1, D);
+            float dot2d = Vector3.Dot(E2, D);
+            float dotdd = D.LengthSquared();
+
+            float s = dot1d * dot22 - dot2d * dot12;
+            float t = dot2d * dot11 - dot1d * dot12;
+            float d = dot11 * dot22 - dot12 * dot12;
+
+            if (dot1d <= 0 && dot2d <= 0)
+            {
+                // nearest point is V0
+                return v0;
+            }
+            if (s <= 0 && dot2d >= 0 && dot2d <= dot22)
+            {
+                // nearest point is on E2
+                return v0 + E2 * (dot2d / dot22);
+            }
+            if (t <= 0 && dot1d >= 0 && dot1d <= dot11)
+            {
+                // nearest point is on E1
+                return v0 + E1 * (dot1d / dot11);
+            }
+            if (s >= 0 && t >= 0 && s + t <= d)
+            {
+                // nearest point is inside the triangle
+                float dr = 1.0f / d;
+                return v0 + (s * dr) * E1 + (t * dr) * E2;
+            }
+
+            // we need to compute u12. This is hairier than
+            // u1 or u2 because we're not in a convenient
+            // basis any more.
+            float u12_num = dot2d - dot1d - dot12 + dot11;
+            float u12_den = dot22 + dot11 - 2 * dot12;
+            if (u12_num <= 0)
+            {
+                return v1;
+            }
+            if (u12_num >= u12_den)
+            {
+                return v2;
+            }
+            return v1 + (v2 - v1) * (u12_num / u12_den);
+        }
+
+        /// <summary>
+        /// Check if an origin-centered, axis-aligned box with the given half extents contains,
+        /// intersects, or is disjoint from the given triangle. This is used for the box and
+        /// frustum vs. triangle tests.
+        /// </summary>
+        public static ContainmentType OriginBoxContains(ref Vector3 halfExtent, ref Triangle tri)
+        {
+            BoundingBox triBounds = new BoundingBox(); // 'new' to work around NetCF bug
+            triBounds.Min.X = Math.Min(tri.V0.X, Math.Min(tri.V1.X, tri.V2.X));
+            triBounds.Min.Y = Math.Min(tri.V0.Y, Math.Min(tri.V1.Y, tri.V2.Y));
+            triBounds.Min.Z = Math.Min(tri.V0.Z, Math.Min(tri.V1.Z, tri.V2.Z));
+
+            triBounds.Max.X = Math.Max(tri.V0.X, Math.Max(tri.V1.X, tri.V2.X));
+            triBounds.Max.Y = Math.Max(tri.V0.Y, Math.Max(tri.V1.Y, tri.V2.Y));
+            triBounds.Max.Z = Math.Max(tri.V0.Z, Math.Max(tri.V1.Z, tri.V2.Z));
+
+            Vector3 triBoundhalfExtent;
+            triBoundhalfExtent.X = (triBounds.Max.X - triBounds.Min.X) * 0.5f;
+            triBoundhalfExtent.Y = (triBounds.Max.Y - triBounds.Min.Y) * 0.5f;
+            triBoundhalfExtent.Z = (triBounds.Max.Z - triBounds.Min.Z) * 0.5f;
+
+            Vector3 triBoundCenter;
+            triBoundCenter.X = (triBounds.Max.X + triBounds.Min.X) * 0.5f;
+            triBoundCenter.Y = (triBounds.Max.Y + triBounds.Min.Y) * 0.5f;
+            triBoundCenter.Z = (triBounds.Max.Z + triBounds.Min.Z) * 0.5f;
+
+            if (triBoundhalfExtent.X + halfExtent.X <= Math.Abs(triBoundCenter.X) ||
+                triBoundhalfExtent.Y + halfExtent.Y <= Math.Abs(triBoundCenter.Y) ||
+                triBoundhalfExtent.Z + halfExtent.Z <= Math.Abs(triBoundCenter.Z))
+            {
+                return ContainmentType.Disjoint;
+            }
+
+            if (triBoundhalfExtent.X + Math.Abs(triBoundCenter.X) <= halfExtent.X &&
+                triBoundhalfExtent.Y + Math.Abs(triBoundCenter.Y) <= halfExtent.Y &&
+                triBoundhalfExtent.Z + Math.Abs(triBoundCenter.Z) <= halfExtent.Z)
+            {
+                return ContainmentType.Contains;
+            }
+
+            Vector3 edge1, edge2, edge3;
+            Vector3.Subtract(ref tri.V1, ref tri.V0, out edge1);
+            Vector3.Subtract(ref tri.V2, ref tri.V0, out edge2);
+
+            Vector3 normal;
+            Vector3.Cross(ref edge1, ref edge2, out normal);
+            float triangleDist = Vector3.Dot(tri.V0, normal);
+            if(Math.Abs(normal.X*halfExtent.X) + Math.Abs(normal.Y*halfExtent.Y) + Math.Abs(normal.Z*halfExtent.Z) <= Math.Abs(triangleDist))
+            {
+                return ContainmentType.Disjoint;
+            }
+
+            // Worst case: we need to check all 9 possible separating planes
+            // defined by Cross(box edge,triangle edge)
+            // Check for separation in plane containing an axis of box A and and axis of box B
+            //
+            // We need to compute all 9 cross products to find them, but a lot of terms drop out
+            // since we're working in A's local space. Also, since each such plane is parallel
+            // to the defining axis in each box, we know those dot products will be 0 and can
+            // omit them.
+            Vector3.Subtract(ref tri.V1, ref tri.V2, out edge3);
+            float dv0, dv1, dv2, dhalf;
+
+            // a.X ^ b.X = (1,0,0) ^ edge1
+            // axis = Vector3(0, -edge1.Z, edge1.Y);
+            dv0 = tri.V0.Z * edge1.Y - tri.V0.Y * edge1.Z;
+            dv1 = tri.V1.Z * edge1.Y - tri.V1.Y * edge1.Z;
+            dv2 = tri.V2.Z * edge1.Y - tri.V2.Y * edge1.Z;
+            dhalf = Math.Abs(halfExtent.Y * edge1.Z) + Math.Abs(halfExtent.Z * edge1.Y);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.X ^ b.Y = (1,0,0) ^ edge2
+            // axis = Vector3(0, -edge2.Z, edge2.Y);
+            dv0 = tri.V0.Z * edge2.Y - tri.V0.Y * edge2.Z;
+            dv1 = tri.V1.Z * edge2.Y - tri.V1.Y * edge2.Z;
+            dv2 = tri.V2.Z * edge2.Y - tri.V2.Y * edge2.Z;
+            dhalf = Math.Abs(halfExtent.Y * edge2.Z) + Math.Abs(halfExtent.Z * edge2.Y);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.X ^ b.Y = (1,0,0) ^ edge3
+            // axis = Vector3(0, -edge3.Z, edge3.Y);
+            dv0 = tri.V0.Z * edge3.Y - tri.V0.Y * edge3.Z;
+            dv1 = tri.V1.Z * edge3.Y - tri.V1.Y * edge3.Z;
+            dv2 = tri.V2.Z * edge3.Y - tri.V2.Y * edge3.Z;
+            dhalf = Math.Abs(halfExtent.Y * edge3.Z) + Math.Abs(halfExtent.Z * edge3.Y);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.X = (0,1,0) ^ edge1
+            // axis = Vector3(edge1.Z, 0, -edge1.X);
+            dv0 = tri.V0.X * edge1.Z - tri.V0.Z * edge1.X;
+            dv1 = tri.V1.X * edge1.Z - tri.V1.Z * edge1.X;
+            dv2 = tri.V2.X * edge1.Z - tri.V2.Z * edge1.X;
+            dhalf = Math.Abs(halfExtent.X * edge1.Z) + Math.Abs(halfExtent.Z * edge1.X);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.X = (0,1,0) ^ edge2
+            // axis = Vector3(edge2.Z, 0, -edge2.X);
+            dv0 = tri.V0.X * edge2.Z - tri.V0.Z * edge2.X;
+            dv1 = tri.V1.X * edge2.Z - tri.V1.Z * edge2.X;
+            dv2 = tri.V2.X * edge2.Z - tri.V2.Z * edge2.X;
+            dhalf = Math.Abs(halfExtent.X * edge2.Z) + Math.Abs(halfExtent.Z * edge2.X);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.X = (0,1,0) ^ bX
+            // axis = Vector3(edge3.Z, 0, -edge3.X);
+            dv0 = tri.V0.X * edge3.Z - tri.V0.Z * edge3.X;
+            dv1 = tri.V1.X * edge3.Z - tri.V1.Z * edge3.X;
+            dv2 = tri.V2.X * edge3.Z - tri.V2.Z * edge3.X;
+            dhalf = Math.Abs(halfExtent.X * edge3.Z) + Math.Abs(halfExtent.Z * edge3.X);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.X = (0,0,1) ^ edge1
+            // axis = Vector3(-edge1.Y, edge1.X, 0);
+            dv0 = tri.V0.Y * edge1.X - tri.V0.X * edge1.Y;
+            dv1 = tri.V1.Y * edge1.X - tri.V1.X * edge1.Y;
+            dv2 = tri.V2.Y * edge1.X - tri.V2.X * edge1.Y;
+            dhalf = Math.Abs(halfExtent.Y * edge1.X) + Math.Abs(halfExtent.X * edge1.Y);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.X = (0,0,1) ^ edge2
+            // axis = Vector3(-edge2.Y, edge2.X, 0);
+            dv0 = tri.V0.Y * edge2.X - tri.V0.X * edge2.Y;
+            dv1 = tri.V1.Y * edge2.X - tri.V1.X * edge2.Y;
+            dv2 = tri.V2.Y * edge2.X - tri.V2.X * edge2.Y;
+            dhalf = Math.Abs(halfExtent.Y * edge2.X) + Math.Abs(halfExtent.X * edge2.Y);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            // a.Y ^ b.X = (0,0,1) ^ edge3
+            // axis = Vector3(-edge3.Y, edge3.X, 0);
+            dv0 = tri.V0.Y * edge3.X - tri.V0.X * edge3.Y;
+            dv1 = tri.V1.Y * edge3.X - tri.V1.X * edge3.Y;
+            dv2 = tri.V2.Y * edge3.X - tri.V2.X * edge3.Y;
+            dhalf = Math.Abs(halfExtent.Y * edge3.X) + Math.Abs(halfExtent.X * edge3.Y);
+            if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
+                return ContainmentType.Disjoint;
+
+            return ContainmentType.Intersects;
+        }
+
+    }
+}

+ 36 - 0
CollisionSample/Platforms/Android/Activity1.cs

@@ -0,0 +1,36 @@
+#if ANDROID
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using Android.Views;
+using Microsoft.Xna.Framework;
+
+namespace CollisionSample
+{
+    [Activity(
+        Label = "@string/app_name",
+        MainLauncher = true,
+        Icon = "@drawable/icon",
+        AlwaysRetainTaskState = true,
+        LaunchMode = LaunchMode.SingleInstance,
+        ScreenOrientation = ScreenOrientation.FullSensor,
+        ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize
+    )]
+    public class Activity1 : AndroidGameActivity
+    {
+        private CollisionGame _game;
+        private View _view;
+
+        protected override void OnCreate(Bundle bundle)
+        {
+            base.OnCreate(bundle);
+
+            _game = new CollisionGame();
+            _view = _game.Services.GetService(typeof(View)) as View;
+
+            SetContentView(_view);
+            _game.Run();
+        }
+    }
+}
+#endif

+ 22 - 0
CollisionSample/Platforms/Android/CollisionSample.Android.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0-android</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>CollisionSample</RootNamespace>
+    <AssemblyName>Collision</AssemblyName>
+    <SupportedOSPlatformVersion>28.0</SupportedOSPlatformVersion>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Android" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\CollisionSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\..\CompiledContent\Android\Content\Fonts\Arial.xnb" Link="Content\Arial.xnb">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 8 - 0
CollisionSample/Platforms/Android/Properties/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
+          android:versionCode="1" 
+          android:versionName="1.0" 
+          android:installLocation="auto">
+	<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="34" />
+	<application android:label="@string/app_name" android:icon="@drawable/icon"></application>
+</manifest>

+ 4 - 0
CollisionSample/Platforms/Android/Resources/Values/Strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">Collision Sample</string>
+</resources>

+ 6 - 0
CollisionSample/Platforms/Android/Resources/Values/Styles.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <style name="Theme.Splash" parent="android:Theme">
+    <item name="android:windowNoTitle">true</item>
+  </style>
+</resources>

BIN
CollisionSample/Platforms/Android/Resources/drawable/icon.png


+ 27 - 0
CollisionSample/Platforms/Desktop/CollisionSample.DesktopGL.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <AssemblyName>Collision</AssemblyName>
+    <RootNamespace>CollisionSample</RootNamespace>
+    <ApplicationManifest>..\..\Core\Content\app.manifest</ApplicationManifest>
+    <ApplicationIcon>..\..\Core\Content\Game.ico</ApplicationIcon>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\CollisionSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\..\CompiledContent\Android\Content\Fonts\Arial.xnb" Link="Content\Arial.xnb">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 14 - 0
CollisionSample/Platforms/Desktop/Program.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace CollisionSample
+{
+    public static class Program
+    {
+        [STAThread]
+        static void Main()
+        {
+            using (var game = new CollisionGame())
+                game.Run();
+        }
+    }
+}

+ 28 - 0
CollisionSample/Platforms/Windows/CollisionSample.Windows.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net8.0-windows</TargetFramework>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <UseWindowsForms>true</UseWindowsForms>
+    <AssemblyName>Collision</AssemblyName>
+    <RootNamespace>CollisionSample</RootNamespace>
+    <ApplicationManifest>..\..\Core\Content\app.manifest</ApplicationManifest>
+    <ApplicationIcon>..\..\Core\Content\Game.ico</ApplicationIcon>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\CollisionSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\..\CompiledContent\Android\Content\Fonts\Arial.xnb" Link="Content\Arial.xnb">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 15 - 0
CollisionSample/Platforms/Windows/Program.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Windows.Forms;
+
+namespace CollisionSample
+{
+    public static class Program
+    {
+        [STAThread]
+        static void Main()
+        {
+            using (var game = new CollisionGame())
+                game.Run();
+        }
+    }
+}

+ 19 - 0
CollisionSample/Platforms/iOS/AppDelegate.cs

@@ -0,0 +1,19 @@
+using Foundation;
+using UIKit;
+using Microsoft.Xna.Framework;
+
+namespace CollisionSample
+{
+    [Register("AppDelegate")]
+    public class AppDelegate : UIApplicationDelegate
+    {
+        private Game _game;
+
+        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
+        {
+            _game = new CollisionGame();
+            _game.Run();
+            return true;
+        }
+    }
+}

+ 27 - 0
CollisionSample/Platforms/iOS/CollisionSample.iOS.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0-ios</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <AssemblyName>Collision</AssemblyName>
+    <RootNamespace>CollisionSample</RootNamespace>
+    <RuntimeIdentifier>ios-arm64</RuntimeIdentifier>
+    <UseInterpreter>false</UseInterpreter>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.iOS" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\CollisionSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\..\CompiledContent\Android\Content\Fonts\Arial.xnb" Link="Content\Arial.xnb">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 0 - 0
CollisionSample/Info.plist → CollisionSample/Platforms/iOS/Info.plist


+ 13 - 0
CollisionSample/Platforms/iOS/Program.cs

@@ -0,0 +1,13 @@
+using UIKit;
+
+namespace CollisionSample
+{
+    public class Application
+    {
+        // This is the main entry point of the application.
+        static void Main(string[] args)
+        {
+            UIApplication.Main(args, null, typeof(AppDelegate));
+        }
+    }
+}

+ 0 - 64
CollisionSample/Program.cs

@@ -1,64 +0,0 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// Program.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-using System;
-
-namespace CollisionSample
-{
-#if WINDOWS || XBOX
-    static class Program
-    {
-        /// <summary>
-        /// The main entry point for the application.
-        /// </summary>
-        static void Main(string[] args)
-        {
-            using (CollisionSample game = new CollisionSample())
-            {
-                game.Run();
-            }
-        }
-    }
-#endif
-#if MONOMAC
-
-	static class Program
-	{	
-		/// <summary>
-		/// The main entry point for the application.
-		/// </summary>
-		static void Main (string[] args)
-		{
-			MonoMac.AppKit.NSApplication.Init ();
-			
-			using (var p = new MonoMac.Foundation.NSAutoreleasePool ()) {
-				MonoMac.AppKit.NSApplication.SharedApplication.Delegate = new AppDelegate();
-				MonoMac.AppKit.NSApplication.Main(args);
-			}
-		}
-	}
-	
-	class AppDelegate : MonoMac.AppKit.NSApplicationDelegate
-	{
-		CollisionSample game;
-		public override void FinishedLaunching (MonoMac.Foundation.NSObject notification)
-		{
-			game = new CollisionSample();
-			game.Run ();
-
-		}
-		
-		public override bool ApplicationShouldTerminateAfterLastWindowClosed (MonoMac.AppKit.NSApplication sender)
-		{
-			return true;
-		}
-	}
-#endif
-}
-

+ 51 - 0
CollisionSample/README.md

@@ -0,0 +1,51 @@
+# CollisionSample (MonoGame 3.8.4, .NET 8)
+
+This project demonstrates various forms of collision detection for primitives in MonoGame, including oriented bounding boxes and triangles. It is structured for modern cross-platform .NET 8 development, with a clean separation between shared game logic and platform-specific entry points.
+
+## Directory Structure
+
+- **Core/**: Shared game logic and content (.xnb files)
+- **Platforms/Windows/**: Windows-specific project (net8.0-windows)
+- **Platforms/Desktop/**: DesktopGL (cross-platform) project (net8.0)
+- **Platforms/Android/**: Android project (net8.0-android)
+- **Platforms/iOS/**: iOS project (net8.0-ios)
+
+## Prerequisites
+- .NET 8 SDK
+- MonoGame 3.8.4 NuGet packages (restored automatically)
+- For Android/iOS: Appropriate .NET workloads and platform SDKs (e.g., Android Studio, Xcode)
+
+## Building and Running
+
+### Windows
+```
+dotnet build Platforms/Windows/CollisionSample.Windows.csproj
+```
+Run the resulting executable from `bin/Debug/net8.0-windows/`.
+
+### DesktopGL (Cross-platform)
+```
+dotnet build Platforms/Desktop/CollisionSample.DesktopGL.csproj
+```
+Run the resulting executable from `bin/Debug/net8.0/`.
+
+### Android
+```
+dotnet build Platforms/Android/CollisionSample.Android.csproj
+```
+Deploy the resulting APK to an Android device or emulator.
+
+### iOS
+```
+dotnet build Platforms/iOS/CollisionSample.iOS.csproj
+```
+Deploy using Xcode or the .NET iOS workload on a Mac.
+
+## Notes
+- No .mgcb file is used; the project loads pre-built .xnb content directly.
+- Platform-specific entry points are in their respective folders.
+- All shared code is in the `Core` project.
+- If you encounter missing SDK/platform errors, ensure you have installed the required .NET workloads for Android/iOS.
+
+## License
+This sample is based on Microsoft XNA Community Game Platform code and is provided for educational purposes.