Browse Source

Added BlurIntensity to VR. Also modifications to tests and samples.

Kenneth Pouncey 13 years ago
parent
commit
de1b9dc2c9
26 changed files with 2167 additions and 7 deletions
  1. 6 0
      Samples/MacOS/CollisionSample/App.config
  2. BIN
      Samples/MacOS/CollisionSample/Background.png
  3. 692 0
      Samples/MacOS/CollisionSample/BoundingOrientedBox.cs
  4. 565 0
      Samples/MacOS/CollisionSample/CollisionSample.cs
  5. 72 0
      Samples/MacOS/CollisionSample/CollisionSample.csproj
  6. BIN
      Samples/MacOS/CollisionSample/Content/Font.xnb
  7. 95 0
      Samples/MacOS/CollisionSample/FrameRateCounter.cs
  8. BIN
      Samples/MacOS/CollisionSample/Game.ico
  9. BIN
      Samples/MacOS/CollisionSample/GameThumbnail.png
  10. 40 0
      Samples/MacOS/CollisionSample/GeomUtil.cs
  11. 16 0
      Samples/MacOS/CollisionSample/Info.plist
  12. 64 0
      Samples/MacOS/CollisionSample/Program.cs
  13. 578 0
      Samples/MacOS/CollisionSample/TriangleTest.cs
  14. 1 1
      Samples/MacOS/Colored3DCube/Game1.cs
  15. BIN
      Samples/MacOS/UseCustomVertex/Content/Glass.png
  16. BIN
      Samples/MacOS/UseCustomVertex/Content/Glass.xnb
  17. BIN
      Samples/MacOS/UseCustomVertex/Content/Glass2.png
  18. BIN
      Samples/MacOS/UseCustomVertex/Content/XNA_pow2.jpg
  19. BIN
      Samples/MacOS/UseCustomVertex/Game1.cs
  20. 5 2
      Samples/MacOS/UseCustomVertex/UseCustomVertex.csproj
  21. 17 0
      StarterKits/MacOS/VectorRumble/Screens/OptionsMenuScreen.cs
  22. 5 1
      StarterKits/MacOS/VectorRumble/Simulation/Actor.cs
  23. 0 1
      StarterKits/MacOS/VectorRumble/Simulation/World.cs
  24. 1 0
      StarterKits/MacOS/VectorRumble/Simulation/WorldRules.cs
  25. 1 1
      Tests/MacOS/MouseGetStateAndIsMouseVisibleTester/MouseGetStateAndIsMouseVisibleTester/MouseGetStateAndIsMouseVisibleTester.csproj
  26. 9 1
      Tests/MonoGame.Tests.MacOS.sln

+ 6 - 0
Samples/MacOS/CollisionSample/App.config

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

BIN
Samples/MacOS/CollisionSample/Background.png


+ 692 - 0
Samples/MacOS/CollisionSample/BoundingOrientedBox.cs

@@ -0,0 +1,692 @@
+//-----------------------------------------------------------------------------
+// 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
+    }
+}

+ 565 - 0
Samples/MacOS/CollisionSample/CollisionSample.cs

@@ -0,0 +1,565 @@
+//-----------------------------------------------------------------------------
+// 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
+    }
+
+}

+ 72 - 0
Samples/MacOS/CollisionSample/CollisionSample.csproj

@@ -0,0 +1,72 @@
+<?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; MAC</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\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>

BIN
Samples/MacOS/CollisionSample/Content/Font.xnb


+ 95 - 0
Samples/MacOS/CollisionSample/FrameRateCounter.cs

@@ -0,0 +1,95 @@
+#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
+    }
+
+}

BIN
Samples/MacOS/CollisionSample/Game.ico


BIN
Samples/MacOS/CollisionSample/GameThumbnail.png


+ 40 - 0
Samples/MacOS/CollisionSample/GeomUtil.cs

@@ -0,0 +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;
+        }
+    }
+}

+ 16 - 0
Samples/MacOS/CollisionSample/Info.plist

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleIdentifier</key>
+	<string>com.yourcompany.Collision</string>
+	<key>CFBundleName</key>
+	<string>Collision</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.6</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 64 - 0
Samples/MacOS/CollisionSample/Program.cs

@@ -0,0 +1,64 @@
+#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 MAC
+
+	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
+	{
+		
+		public override void FinishedLaunching (MonoMac.Foundation.NSObject notification)
+		{
+			using (CollisionSample game = new CollisionSample()) {
+				game.Run ();
+			}
+		}
+		
+		public override bool ApplicationShouldTerminateAfterLastWindowClosed (MonoMac.AppKit.NSApplication sender)
+		{
+			return true;
+		}
+	}
+#endif
+}
+

+ 578 - 0
Samples/MacOS/CollisionSample/TriangleTest.cs

