Home | History | Annotate | Download | only in Collision
      1 /*
      2 * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
      3 *
      4 * This software is provided 'as-is', without any express or implied
      5 * warranty.  In no event will the authors be held liable for any damages
      6 * arising from the use of this software.
      7 * Permission is granted to anyone to use this software for any purpose,
      8 * including commercial applications, and to alter it and redistribute it
      9 * freely, subject to the following restrictions:
     10 * 1. The origin of this software must not be misrepresented; you must not
     11 * claim that you wrote the original software. If you use this software
     12 * in a product, an acknowledgment in the product documentation would be
     13 * appreciated but is not required.
     14 * 2. Altered source versions must be plainly marked as such, and must not be
     15 * misrepresented as being the original software.
     16 * 3. This notice may not be removed or altered from any source distribution.
     17 */
     18 
     19 #include <Box2D/Collision/b2Collision.h>
     20 #include <Box2D/Collision/Shapes/b2PolygonShape.h>
     21 
     22 // Find the max separation between poly1 and poly2 using edge normals from poly1.
     23 static float32 b2FindMaxSeparation(int32* edgeIndex,
     24 								 const b2PolygonShape* poly1, const b2Transform& xf1,
     25 								 const b2PolygonShape* poly2, const b2Transform& xf2)
     26 {
     27 	int32 count1 = poly1->m_count;
     28 	int32 count2 = poly2->m_count;
     29 	const b2Vec2* n1s = poly1->m_normals;
     30 	const b2Vec2* v1s = poly1->m_vertices;
     31 	const b2Vec2* v2s = poly2->m_vertices;
     32 	b2Transform xf = b2MulT(xf2, xf1);
     33 
     34 	int32 bestIndex = 0;
     35 	float32 maxSeparation = -b2_maxFloat;
     36 	for (int32 i = 0; i < count1; ++i)
     37 		{
     38 		// Get poly1 normal in frame2.
     39 		b2Vec2 n = b2Mul(xf.q, n1s[i]);
     40 		b2Vec2 v1 = b2Mul(xf, v1s[i]);
     41 
     42 		// Find deepest point for normal i.
     43 		float32 si = b2_maxFloat;
     44 		for (int32 j = 0; j < count2; ++j)
     45 	{
     46 			float32 sij = b2Dot(n, v2s[j] - v1);
     47 			if (sij < si)
     48 	{
     49 				si = sij;
     50 	}
     51 	}
     52 
     53 		if (si > maxSeparation)
     54 		{
     55 			maxSeparation = si;
     56 			bestIndex = i;
     57 		}
     58 	}
     59 
     60 	*edgeIndex = bestIndex;
     61 	return maxSeparation;
     62 }
     63 
     64 static void b2FindIncidentEdge(b2ClipVertex c[2],
     65 							 const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1,
     66 							 const b2PolygonShape* poly2, const b2Transform& xf2)
     67 {
     68 	const b2Vec2* normals1 = poly1->m_normals;
     69 
     70 	int32 count2 = poly2->m_count;
     71 	const b2Vec2* vertices2 = poly2->m_vertices;
     72 	const b2Vec2* normals2 = poly2->m_normals;
     73 
     74 	b2Assert(0 <= edge1 && edge1 < poly1->m_count);
     75 
     76 	// Get the normal of the reference edge in poly2's frame.
     77 	b2Vec2 normal1 = b2MulT(xf2.q, b2Mul(xf1.q, normals1[edge1]));
     78 
     79 	// Find the incident edge on poly2.
     80 	int32 index = 0;
     81 	float32 minDot = b2_maxFloat;
     82 	for (int32 i = 0; i < count2; ++i)
     83 	{
     84 		float32 dot = b2Dot(normal1, normals2[i]);
     85 		if (dot < minDot)
     86 		{
     87 			minDot = dot;
     88 			index = i;
     89 		}
     90 	}
     91 
     92 	// Build the clip vertices for the incident edge.
     93 	int32 i1 = index;
     94 	int32 i2 = i1 + 1 < count2 ? i1 + 1 : 0;
     95 
     96 	c[0].v = b2Mul(xf2, vertices2[i1]);
     97 	c[0].id.cf.indexA = (uint8)edge1;
     98 	c[0].id.cf.indexB = (uint8)i1;
     99 	c[0].id.cf.typeA = b2ContactFeature::e_face;
    100 	c[0].id.cf.typeB = b2ContactFeature::e_vertex;
    101 
    102 	c[1].v = b2Mul(xf2, vertices2[i2]);
    103 	c[1].id.cf.indexA = (uint8)edge1;
    104 	c[1].id.cf.indexB = (uint8)i2;
    105 	c[1].id.cf.typeA = b2ContactFeature::e_face;
    106 	c[1].id.cf.typeB = b2ContactFeature::e_vertex;
    107 }
    108 
    109 // Find edge normal of max separation on A - return if separating axis is found
    110 // Find edge normal of max separation on B - return if separation axis is found
    111 // Choose reference edge as min(minA, minB)
    112 // Find incident edge
    113 // Clip
    114 
    115 // The normal points from 1 to 2
    116 void b2CollidePolygons(b2Manifold* manifold,
    117 					  const b2PolygonShape* polyA, const b2Transform& xfA,
    118 					  const b2PolygonShape* polyB, const b2Transform& xfB)
    119 {
    120 	manifold->pointCount = 0;
    121 	float32 totalRadius = polyA->m_radius + polyB->m_radius;
    122 
    123 	int32 edgeA = 0;
    124 	float32 separationA = b2FindMaxSeparation(&edgeA, polyA, xfA, polyB, xfB);
    125 	if (separationA > totalRadius)
    126 		return;
    127 
    128 	int32 edgeB = 0;
    129 	float32 separationB = b2FindMaxSeparation(&edgeB, polyB, xfB, polyA, xfA);
    130 	if (separationB > totalRadius)
    131 		return;
    132 
    133 	const b2PolygonShape* poly1;	// reference polygon
    134 	const b2PolygonShape* poly2;	// incident polygon
    135 	b2Transform xf1, xf2;
    136 	int32 edge1;					// reference edge
    137 	uint8 flip;
    138 	const float32 k_tol = 0.1f * b2_linearSlop;
    139 
    140 	if (separationB > separationA + k_tol)
    141 	{
    142 		poly1 = polyB;
    143 		poly2 = polyA;
    144 		xf1 = xfB;
    145 		xf2 = xfA;
    146 		edge1 = edgeB;
    147 		manifold->type = b2Manifold::e_faceB;
    148 		flip = 1;
    149 	}
    150 	else
    151 	{
    152 		poly1 = polyA;
    153 		poly2 = polyB;
    154 		xf1 = xfA;
    155 		xf2 = xfB;
    156 		edge1 = edgeA;
    157 		manifold->type = b2Manifold::e_faceA;
    158 		flip = 0;
    159 	}
    160 
    161 	b2ClipVertex incidentEdge[2];
    162 	b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);
    163 
    164 	int32 count1 = poly1->m_count;
    165 	const b2Vec2* vertices1 = poly1->m_vertices;
    166 
    167 	int32 iv1 = edge1;
    168 	int32 iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
    169 
    170 	b2Vec2 v11 = vertices1[iv1];
    171 	b2Vec2 v12 = vertices1[iv2];
    172 
    173 	b2Vec2 localTangent = v12 - v11;
    174 	localTangent.Normalize();
    175 
    176 	b2Vec2 localNormal = b2Cross(localTangent, 1.0f);
    177 	b2Vec2 planePoint = 0.5f * (v11 + v12);
    178 
    179 	b2Vec2 tangent = b2Mul(xf1.q, localTangent);
    180 	b2Vec2 normal = b2Cross(tangent, 1.0f);
    181 
    182 	v11 = b2Mul(xf1, v11);
    183 	v12 = b2Mul(xf1, v12);
    184 
    185 	// Face offset.
    186 	float32 frontOffset = b2Dot(normal, v11);
    187 
    188 	// Side offsets, extended by polytope skin thickness.
    189 	float32 sideOffset1 = -b2Dot(tangent, v11) + totalRadius;
    190 	float32 sideOffset2 = b2Dot(tangent, v12) + totalRadius;
    191 
    192 	// Clip incident edge against extruded edge1 side edges.
    193 	b2ClipVertex clipPoints1[2];
    194 	b2ClipVertex clipPoints2[2];
    195 	int np;
    196 
    197 	// Clip to box side 1
    198 	np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1);
    199 
    200 	if (np < 2)
    201 		return;
    202 
    203 	// Clip to negative box side 1
    204 	np = b2ClipSegmentToLine(clipPoints2, clipPoints1,  tangent, sideOffset2, iv2);
    205 
    206 	if (np < 2)
    207 	{
    208 		return;
    209 	}
    210 
    211 	// Now clipPoints2 contains the clipped points.
    212 	manifold->localNormal = localNormal;
    213 	manifold->localPoint = planePoint;
    214 
    215 	int32 pointCount = 0;
    216 	for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
    217 	{
    218 		float32 separation = b2Dot(normal, clipPoints2[i].v) - frontOffset;
    219 
    220 		if (separation <= totalRadius)
    221 		{
    222 			b2ManifoldPoint* cp = manifold->points + pointCount;
    223 			cp->localPoint = b2MulT(xf2, clipPoints2[i].v);
    224 			cp->id = clipPoints2[i].id;
    225 			if (flip)
    226 			{
    227 				// Swap features
    228 				b2ContactFeature cf = cp->id.cf;
    229 				cp->id.cf.indexA = cf.indexB;
    230 				cp->id.cf.indexB = cf.indexA;
    231 				cp->id.cf.typeA = cf.typeB;
    232 				cp->id.cf.typeB = cf.typeA;
    233 			}
    234 			++pointCount;
    235 		}
    236 	}
    237 
    238 	manifold->pointCount = pointCount;
    239 }
    240