1945 lines
70 KiB
C#
1945 lines
70 KiB
C#
|
/*
|
|||
|
* Farseer Physics Engine based on Box2D.XNA port:
|
|||
|
* Copyright (c) 2010 Ian Qvist
|
|||
|
*
|
|||
|
* Box2D.XNA port of Box2D:
|
|||
|
* Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
|
|||
|
*
|
|||
|
* Original source Box2D:
|
|||
|
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
|
|||
|
*
|
|||
|
* This software is provided 'as-is', without any express or implied
|
|||
|
* warranty. In no event will the authors be held liable for any damages
|
|||
|
* arising from the use of this software.
|
|||
|
* Permission is granted to anyone to use this software for any purpose,
|
|||
|
* including commercial applications, and to alter it and redistribute it
|
|||
|
* freely, subject to the following restrictions:
|
|||
|
* 1. The origin of this software must not be misrepresented; you must not
|
|||
|
* claim that you wrote the original software. If you use this software
|
|||
|
* in a product, an acknowledgment in the product documentation would be
|
|||
|
* appreciated but is not required.
|
|||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|||
|
* misrepresented as being the original software.
|
|||
|
* 3. This notice may not be removed or altered from any source distribution.
|
|||
|
*/
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
using FarseerPhysics.Collision.Shapes;
|
|||
|
using FarseerPhysics.Common;
|
|||
|
using Microsoft.Xna.Framework;
|
|||
|
|
|||
|
namespace FarseerPhysics.Collision
|
|||
|
{
|
|||
|
internal enum ContactFeatureType : byte
|
|||
|
{
|
|||
|
Vertex = 0,
|
|||
|
Face = 1,
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The features that intersect to form the contact point
|
|||
|
/// This must be 4 bytes or less.
|
|||
|
/// </summary>
|
|||
|
public struct ContactFeature
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Feature index on ShapeA
|
|||
|
/// </summary>
|
|||
|
public byte IndexA;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Feature index on ShapeB
|
|||
|
/// </summary>
|
|||
|
public byte IndexB;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The feature type on ShapeA
|
|||
|
/// </summary>
|
|||
|
public byte TypeA;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The feature type on ShapeB
|
|||
|
/// </summary>
|
|||
|
public byte TypeB;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Contact ids to facilitate warm starting.
|
|||
|
/// </summary>
|
|||
|
[StructLayout(LayoutKind.Explicit)]
|
|||
|
public struct ContactID
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// The features that intersect to form the contact point
|
|||
|
/// </summary>
|
|||
|
[FieldOffset(0)]
|
|||
|
public ContactFeature Features;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Used to quickly compare contact ids.
|
|||
|
/// </summary>
|
|||
|
[FieldOffset(0)]
|
|||
|
public uint Key;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// A manifold point is a contact point belonging to a contact
|
|||
|
/// manifold. It holds details related to the geometry and dynamics
|
|||
|
/// of the contact points.
|
|||
|
/// The local point usage depends on the manifold type:
|
|||
|
/// -ShapeType.Circles: the local center of circleB
|
|||
|
/// -SeparationFunction.FaceA: the local center of cirlceB or the clip point of polygonB
|
|||
|
/// -SeparationFunction.FaceB: the clip point of polygonA
|
|||
|
/// This structure is stored across time steps, so we keep it small.
|
|||
|
/// Note: the impulses are used for internal caching and may not
|
|||
|
/// provide reliable contact forces, especially for high speed collisions.
|
|||
|
/// </summary>
|
|||
|
public struct ManifoldPoint
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Uniquely identifies a contact point between two Shapes
|
|||
|
/// </summary>
|
|||
|
public ContactID Id;
|
|||
|
|
|||
|
public Vector2 LocalPoint;
|
|||
|
|
|||
|
public float NormalImpulse;
|
|||
|
|
|||
|
public float TangentImpulse;
|
|||
|
}
|
|||
|
|
|||
|
public enum ManifoldType
|
|||
|
{
|
|||
|
Circles,
|
|||
|
FaceA,
|
|||
|
FaceB
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// A manifold for two touching convex Shapes.
|
|||
|
/// Box2D supports multiple types of contact:
|
|||
|
/// - clip point versus plane with radius
|
|||
|
/// - point versus point with radius (circles)
|
|||
|
/// The local point usage depends on the manifold type:
|
|||
|
/// -ShapeType.Circles: the local center of circleA
|
|||
|
/// -SeparationFunction.FaceA: the center of faceA
|
|||
|
/// -SeparationFunction.FaceB: the center of faceB
|
|||
|
/// Similarly the local normal usage:
|
|||
|
/// -ShapeType.Circles: not used
|
|||
|
/// -SeparationFunction.FaceA: the normal on polygonA
|
|||
|
/// -SeparationFunction.FaceB: the normal on polygonB
|
|||
|
/// We store contacts in this way so that position correction can
|
|||
|
/// account for movement, which is critical for continuous physics.
|
|||
|
/// All contact scenarios must be expressed in one of these types.
|
|||
|
/// This structure is stored across time steps, so we keep it small.
|
|||
|
/// </summary>
|
|||
|
public struct Manifold
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Not use for Type.SeparationFunction.Points
|
|||
|
/// </summary>
|
|||
|
public Vector2 LocalNormal;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Usage depends on manifold type
|
|||
|
/// </summary>
|
|||
|
public Vector2 LocalPoint;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The number of manifold points
|
|||
|
/// </summary>
|
|||
|
public int PointCount;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The points of contact
|
|||
|
/// </summary>
|
|||
|
public FixedArray2<ManifoldPoint> Points;
|
|||
|
|
|||
|
public ManifoldType Type;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This is used for determining the state of contact points.
|
|||
|
/// </summary>
|
|||
|
public enum PointState
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Point does not exist
|
|||
|
/// </summary>
|
|||
|
Null,
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Point was added in the update
|
|||
|
/// </summary>
|
|||
|
Add,
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Point persisted across the update
|
|||
|
/// </summary>
|
|||
|
Persist,
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Point was removed in the update
|
|||
|
/// </summary>
|
|||
|
Remove,
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Used for computing contact manifolds.
|
|||
|
/// </summary>
|
|||
|
public struct ClipVertex
|
|||
|
{
|
|||
|
public ContactID ID;
|
|||
|
public Vector2 V;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
|
|||
|
/// </summary>
|
|||
|
public struct RayCastInput
|
|||
|
{
|
|||
|
public float MaxFraction;
|
|||
|
public Vector2 Point1, Point2;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2
|
|||
|
/// come from RayCastInput.
|
|||
|
/// </summary>
|
|||
|
public struct RayCastOutput
|
|||
|
{
|
|||
|
public float Fraction;
|
|||
|
public Vector2 Normal;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// An axis aligned bounding box.
|
|||
|
/// </summary>
|
|||
|
public struct AABB
|
|||
|
{
|
|||
|
private static DistanceInput _input = new DistanceInput();
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The lower vertex
|
|||
|
/// </summary>
|
|||
|
public Vector2 LowerBound;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The upper vertex
|
|||
|
/// </summary>
|
|||
|
public Vector2 UpperBound;
|
|||
|
|
|||
|
public AABB(Vector2 min, Vector2 max)
|
|||
|
: this(ref min, ref max)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public AABB(ref Vector2 min, ref Vector2 max)
|
|||
|
{
|
|||
|
LowerBound = min;
|
|||
|
UpperBound = max;
|
|||
|
}
|
|||
|
|
|||
|
public AABB(Vector2 center, float width, float height)
|
|||
|
{
|
|||
|
LowerBound = center - new Vector2(width / 2, height / 2);
|
|||
|
UpperBound = center + new Vector2(width / 2, height / 2);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get the center of the AABB.
|
|||
|
/// </summary>
|
|||
|
/// <value></value>
|
|||
|
public Vector2 Center
|
|||
|
{
|
|||
|
get { return 0.5f * (LowerBound + UpperBound); }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get the extents of the AABB (half-widths).
|
|||
|
/// </summary>
|
|||
|
/// <value></value>
|
|||
|
public Vector2 Extents
|
|||
|
{
|
|||
|
get { return 0.5f * (UpperBound - LowerBound); }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get the perimeter length
|
|||
|
/// </summary>
|
|||
|
/// <value></value>
|
|||
|
public float Perimeter
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
float wx = UpperBound.X - LowerBound.X;
|
|||
|
float wy = UpperBound.Y - LowerBound.Y;
|
|||
|
return 2.0f * (wx + wy);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the vertices of the AABB.
|
|||
|
/// </summary>
|
|||
|
/// <value>The corners of the AABB</value>
|
|||
|
public Vertices Vertices
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
Vertices vertices = new Vertices();
|
|||
|
vertices.Add(LowerBound);
|
|||
|
vertices.Add(new Vector2(LowerBound.X, UpperBound.Y));
|
|||
|
vertices.Add(UpperBound);
|
|||
|
vertices.Add(new Vector2(UpperBound.X, LowerBound.Y));
|
|||
|
return vertices;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// first quadrant
|
|||
|
/// </summary>
|
|||
|
public AABB Q1
|
|||
|
{
|
|||
|
get { return new AABB(Center, UpperBound); }
|
|||
|
}
|
|||
|
|
|||
|
public AABB Q2
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return new AABB(new Vector2(LowerBound.X, Center.Y), new Vector2(Center.X, UpperBound.Y));
|
|||
|
;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public AABB Q3
|
|||
|
{
|
|||
|
get { return new AABB(LowerBound, Center); }
|
|||
|
}
|
|||
|
|
|||
|
public AABB Q4
|
|||
|
{
|
|||
|
get { return new AABB(new Vector2(Center.X, LowerBound.Y), new Vector2(UpperBound.X, Center.Y)); }
|
|||
|
}
|
|||
|
|
|||
|
public Vector2[] GetVertices()
|
|||
|
{
|
|||
|
Vector2 p1 = UpperBound;
|
|||
|
Vector2 p2 = new Vector2(UpperBound.X, LowerBound.Y);
|
|||
|
Vector2 p3 = LowerBound;
|
|||
|
Vector2 p4 = new Vector2(LowerBound.X, UpperBound.Y);
|
|||
|
return new[] { p1, p2, p3, p4 };
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Verify that the bounds are sorted.
|
|||
|
/// </summary>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if this instance is valid; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
public bool IsValid()
|
|||
|
{
|
|||
|
Vector2 d = UpperBound - LowerBound;
|
|||
|
bool valid = d.X >= 0.0f && d.Y >= 0.0f;
|
|||
|
valid = valid && LowerBound.IsValid() && UpperBound.IsValid();
|
|||
|
return valid;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Combine an AABB into this one.
|
|||
|
/// </summary>
|
|||
|
/// <param name="aabb">The aabb.</param>
|
|||
|
public void Combine(ref AABB aabb)
|
|||
|
{
|
|||
|
LowerBound = Vector2.Min(LowerBound, aabb.LowerBound);
|
|||
|
UpperBound = Vector2.Max(UpperBound, aabb.UpperBound);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Combine two AABBs into this one.
|
|||
|
/// </summary>
|
|||
|
/// <param name="aabb1">The aabb1.</param>
|
|||
|
/// <param name="aabb2">The aabb2.</param>
|
|||
|
public void Combine(ref AABB aabb1, ref AABB aabb2)
|
|||
|
{
|
|||
|
LowerBound = Vector2.Min(aabb1.LowerBound, aabb2.LowerBound);
|
|||
|
UpperBound = Vector2.Max(aabb1.UpperBound, aabb2.UpperBound);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Does this aabb contain the provided AABB.
|
|||
|
/// </summary>
|
|||
|
/// <param name="aabb">The aabb.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if it contains the specified aabb; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
public bool Contains(ref AABB aabb)
|
|||
|
{
|
|||
|
bool result = true;
|
|||
|
result = result && LowerBound.X <= aabb.LowerBound.X;
|
|||
|
result = result && LowerBound.Y <= aabb.LowerBound.Y;
|
|||
|
result = result && aabb.UpperBound.X <= UpperBound.X;
|
|||
|
result = result && aabb.UpperBound.Y <= UpperBound.Y;
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determines whether the AAABB contains the specified point.
|
|||
|
/// </summary>
|
|||
|
/// <param name="point">The point.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if it contains the specified point; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
public bool Contains(ref Vector2 point)
|
|||
|
{
|
|||
|
//using epsilon to try and gaurd against float rounding errors.
|
|||
|
if ((point.X > (LowerBound.X + Settings.Epsilon) && point.X < (UpperBound.X - Settings.Epsilon) &&
|
|||
|
(point.Y > (LowerBound.Y + Settings.Epsilon) && point.Y < (UpperBound.Y - Settings.Epsilon))))
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public static bool TestOverlap(AABB a, AABB b)
|
|||
|
{
|
|||
|
return TestOverlap(ref a, ref b);
|
|||
|
}
|
|||
|
|
|||
|
public static bool TestOverlap(ref AABB a, ref AABB b)
|
|||
|
{
|
|||
|
Vector2 d1 = b.LowerBound - a.UpperBound;
|
|||
|
Vector2 d2 = a.LowerBound - b.UpperBound;
|
|||
|
|
|||
|
if (d1.X > 0.0f || d1.Y > 0.0f)
|
|||
|
return false;
|
|||
|
|
|||
|
if (d2.X > 0.0f || d2.Y > 0.0f)
|
|||
|
return false;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public static bool TestOverlap(Shape shapeA, int indexA,
|
|||
|
Shape shapeB, int indexB,
|
|||
|
ref Transform xfA, ref Transform xfB)
|
|||
|
{
|
|||
|
_input.ProxyA.Set(shapeA, indexA);
|
|||
|
_input.ProxyB.Set(shapeB, indexB);
|
|||
|
_input.TransformA = xfA;
|
|||
|
_input.TransformB = xfB;
|
|||
|
_input.UseRadii = true;
|
|||
|
|
|||
|
SimplexCache cache;
|
|||
|
DistanceOutput output;
|
|||
|
Distance.ComputeDistance(out output, out cache, _input);
|
|||
|
|
|||
|
return output.Distance < 10.0f * Settings.Epsilon;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// From Real-time Collision Detection, p179.
|
|||
|
public bool RayCast(out RayCastOutput output, ref RayCastInput input)
|
|||
|
{
|
|||
|
output = new RayCastOutput();
|
|||
|
|
|||
|
float tmin = -Settings.MaxFloat;
|
|||
|
float tmax = Settings.MaxFloat;
|
|||
|
|
|||
|
Vector2 p = input.Point1;
|
|||
|
Vector2 d = input.Point2 - input.Point1;
|
|||
|
Vector2 absD = MathUtils.Abs(d);
|
|||
|
|
|||
|
Vector2 normal = Vector2.Zero;
|
|||
|
|
|||
|
for (int i = 0; i < 2; ++i)
|
|||
|
{
|
|||
|
float absD_i = i == 0 ? absD.X : absD.Y;
|
|||
|
float lowerBound_i = i == 0 ? LowerBound.X : LowerBound.Y;
|
|||
|
float upperBound_i = i == 0 ? UpperBound.X : UpperBound.Y;
|
|||
|
float p_i = i == 0 ? p.X : p.Y;
|
|||
|
|
|||
|
if (absD_i < Settings.Epsilon)
|
|||
|
{
|
|||
|
// Parallel.
|
|||
|
if (p_i < lowerBound_i || upperBound_i < p_i)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
float d_i = i == 0 ? d.X : d.Y;
|
|||
|
|
|||
|
float inv_d = 1.0f / d_i;
|
|||
|
float t1 = (lowerBound_i - p_i) * inv_d;
|
|||
|
float t2 = (upperBound_i - p_i) * inv_d;
|
|||
|
|
|||
|
// Sign of the normal vector.
|
|||
|
float s = -1.0f;
|
|||
|
|
|||
|
if (t1 > t2)
|
|||
|
{
|
|||
|
MathUtils.Swap(ref t1, ref t2);
|
|||
|
s = 1.0f;
|
|||
|
}
|
|||
|
|
|||
|
// Push the min up
|
|||
|
if (t1 > tmin)
|
|||
|
{
|
|||
|
if (i == 0)
|
|||
|
{
|
|||
|
normal.X = s;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
normal.Y = s;
|
|||
|
}
|
|||
|
|
|||
|
tmin = t1;
|
|||
|
}
|
|||
|
|
|||
|
// Pull the max down
|
|||
|
tmax = Math.Min(tmax, t2);
|
|||
|
|
|||
|
if (tmin > tmax)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Does the ray start inside the box?
|
|||
|
// Does the ray intersect beyond the max fraction?
|
|||
|
if (tmin < 0.0f || input.MaxFraction < tmin)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// Intersection.
|
|||
|
output.Fraction = tmin;
|
|||
|
output.Normal = normal;
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Edge shape plus more stuff.
|
|||
|
/// </summary>
|
|||
|
public struct FatEdge
|
|||
|
{
|
|||
|
public bool HasVertex0, HasVertex3;
|
|||
|
public Vector2 Normal;
|
|||
|
public Vector2 V0, V1, V2, V3;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This lets us treate and edge shape and a polygon in the same
|
|||
|
/// way in the SAT collider.
|
|||
|
/// </summary>
|
|||
|
public class EPProxy
|
|||
|
{
|
|||
|
public Vector2 Centroid;
|
|||
|
public int Count;
|
|||
|
public Vector2[] Normals = new Vector2[Settings.MaxPolygonVertices];
|
|||
|
public Vector2[] Vertices = new Vector2[Settings.MaxPolygonVertices];
|
|||
|
}
|
|||
|
|
|||
|
public struct EPAxis
|
|||
|
{
|
|||
|
public int Index;
|
|||
|
public float Separation;
|
|||
|
public EPAxisType Type;
|
|||
|
}
|
|||
|
|
|||
|
public enum EPAxisType
|
|||
|
{
|
|||
|
Unknown,
|
|||
|
EdgeA,
|
|||
|
EdgeB,
|
|||
|
}
|
|||
|
|
|||
|
public static class Collision
|
|||
|
{
|
|||
|
private static FatEdge _edgeA;
|
|||
|
|
|||
|
private static EPProxy _proxyA = new EPProxy();
|
|||
|
private static EPProxy _proxyB = new EPProxy();
|
|||
|
|
|||
|
private static Transform _xf;
|
|||
|
private static Vector2 _limit11, _limit12;
|
|||
|
private static Vector2 _limit21, _limit22;
|
|||
|
private static float _radius;
|
|||
|
private static Vector2[] _tmpNormals = new Vector2[2];
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Evaluate the manifold with supplied transforms. This assumes
|
|||
|
/// modest motion from the original state. This does not change the
|
|||
|
/// point count, impulses, etc. The radii must come from the Shapes
|
|||
|
/// that generated the manifold.
|
|||
|
/// </summary>
|
|||
|
/// <param name="manifold">The manifold.</param>
|
|||
|
/// <param name="transformA">The transform for A.</param>
|
|||
|
/// <param name="radiusA">The radius for A.</param>
|
|||
|
/// <param name="transformB">The transform for B.</param>
|
|||
|
/// <param name="radiusB">The radius for B.</param>
|
|||
|
/// <param name="normal">World vector pointing from A to B</param>
|
|||
|
/// <param name="points">Torld contact point (point of intersection).</param>
|
|||
|
public static void GetWorldManifold(ref Manifold manifold,
|
|||
|
ref Transform transformA, float radiusA,
|
|||
|
ref Transform transformB, float radiusB, out Vector2 normal,
|
|||
|
out FixedArray2<Vector2> points)
|
|||
|
{
|
|||
|
points = new FixedArray2<Vector2>();
|
|||
|
normal = Vector2.Zero;
|
|||
|
|
|||
|
if (manifold.PointCount == 0)
|
|||
|
{
|
|||
|
normal = Vector2.UnitY;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
switch (manifold.Type)
|
|||
|
{
|
|||
|
case ManifoldType.Circles:
|
|||
|
{
|
|||
|
Vector2 tmp = manifold.Points[0].LocalPoint;
|
|||
|
float pointAx = transformA.Position.X + transformA.R.Col1.X * manifold.LocalPoint.X +
|
|||
|
transformA.R.Col2.X * manifold.LocalPoint.Y;
|
|||
|
|
|||
|
float pointAy = transformA.Position.Y + transformA.R.Col1.Y * manifold.LocalPoint.X +
|
|||
|
transformA.R.Col2.Y * manifold.LocalPoint.Y;
|
|||
|
|
|||
|
float pointBx = transformB.Position.X + transformB.R.Col1.X * tmp.X +
|
|||
|
transformB.R.Col2.X * tmp.Y;
|
|||
|
|
|||
|
float pointBy = transformB.Position.Y + transformB.R.Col1.Y * tmp.X +
|
|||
|
transformB.R.Col2.Y * tmp.Y;
|
|||
|
|
|||
|
normal.X = 1;
|
|||
|
normal.Y = 0;
|
|||
|
|
|||
|
float result = (pointAx - pointBx) * (pointAx - pointBx) +
|
|||
|
(pointAy - pointBy) * (pointAy - pointBy);
|
|||
|
if (result > Settings.Epsilon * Settings.Epsilon)
|
|||
|
{
|
|||
|
float tmpNormalx = pointBx - pointAx;
|
|||
|
float tmpNormaly = pointBy - pointAy;
|
|||
|
float factor = 1f / (float)Math.Sqrt(tmpNormalx * tmpNormalx + tmpNormaly * tmpNormaly);
|
|||
|
normal.X = tmpNormalx * factor;
|
|||
|
normal.Y = tmpNormaly * factor;
|
|||
|
}
|
|||
|
|
|||
|
Vector2 c = Vector2.Zero;
|
|||
|
c.X = (pointAx + radiusA * normal.X) + (pointBx - radiusB * normal.X);
|
|||
|
c.Y = (pointAy + radiusA * normal.Y) + (pointBy - radiusB * normal.Y);
|
|||
|
|
|||
|
points[0] = 0.5f * c;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case ManifoldType.FaceA:
|
|||
|
{
|
|||
|
normal.X = transformA.R.Col1.X * manifold.LocalNormal.X +
|
|||
|
transformA.R.Col2.X * manifold.LocalNormal.Y;
|
|||
|
normal.Y = transformA.R.Col1.Y * manifold.LocalNormal.X +
|
|||
|
transformA.R.Col2.Y * manifold.LocalNormal.Y;
|
|||
|
|
|||
|
float planePointx = transformA.Position.X + transformA.R.Col1.X * manifold.LocalPoint.X +
|
|||
|
transformA.R.Col2.X * manifold.LocalPoint.Y;
|
|||
|
|
|||
|
float planePointy = transformA.Position.Y + transformA.R.Col1.Y * manifold.LocalPoint.X +
|
|||
|
transformA.R.Col2.Y * manifold.LocalPoint.Y;
|
|||
|
|
|||
|
for (int i = 0; i < manifold.PointCount; ++i)
|
|||
|
{
|
|||
|
Vector2 tmp = manifold.Points[i].LocalPoint;
|
|||
|
|
|||
|
float clipPointx = transformB.Position.X + transformB.R.Col1.X * tmp.X +
|
|||
|
transformB.R.Col2.X * tmp.Y;
|
|||
|
|
|||
|
float clipPointy = transformB.Position.Y + transformB.R.Col1.Y * tmp.X +
|
|||
|
transformB.R.Col2.Y * tmp.Y;
|
|||
|
|
|||
|
float value = (clipPointx - planePointx) * normal.X + (clipPointy - planePointy) * normal.Y;
|
|||
|
|
|||
|
Vector2 c = Vector2.Zero;
|
|||
|
c.X = (clipPointx + (radiusA - value) * normal.X) + (clipPointx - radiusB * normal.X);
|
|||
|
c.Y = (clipPointy + (radiusA - value) * normal.Y) + (clipPointy - radiusB * normal.Y);
|
|||
|
|
|||
|
points[i] = 0.5f * c;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case ManifoldType.FaceB:
|
|||
|
{
|
|||
|
normal.X = transformB.R.Col1.X * manifold.LocalNormal.X +
|
|||
|
transformB.R.Col2.X * manifold.LocalNormal.Y;
|
|||
|
normal.Y = transformB.R.Col1.Y * manifold.LocalNormal.X +
|
|||
|
transformB.R.Col2.Y * manifold.LocalNormal.Y;
|
|||
|
|
|||
|
float planePointx = transformB.Position.X + transformB.R.Col1.X * manifold.LocalPoint.X +
|
|||
|
transformB.R.Col2.X * manifold.LocalPoint.Y;
|
|||
|
|
|||
|
float planePointy = transformB.Position.Y + transformB.R.Col1.Y * manifold.LocalPoint.X +
|
|||
|
transformB.R.Col2.Y * manifold.LocalPoint.Y;
|
|||
|
|
|||
|
for (int i = 0; i < manifold.PointCount; ++i)
|
|||
|
{
|
|||
|
Vector2 tmp = manifold.Points[i].LocalPoint;
|
|||
|
|
|||
|
float clipPointx = transformA.Position.X + transformA.R.Col1.X * tmp.X +
|
|||
|
transformA.R.Col2.X * tmp.Y;
|
|||
|
|
|||
|
float clipPointy = transformA.Position.Y + transformA.R.Col1.Y * tmp.X +
|
|||
|
transformA.R.Col2.Y * tmp.Y;
|
|||
|
|
|||
|
float value = (clipPointx - planePointx) * normal.X + (clipPointy - planePointy) * normal.Y;
|
|||
|
|
|||
|
Vector2 c = Vector2.Zero;
|
|||
|
c.X = (clipPointx - radiusA * normal.X) + (clipPointx + (radiusB - value) * normal.X);
|
|||
|
c.Y = (clipPointy - radiusA * normal.Y) + (clipPointy + (radiusB - value) * normal.Y);
|
|||
|
|
|||
|
points[i] = 0.5f * c;
|
|||
|
}
|
|||
|
// Ensure normal points from A to B.
|
|||
|
normal *= -1;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
normal = Vector2.UnitY;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void GetPointStates(out FixedArray2<PointState> state1, out FixedArray2<PointState> state2,
|
|||
|
ref Manifold manifold1, ref Manifold manifold2)
|
|||
|
{
|
|||
|
state1 = new FixedArray2<PointState>();
|
|||
|
state2 = new FixedArray2<PointState>();
|
|||
|
|
|||
|
// Detect persists and removes.
|
|||
|
for (int i = 0; i < manifold1.PointCount; ++i)
|
|||
|
{
|
|||
|
ContactID id = manifold1.Points[i].Id;
|
|||
|
|
|||
|
state1[i] = PointState.Remove;
|
|||
|
|
|||
|
for (int j = 0; j < manifold2.PointCount; ++j)
|
|||
|
{
|
|||
|
if (manifold2.Points[j].Id.Key == id.Key)
|
|||
|
{
|
|||
|
state1[i] = PointState.Persist;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Detect persists and adds.
|
|||
|
for (int i = 0; i < manifold2.PointCount; ++i)
|
|||
|
{
|
|||
|
ContactID id = manifold2.Points[i].Id;
|
|||
|
|
|||
|
state2[i] = PointState.Add;
|
|||
|
|
|||
|
for (int j = 0; j < manifold1.PointCount; ++j)
|
|||
|
{
|
|||
|
if (manifold1.Points[j].Id.Key == id.Key)
|
|||
|
{
|
|||
|
state2[i] = PointState.Persist;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// Compute the collision manifold between two circles.
|
|||
|
public static void CollideCircles(ref Manifold manifold,
|
|||
|
CircleShape circleA, ref Transform xfA,
|
|||
|
CircleShape circleB, ref Transform xfB)
|
|||
|
{
|
|||
|
manifold.PointCount = 0;
|
|||
|
|
|||
|
float pAx = xfA.Position.X + xfA.R.Col1.X * circleA.Position.X + xfA.R.Col2.X * circleA.Position.Y;
|
|||
|
float pAy = xfA.Position.Y + xfA.R.Col1.Y * circleA.Position.X + xfA.R.Col2.Y * circleA.Position.Y;
|
|||
|
float pBx = xfB.Position.X + xfB.R.Col1.X * circleB.Position.X + xfB.R.Col2.X * circleB.Position.Y;
|
|||
|
float pBy = xfB.Position.Y + xfB.R.Col1.Y * circleB.Position.X + xfB.R.Col2.Y * circleB.Position.Y;
|
|||
|
|
|||
|
float distSqr = (pBx - pAx) * (pBx - pAx) + (pBy - pAy) * (pBy - pAy);
|
|||
|
float radius = circleA.Radius + circleB.Radius;
|
|||
|
if (distSqr > radius * radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
manifold.Type = ManifoldType.Circles;
|
|||
|
manifold.LocalPoint = circleA.Position;
|
|||
|
manifold.LocalNormal = Vector2.Zero;
|
|||
|
manifold.PointCount = 1;
|
|||
|
|
|||
|
ManifoldPoint p0 = manifold.Points[0];
|
|||
|
|
|||
|
p0.LocalPoint = circleB.Position;
|
|||
|
p0.Id.Key = 0;
|
|||
|
|
|||
|
manifold.Points[0] = p0;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compute the collision manifold between a polygon and a circle.
|
|||
|
/// </summary>
|
|||
|
/// <param name="manifold">The manifold.</param>
|
|||
|
/// <param name="polygonA">The polygon A.</param>
|
|||
|
/// <param name="transformA">The transform of A.</param>
|
|||
|
/// <param name="circleB">The circle B.</param>
|
|||
|
/// <param name="transformB">The transform of B.</param>
|
|||
|
public static void CollidePolygonAndCircle(ref Manifold manifold,
|
|||
|
PolygonShape polygonA, ref Transform transformA,
|
|||
|
CircleShape circleB, ref Transform transformB)
|
|||
|
{
|
|||
|
manifold.PointCount = 0;
|
|||
|
|
|||
|
// Compute circle position in the frame of the polygon.
|
|||
|
Vector2 c =
|
|||
|
new Vector2(
|
|||
|
transformB.Position.X + transformB.R.Col1.X * circleB.Position.X +
|
|||
|
transformB.R.Col2.X * circleB.Position.Y,
|
|||
|
transformB.Position.Y + transformB.R.Col1.Y * circleB.Position.X +
|
|||
|
transformB.R.Col2.Y * circleB.Position.Y);
|
|||
|
Vector2 cLocal =
|
|||
|
new Vector2(
|
|||
|
(c.X - transformA.Position.X) * transformA.R.Col1.X +
|
|||
|
(c.Y - transformA.Position.Y) * transformA.R.Col1.Y,
|
|||
|
(c.X - transformA.Position.X) * transformA.R.Col2.X +
|
|||
|
(c.Y - transformA.Position.Y) * transformA.R.Col2.Y);
|
|||
|
|
|||
|
// Find the min separating edge.
|
|||
|
int normalIndex = 0;
|
|||
|
float separation = -Settings.MaxFloat;
|
|||
|
float radius = polygonA.Radius + circleB.Radius;
|
|||
|
int vertexCount = polygonA.Vertices.Count;
|
|||
|
|
|||
|
for (int i = 0; i < vertexCount; ++i)
|
|||
|
{
|
|||
|
Vector2 value1 = polygonA.Normals[i];
|
|||
|
Vector2 value2 = cLocal - polygonA.Vertices[i];
|
|||
|
float s = value1.X * value2.X + value1.Y * value2.Y;
|
|||
|
|
|||
|
if (s > radius)
|
|||
|
{
|
|||
|
// Early out.
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (s > separation)
|
|||
|
{
|
|||
|
separation = s;
|
|||
|
normalIndex = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Vertices that subtend the incident face.
|
|||
|
int vertIndex1 = normalIndex;
|
|||
|
int vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
|
|||
|
Vector2 v1 = polygonA.Vertices[vertIndex1];
|
|||
|
Vector2 v2 = polygonA.Vertices[vertIndex2];
|
|||
|
|
|||
|
// If the center is inside the polygon ...
|
|||
|
if (separation < Settings.Epsilon)
|
|||
|
{
|
|||
|
manifold.PointCount = 1;
|
|||
|
manifold.Type = ManifoldType.FaceA;
|
|||
|
manifold.LocalNormal = polygonA.Normals[normalIndex];
|
|||
|
manifold.LocalPoint = 0.5f * (v1 + v2);
|
|||
|
|
|||
|
ManifoldPoint p0 = manifold.Points[0];
|
|||
|
|
|||
|
p0.LocalPoint = circleB.Position;
|
|||
|
p0.Id.Key = 0;
|
|||
|
|
|||
|
manifold.Points[0] = p0;
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Compute barycentric coordinates
|
|||
|
float u1 = (cLocal.X - v1.X) * (v2.X - v1.X) + (cLocal.Y - v1.Y) * (v2.Y - v1.Y);
|
|||
|
float u2 = (cLocal.X - v2.X) * (v1.X - v2.X) + (cLocal.Y - v2.Y) * (v1.Y - v2.Y);
|
|||
|
|
|||
|
if (u1 <= 0.0f)
|
|||
|
{
|
|||
|
float r = (cLocal.X - v1.X) * (cLocal.X - v1.X) + (cLocal.Y - v1.Y) * (cLocal.Y - v1.Y);
|
|||
|
if (r > radius * radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
manifold.PointCount = 1;
|
|||
|
manifold.Type = ManifoldType.FaceA;
|
|||
|
manifold.LocalNormal = cLocal - v1;
|
|||
|
float factor = 1f /
|
|||
|
(float)
|
|||
|
Math.Sqrt(manifold.LocalNormal.X * manifold.LocalNormal.X +
|
|||
|
manifold.LocalNormal.Y * manifold.LocalNormal.Y);
|
|||
|
manifold.LocalNormal.X = manifold.LocalNormal.X * factor;
|
|||
|
manifold.LocalNormal.Y = manifold.LocalNormal.Y * factor;
|
|||
|
manifold.LocalPoint = v1;
|
|||
|
|
|||
|
ManifoldPoint p0b = manifold.Points[0];
|
|||
|
|
|||
|
p0b.LocalPoint = circleB.Position;
|
|||
|
p0b.Id.Key = 0;
|
|||
|
|
|||
|
manifold.Points[0] = p0b;
|
|||
|
}
|
|||
|
else if (u2 <= 0.0f)
|
|||
|
{
|
|||
|
float r = (cLocal.X - v2.X) * (cLocal.X - v2.X) + (cLocal.Y - v2.Y) * (cLocal.Y - v2.Y);
|
|||
|
if (r > radius * radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
manifold.PointCount = 1;
|
|||
|
manifold.Type = ManifoldType.FaceA;
|
|||
|
manifold.LocalNormal = cLocal - v2;
|
|||
|
float factor = 1f /
|
|||
|
(float)
|
|||
|
Math.Sqrt(manifold.LocalNormal.X * manifold.LocalNormal.X +
|
|||
|
manifold.LocalNormal.Y * manifold.LocalNormal.Y);
|
|||
|
manifold.LocalNormal.X = manifold.LocalNormal.X * factor;
|
|||
|
manifold.LocalNormal.Y = manifold.LocalNormal.Y * factor;
|
|||
|
manifold.LocalPoint = v2;
|
|||
|
|
|||
|
ManifoldPoint p0c = manifold.Points[0];
|
|||
|
|
|||
|
p0c.LocalPoint = circleB.Position;
|
|||
|
p0c.Id.Key = 0;
|
|||
|
|
|||
|
manifold.Points[0] = p0c;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Vector2 faceCenter = 0.5f * (v1 + v2);
|
|||
|
Vector2 value1 = cLocal - faceCenter;
|
|||
|
Vector2 value2 = polygonA.Normals[vertIndex1];
|
|||
|
float separation2 = value1.X * value2.X + value1.Y * value2.Y;
|
|||
|
if (separation2 > radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
manifold.PointCount = 1;
|
|||
|
manifold.Type = ManifoldType.FaceA;
|
|||
|
manifold.LocalNormal = polygonA.Normals[vertIndex1];
|
|||
|
manifold.LocalPoint = faceCenter;
|
|||
|
|
|||
|
ManifoldPoint p0d = manifold.Points[0];
|
|||
|
|
|||
|
p0d.LocalPoint = circleB.Position;
|
|||
|
p0d.Id.Key = 0;
|
|||
|
|
|||
|
manifold.Points[0] = p0d;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compute the collision manifold between two polygons.
|
|||
|
/// </summary>
|
|||
|
/// <param name="manifold">The manifold.</param>
|
|||
|
/// <param name="polyA">The poly A.</param>
|
|||
|
/// <param name="transformA">The transform A.</param>
|
|||
|
/// <param name="polyB">The poly B.</param>
|
|||
|
/// <param name="transformB">The transform B.</param>
|
|||
|
public static void CollidePolygons(ref Manifold manifold,
|
|||
|
PolygonShape polyA, ref Transform transformA,
|
|||
|
PolygonShape polyB, ref Transform transformB)
|
|||
|
{
|
|||
|
manifold.PointCount = 0;
|
|||
|
float totalRadius = polyA.Radius + polyB.Radius;
|
|||
|
|
|||
|
int edgeA = 0;
|
|||
|
float separationA = FindMaxSeparation(out edgeA, polyA, ref transformA, polyB, ref transformB);
|
|||
|
if (separationA > totalRadius)
|
|||
|
return;
|
|||
|
|
|||
|
int edgeB = 0;
|
|||
|
float separationB = FindMaxSeparation(out edgeB, polyB, ref transformB, polyA, ref transformA);
|
|||
|
if (separationB > totalRadius)
|
|||
|
return;
|
|||
|
|
|||
|
PolygonShape poly1; // reference polygon
|
|||
|
PolygonShape poly2; // incident polygon
|
|||
|
Transform xf1, xf2;
|
|||
|
int edge1; // reference edge
|
|||
|
bool flip;
|
|||
|
const float k_relativeTol = 0.98f;
|
|||
|
const float k_absoluteTol = 0.001f;
|
|||
|
|
|||
|
if (separationB > k_relativeTol * separationA + k_absoluteTol)
|
|||
|
{
|
|||
|
poly1 = polyB;
|
|||
|
poly2 = polyA;
|
|||
|
xf1 = transformB;
|
|||
|
xf2 = transformA;
|
|||
|
edge1 = edgeB;
|
|||
|
manifold.Type = ManifoldType.FaceB;
|
|||
|
flip = true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
poly1 = polyA;
|
|||
|
poly2 = polyB;
|
|||
|
xf1 = transformA;
|
|||
|
xf2 = transformB;
|
|||
|
edge1 = edgeA;
|
|||
|
manifold.Type = ManifoldType.FaceA;
|
|||
|
flip = false;
|
|||
|
}
|
|||
|
|
|||
|
FixedArray2<ClipVertex> incidentEdge;
|
|||
|
FindIncidentEdge(out incidentEdge, poly1, ref xf1, edge1, poly2, ref xf2);
|
|||
|
|
|||
|
int count1 = poly1.Vertices.Count;
|
|||
|
|
|||
|
int iv1 = edge1;
|
|||
|
int iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
|
|||
|
|
|||
|
Vector2 v11 = poly1.Vertices[iv1];
|
|||
|
Vector2 v12 = poly1.Vertices[iv2];
|
|||
|
|
|||
|
float localTangentX = v12.X - v11.X;
|
|||
|
float localTangentY = v12.Y - v11.Y;
|
|||
|
|
|||
|
float factor = 1f / (float)Math.Sqrt(localTangentX * localTangentX + localTangentY * localTangentY);
|
|||
|
localTangentX = localTangentX * factor;
|
|||
|
localTangentY = localTangentY * factor;
|
|||
|
|
|||
|
Vector2 localNormal = new Vector2(localTangentY, -localTangentX);
|
|||
|
Vector2 planePoint = 0.5f * (v11 + v12);
|
|||
|
|
|||
|
Vector2 tangent = new Vector2(xf1.R.Col1.X * localTangentX + xf1.R.Col2.X * localTangentY,
|
|||
|
xf1.R.Col1.Y * localTangentX + xf1.R.Col2.Y * localTangentY);
|
|||
|
float normalx = tangent.Y;
|
|||
|
float normaly = -tangent.X;
|
|||
|
|
|||
|
v11 = new Vector2(xf1.Position.X + xf1.R.Col1.X * v11.X + xf1.R.Col2.X * v11.Y,
|
|||
|
xf1.Position.Y + xf1.R.Col1.Y * v11.X + xf1.R.Col2.Y * v11.Y);
|
|||
|
v12 = new Vector2(xf1.Position.X + xf1.R.Col1.X * v12.X + xf1.R.Col2.X * v12.Y,
|
|||
|
xf1.Position.Y + xf1.R.Col1.Y * v12.X + xf1.R.Col2.Y * v12.Y);
|
|||
|
|
|||
|
// Face offset.
|
|||
|
float frontOffset = normalx * v11.X + normaly * v11.Y;
|
|||
|
|
|||
|
// Side offsets, extended by polytope skin thickness.
|
|||
|
float sideOffset1 = -(tangent.X * v11.X + tangent.Y * v11.Y) + totalRadius;
|
|||
|
float sideOffset2 = tangent.X * v12.X + tangent.Y * v12.Y + totalRadius;
|
|||
|
|
|||
|
// Clip incident edge against extruded edge1 side edges.
|
|||
|
FixedArray2<ClipVertex> clipPoints1;
|
|||
|
FixedArray2<ClipVertex> clipPoints2;
|
|||
|
|
|||
|
// Clip to box side 1
|
|||
|
int np = ClipSegmentToLine(out clipPoints1, ref incidentEdge, -tangent, sideOffset1, iv1);
|
|||
|
|
|||
|
if (np < 2)
|
|||
|
return;
|
|||
|
|
|||
|
// Clip to negative box side 1
|
|||
|
np = ClipSegmentToLine(out clipPoints2, ref clipPoints1, tangent, sideOffset2, iv2);
|
|||
|
|
|||
|
if (np < 2)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Now clipPoints2 contains the clipped points.
|
|||
|
manifold.LocalNormal = localNormal;
|
|||
|
manifold.LocalPoint = planePoint;
|
|||
|
|
|||
|
int pointCount = 0;
|
|||
|
for (int i = 0; i < Settings.MaxManifoldPoints; ++i)
|
|||
|
{
|
|||
|
Vector2 value = clipPoints2[i].V;
|
|||
|
float separation = normalx * value.X + normaly * value.Y - frontOffset;
|
|||
|
|
|||
|
if (separation <= totalRadius)
|
|||
|
{
|
|||
|
ManifoldPoint cp = manifold.Points[pointCount];
|
|||
|
Vector2 tmp = clipPoints2[i].V;
|
|||
|
float tmp1X = tmp.X - xf2.Position.X;
|
|||
|
float tmp1Y = tmp.Y - xf2.Position.Y;
|
|||
|
cp.LocalPoint.X = tmp1X * xf2.R.Col1.X + tmp1Y * xf2.R.Col1.Y;
|
|||
|
cp.LocalPoint.Y = tmp1X * xf2.R.Col2.X + tmp1Y * xf2.R.Col2.Y;
|
|||
|
cp.Id = clipPoints2[i].ID;
|
|||
|
|
|||
|
if (flip)
|
|||
|
{
|
|||
|
// Swap features
|
|||
|
ContactFeature cf = cp.Id.Features;
|
|||
|
cp.Id.Features.IndexA = cf.IndexB;
|
|||
|
cp.Id.Features.IndexB = cf.IndexA;
|
|||
|
cp.Id.Features.TypeA = cf.TypeB;
|
|||
|
cp.Id.Features.TypeB = cf.TypeA;
|
|||
|
}
|
|||
|
|
|||
|
manifold.Points[pointCount] = cp;
|
|||
|
|
|||
|
++pointCount;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
manifold.PointCount = pointCount;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Compute contact points for edge versus circle.
|
|||
|
/// This accounts for edge connectivity.
|
|||
|
/// </summary>
|
|||
|
/// <param name="manifold">The manifold.</param>
|
|||
|
/// <param name="edgeA">The edge A.</param>
|
|||
|
/// <param name="transformA">The transform A.</param>
|
|||
|
/// <param name="circleB">The circle B.</param>
|
|||
|
/// <param name="transformB">The transform B.</param>
|
|||
|
public static void CollideEdgeAndCircle(ref Manifold manifold,
|
|||
|
EdgeShape edgeA, ref Transform transformA,
|
|||
|
CircleShape circleB, ref Transform transformB)
|
|||
|
{
|
|||
|
manifold.PointCount = 0;
|
|||
|
|
|||
|
// Compute circle in frame of edge
|
|||
|
Vector2 Q = MathUtils.MultiplyT(ref transformA, MathUtils.Multiply(ref transformB, ref circleB._position));
|
|||
|
|
|||
|
Vector2 A = edgeA.Vertex1, B = edgeA.Vertex2;
|
|||
|
Vector2 e = B - A;
|
|||
|
|
|||
|
// Barycentric coordinates
|
|||
|
float u = Vector2.Dot(e, B - Q);
|
|||
|
float v = Vector2.Dot(e, Q - A);
|
|||
|
|
|||
|
float radius = edgeA.Radius + circleB.Radius;
|
|||
|
|
|||
|
ContactFeature cf;
|
|||
|
cf.IndexB = 0;
|
|||
|
cf.TypeB = (byte)ContactFeatureType.Vertex;
|
|||
|
|
|||
|
Vector2 P, d;
|
|||
|
|
|||
|
// Region A
|
|||
|
if (v <= 0.0f)
|
|||
|
{
|
|||
|
P = A;
|
|||
|
d = Q - P;
|
|||
|
float dd;
|
|||
|
Vector2.Dot(ref d, ref d, out dd);
|
|||
|
if (dd > radius * radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Is there an edge connected to A?
|
|||
|
if (edgeA.HasVertex0)
|
|||
|
{
|
|||
|
Vector2 A1 = edgeA.Vertex0;
|
|||
|
Vector2 B1 = A;
|
|||
|
Vector2 e1 = B1 - A1;
|
|||
|
float u1 = Vector2.Dot(e1, B1 - Q);
|
|||
|
|
|||
|
// Is the circle in Region AB of the previous edge?
|
|||
|
if (u1 > 0.0f)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cf.IndexA = 0;
|
|||
|
cf.TypeA = (byte)ContactFeatureType.Vertex;
|
|||
|
manifold.PointCount = 1;
|
|||
|
manifold.Type = ManifoldType.Circles;
|
|||
|
manifold.LocalNormal = Vector2.Zero;
|
|||
|
manifold.LocalPoint = P;
|
|||
|
ManifoldPoint mp = new ManifoldPoint();
|
|||
|
mp.Id.Key = 0;
|
|||
|
mp.Id.Features = cf;
|
|||
|
mp.LocalPoint = circleB.Position;
|
|||
|
manifold.Points[0] = mp;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Region B
|
|||
|
if (u <= 0.0f)
|
|||
|
{
|
|||
|
P = B;
|
|||
|
d = Q - P;
|
|||
|
float dd;
|
|||
|
Vector2.Dot(ref d, ref d, out dd);
|
|||
|
if (dd > radius * radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Is there an edge connected to B?
|
|||
|
if (edgeA.HasVertex3)
|
|||
|
{
|
|||
|
Vector2 B2 = edgeA.Vertex3;
|
|||
|
Vector2 A2 = B;
|
|||
|
Vector2 e2 = B2 - A2;
|
|||
|
float v2 = Vector2.Dot(e2, Q - A2);
|
|||
|
|
|||
|
// Is the circle in Region AB of the next edge?
|
|||
|
if (v2 > 0.0f)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cf.IndexA = 1;
|
|||
|
cf.TypeA = (byte)ContactFeatureType.Vertex;
|
|||
|
manifold.PointCount = 1;
|
|||
|
manifold.Type = ManifoldType.Circles;
|
|||
|
manifold.LocalNormal = Vector2.Zero;
|
|||
|
manifold.LocalPoint = P;
|
|||
|
ManifoldPoint mp = new ManifoldPoint();
|
|||
|
mp.Id.Key = 0;
|
|||
|
mp.Id.Features = cf;
|
|||
|
mp.LocalPoint = circleB.Position;
|
|||
|
manifold.Points[0] = mp;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Region AB
|
|||
|
float den;
|
|||
|
Vector2.Dot(ref e, ref e, out den);
|
|||
|
Debug.Assert(den > 0.0f);
|
|||
|
P = (1.0f / den) * (u * A + v * B);
|
|||
|
d = Q - P;
|
|||
|
float dd2;
|
|||
|
Vector2.Dot(ref d, ref d, out dd2);
|
|||
|
if (dd2 > radius * radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Vector2 n = new Vector2(-e.Y, e.X);
|
|||
|
if (Vector2.Dot(n, Q - A) < 0.0f)
|
|||
|
{
|
|||
|
n = new Vector2(-n.X, -n.Y);
|
|||
|
}
|
|||
|
n.Normalize();
|
|||
|
|
|||
|
cf.IndexA = 0;
|
|||
|
cf.TypeA = (byte)ContactFeatureType.Face;
|
|||
|
manifold.PointCount = 1;
|
|||
|
manifold.Type = ManifoldType.FaceA;
|
|||
|
manifold.LocalNormal = n;
|
|||
|
manifold.LocalPoint = A;
|
|||
|
ManifoldPoint mp2 = new ManifoldPoint();
|
|||
|
mp2.Id.Key = 0;
|
|||
|
mp2.Id.Features = cf;
|
|||
|
mp2.LocalPoint = circleB.Position;
|
|||
|
manifold.Points[0] = mp2;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Collides and edge and a polygon, taking into account edge adjacency.
|
|||
|
/// </summary>
|
|||
|
/// <param name="manifold">The manifold.</param>
|
|||
|
/// <param name="edgeA">The edge A.</param>
|
|||
|
/// <param name="xfA">The xf A.</param>
|
|||
|
/// <param name="polygonB">The polygon B.</param>
|
|||
|
/// <param name="xfB">The xf B.</param>
|
|||
|
public static void CollideEdgeAndPolygon(ref Manifold manifold,
|
|||
|
EdgeShape edgeA, ref Transform xfA,
|
|||
|
PolygonShape polygonB, ref Transform xfB)
|
|||
|
{
|
|||
|
MathUtils.MultiplyT(ref xfA, ref xfB, out _xf);
|
|||
|
|
|||
|
// Edge geometry
|
|||
|
_edgeA.V0 = edgeA.Vertex0;
|
|||
|
_edgeA.V1 = edgeA.Vertex1;
|
|||
|
_edgeA.V2 = edgeA.Vertex2;
|
|||
|
_edgeA.V3 = edgeA.Vertex3;
|
|||
|
Vector2 e = _edgeA.V2 - _edgeA.V1;
|
|||
|
|
|||
|
// Normal points outwards in CCW order.
|
|||
|
_edgeA.Normal = new Vector2(e.Y, -e.X);
|
|||
|
_edgeA.Normal.Normalize();
|
|||
|
_edgeA.HasVertex0 = edgeA.HasVertex0;
|
|||
|
_edgeA.HasVertex3 = edgeA.HasVertex3;
|
|||
|
|
|||
|
// Proxy for edge
|
|||
|
_proxyA.Vertices[0] = _edgeA.V1;
|
|||
|
_proxyA.Vertices[1] = _edgeA.V2;
|
|||
|
_proxyA.Normals[0] = _edgeA.Normal;
|
|||
|
_proxyA.Normals[1] = -_edgeA.Normal;
|
|||
|
_proxyA.Centroid = 0.5f * (_edgeA.V1 + _edgeA.V2);
|
|||
|
_proxyA.Count = 2;
|
|||
|
|
|||
|
// Proxy for polygon
|
|||
|
_proxyB.Count = polygonB.Vertices.Count;
|
|||
|
_proxyB.Centroid = MathUtils.Multiply(ref _xf, ref polygonB.MassData.Centroid);
|
|||
|
for (int i = 0; i < polygonB.Vertices.Count; ++i)
|
|||
|
{
|
|||
|
_proxyB.Vertices[i] = MathUtils.Multiply(ref _xf, polygonB.Vertices[i]);
|
|||
|
_proxyB.Normals[i] = MathUtils.Multiply(ref _xf.R, polygonB.Normals[i]);
|
|||
|
}
|
|||
|
|
|||
|
_radius = 2.0f * Settings.PolygonRadius;
|
|||
|
|
|||
|
_limit11 = Vector2.Zero;
|
|||
|
_limit12 = Vector2.Zero;
|
|||
|
_limit21 = Vector2.Zero;
|
|||
|
_limit22 = Vector2.Zero;
|
|||
|
|
|||
|
//Collide(ref manifold); inline start
|
|||
|
manifold.PointCount = 0;
|
|||
|
|
|||
|
//ComputeAdjacency(); inline start
|
|||
|
Vector2 v0 = _edgeA.V0;
|
|||
|
Vector2 v1 = _edgeA.V1;
|
|||
|
Vector2 v2 = _edgeA.V2;
|
|||
|
Vector2 v3 = _edgeA.V3;
|
|||
|
|
|||
|
// Determine allowable the normal regions based on adjacency.
|
|||
|
// Note: it may be possible that no normal is admissable.
|
|||
|
Vector2 centerB = _proxyB.Centroid;
|
|||
|
if (_edgeA.HasVertex0)
|
|||
|
{
|
|||
|
Vector2 e0 = v1 - v0;
|
|||
|
Vector2 e1 = v2 - v1;
|
|||
|
Vector2 n0 = new Vector2(e0.Y, -e0.X);
|
|||
|
Vector2 n1 = new Vector2(e1.Y, -e1.X);
|
|||
|
n0.Normalize();
|
|||
|
n1.Normalize();
|
|||
|
|
|||
|
bool convex = MathUtils.Cross(n0, n1) >= 0.0f;
|
|||
|
bool front0 = Vector2.Dot(n0, centerB - v0) >= 0.0f;
|
|||
|
bool front1 = Vector2.Dot(n1, centerB - v1) >= 0.0f;
|
|||
|
|
|||
|
if (convex)
|
|||
|
{
|
|||
|
if (front0 || front1)
|
|||
|
{
|
|||
|
_limit11 = n1;
|
|||
|
_limit12 = n0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_limit11 = -n1;
|
|||
|
_limit12 = -n0;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (front0 && front1)
|
|||
|
{
|
|||
|
_limit11 = n0;
|
|||
|
_limit12 = n1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_limit11 = -n0;
|
|||
|
_limit12 = -n1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_limit11 = Vector2.Zero;
|
|||
|
_limit12 = Vector2.Zero;
|
|||
|
}
|
|||
|
|
|||
|
if (_edgeA.HasVertex3)
|
|||
|
{
|
|||
|
Vector2 e1 = v2 - v1;
|
|||
|
Vector2 e2 = v3 - v2;
|
|||
|
Vector2 n1 = new Vector2(e1.Y, -e1.X);
|
|||
|
Vector2 n2 = new Vector2(e2.Y, -e2.X);
|
|||
|
n1.Normalize();
|
|||
|
n2.Normalize();
|
|||
|
|
|||
|
bool convex = MathUtils.Cross(n1, n2) >= 0.0f;
|
|||
|
bool front1 = Vector2.Dot(n1, centerB - v1) >= 0.0f;
|
|||
|
bool front2 = Vector2.Dot(n2, centerB - v2) >= 0.0f;
|
|||
|
|
|||
|
if (convex)
|
|||
|
{
|
|||
|
if (front1 || front2)
|
|||
|
{
|
|||
|
_limit21 = n2;
|
|||
|
_limit22 = n1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_limit21 = -n2;
|
|||
|
_limit22 = -n1;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (front1 && front2)
|
|||
|
{
|
|||
|
_limit21 = n1;
|
|||
|
_limit22 = n2;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_limit21 = -n1;
|
|||
|
_limit22 = -n2;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_limit21 = Vector2.Zero;
|
|||
|
_limit22 = Vector2.Zero;
|
|||
|
}
|
|||
|
|
|||
|
//ComputeAdjacency(); inline end
|
|||
|
|
|||
|
//EPAxis edgeAxis = ComputeEdgeSeparation(); inline start
|
|||
|
EPAxis edgeAxis = ComputeEdgeSeparation();
|
|||
|
|
|||
|
// If no valid normal can be found than this edge should not collide.
|
|||
|
// This can happen on the middle edge of a 3-edge zig-zag chain.
|
|||
|
if (edgeAxis.Type == EPAxisType.Unknown)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (edgeAxis.Separation > _radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
EPAxis polygonAxis = ComputePolygonSeparation();
|
|||
|
if (polygonAxis.Type != EPAxisType.Unknown && polygonAxis.Separation > _radius)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Use hysteresis for jitter reduction.
|
|||
|
const float k_relativeTol = 0.98f;
|
|||
|
const float k_absoluteTol = 0.001f;
|
|||
|
|
|||
|
EPAxis primaryAxis;
|
|||
|
if (polygonAxis.Type == EPAxisType.Unknown)
|
|||
|
{
|
|||
|
primaryAxis = edgeAxis;
|
|||
|
}
|
|||
|
else if (polygonAxis.Separation > k_relativeTol * edgeAxis.Separation + k_absoluteTol)
|
|||
|
{
|
|||
|
primaryAxis = polygonAxis;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
primaryAxis = edgeAxis;
|
|||
|
}
|
|||
|
|
|||
|
EPProxy proxy1;
|
|||
|
EPProxy proxy2;
|
|||
|
FixedArray2<ClipVertex> incidentEdge = new FixedArray2<ClipVertex>();
|
|||
|
if (primaryAxis.Type == EPAxisType.EdgeA)
|
|||
|
{
|
|||
|
proxy1 = _proxyA;
|
|||
|
proxy2 = _proxyB;
|
|||
|
manifold.Type = ManifoldType.FaceA;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
proxy1 = _proxyB;
|
|||
|
proxy2 = _proxyA;
|
|||
|
manifold.Type = ManifoldType.FaceB;
|
|||
|
}
|
|||
|
|
|||
|
int edge1 = primaryAxis.Index;
|
|||
|
|
|||
|
FindIncidentEdge(ref incidentEdge, proxy1, primaryAxis.Index, proxy2);
|
|||
|
int count1 = proxy1.Count;
|
|||
|
|
|||
|
int iv1 = edge1;
|
|||
|
int iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
|
|||
|
|
|||
|
Vector2 v11 = proxy1.Vertices[iv1];
|
|||
|
Vector2 v12 = proxy1.Vertices[iv2];
|
|||
|
|
|||
|
Vector2 tangent = v12 - v11;
|
|||
|
tangent.Normalize();
|
|||
|
|
|||
|
Vector2 normal = MathUtils.Cross(tangent, 1.0f);
|
|||
|
Vector2 planePoint = 0.5f * (v11 + v12);
|
|||
|
|
|||
|
// Face offset.
|
|||
|
float frontOffset = Vector2.Dot(normal, v11);
|
|||
|
|
|||
|
// Side offsets, extended by polytope skin thickness.
|
|||
|
float sideOffset1 = -Vector2.Dot(tangent, v11) + _radius;
|
|||
|
float sideOffset2 = Vector2.Dot(tangent, v12) + _radius;
|
|||
|
|
|||
|
// Clip incident edge against extruded edge1 side edges.
|
|||
|
FixedArray2<ClipVertex> clipPoints1;
|
|||
|
FixedArray2<ClipVertex> clipPoints2;
|
|||
|
int np;
|
|||
|
|
|||
|
// Clip to box side 1
|
|||
|
np = ClipSegmentToLine(out clipPoints1, ref incidentEdge, -tangent, sideOffset1, iv1);
|
|||
|
|
|||
|
if (np < Settings.MaxManifoldPoints)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Clip to negative box side 1
|
|||
|
np = ClipSegmentToLine(out clipPoints2, ref clipPoints1, tangent, sideOffset2, iv2);
|
|||
|
|
|||
|
if (np < Settings.MaxManifoldPoints)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Now clipPoints2 contains the clipped points.
|
|||
|
if (primaryAxis.Type == EPAxisType.EdgeA)
|
|||
|
{
|
|||
|
manifold.LocalNormal = normal;
|
|||
|
manifold.LocalPoint = planePoint;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
manifold.LocalNormal = MathUtils.MultiplyT(ref _xf.R, ref normal);
|
|||
|
manifold.LocalPoint = MathUtils.MultiplyT(ref _xf, ref planePoint);
|
|||
|
}
|
|||
|
|
|||
|
int pointCount = 0;
|
|||
|
for (int i1 = 0; i1 < Settings.MaxManifoldPoints; ++i1)
|
|||
|
{
|
|||
|
float separation = Vector2.Dot(normal, clipPoints2[i1].V) - frontOffset;
|
|||
|
|
|||
|
if (separation <= _radius)
|
|||
|
{
|
|||
|
ManifoldPoint cp = manifold.Points[pointCount];
|
|||
|
|
|||
|
if (primaryAxis.Type == EPAxisType.EdgeA)
|
|||
|
{
|
|||
|
cp.LocalPoint = MathUtils.MultiplyT(ref _xf, clipPoints2[i1].V);
|
|||
|
cp.Id = clipPoints2[i1].ID;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
cp.LocalPoint = clipPoints2[i1].V;
|
|||
|
cp.Id.Features.TypeA = clipPoints2[i1].ID.Features.TypeB;
|
|||
|
cp.Id.Features.TypeB = clipPoints2[i1].ID.Features.TypeA;
|
|||
|
cp.Id.Features.IndexA = clipPoints2[i1].ID.Features.IndexB;
|
|||
|
cp.Id.Features.IndexB = clipPoints2[i1].ID.Features.IndexA;
|
|||
|
}
|
|||
|
|
|||
|
manifold.Points[pointCount] = cp;
|
|||
|
|
|||
|
++pointCount;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
manifold.PointCount = pointCount;
|
|||
|
|
|||
|
//Collide(ref manifold); inline end
|
|||
|
}
|
|||
|
|
|||
|
private static EPAxis ComputeEdgeSeparation()
|
|||
|
{
|
|||
|
// EdgeA separation
|
|||
|
EPAxis bestAxis;
|
|||
|
bestAxis.Type = EPAxisType.Unknown;
|
|||
|
bestAxis.Index = -1;
|
|||
|
bestAxis.Separation = -Settings.MaxFloat;
|
|||
|
_tmpNormals[0] = _edgeA.Normal;
|
|||
|
_tmpNormals[1] = -_edgeA.Normal;
|
|||
|
|
|||
|
for (int i = 0; i < 2; ++i)
|
|||
|
{
|
|||
|
Vector2 n = _tmpNormals[i];
|
|||
|
|
|||
|
// Adjacency
|
|||
|
bool valid1 = MathUtils.Cross(n, _limit11) >= -Settings.AngularSlop &&
|
|||
|
MathUtils.Cross(_limit12, n) >= -Settings.AngularSlop;
|
|||
|
bool valid2 = MathUtils.Cross(n, _limit21) >= -Settings.AngularSlop &&
|
|||
|
MathUtils.Cross(_limit22, n) >= -Settings.AngularSlop;
|
|||
|
|
|||
|
if (valid1 == false || valid2 == false)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
EPAxis axis;
|
|||
|
axis.Type = EPAxisType.EdgeA;
|
|||
|
axis.Index = i;
|
|||
|
axis.Separation = Settings.MaxFloat;
|
|||
|
|
|||
|
for (int j = 0; j < _proxyB.Count; ++j)
|
|||
|
{
|
|||
|
float s = Vector2.Dot(n, _proxyB.Vertices[j] - _edgeA.V1);
|
|||
|
if (s < axis.Separation)
|
|||
|
{
|
|||
|
axis.Separation = s;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (axis.Separation > _radius)
|
|||
|
{
|
|||
|
return axis;
|
|||
|
}
|
|||
|
|
|||
|
if (axis.Separation > bestAxis.Separation)
|
|||
|
{
|
|||
|
bestAxis = axis;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return bestAxis;
|
|||
|
}
|
|||
|
|
|||
|
private static EPAxis ComputePolygonSeparation()
|
|||
|
{
|
|||
|
EPAxis axis;
|
|||
|
axis.Type = EPAxisType.Unknown;
|
|||
|
axis.Index = -1;
|
|||
|
axis.Separation = -Settings.MaxFloat;
|
|||
|
for (int i = 0; i < _proxyB.Count; ++i)
|
|||
|
{
|
|||
|
Vector2 n = -_proxyB.Normals[i];
|
|||
|
|
|||
|
// Adjacency
|
|||
|
bool valid1 = MathUtils.Cross(n, _limit11) >= -Settings.AngularSlop &&
|
|||
|
MathUtils.Cross(_limit12, n) >= -Settings.AngularSlop;
|
|||
|
bool valid2 = MathUtils.Cross(n, _limit21) >= -Settings.AngularSlop &&
|
|||
|
MathUtils.Cross(_limit22, n) >= -Settings.AngularSlop;
|
|||
|
|
|||
|
if (valid1 == false && valid2 == false)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
float s1 = Vector2.Dot(n, _proxyB.Vertices[i] - _edgeA.V1);
|
|||
|
float s2 = Vector2.Dot(n, _proxyB.Vertices[i] - _edgeA.V2);
|
|||
|
float s = Math.Min(s1, s2);
|
|||
|
|
|||
|
if (s > _radius)
|
|||
|
{
|
|||
|
axis.Type = EPAxisType.EdgeB;
|
|||
|
axis.Index = i;
|
|||
|
axis.Separation = s;
|
|||
|
}
|
|||
|
|
|||
|
if (s > axis.Separation)
|
|||
|
{
|
|||
|
axis.Type = EPAxisType.EdgeB;
|
|||
|
axis.Index = i;
|
|||
|
axis.Separation = s;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return axis;
|
|||
|
}
|
|||
|
|
|||
|
private static void FindIncidentEdge(ref FixedArray2<ClipVertex> c, EPProxy proxy1, int edge1, EPProxy proxy2)
|
|||
|
{
|
|||
|
int count2 = proxy2.Count;
|
|||
|
|
|||
|
Debug.Assert(0 <= edge1 && edge1 < proxy1.Count);
|
|||
|
|
|||
|
// Get the normal of the reference edge in proxy2's frame.
|
|||
|
Vector2 normal1 = proxy1.Normals[edge1];
|
|||
|
|
|||
|
// Find the incident edge on proxy2.
|
|||
|
int index = 0;
|
|||
|
float minDot = float.MaxValue;
|
|||
|
for (int i = 0; i < count2; ++i)
|
|||
|
{
|
|||
|
float dot = Vector2.Dot(normal1, proxy2.Normals[i]);
|
|||
|
if (dot < minDot)
|
|||
|
{
|
|||
|
minDot = dot;
|
|||
|
index = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Build the clip vertices for the incident edge.
|
|||
|
int i1 = index;
|
|||
|
int i2 = i1 + 1 < count2 ? i1 + 1 : 0;
|
|||
|
|
|||
|
ClipVertex cTemp = new ClipVertex();
|
|||
|
cTemp.V = proxy2.Vertices[i1];
|
|||
|
cTemp.ID.Features.IndexA = (byte)edge1;
|
|||
|
cTemp.ID.Features.IndexB = (byte)i1;
|
|||
|
cTemp.ID.Features.TypeA = (byte)ContactFeatureType.Face;
|
|||
|
cTemp.ID.Features.TypeB = (byte)ContactFeatureType.Vertex;
|
|||
|
c[0] = cTemp;
|
|||
|
|
|||
|
cTemp.V = proxy2.Vertices[i2];
|
|||
|
cTemp.ID.Features.IndexA = (byte)edge1;
|
|||
|
cTemp.ID.Features.IndexB = (byte)i2;
|
|||
|
cTemp.ID.Features.TypeA = (byte)ContactFeatureType.Face;
|
|||
|
cTemp.ID.Features.TypeB = (byte)ContactFeatureType.Vertex;
|
|||
|
c[1] = cTemp;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Clipping for contact manifolds.
|
|||
|
/// </summary>
|
|||
|
/// <param name="vOut">The v out.</param>
|
|||
|
/// <param name="vIn">The v in.</param>
|
|||
|
/// <param name="normal">The normal.</param>
|
|||
|
/// <param name="offset">The offset.</param>
|
|||
|
/// <param name="vertexIndexA">The vertex index A.</param>
|
|||
|
/// <returns></returns>
|
|||
|
private static int ClipSegmentToLine(out FixedArray2<ClipVertex> vOut, ref FixedArray2<ClipVertex> vIn,
|
|||
|
Vector2 normal, float offset, int vertexIndexA)
|
|||
|
{
|
|||
|
vOut = new FixedArray2<ClipVertex>();
|
|||
|
|
|||
|
ClipVertex v0 = vIn[0];
|
|||
|
ClipVertex v1 = vIn[1];
|
|||
|
|
|||
|
// Start with no output points
|
|||
|
int numOut = 0;
|
|||
|
|
|||
|
// Calculate the distance of end points to the line
|
|||
|
float distance0 = normal.X * v0.V.X + normal.Y * v0.V.Y - offset;
|
|||
|
float distance1 = normal.X * v1.V.X + normal.Y * v1.V.Y - offset;
|
|||
|
|
|||
|
// If the points are behind the plane
|
|||
|
if (distance0 <= 0.0f) vOut[numOut++] = v0;
|
|||
|
if (distance1 <= 0.0f) vOut[numOut++] = v1;
|
|||
|
|
|||
|
// If the points are on different sides of the plane
|
|||
|
if (distance0 * distance1 < 0.0f)
|
|||
|
{
|
|||
|
// Find intersection point of edge and plane
|
|||
|
float interp = distance0 / (distance0 - distance1);
|
|||
|
|
|||
|
ClipVertex cv = vOut[numOut];
|
|||
|
|
|||
|
cv.V.X = v0.V.X + interp * (v1.V.X - v0.V.X);
|
|||
|
cv.V.Y = v0.V.Y + interp * (v1.V.Y - v0.V.Y);
|
|||
|
|
|||
|
// VertexA is hitting edgeB.
|
|||
|
cv.ID.Features.IndexA = (byte)vertexIndexA;
|
|||
|
cv.ID.Features.IndexB = v0.ID.Features.IndexB;
|
|||
|
cv.ID.Features.TypeA = (byte)ContactFeatureType.Vertex;
|
|||
|
cv.ID.Features.TypeB = (byte)ContactFeatureType.Face;
|
|||
|
|
|||
|
vOut[numOut] = cv;
|
|||
|
|
|||
|
++numOut;
|
|||
|
}
|
|||
|
|
|||
|
return numOut;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Find the separation between poly1 and poly2 for a give edge normal on poly1.
|
|||
|
/// </summary>
|
|||
|
/// <param name="poly1">The poly1.</param>
|
|||
|
/// <param name="xf1">The XF1.</param>
|
|||
|
/// <param name="edge1">The edge1.</param>
|
|||
|
/// <param name="poly2">The poly2.</param>
|
|||
|
/// <param name="xf2">The XF2.</param>
|
|||
|
/// <returns></returns>
|
|||
|
private static float EdgeSeparation(PolygonShape poly1, ref Transform xf1, int edge1,
|
|||
|
PolygonShape poly2, ref Transform xf2)
|
|||
|
{
|
|||
|
int count2 = poly2.Vertices.Count;
|
|||
|
|
|||
|
Debug.Assert(0 <= edge1 && edge1 < poly1.Vertices.Count);
|
|||
|
|
|||
|
// Convert normal from poly1's frame into poly2's frame.
|
|||
|
Vector2 p1n = poly1.Normals[edge1];
|
|||
|
|
|||
|
float normalWorldx = xf1.R.Col1.X * p1n.X + xf1.R.Col2.X * p1n.Y;
|
|||
|
float normalWorldy = xf1.R.Col1.Y * p1n.X + xf1.R.Col2.Y * p1n.Y;
|
|||
|
|
|||
|
Vector2 normal = new Vector2(normalWorldx * xf2.R.Col1.X + normalWorldy * xf2.R.Col1.Y,
|
|||
|
normalWorldx * xf2.R.Col2.X + normalWorldy * xf2.R.Col2.Y);
|
|||
|
|
|||
|
// Find support vertex on poly2 for -normal.
|
|||
|
int index = 0;
|
|||
|
float minDot = Settings.MaxFloat;
|
|||
|
|
|||
|
for (int i = 0; i < count2; ++i)
|
|||
|
{
|
|||
|
float dot = Vector2.Dot(poly2.Vertices[i], normal);
|
|||
|
|
|||
|
if (dot < minDot)
|
|||
|
{
|
|||
|
minDot = dot;
|
|||
|
index = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Vector2 p1ve = poly1.Vertices[edge1];
|
|||
|
Vector2 p2vi = poly2.Vertices[index];
|
|||
|
|
|||
|
return ((xf2.Position.X + xf2.R.Col1.X * p2vi.X + xf2.R.Col2.X * p2vi.Y) -
|
|||
|
(xf1.Position.X + xf1.R.Col1.X * p1ve.X + xf1.R.Col2.X * p1ve.Y)) * normalWorldx +
|
|||
|
((xf2.Position.Y + xf2.R.Col1.Y * p2vi.X + xf2.R.Col2.Y * p2vi.Y) -
|
|||
|
(xf1.Position.Y + xf1.R.Col1.Y * p1ve.X + xf1.R.Col2.Y * p1ve.Y)) * normalWorldy;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Find the max separation between poly1 and poly2 using edge normals from poly1.
|
|||
|
/// </summary>
|
|||
|
/// <param name="edgeIndex">Index of the edge.</param>
|
|||
|
/// <param name="poly1">The poly1.</param>
|
|||
|
/// <param name="xf1">The XF1.</param>
|
|||
|
/// <param name="poly2">The poly2.</param>
|
|||
|
/// <param name="xf2">The XF2.</param>
|
|||
|
/// <returns></returns>
|
|||
|
private static float FindMaxSeparation(out int edgeIndex,
|
|||
|
PolygonShape poly1, ref Transform xf1,
|
|||
|
PolygonShape poly2, ref Transform xf2)
|
|||
|
{
|
|||
|
int count1 = poly1.Vertices.Count;
|
|||
|
|
|||
|
// Vector pointing from the centroid of poly1 to the centroid of poly2.
|
|||
|
float dx = (xf2.Position.X + xf2.R.Col1.X * poly2.MassData.Centroid.X +
|
|||
|
xf2.R.Col2.X * poly2.MassData.Centroid.Y) -
|
|||
|
(xf1.Position.X + xf1.R.Col1.X * poly1.MassData.Centroid.X +
|
|||
|
xf1.R.Col2.X * poly1.MassData.Centroid.Y);
|
|||
|
float dy = (xf2.Position.Y + xf2.R.Col1.Y * poly2.MassData.Centroid.X +
|
|||
|
xf2.R.Col2.Y * poly2.MassData.Centroid.Y) -
|
|||
|
(xf1.Position.Y + xf1.R.Col1.Y * poly1.MassData.Centroid.X +
|
|||
|
xf1.R.Col2.Y * poly1.MassData.Centroid.Y);
|
|||
|
Vector2 dLocal1 = new Vector2(dx * xf1.R.Col1.X + dy * xf1.R.Col1.Y, dx * xf1.R.Col2.X + dy * xf1.R.Col2.Y);
|
|||
|
|
|||
|
// Find edge normal on poly1 that has the largest projection onto d.
|
|||
|
int edge = 0;
|
|||
|
float maxDot = -Settings.MaxFloat;
|
|||
|
for (int i = 0; i < count1; ++i)
|
|||
|
{
|
|||
|
float dot = Vector2.Dot(poly1.Normals[i], dLocal1);
|
|||
|
if (dot > maxDot)
|
|||
|
{
|
|||
|
maxDot = dot;
|
|||
|
edge = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Get the separation for the edge normal.
|
|||
|
float s = EdgeSeparation(poly1, ref xf1, edge, poly2, ref xf2);
|
|||
|
|
|||
|
// Check the separation for the previous edge normal.
|
|||
|
int prevEdge = edge - 1 >= 0 ? edge - 1 : count1 - 1;
|
|||
|
float sPrev = EdgeSeparation(poly1, ref xf1, prevEdge, poly2, ref xf2);
|
|||
|
|
|||
|
// Check the separation for the next edge normal.
|
|||
|
int nextEdge = edge + 1 < count1 ? edge + 1 : 0;
|
|||
|
float sNext = EdgeSeparation(poly1, ref xf1, nextEdge, poly2, ref xf2);
|
|||
|
|
|||
|
// Find the best edge and the search direction.
|
|||
|
int bestEdge;
|
|||
|
float bestSeparation;
|
|||
|
int increment;
|
|||
|
if (sPrev > s && sPrev > sNext)
|
|||
|
{
|
|||
|
increment = -1;
|
|||
|
bestEdge = prevEdge;
|
|||
|
bestSeparation = sPrev;
|
|||
|
}
|
|||
|
else if (sNext > s)
|
|||
|
{
|
|||
|
increment = 1;
|
|||
|
bestEdge = nextEdge;
|
|||
|
bestSeparation = sNext;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
edgeIndex = edge;
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
// Perform a local search for the best edge normal.
|
|||
|
for (; ; )
|
|||
|
{
|
|||
|
if (increment == -1)
|
|||
|
edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1;
|
|||
|
else
|
|||
|
edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0;
|
|||
|
|
|||
|
s = EdgeSeparation(poly1, ref xf1, edge, poly2, ref xf2);
|
|||
|
|
|||
|
if (s > bestSeparation)
|
|||
|
{
|
|||
|
bestEdge = edge;
|
|||
|
bestSeparation = s;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
edgeIndex = bestEdge;
|
|||
|
return bestSeparation;
|
|||
|
}
|
|||
|
|
|||
|
private static void FindIncidentEdge(out FixedArray2<ClipVertex> c,
|
|||
|
PolygonShape poly1, ref Transform xf1, int edge1,
|
|||
|
PolygonShape poly2, ref Transform xf2)
|
|||
|
{
|
|||
|
c = new FixedArray2<ClipVertex>();
|
|||
|
|
|||
|
int count2 = poly2.Vertices.Count;
|
|||
|
|
|||
|
Debug.Assert(0 <= edge1 && edge1 < poly1.Vertices.Count);
|
|||
|
|
|||
|
// Get the normal of the reference edge in poly2's frame.
|
|||
|
Vector2 v = poly1.Normals[edge1];
|
|||
|
float tmpx = xf1.R.Col1.X * v.X + xf1.R.Col2.X * v.Y;
|
|||
|
float tmpy = xf1.R.Col1.Y * v.X + xf1.R.Col2.Y * v.Y;
|
|||
|
Vector2 normal1 = new Vector2(tmpx * xf2.R.Col1.X + tmpy * xf2.R.Col1.Y,
|
|||
|
tmpx * xf2.R.Col2.X + tmpy * xf2.R.Col2.Y);
|
|||
|
|
|||
|
// Find the incident edge on poly2.
|
|||
|
int index = 0;
|
|||
|
float minDot = Settings.MaxFloat;
|
|||
|
for (int i = 0; i < count2; ++i)
|
|||
|
{
|
|||
|
float dot = Vector2.Dot(normal1, poly2.Normals[i]);
|
|||
|
if (dot < minDot)
|
|||
|
{
|
|||
|
minDot = dot;
|
|||
|
index = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Build the clip vertices for the incident edge.
|
|||
|
int i1 = index;
|
|||
|
int i2 = i1 + 1 < count2 ? i1 + 1 : 0;
|
|||
|
|
|||
|
ClipVertex cv0 = c[0];
|
|||
|
|
|||
|
Vector2 v1 = poly2.Vertices[i1];
|
|||
|
cv0.V.X = xf2.Position.X + xf2.R.Col1.X * v1.X + xf2.R.Col2.X * v1.Y;
|
|||
|
cv0.V.Y = xf2.Position.Y + xf2.R.Col1.Y * v1.X + xf2.R.Col2.Y * v1.Y;
|
|||
|
cv0.ID.Features.IndexA = (byte)edge1;
|
|||
|
cv0.ID.Features.IndexB = (byte)i1;
|
|||
|
cv0.ID.Features.TypeA = (byte)ContactFeatureType.Face;
|
|||
|
cv0.ID.Features.TypeB = (byte)ContactFeatureType.Vertex;
|
|||
|
|
|||
|
c[0] = cv0;
|
|||
|
|
|||
|
ClipVertex cv1 = c[1];
|
|||
|
Vector2 v2 = poly2.Vertices[i2];
|
|||
|
cv1.V.X = xf2.Position.X + xf2.R.Col1.X * v2.X + xf2.R.Col2.X * v2.Y;
|
|||
|
cv1.V.Y = xf2.Position.Y + xf2.R.Col1.Y * v2.X + xf2.R.Col2.Y * v2.Y;
|
|||
|
cv1.ID.Features.IndexA = (byte)edge1;
|
|||
|
cv1.ID.Features.IndexB = (byte)i2;
|
|||
|
cv1.ID.Features.TypeA = (byte)ContactFeatureType.Face;
|
|||
|
cv1.ID.Features.TypeB = (byte)ContactFeatureType.Vertex;
|
|||
|
|
|||
|
c[1] = cv1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|