@@ -0,0 +1,578 @@
+//---------------------------------------------------------------------------------------------------------------------
+// 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
+    }
+}

+ 1 - 1
Samples/MacOS/Colored3DCube/Game1.cs

@@ -41,7 +41,7 @@ namespace Colored3DCube
 			basicEffect.World = worldMatrix;
 			basicEffect.View = viewMatrix;
 			basicEffect.Projection = projectionMatrix;
-			//basicEffect.VertexColorEnabled = true;
+			basicEffect.VertexColorEnabled = true;
 
 			CreateCubeVertexBuffer ();
 			CreateCubeIndexBuffer ();

BIN
Samples/MacOS/UseCustomVertex/Content/Glass.png


BIN
Samples/MacOS/UseCustomVertex/Content/Glass.xnb


BIN
Samples/MacOS/UseCustomVertex/Content/Glass2.png


BIN
Samples/MacOS/UseCustomVertex/Content/XNA_pow2.jpg


BIN
Samples/MacOS/UseCustomVertex/Game1.cs


+ 5 - 2
Samples/MacOS/UseCustomVertex/UseCustomVertex.csproj

@@ -44,7 +44,9 @@
     <None Include="App.config" />
     <None Include="Game.ico" />
     <None Include="GameThumbnail.png" />
-    <None Include="Content\XNA_pow2.jpg" />
+    <None Include="Content\Glass.xnb" />
+    <None Include="Content\XNA_pow2.xnb" />
+    <None Include="Content\Glass.png" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
@@ -64,6 +66,7 @@
     <Folder Include="Content\" />
   </ItemGroup>
   <ItemGroup>
-    <Content Include="Content\XNA_pow2.xnb" />
+    <Content Include="Content\XNA_pow2.jpg" />
+    <Content Include="Content\Glass2.png" />
   </ItemGroup>
 </Project>

+ 17 - 0
StarterKits/MacOS/VectorRumble/Screens/OptionsMenuScreen.cs

@@ -35,12 +35,14 @@ namespace VectorRumble
 
         static int scoreLimit = 10;
         static bool motionBlur = true;
+	static int blurIntensity = 5;
         static bool neonEffect = true;
 
         MenuEntry scoreLimitMenuEntry = new MenuEntry(String.Empty);
         MenuEntry asteroidDensityMenuEntry = new MenuEntry(String.Empty);
         MenuEntry wallStyleMenuEntry = new MenuEntry(String.Empty);
         MenuEntry motionBlurMenuEntry = new MenuEntry(String.Empty);
+        MenuEntry blurIntensityMenuEntry = new MenuEntry(String.Empty);
         MenuEntry neonEffectMenuEntry = new MenuEntry(String.Empty);
 
 
@@ -59,18 +61,21 @@ namespace VectorRumble
             currentWallStyle = (int)WorldRules.WallStyle;
             scoreLimit = WorldRules.ScoreLimit;
             motionBlur = WorldRules.MotionBlur;
+		blurIntensity = WorldRules.BlurIntensity;
             neonEffect = WorldRules.NeonEffect;
 
             scoreLimitMenuEntry.Selected += ScoreLimitMenuEntrySelected;
             asteroidDensityMenuEntry.Selected += AsteroidDensityMenuEntrySelected;
             wallStyleMenuEntry.Selected += WallStyleMenuEntrySelected;
             motionBlurMenuEntry.Selected += MotionBlurMenuEntrySelected;
+            blurIntensityMenuEntry.Selected += BlurIntensityMenuEntrySelected;
             neonEffectMenuEntry.Selected += NeonEffectMenuEntrySelected;
 
             MenuEntries.Add(scoreLimitMenuEntry);
             MenuEntries.Add(asteroidDensityMenuEntry);
             MenuEntries.Add(wallStyleMenuEntry);
             MenuEntries.Add(motionBlurMenuEntry);
+            MenuEntries.Add(blurIntensityMenuEntry);
             MenuEntries.Add(neonEffectMenuEntry);
         }
 
@@ -94,6 +99,8 @@ namespace VectorRumble
             wallStyleMenuEntry.Text = "Wall Style : " + wallStyle[currentWallStyle];
             motionBlurMenuEntry.Text = "Motion Blur : " + motionBlur.ToString();
             neonEffectMenuEntry.Text = "Neon Effect : " + neonEffect.ToString();
+            blurIntensityMenuEntry.Text = "Blur Intesity : " + blurIntensity.ToString();
+
         }
 
 
@@ -107,6 +114,15 @@ namespace VectorRumble
                 scoreLimit = 5;
         }
 
+        /// <summary>
+        /// Event handler for when the Score Limit menu entry is selected.
+        /// </summary>
+        void BlurIntensityMenuEntrySelected(object sender, EventArgs e)
+        {
+            blurIntensity += 1;
+            if (blurIntensity > 10)
+                blurIntensity = 1;
+        }
 
         /// <summary>
         /// Event handler for when the Asteroid Density menu entry is selected.
