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 | |