@@ -158,6 +174,7 @@ namespace VectorRumble
             WorldRules.ScoreLimit = scoreLimit;
             WorldRules.MotionBlur = motionBlur;
             WorldRules.NeonEffect = neonEffect;
+            WorldRules.BlurIntensity = blurIntensity;
 
             ExitScreen();
         }

+ 5 - 1
StarterKits/MacOS/VectorRumble/Simulation/Actor.cs

@@ -165,15 +165,19 @@ namespace VectorRumble
                     {
                         // calculate the "blur" polygon's position
                         Vector2 blurPosition = this.position - backwards * (i * 4);
+                        //Vector2 blurPosition = this.position - backwards * (i * 20);
+
                         // calculate the transformation for the "blur" polygon
                         Matrix blurWorld = rotationMatrix *
                             Matrix.CreateTranslation(blurPosition.X, blurPosition.Y, 0);
                         // transform the polygon to the "blur" location
                         polygon.Transform(blurWorld);
                         // calculate the alpha of the "blur" location
-                        byte alpha = (byte)(160 / (i + 1));
+                        //byte alpha = (byte)(160 / (i + 1));
+                        byte alpha = (byte)( WorldRules.BlurIntensity * 100/ (i + 1));
                         if (alpha < 1)
                             break;
+
                         // draw the "blur" polygon
                         lineBatch.DrawPolygon(polygon,
                             new Color(color.R, color.G, color.B, alpha));

+ 0 - 1
StarterKits/MacOS/VectorRumble/Simulation/World.cs

@@ -728,7 +728,6 @@ namespace VectorRumble
                     break;
                 }
             }
-
             return spawnPoint;
         }
         #endregion

+ 1 - 0
StarterKits/MacOS/VectorRumble/Simulation/WorldRules.cs

@@ -38,6 +38,7 @@ namespace VectorRumble
         public static AsteroidDensity AsteroidDensity = AsteroidDensity.Low;
         public static WallStyle WallStyle = WallStyle.Three;
         public static bool MotionBlur = true;
+	public static int BlurIntensity = 5;
         public static bool NeonEffect = true;
     }
 }

+ 1 - 1
Tests/MacOS/MouseGetStateAndIsMouseVisibleTester/MouseGetStateAndIsMouseVisibleTester/MouseGetStateAndIsMouseVisibleTester.csproj

@@ -16,7 +16,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;</DefineConstants>
+    <DefineConstants>DEBUG; MAC</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>x86</PlatformTarget>

+ 9 - 1
Tests/MonoGame.Tests.MacOS.sln

@@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UseCustomVertex", "..\Sampl
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Colored3DCube", "..\Samples\MacOS\Colored3DCube\Colored3DCube.csproj", "{CF7A194A-CF38-4379-909F-1B5FFB8F79C4}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CollisionSample", "..\Samples\MacOS\CollisionSample\CollisionSample.csproj", "{AFFFC991-4956-45EF-8746-C1AE3FC7A245}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x86 = Debug|x86
@@ -72,6 +74,12 @@ Global
 		{AE483C29-042E-4226-BA52-D247CE7676DA}.Distribution|Any CPU.Build.0 = Debug|Any CPU
 		{AE483C29-042E-4226-BA52-D247CE7676DA}.Release|x86.ActiveCfg = Release|Any CPU
 		{AE483C29-042E-4226-BA52-D247CE7676DA}.Release|x86.Build.0 = Release|Any CPU
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A245}.Debug|x86.ActiveCfg = Debug|x86
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A245}.Debug|x86.Build.0 = Debug|x86
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A245}.Distribution|Any CPU.ActiveCfg = Debug|x86
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A245}.Distribution|Any CPU.Build.0 = Debug|x86
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A245}.Release|x86.ActiveCfg = Release|x86
+		{AFFFC991-4956-45EF-8746-C1AE3FC7A245}.Release|x86.Build.0 = Release|x86
 		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Debug|x86.ActiveCfg = Debug|Any CPU
 		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Debug|x86.Build.0 = Debug|Any CPU
 		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Distribution|Any CPU.ActiveCfg = Debug|Any CPU
@@ -98,7 +106,7 @@ Global
 		{ECD1D53E-F50A-4299-9B0F-2F64D6063513}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(MonoDevelopProperties) = preSolution
-		StartupItem = ..\Samples\MacOS\Colored3DCube\Colored3DCube.csproj
+		StartupItem = ..\StarterKits\MacOS\VectorRumble\VectorRumble.csproj
 		Policies = $0
 		$0.TextStylePolicy = $1
 		$1.FileWidth = 120