box2d-ruby 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +336 -0
- data/examples/contact_events.rb +22 -0
- data/examples/debug_draw.rb +15 -0
- data/examples/falling_ball.rb +19 -0
- data/examples/rugl_debug_draw.rb +68 -0
- data/examples/stagecraft_debug_draw.rb +64 -0
- data/examples/support/box2d_debug_lines.rb +125 -0
- data/ext/box2d/CMakeLists.txt +35 -0
- data/ext/box2d/extconf.rb +42 -0
- data/ext/box2d/native.c +9 -0
- data/ext/box2d/vendor/box2d/LICENSE +21 -0
- data/ext/box2d/vendor/box2d/VERSION +1 -0
- data/ext/box2d/vendor/box2d/include/box2d/base.h +131 -0
- data/ext/box2d/vendor/box2d/include/box2d/box2d.h +1222 -0
- data/ext/box2d/vendor/box2d/include/box2d/collision.h +830 -0
- data/ext/box2d/vendor/box2d/include/box2d/id.h +144 -0
- data/ext/box2d/vendor/box2d/include/box2d/math_functions.h +761 -0
- data/ext/box2d/vendor/box2d/include/box2d/types.h +1457 -0
- data/ext/box2d/vendor/box2d/src/CMakeLists.txt +223 -0
- data/ext/box2d/vendor/box2d/src/aabb.c +132 -0
- data/ext/box2d/vendor/box2d/src/aabb.h +56 -0
- data/ext/box2d/vendor/box2d/src/arena_allocator.c +112 -0
- data/ext/box2d/vendor/box2d/src/arena_allocator.h +48 -0
- data/ext/box2d/vendor/box2d/src/array.c +8 -0
- data/ext/box2d/vendor/box2d/src/array.h +179 -0
- data/ext/box2d/vendor/box2d/src/atomic.h +79 -0
- data/ext/box2d/vendor/box2d/src/bitset.c +67 -0
- data/ext/box2d/vendor/box2d/src/bitset.h +65 -0
- data/ext/box2d/vendor/box2d/src/body.c +1884 -0
- data/ext/box2d/vendor/box2d/src/body.h +194 -0
- data/ext/box2d/vendor/box2d/src/box2d.natvis +41 -0
- data/ext/box2d/vendor/box2d/src/broad_phase.c +524 -0
- data/ext/box2d/vendor/box2d/src/broad_phase.h +83 -0
- data/ext/box2d/vendor/box2d/src/constants.h +54 -0
- data/ext/box2d/vendor/box2d/src/constraint_graph.c +322 -0
- data/ext/box2d/vendor/box2d/src/constraint_graph.h +58 -0
- data/ext/box2d/vendor/box2d/src/contact.c +650 -0
- data/ext/box2d/vendor/box2d/src/contact.h +148 -0
- data/ext/box2d/vendor/box2d/src/contact_solver.c +2120 -0
- data/ext/box2d/vendor/box2d/src/contact_solver.h +54 -0
- data/ext/box2d/vendor/box2d/src/core.c +178 -0
- data/ext/box2d/vendor/box2d/src/core.h +149 -0
- data/ext/box2d/vendor/box2d/src/ctz.h +112 -0
- data/ext/box2d/vendor/box2d/src/distance.c +1415 -0
- data/ext/box2d/vendor/box2d/src/distance_joint.c +556 -0
- data/ext/box2d/vendor/box2d/src/dynamic_tree.c +1989 -0
- data/ext/box2d/vendor/box2d/src/geometry.c +1028 -0
- data/ext/box2d/vendor/box2d/src/hull.c +328 -0
- data/ext/box2d/vendor/box2d/src/id_pool.c +79 -0
- data/ext/box2d/vendor/box2d/src/id_pool.h +35 -0
- data/ext/box2d/vendor/box2d/src/island.c +977 -0
- data/ext/box2d/vendor/box2d/src/island.h +89 -0
- data/ext/box2d/vendor/box2d/src/joint.c +1272 -0
- data/ext/box2d/vendor/box2d/src/joint.h +335 -0
- data/ext/box2d/vendor/box2d/src/manifold.c +1726 -0
- data/ext/box2d/vendor/box2d/src/math_functions.c +159 -0
- data/ext/box2d/vendor/box2d/src/motor_joint.c +283 -0
- data/ext/box2d/vendor/box2d/src/mouse_joint.c +214 -0
- data/ext/box2d/vendor/box2d/src/mover.c +73 -0
- data/ext/box2d/vendor/box2d/src/prismatic_joint.c +656 -0
- data/ext/box2d/vendor/box2d/src/revolute_joint.c +534 -0
- data/ext/box2d/vendor/box2d/src/sensor.c +389 -0
- data/ext/box2d/vendor/box2d/src/sensor.h +36 -0
- data/ext/box2d/vendor/box2d/src/shape.c +1714 -0
- data/ext/box2d/vendor/box2d/src/shape.h +123 -0
- data/ext/box2d/vendor/box2d/src/solver.c +2038 -0
- data/ext/box2d/vendor/box2d/src/solver.h +155 -0
- data/ext/box2d/vendor/box2d/src/solver_set.c +613 -0
- data/ext/box2d/vendor/box2d/src/solver_set.h +57 -0
- data/ext/box2d/vendor/box2d/src/table.c +238 -0
- data/ext/box2d/vendor/box2d/src/table.h +37 -0
- data/ext/box2d/vendor/box2d/src/timer.c +185 -0
- data/ext/box2d/vendor/box2d/src/types.c +151 -0
- data/ext/box2d/vendor/box2d/src/weld_joint.c +310 -0
- data/ext/box2d/vendor/box2d/src/wheel_joint.c +551 -0
- data/ext/box2d/vendor/box2d/src/world.c +3301 -0
- data/ext/box2d/vendor/box2d/src/world.h +192 -0
- data/generator/generate.rb +316 -0
- data/generator/layout_probe.c +507 -0
- data/generator/verify_layouts.rb +32 -0
- data/lib/box2d/body.rb +172 -0
- data/lib/box2d/body_definition.rb +38 -0
- data/lib/box2d/body_shapes.rb +135 -0
- data/lib/box2d/chain.rb +61 -0
- data/lib/box2d/debug_draw.rb +118 -0
- data/lib/box2d/events.rb +103 -0
- data/lib/box2d/fixed_stepper.rb +39 -0
- data/lib/box2d/handle.rb +46 -0
- data/lib/box2d/hit.rb +5 -0
- data/lib/box2d/joint.rb +197 -0
- data/lib/box2d/native.rb +1462 -0
- data/lib/box2d/native_loader.rb +45 -0
- data/lib/box2d/pixel_scale.rb +29 -0
- data/lib/box2d/shape.rb +109 -0
- data/lib/box2d/shape_definition.rb +44 -0
- data/lib/box2d/value_conversion.rb +80 -0
- data/lib/box2d/version.rb +7 -0
- data/lib/box2d/world.rb +135 -0
- data/lib/box2d/world_joints.rb +156 -0
- data/lib/box2d/world_queries.rb +122 -0
- data/lib/box2d/world_registry.rb +95 -0
- data/lib/box2d.rb +31 -0
- data/script/build_platform_gem.rb +24 -0
- data/script/deterministic_scene.rb +56 -0
- data/script/update_deterministic_snapshot.rb +11 -0
- data/script/verify_platform_gem.rb +24 -0
- metadata +164 -0
|
@@ -0,0 +1,1726 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2023 Erin Catto
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
#include "constants.h"
|
|
5
|
+
#include "core.h"
|
|
6
|
+
|
|
7
|
+
#include "box2d/collision.h"
|
|
8
|
+
#include "box2d/math_functions.h"
|
|
9
|
+
|
|
10
|
+
#include <float.h>
|
|
11
|
+
#include <stddef.h>
|
|
12
|
+
#include <stdlib.h>
|
|
13
|
+
|
|
14
|
+
#define B2_MAKE_ID( A, B ) ( (uint8_t)( A ) << 8 | (uint8_t)( B ) )
|
|
15
|
+
|
|
16
|
+
static b2Polygon b2MakeCapsule( b2Vec2 p1, b2Vec2 p2, float radius )
|
|
17
|
+
{
|
|
18
|
+
b2Polygon shape = { 0 };
|
|
19
|
+
shape.vertices[0] = p1;
|
|
20
|
+
shape.vertices[1] = p2;
|
|
21
|
+
shape.centroid = b2Lerp( p1, p2, 0.5f );
|
|
22
|
+
|
|
23
|
+
b2Vec2 d = b2Sub( p2, p1 );
|
|
24
|
+
B2_ASSERT( b2LengthSquared( d ) > FLT_EPSILON );
|
|
25
|
+
b2Vec2 axis = b2Normalize( d );
|
|
26
|
+
b2Vec2 normal = b2RightPerp( axis );
|
|
27
|
+
|
|
28
|
+
shape.normals[0] = normal;
|
|
29
|
+
shape.normals[1] = b2Neg( normal );
|
|
30
|
+
shape.count = 2;
|
|
31
|
+
shape.radius = radius;
|
|
32
|
+
|
|
33
|
+
return shape;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// point = qA * localAnchorA + pA
|
|
37
|
+
// localAnchorB = qBc * (point - pB)
|
|
38
|
+
// anchorB = point - pB = qA * localAnchorA + pA - pB
|
|
39
|
+
// = anchorA + (pA - pB)
|
|
40
|
+
b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
|
|
41
|
+
{
|
|
42
|
+
b2Manifold manifold = { 0 };
|
|
43
|
+
|
|
44
|
+
b2Transform xf = b2InvMulTransforms( xfA, xfB );
|
|
45
|
+
|
|
46
|
+
b2Vec2 pointA = circleA->center;
|
|
47
|
+
b2Vec2 pointB = b2TransformPoint( xf, circleB->center );
|
|
48
|
+
|
|
49
|
+
float distance;
|
|
50
|
+
b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pointB, pointA ) );
|
|
51
|
+
|
|
52
|
+
float radiusA = circleA->radius;
|
|
53
|
+
float radiusB = circleB->radius;
|
|
54
|
+
|
|
55
|
+
float separation = distance - radiusA - radiusB;
|
|
56
|
+
if ( separation > B2_SPECULATIVE_DISTANCE )
|
|
57
|
+
{
|
|
58
|
+
return manifold;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
b2Vec2 cA = b2MulAdd( pointA, radiusA, normal );
|
|
62
|
+
b2Vec2 cB = b2MulAdd( pointB, -radiusB, normal );
|
|
63
|
+
b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
|
|
64
|
+
|
|
65
|
+
manifold.normal = b2RotateVector( xfA.q, normal );
|
|
66
|
+
b2ManifoldPoint* mp = manifold.points + 0;
|
|
67
|
+
mp->anchorA = b2RotateVector( xfA.q, contactPointA );
|
|
68
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
69
|
+
mp->point = b2Add( mp->anchorA, xfA.p );
|
|
70
|
+
mp->separation = separation;
|
|
71
|
+
mp->id = 0;
|
|
72
|
+
manifold.pointCount = 1;
|
|
73
|
+
return manifold;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// Compute the collision manifold between a capsule and circle
|
|
77
|
+
b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
|
|
78
|
+
{
|
|
79
|
+
b2Manifold manifold = { 0 };
|
|
80
|
+
|
|
81
|
+
b2Transform xf = b2InvMulTransforms( xfA, xfB );
|
|
82
|
+
|
|
83
|
+
// Compute circle position in the frame of the capsule.
|
|
84
|
+
b2Vec2 pB = b2TransformPoint( xf, circleB->center );
|
|
85
|
+
|
|
86
|
+
// Compute closest point
|
|
87
|
+
b2Vec2 p1 = capsuleA->center1;
|
|
88
|
+
b2Vec2 p2 = capsuleA->center2;
|
|
89
|
+
|
|
90
|
+
b2Vec2 e = b2Sub( p2, p1 );
|
|
91
|
+
|
|
92
|
+
// dot(p - pA, e) = 0
|
|
93
|
+
// dot(p - (p1 + s1 * e), e) = 0
|
|
94
|
+
// s1 = dot(p - p1, e)
|
|
95
|
+
b2Vec2 pA;
|
|
96
|
+
float s1 = b2Dot( b2Sub( pB, p1 ), e );
|
|
97
|
+
float s2 = b2Dot( b2Sub( p2, pB ), e );
|
|
98
|
+
if ( s1 < 0.0f )
|
|
99
|
+
{
|
|
100
|
+
// p1 region
|
|
101
|
+
pA = p1;
|
|
102
|
+
}
|
|
103
|
+
else if ( s2 < 0.0f )
|
|
104
|
+
{
|
|
105
|
+
// p2 region
|
|
106
|
+
pA = p2;
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
{
|
|
110
|
+
// circle colliding with segment interior
|
|
111
|
+
float s = s1 / b2Dot( e, e );
|
|
112
|
+
pA = b2MulAdd( p1, s, e );
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
float distance;
|
|
116
|
+
b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) );
|
|
117
|
+
|
|
118
|
+
float radiusA = capsuleA->radius;
|
|
119
|
+
float radiusB = circleB->radius;
|
|
120
|
+
float separation = distance - radiusA - radiusB;
|
|
121
|
+
if ( separation > B2_SPECULATIVE_DISTANCE )
|
|
122
|
+
{
|
|
123
|
+
return manifold;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
b2Vec2 cA = b2MulAdd( pA, radiusA, normal );
|
|
127
|
+
b2Vec2 cB = b2MulAdd( pB, -radiusB, normal );
|
|
128
|
+
b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
|
|
129
|
+
|
|
130
|
+
manifold.normal = b2RotateVector( xfA.q, normal );
|
|
131
|
+
b2ManifoldPoint* mp = manifold.points + 0;
|
|
132
|
+
mp->anchorA = b2RotateVector( xfA.q, contactPointA );
|
|
133
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
134
|
+
mp->point = b2Add( xfA.p, mp->anchorA );
|
|
135
|
+
mp->separation = separation;
|
|
136
|
+
mp->id = 0;
|
|
137
|
+
manifold.pointCount = 1;
|
|
138
|
+
return manifold;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
|
|
142
|
+
{
|
|
143
|
+
b2Manifold manifold = { 0 };
|
|
144
|
+
const float speculativeDistance = B2_SPECULATIVE_DISTANCE;
|
|
145
|
+
|
|
146
|
+
b2Transform xf = b2InvMulTransforms( xfA, xfB );
|
|
147
|
+
|
|
148
|
+
// Compute circle position in the frame of the polygon.
|
|
149
|
+
b2Vec2 center = b2TransformPoint( xf, circleB->center );
|
|
150
|
+
float radiusA = polygonA->radius;
|
|
151
|
+
float radiusB = circleB->radius;
|
|
152
|
+
float radius = radiusA + radiusB;
|
|
153
|
+
|
|
154
|
+
// Find the min separating edge.
|
|
155
|
+
int normalIndex = 0;
|
|
156
|
+
float separation = -FLT_MAX;
|
|
157
|
+
int vertexCount = polygonA->count;
|
|
158
|
+
const b2Vec2* vertices = polygonA->vertices;
|
|
159
|
+
const b2Vec2* normals = polygonA->normals;
|
|
160
|
+
|
|
161
|
+
for ( int i = 0; i < vertexCount; ++i )
|
|
162
|
+
{
|
|
163
|
+
float s = b2Dot( normals[i], b2Sub( center, vertices[i] ) );
|
|
164
|
+
if ( s > separation )
|
|
165
|
+
{
|
|
166
|
+
separation = s;
|
|
167
|
+
normalIndex = i;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if ( separation > radius + speculativeDistance )
|
|
172
|
+
{
|
|
173
|
+
return manifold;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Vertices of the reference edge.
|
|
177
|
+
int vertIndex1 = normalIndex;
|
|
178
|
+
int vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
|
|
179
|
+
b2Vec2 v1 = vertices[vertIndex1];
|
|
180
|
+
b2Vec2 v2 = vertices[vertIndex2];
|
|
181
|
+
|
|
182
|
+
// Compute barycentric coordinates
|
|
183
|
+
float u1 = b2Dot( b2Sub( center, v1 ), b2Sub( v2, v1 ) );
|
|
184
|
+
float u2 = b2Dot( b2Sub( center, v2 ), b2Sub( v1, v2 ) );
|
|
185
|
+
|
|
186
|
+
if ( u1 < 0.0f && separation > FLT_EPSILON )
|
|
187
|
+
{
|
|
188
|
+
// Circle center is closest to v1 and safely outside the polygon
|
|
189
|
+
b2Vec2 normal = b2Normalize( b2Sub( center, v1 ) );
|
|
190
|
+
separation = b2Dot( b2Sub( center, v1 ), normal );
|
|
191
|
+
if ( separation > radius + speculativeDistance )
|
|
192
|
+
{
|
|
193
|
+
return manifold;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
b2Vec2 cA = b2MulAdd( v1, radiusA, normal );
|
|
197
|
+
b2Vec2 cB = b2MulSub( center, radiusB, normal );
|
|
198
|
+
b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
|
|
199
|
+
|
|
200
|
+
manifold.normal = b2RotateVector( xfA.q, normal );
|
|
201
|
+
b2ManifoldPoint* mp = manifold.points + 0;
|
|
202
|
+
mp->anchorA = b2RotateVector( xfA.q, contactPointA );
|
|
203
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
204
|
+
mp->point = b2Add( xfA.p, mp->anchorA );
|
|
205
|
+
mp->separation = b2Dot( b2Sub( cB, cA ), normal );
|
|
206
|
+
mp->id = 0;
|
|
207
|
+
manifold.pointCount = 1;
|
|
208
|
+
}
|
|
209
|
+
else if ( u2 < 0.0f && separation > FLT_EPSILON )
|
|
210
|
+
{
|
|
211
|
+
// Circle center is closest to v2 and safely outside the polygon
|
|
212
|
+
b2Vec2 normal = b2Normalize( b2Sub( center, v2 ) );
|
|
213
|
+
separation = b2Dot( b2Sub( center, v2 ), normal );
|
|
214
|
+
if ( separation > radius + speculativeDistance )
|
|
215
|
+
{
|
|
216
|
+
return manifold;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
b2Vec2 cA = b2MulAdd( v2, radiusA, normal );
|
|
220
|
+
b2Vec2 cB = b2MulSub( center, radiusB, normal );
|
|
221
|
+
b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
|
|
222
|
+
|
|
223
|
+
manifold.normal = b2RotateVector( xfA.q, normal );
|
|
224
|
+
b2ManifoldPoint* mp = manifold.points + 0;
|
|
225
|
+
mp->anchorA = b2RotateVector( xfA.q, contactPointA );
|
|
226
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
227
|
+
mp->point = b2Add( xfA.p, mp->anchorA );
|
|
228
|
+
mp->separation = b2Dot( b2Sub( cB, cA ), normal );
|
|
229
|
+
mp->id = 0;
|
|
230
|
+
manifold.pointCount = 1;
|
|
231
|
+
}
|
|
232
|
+
else
|
|
233
|
+
{
|
|
234
|
+
// Circle center is between v1 and v2. Center may be inside polygon
|
|
235
|
+
b2Vec2 normal = normals[normalIndex];
|
|
236
|
+
manifold.normal = b2RotateVector( xfA.q, normal );
|
|
237
|
+
|
|
238
|
+
// cA is the projection of the circle center onto to the reference edge
|
|
239
|
+
b2Vec2 cA = b2MulAdd( center, radiusA - b2Dot( b2Sub( center, v1 ), normal ), normal );
|
|
240
|
+
|
|
241
|
+
// cB is the deepest point on the circle with respect to the reference edge
|
|
242
|
+
b2Vec2 cB = b2MulSub( center, radiusB, normal );
|
|
243
|
+
|
|
244
|
+
b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
|
|
245
|
+
|
|
246
|
+
// The contact point is the midpoint in world space
|
|
247
|
+
b2ManifoldPoint* mp = manifold.points + 0;
|
|
248
|
+
mp->anchorA = b2RotateVector( xfA.q, contactPointA );
|
|
249
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
250
|
+
mp->point = b2Add( xfA.p, mp->anchorA );
|
|
251
|
+
mp->separation = separation - radius;
|
|
252
|
+
mp->id = 0;
|
|
253
|
+
manifold.pointCount = 1;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return manifold;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Follows Ericson 5.1.9 Closest Points of Two Line Segments
|
|
260
|
+
// Adds some logic to support clipping to get two contact points
|
|
261
|
+
b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )
|
|
262
|
+
{
|
|
263
|
+
b2Vec2 origin = capsuleA->center1;
|
|
264
|
+
|
|
265
|
+
// Shift polyA to origin
|
|
266
|
+
// pw = q * pb + p
|
|
267
|
+
// pw = q * (pbs + origin) + p
|
|
268
|
+
// pw = q * pbs + (p + q * origin)
|
|
269
|
+
b2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q };
|
|
270
|
+
b2Transform xf = b2InvMulTransforms( sfA, xfB );
|
|
271
|
+
|
|
272
|
+
b2Vec2 p1 = b2Vec2_zero;
|
|
273
|
+
b2Vec2 q1 = b2Sub( capsuleA->center2, origin );
|
|
274
|
+
|
|
275
|
+
b2Vec2 p2 = b2TransformPoint( xf, capsuleB->center1 );
|
|
276
|
+
b2Vec2 q2 = b2TransformPoint( xf, capsuleB->center2 );
|
|
277
|
+
|
|
278
|
+
b2Vec2 d1 = b2Sub( q1, p1 );
|
|
279
|
+
b2Vec2 d2 = b2Sub( q2, p2 );
|
|
280
|
+
|
|
281
|
+
float dd1 = b2Dot( d1, d1 );
|
|
282
|
+
float dd2 = b2Dot( d2, d2 );
|
|
283
|
+
|
|
284
|
+
const float epsSqr = FLT_EPSILON * FLT_EPSILON;
|
|
285
|
+
B2_ASSERT( dd1 > epsSqr && dd2 > epsSqr );
|
|
286
|
+
|
|
287
|
+
b2Vec2 r = b2Sub( p1, p2 );
|
|
288
|
+
float rd1 = b2Dot( r, d1 );
|
|
289
|
+
float rd2 = b2Dot( r, d2 );
|
|
290
|
+
|
|
291
|
+
float d12 = b2Dot( d1, d2 );
|
|
292
|
+
|
|
293
|
+
float denom = dd1 * dd2 - d12 * d12;
|
|
294
|
+
|
|
295
|
+
// Fraction on segment 1
|
|
296
|
+
float f1 = 0.0f;
|
|
297
|
+
if ( denom != 0.0f )
|
|
298
|
+
{
|
|
299
|
+
// not parallel
|
|
300
|
+
f1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denom, 0.0f, 1.0f );
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Compute point on segment 2 closest to p1 + f1 * d1
|
|
304
|
+
float f2 = ( d12 * f1 + rd2 ) / dd2;
|
|
305
|
+
|
|
306
|
+
// Clamping of segment 2 requires a do over on segment 1
|
|
307
|
+
if ( f2 < 0.0f )
|
|
308
|
+
{
|
|
309
|
+
f2 = 0.0f;
|
|
310
|
+
f1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );
|
|
311
|
+
}
|
|
312
|
+
else if ( f2 > 1.0f )
|
|
313
|
+
{
|
|
314
|
+
f2 = 1.0f;
|
|
315
|
+
f1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f );
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
b2Vec2 closest1 = b2MulAdd( p1, f1, d1 );
|
|
319
|
+
b2Vec2 closest2 = b2MulAdd( p2, f2, d2 );
|
|
320
|
+
float distanceSquared = b2DistanceSquared( closest1, closest2 );
|
|
321
|
+
|
|
322
|
+
b2Manifold manifold = { 0 };
|
|
323
|
+
float radiusA = capsuleA->radius;
|
|
324
|
+
float radiusB = capsuleB->radius;
|
|
325
|
+
float radius = radiusA + radiusB;
|
|
326
|
+
float maxDistance = radius + B2_SPECULATIVE_DISTANCE;
|
|
327
|
+
|
|
328
|
+
if ( distanceSquared > maxDistance * maxDistance )
|
|
329
|
+
{
|
|
330
|
+
return manifold;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
float distance = sqrtf( distanceSquared );
|
|
334
|
+
|
|
335
|
+
float length1, length2;
|
|
336
|
+
b2Vec2 u1 = b2GetLengthAndNormalize( &length1, d1 );
|
|
337
|
+
b2Vec2 u2 = b2GetLengthAndNormalize( &length2, d2 );
|
|
338
|
+
|
|
339
|
+
// Does segment B project outside segment A?
|
|
340
|
+
float fp2 = b2Dot( b2Sub( p2, p1 ), u1 );
|
|
341
|
+
float fq2 = b2Dot( b2Sub( q2, p1 ), u1 );
|
|
342
|
+
bool outsideA = ( fp2 <= 0.0f && fq2 <= 0.0f ) || ( fp2 >= length1 && fq2 >= length1 );
|
|
343
|
+
|
|
344
|
+
// Does segment A project outside segment B?
|
|
345
|
+
float fp1 = b2Dot( b2Sub( p1, p2 ), u2 );
|
|
346
|
+
float fq1 = b2Dot( b2Sub( q1, p2 ), u2 );
|
|
347
|
+
bool outsideB = ( fp1 <= 0.0f && fq1 <= 0.0f ) || ( fp1 >= length2 && fq1 >= length2 );
|
|
348
|
+
|
|
349
|
+
if ( outsideA == false && outsideB == false )
|
|
350
|
+
{
|
|
351
|
+
// attempt to clip
|
|
352
|
+
// this may yield contact points with excessive separation
|
|
353
|
+
// in that case the algorithm falls back to single point collision
|
|
354
|
+
|
|
355
|
+
// find reference edge using SAT
|
|
356
|
+
b2Vec2 normalA;
|
|
357
|
+
float separationA;
|
|
358
|
+
|
|
359
|
+
{
|
|
360
|
+
normalA = b2LeftPerp( u1 );
|
|
361
|
+
float ss1 = b2Dot( b2Sub( p2, p1 ), normalA );
|
|
362
|
+
float ss2 = b2Dot( b2Sub( q2, p1 ), normalA );
|
|
363
|
+
float s1p = ss1 < ss2 ? ss1 : ss2;
|
|
364
|
+
float s1n = -ss1 < -ss2 ? -ss1 : -ss2;
|
|
365
|
+
|
|
366
|
+
if ( s1p > s1n )
|
|
367
|
+
{
|
|
368
|
+
separationA = s1p;
|
|
369
|
+
}
|
|
370
|
+
else
|
|
371
|
+
{
|
|
372
|
+
separationA = s1n;
|
|
373
|
+
normalA = b2Neg( normalA );
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
b2Vec2 normalB;
|
|
378
|
+
float separationB;
|
|
379
|
+
{
|
|
380
|
+
normalB = b2LeftPerp( u2 );
|
|
381
|
+
float ss1 = b2Dot( b2Sub( p1, p2 ), normalB );
|
|
382
|
+
float ss2 = b2Dot( b2Sub( q1, p2 ), normalB );
|
|
383
|
+
float s1p = ss1 < ss2 ? ss1 : ss2;
|
|
384
|
+
float s1n = -ss1 < -ss2 ? -ss1 : -ss2;
|
|
385
|
+
|
|
386
|
+
if ( s1p > s1n )
|
|
387
|
+
{
|
|
388
|
+
separationB = s1p;
|
|
389
|
+
}
|
|
390
|
+
else
|
|
391
|
+
{
|
|
392
|
+
separationB = s1n;
|
|
393
|
+
normalB = b2Neg( normalB );
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// biased to avoid feature flip-flop
|
|
398
|
+
// todo more testing?
|
|
399
|
+
if ( separationA + 0.1f * B2_LINEAR_SLOP >= separationB )
|
|
400
|
+
{
|
|
401
|
+
manifold.normal = normalA;
|
|
402
|
+
|
|
403
|
+
b2Vec2 cp = p2;
|
|
404
|
+
b2Vec2 cq = q2;
|
|
405
|
+
|
|
406
|
+
// clip to p1
|
|
407
|
+
if ( fp2 < 0.0f && fq2 > 0.0f )
|
|
408
|
+
{
|
|
409
|
+
cp = b2Lerp( p2, q2, ( 0.0f - fp2 ) / ( fq2 - fp2 ) );
|
|
410
|
+
}
|
|
411
|
+
else if ( fq2 < 0.0f && fp2 > 0.0f )
|
|
412
|
+
{
|
|
413
|
+
cq = b2Lerp( q2, p2, ( 0.0f - fq2 ) / ( fp2 - fq2 ) );
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// clip to q1
|
|
417
|
+
if ( fp2 > length1 && fq2 < length1 )
|
|
418
|
+
{
|
|
419
|
+
cp = b2Lerp( p2, q2, ( fp2 - length1 ) / ( fp2 - fq2 ) );
|
|
420
|
+
}
|
|
421
|
+
else if ( fq2 > length1 && fp2 < length1 )
|
|
422
|
+
{
|
|
423
|
+
cq = b2Lerp( q2, p2, ( fq2 - length1 ) / ( fq2 - fp2 ) );
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
float sp = b2Dot( b2Sub( cp, p1 ), normalA );
|
|
427
|
+
float sq = b2Dot( b2Sub( cq, p1 ), normalA );
|
|
428
|
+
|
|
429
|
+
if ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP )
|
|
430
|
+
{
|
|
431
|
+
b2ManifoldPoint* mp;
|
|
432
|
+
mp = manifold.points + 0;
|
|
433
|
+
mp->anchorA = b2MulAdd( cp, 0.5f * ( radiusA - radiusB - sp ), normalA );
|
|
434
|
+
mp->separation = sp - radius;
|
|
435
|
+
mp->id = B2_MAKE_ID( 0, 0 );
|
|
436
|
+
|
|
437
|
+
mp = manifold.points + 1;
|
|
438
|
+
mp->anchorA = b2MulAdd( cq, 0.5f * ( radiusA - radiusB - sq ), normalA );
|
|
439
|
+
mp->separation = sq - radius;
|
|
440
|
+
mp->id = B2_MAKE_ID( 0, 1 );
|
|
441
|
+
manifold.pointCount = 2;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else
|
|
445
|
+
{
|
|
446
|
+
// normal always points from A to B
|
|
447
|
+
manifold.normal = b2Neg( normalB );
|
|
448
|
+
|
|
449
|
+
b2Vec2 cp = p1;
|
|
450
|
+
b2Vec2 cq = q1;
|
|
451
|
+
|
|
452
|
+
// clip to p2
|
|
453
|
+
if ( fp1 < 0.0f && fq1 > 0.0f )
|
|
454
|
+
{
|
|
455
|
+
cp = b2Lerp( p1, q1, ( 0.0f - fp1 ) / ( fq1 - fp1 ) );
|
|
456
|
+
}
|
|
457
|
+
else if ( fq1 < 0.0f && fp1 > 0.0f )
|
|
458
|
+
{
|
|
459
|
+
cq = b2Lerp( q1, p1, ( 0.0f - fq1 ) / ( fp1 - fq1 ) );
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// clip to q2
|
|
463
|
+
if ( fp1 > length2 && fq1 < length2 )
|
|
464
|
+
{
|
|
465
|
+
cp = b2Lerp( p1, q1, ( fp1 - length2 ) / ( fp1 - fq1 ) );
|
|
466
|
+
}
|
|
467
|
+
else if ( fq1 > length2 && fp1 < length2 )
|
|
468
|
+
{
|
|
469
|
+
cq = b2Lerp( q1, p1, ( fq1 - length2 ) / ( fq1 - fp1 ) );
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
float sp = b2Dot( b2Sub( cp, p2 ), normalB );
|
|
473
|
+
float sq = b2Dot( b2Sub( cq, p2 ), normalB );
|
|
474
|
+
|
|
475
|
+
if ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP )
|
|
476
|
+
{
|
|
477
|
+
b2ManifoldPoint* mp;
|
|
478
|
+
mp = manifold.points + 0;
|
|
479
|
+
mp->anchorA = b2MulAdd( cp, 0.5f * ( radiusB - radiusA - sp ), normalB );
|
|
480
|
+
mp->separation = sp - radius;
|
|
481
|
+
mp->id = B2_MAKE_ID( 0, 0 );
|
|
482
|
+
mp = manifold.points + 1;
|
|
483
|
+
mp->anchorA = b2MulAdd( cq, 0.5f * ( radiusB - radiusA - sq ), normalB );
|
|
484
|
+
mp->separation = sq - radius;
|
|
485
|
+
mp->id = B2_MAKE_ID( 1, 0 );
|
|
486
|
+
manifold.pointCount = 2;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if ( manifold.pointCount == 0 )
|
|
492
|
+
{
|
|
493
|
+
// single point collision
|
|
494
|
+
b2Vec2 normal = b2Sub( closest2, closest1 );
|
|
495
|
+
if ( b2Dot( normal, normal ) > epsSqr )
|
|
496
|
+
{
|
|
497
|
+
normal = b2Normalize( normal );
|
|
498
|
+
}
|
|
499
|
+
else
|
|
500
|
+
{
|
|
501
|
+
normal = b2LeftPerp( u1 );
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
b2Vec2 c1 = b2MulAdd( closest1, radiusA, normal );
|
|
505
|
+
b2Vec2 c2 = b2MulAdd( closest2, -radiusB, normal );
|
|
506
|
+
|
|
507
|
+
int i1 = f1 == 0.0f ? 0 : 1;
|
|
508
|
+
int i2 = f2 == 0.0f ? 0 : 1;
|
|
509
|
+
|
|
510
|
+
manifold.normal = normal;
|
|
511
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
512
|
+
manifold.points[0].separation = sqrtf( distanceSquared ) - radius;
|
|
513
|
+
manifold.points[0].id = B2_MAKE_ID( i1, i2 );
|
|
514
|
+
manifold.pointCount = 1;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Convert manifold to world space
|
|
518
|
+
manifold.normal = b2RotateVector( xfA.q, manifold.normal );
|
|
519
|
+
for ( int i = 0; i < manifold.pointCount; ++i )
|
|
520
|
+
{
|
|
521
|
+
b2ManifoldPoint* mp = manifold.points + i;
|
|
522
|
+
|
|
523
|
+
// anchor points relative to shape origin in world space
|
|
524
|
+
mp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) );
|
|
525
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
526
|
+
mp->point = b2Add( xfA.p, mp->anchorA );
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return manifold;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )
|
|
533
|
+
{
|
|
534
|
+
b2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f };
|
|
535
|
+
return b2CollideCapsules( &capsuleA, xfA, capsuleB, xfB );
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )
|
|
539
|
+
{
|
|
540
|
+
b2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius );
|
|
541
|
+
return b2CollidePolygons( polygonA, xfA, &polyB, xfB );
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Polygon clipper used to compute contact points when there are potentially two contact points.
|
|
545
|
+
static b2Manifold b2ClipPolygons( const b2Polygon* polyA, const b2Polygon* polyB, int edgeA, int edgeB, bool flip )
|
|
546
|
+
{
|
|
547
|
+
b2Manifold manifold = { 0 };
|
|
548
|
+
|
|
549
|
+
// reference polygon
|
|
550
|
+
const b2Polygon* poly1;
|
|
551
|
+
int i11, i12;
|
|
552
|
+
|
|
553
|
+
// incident polygon
|
|
554
|
+
const b2Polygon* poly2;
|
|
555
|
+
int i21, i22;
|
|
556
|
+
|
|
557
|
+
if ( flip )
|
|
558
|
+
{
|
|
559
|
+
poly1 = polyB;
|
|
560
|
+
poly2 = polyA;
|
|
561
|
+
i11 = edgeB;
|
|
562
|
+
i12 = edgeB + 1 < polyB->count ? edgeB + 1 : 0;
|
|
563
|
+
i21 = edgeA;
|
|
564
|
+
i22 = edgeA + 1 < polyA->count ? edgeA + 1 : 0;
|
|
565
|
+
}
|
|
566
|
+
else
|
|
567
|
+
{
|
|
568
|
+
poly1 = polyA;
|
|
569
|
+
poly2 = polyB;
|
|
570
|
+
i11 = edgeA;
|
|
571
|
+
i12 = edgeA + 1 < polyA->count ? edgeA + 1 : 0;
|
|
572
|
+
i21 = edgeB;
|
|
573
|
+
i22 = edgeB + 1 < polyB->count ? edgeB + 1 : 0;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
b2Vec2 normal = poly1->normals[i11];
|
|
577
|
+
|
|
578
|
+
// Reference edge vertices
|
|
579
|
+
b2Vec2 v11 = poly1->vertices[i11];
|
|
580
|
+
b2Vec2 v12 = poly1->vertices[i12];
|
|
581
|
+
|
|
582
|
+
// Incident edge vertices
|
|
583
|
+
b2Vec2 v21 = poly2->vertices[i21];
|
|
584
|
+
b2Vec2 v22 = poly2->vertices[i22];
|
|
585
|
+
|
|
586
|
+
b2Vec2 tangent = b2CrossSV( 1.0f, normal );
|
|
587
|
+
|
|
588
|
+
float lower1 = 0.0f;
|
|
589
|
+
float upper1 = b2Dot( b2Sub( v12, v11 ), tangent );
|
|
590
|
+
|
|
591
|
+
// Incident edge points opposite of tangent due to CCW winding
|
|
592
|
+
float upper2 = b2Dot( b2Sub( v21, v11 ), tangent );
|
|
593
|
+
float lower2 = b2Dot( b2Sub( v22, v11 ), tangent );
|
|
594
|
+
|
|
595
|
+
// Are the segments disjoint?
|
|
596
|
+
if ( upper2 < lower1 || upper1 < lower2 )
|
|
597
|
+
{
|
|
598
|
+
return manifold;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
b2Vec2 vLower;
|
|
602
|
+
if ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON )
|
|
603
|
+
{
|
|
604
|
+
vLower = b2Lerp( v22, v21, ( lower1 - lower2 ) / ( upper2 - lower2 ) );
|
|
605
|
+
}
|
|
606
|
+
else
|
|
607
|
+
{
|
|
608
|
+
vLower = v22;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
b2Vec2 vUpper;
|
|
612
|
+
if ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON )
|
|
613
|
+
{
|
|
614
|
+
vUpper = b2Lerp( v22, v21, ( upper1 - lower2 ) / ( upper2 - lower2 ) );
|
|
615
|
+
}
|
|
616
|
+
else
|
|
617
|
+
{
|
|
618
|
+
vUpper = v21;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// todo vLower can be very close to vUpper, reduce to one point?
|
|
622
|
+
|
|
623
|
+
float separationLower = b2Dot( b2Sub( vLower, v11 ), normal );
|
|
624
|
+
float separationUpper = b2Dot( b2Sub( vUpper, v11 ), normal );
|
|
625
|
+
|
|
626
|
+
float r1 = poly1->radius;
|
|
627
|
+
float r2 = poly2->radius;
|
|
628
|
+
|
|
629
|
+
// Put contact points at midpoint, accounting for radii
|
|
630
|
+
vLower = b2MulAdd( vLower, 0.5f * ( r1 - r2 - separationLower ), normal );
|
|
631
|
+
vUpper = b2MulAdd( vUpper, 0.5f * ( r1 - r2 - separationUpper ), normal );
|
|
632
|
+
|
|
633
|
+
float radius = r1 + r2;
|
|
634
|
+
|
|
635
|
+
if ( flip == false )
|
|
636
|
+
{
|
|
637
|
+
manifold.normal = normal;
|
|
638
|
+
b2ManifoldPoint* cp = manifold.points + 0;
|
|
639
|
+
|
|
640
|
+
{
|
|
641
|
+
cp->anchorA = vLower;
|
|
642
|
+
cp->separation = separationLower - radius;
|
|
643
|
+
cp->id = B2_MAKE_ID( i11, i22 );
|
|
644
|
+
manifold.pointCount += 1;
|
|
645
|
+
cp += 1;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
{
|
|
649
|
+
cp->anchorA = vUpper;
|
|
650
|
+
cp->separation = separationUpper - radius;
|
|
651
|
+
cp->id = B2_MAKE_ID( i12, i21 );
|
|
652
|
+
manifold.pointCount += 1;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
else
|
|
656
|
+
{
|
|
657
|
+
manifold.normal = b2Neg( normal );
|
|
658
|
+
b2ManifoldPoint* cp = manifold.points + 0;
|
|
659
|
+
|
|
660
|
+
{
|
|
661
|
+
cp->anchorA = vUpper;
|
|
662
|
+
cp->separation = separationUpper - radius;
|
|
663
|
+
cp->id = B2_MAKE_ID( i21, i12 );
|
|
664
|
+
manifold.pointCount += 1;
|
|
665
|
+
cp += 1;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
{
|
|
669
|
+
cp->anchorA = vLower;
|
|
670
|
+
cp->separation = separationLower - radius;
|
|
671
|
+
cp->id = B2_MAKE_ID( i22, i11 );
|
|
672
|
+
manifold.pointCount += 1;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return manifold;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Find the max separation between poly1 and poly2 using edge normals from poly1.
|
|
680
|
+
static float b2FindMaxSeparation( int* edgeIndex, const b2Polygon* poly1, const b2Polygon* poly2 )
|
|
681
|
+
{
|
|
682
|
+
int count1 = poly1->count;
|
|
683
|
+
int count2 = poly2->count;
|
|
684
|
+
const b2Vec2* n1s = poly1->normals;
|
|
685
|
+
const b2Vec2* v1s = poly1->vertices;
|
|
686
|
+
const b2Vec2* v2s = poly2->vertices;
|
|
687
|
+
|
|
688
|
+
int bestIndex = 0;
|
|
689
|
+
float maxSeparation = -FLT_MAX;
|
|
690
|
+
for ( int i = 0; i < count1; ++i )
|
|
691
|
+
{
|
|
692
|
+
// Get poly1 normal in frame2.
|
|
693
|
+
b2Vec2 n = n1s[i];
|
|
694
|
+
b2Vec2 v1 = v1s[i];
|
|
695
|
+
|
|
696
|
+
// Find the deepest point for normal i.
|
|
697
|
+
float si = FLT_MAX;
|
|
698
|
+
for ( int j = 0; j < count2; ++j )
|
|
699
|
+
{
|
|
700
|
+
float sij = b2Dot( n, b2Sub( v2s[j], v1 ) );
|
|
701
|
+
if ( sij < si )
|
|
702
|
+
{
|
|
703
|
+
si = sij;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if ( si > maxSeparation )
|
|
708
|
+
{
|
|
709
|
+
maxSeparation = si;
|
|
710
|
+
bestIndex = i;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
*edgeIndex = bestIndex;
|
|
715
|
+
return maxSeparation;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Due to speculation, every polygon is rounded
|
|
719
|
+
// Algorithm:
|
|
720
|
+
//
|
|
721
|
+
// compute edge separation using the separating axis test (SAT)
|
|
722
|
+
// if (separation > speculation_distance)
|
|
723
|
+
// return
|
|
724
|
+
// find reference and incident edge
|
|
725
|
+
// if separation >= 0.1f * B2_LINEAR_SLOP
|
|
726
|
+
// compute closest points between reference and incident edge
|
|
727
|
+
// if vertices are closest
|
|
728
|
+
// single vertex-vertex contact
|
|
729
|
+
// else
|
|
730
|
+
// clip edges
|
|
731
|
+
// end
|
|
732
|
+
// else
|
|
733
|
+
// clip edges
|
|
734
|
+
// end
|
|
735
|
+
|
|
736
|
+
b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB )
|
|
737
|
+
{
|
|
738
|
+
b2Vec2 origin = polygonA->vertices[0];
|
|
739
|
+
float linearSlop = B2_LINEAR_SLOP;
|
|
740
|
+
float speculativeDistance = B2_SPECULATIVE_DISTANCE;
|
|
741
|
+
|
|
742
|
+
// Shift polyA to origin
|
|
743
|
+
// pw = q * pb + p
|
|
744
|
+
// pw = q * (pbs + origin) + p
|
|
745
|
+
// pw = q * pbs + (p + q * origin)
|
|
746
|
+
b2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q };
|
|
747
|
+
b2Transform xf = b2InvMulTransforms( sfA, xfB );
|
|
748
|
+
|
|
749
|
+
b2Polygon localPolyA;
|
|
750
|
+
localPolyA.count = polygonA->count;
|
|
751
|
+
localPolyA.radius = polygonA->radius;
|
|
752
|
+
localPolyA.vertices[0] = b2Vec2_zero;
|
|
753
|
+
localPolyA.normals[0] = polygonA->normals[0];
|
|
754
|
+
for ( int i = 1; i < localPolyA.count; ++i )
|
|
755
|
+
{
|
|
756
|
+
localPolyA.vertices[i] = b2Sub( polygonA->vertices[i], origin );
|
|
757
|
+
localPolyA.normals[i] = polygonA->normals[i];
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Put polyB in polyA's frame to reduce round-off error
|
|
761
|
+
b2Polygon localPolyB;
|
|
762
|
+
localPolyB.count = polygonB->count;
|
|
763
|
+
localPolyB.radius = polygonB->radius;
|
|
764
|
+
for ( int i = 0; i < localPolyB.count; ++i )
|
|
765
|
+
{
|
|
766
|
+
localPolyB.vertices[i] = b2TransformPoint( xf, polygonB->vertices[i] );
|
|
767
|
+
localPolyB.normals[i] = b2RotateVector( xf.q, polygonB->normals[i] );
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
int edgeA = 0;
|
|
771
|
+
float separationA = b2FindMaxSeparation( &edgeA, &localPolyA, &localPolyB );
|
|
772
|
+
|
|
773
|
+
int edgeB = 0;
|
|
774
|
+
float separationB = b2FindMaxSeparation( &edgeB, &localPolyB, &localPolyA );
|
|
775
|
+
|
|
776
|
+
float radius = localPolyA.radius + localPolyB.radius;
|
|
777
|
+
|
|
778
|
+
if ( separationA > speculativeDistance + radius || separationB > speculativeDistance + radius )
|
|
779
|
+
{
|
|
780
|
+
return (b2Manifold){ 0 };
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Find incident edge
|
|
784
|
+
bool flip;
|
|
785
|
+
if ( separationA >= separationB )
|
|
786
|
+
{
|
|
787
|
+
flip = false;
|
|
788
|
+
|
|
789
|
+
b2Vec2 searchDirection = localPolyA.normals[edgeA];
|
|
790
|
+
|
|
791
|
+
// Find the incident edge on polyB
|
|
792
|
+
int count = localPolyB.count;
|
|
793
|
+
const b2Vec2* normals = localPolyB.normals;
|
|
794
|
+
edgeB = 0;
|
|
795
|
+
float minDot = FLT_MAX;
|
|
796
|
+
for ( int i = 0; i < count; ++i )
|
|
797
|
+
{
|
|
798
|
+
float dot = b2Dot( searchDirection, normals[i] );
|
|
799
|
+
if ( dot < minDot )
|
|
800
|
+
{
|
|
801
|
+
minDot = dot;
|
|
802
|
+
edgeB = i;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
else
|
|
807
|
+
{
|
|
808
|
+
flip = true;
|
|
809
|
+
|
|
810
|
+
b2Vec2 searchDirection = localPolyB.normals[edgeB];
|
|
811
|
+
|
|
812
|
+
// Find the incident edge on polyA
|
|
813
|
+
int count = localPolyA.count;
|
|
814
|
+
const b2Vec2* normals = localPolyA.normals;
|
|
815
|
+
edgeA = 0;
|
|
816
|
+
float minDot = FLT_MAX;
|
|
817
|
+
for ( int i = 0; i < count; ++i )
|
|
818
|
+
{
|
|
819
|
+
float dot = b2Dot( searchDirection, normals[i] );
|
|
820
|
+
if ( dot < minDot )
|
|
821
|
+
{
|
|
822
|
+
minDot = dot;
|
|
823
|
+
edgeA = i;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
b2Manifold manifold = { 0 };
|
|
829
|
+
|
|
830
|
+
// Using slop here to ensure vertex-vertex normal vectors can be safely normalized
|
|
831
|
+
// todo this means edge clipping needs to handle slightly non-overlapping edges.
|
|
832
|
+
if ( separationA > 0.1f * linearSlop || separationB > 0.1f * linearSlop )
|
|
833
|
+
{
|
|
834
|
+
#if 1
|
|
835
|
+
// Edges are disjoint. Find closest points between reference edge and incident edge
|
|
836
|
+
// Reference edge on polygon A
|
|
837
|
+
int i11 = edgeA;
|
|
838
|
+
int i12 = edgeA + 1 < localPolyA.count ? edgeA + 1 : 0;
|
|
839
|
+
int i21 = edgeB;
|
|
840
|
+
int i22 = edgeB + 1 < localPolyB.count ? edgeB + 1 : 0;
|
|
841
|
+
|
|
842
|
+
b2Vec2 v11 = localPolyA.vertices[i11];
|
|
843
|
+
b2Vec2 v12 = localPolyA.vertices[i12];
|
|
844
|
+
b2Vec2 v21 = localPolyB.vertices[i21];
|
|
845
|
+
b2Vec2 v22 = localPolyB.vertices[i22];
|
|
846
|
+
|
|
847
|
+
b2SegmentDistanceResult result = b2SegmentDistance( v11, v12, v21, v22 );
|
|
848
|
+
B2_ASSERT( result.distanceSquared > 0.0f );
|
|
849
|
+
float distance = sqrtf( result.distanceSquared );
|
|
850
|
+
float separation = distance - radius;
|
|
851
|
+
|
|
852
|
+
if ( distance - radius > speculativeDistance )
|
|
853
|
+
{
|
|
854
|
+
// This can happen in the vertex-vertex case
|
|
855
|
+
return manifold;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Attempt to clip edges
|
|
859
|
+
manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );
|
|
860
|
+
|
|
861
|
+
float minSeparation = FLT_MAX;
|
|
862
|
+
for ( int i = 0; i < manifold.pointCount; ++i )
|
|
863
|
+
{
|
|
864
|
+
minSeparation = b2MinFloat( minSeparation, manifold.points[i].separation );
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// Does vertex-vertex have substantially larger separation?
|
|
868
|
+
if ( separation + 0.1f * linearSlop < minSeparation )
|
|
869
|
+
{
|
|
870
|
+
if ( result.fraction1 == 0.0f && result.fraction2 == 0.0f )
|
|
871
|
+
{
|
|
872
|
+
// v11 - v21
|
|
873
|
+
b2Vec2 normal = b2Sub( v21, v11 );
|
|
874
|
+
float invDistance = 1.0f / distance;
|
|
875
|
+
normal.x *= invDistance;
|
|
876
|
+
normal.y *= invDistance;
|
|
877
|
+
|
|
878
|
+
b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
|
|
879
|
+
b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
|
|
880
|
+
|
|
881
|
+
manifold.normal = normal;
|
|
882
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
883
|
+
manifold.points[0].separation = distance - radius;
|
|
884
|
+
manifold.points[0].id = B2_MAKE_ID( i11, i21 );
|
|
885
|
+
manifold.pointCount = 1;
|
|
886
|
+
}
|
|
887
|
+
else if ( result.fraction1 == 0.0f && result.fraction2 == 1.0f )
|
|
888
|
+
{
|
|
889
|
+
// v11 - v22
|
|
890
|
+
b2Vec2 normal = b2Sub( v22, v11 );
|
|
891
|
+
float invDistance = 1.0f / distance;
|
|
892
|
+
normal.x *= invDistance;
|
|
893
|
+
normal.y *= invDistance;
|
|
894
|
+
|
|
895
|
+
b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
|
|
896
|
+
b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
|
|
897
|
+
|
|
898
|
+
manifold.normal = normal;
|
|
899
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
900
|
+
manifold.points[0].separation = distance - radius;
|
|
901
|
+
manifold.points[0].id = B2_MAKE_ID( i11, i22 );
|
|
902
|
+
manifold.pointCount = 1;
|
|
903
|
+
}
|
|
904
|
+
else if ( result.fraction1 == 1.0f && result.fraction2 == 0.0f )
|
|
905
|
+
{
|
|
906
|
+
// v12 - v21
|
|
907
|
+
b2Vec2 normal = b2Sub( v21, v12 );
|
|
908
|
+
float invDistance = 1.0f / distance;
|
|
909
|
+
normal.x *= invDistance;
|
|
910
|
+
normal.y *= invDistance;
|
|
911
|
+
|
|
912
|
+
b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
|
|
913
|
+
b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
|
|
914
|
+
|
|
915
|
+
manifold.normal = normal;
|
|
916
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
917
|
+
manifold.points[0].separation = distance - radius;
|
|
918
|
+
manifold.points[0].id = B2_MAKE_ID( i12, i21 );
|
|
919
|
+
manifold.pointCount = 1;
|
|
920
|
+
}
|
|
921
|
+
else if ( result.fraction1 == 1.0f && result.fraction2 == 1.0f )
|
|
922
|
+
{
|
|
923
|
+
// v12 - v22
|
|
924
|
+
b2Vec2 normal = b2Sub( v22, v12 );
|
|
925
|
+
float invDistance = 1.0f / distance;
|
|
926
|
+
normal.x *= invDistance;
|
|
927
|
+
normal.y *= invDistance;
|
|
928
|
+
|
|
929
|
+
b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
|
|
930
|
+
b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
|
|
931
|
+
|
|
932
|
+
manifold.normal = normal;
|
|
933
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
934
|
+
manifold.points[0].separation = distance - radius;
|
|
935
|
+
manifold.points[0].id = B2_MAKE_ID( i12, i22 );
|
|
936
|
+
manifold.pointCount = 1;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
#else
|
|
940
|
+
// Polygons are disjoint. Find closest points between reference edge and incident edge
|
|
941
|
+
// Reference edge on polygon A
|
|
942
|
+
int i11 = edgeA;
|
|
943
|
+
int i12 = edgeA + 1 < localPolyA.count ? edgeA + 1 : 0;
|
|
944
|
+
int i21 = edgeB;
|
|
945
|
+
int i22 = edgeB + 1 < localPolyB.count ? edgeB + 1 : 0;
|
|
946
|
+
|
|
947
|
+
b2Vec2 v11 = localPolyA.vertices[i11];
|
|
948
|
+
b2Vec2 v12 = localPolyA.vertices[i12];
|
|
949
|
+
b2Vec2 v21 = localPolyB.vertices[i21];
|
|
950
|
+
b2Vec2 v22 = localPolyB.vertices[i22];
|
|
951
|
+
|
|
952
|
+
b2SegmentDistanceResult result = b2SegmentDistance( v11, v12, v21, v22 );
|
|
953
|
+
|
|
954
|
+
if ( result.fraction1 == 0.0f && result.fraction2 == 0.0f )
|
|
955
|
+
{
|
|
956
|
+
// v11 - v21
|
|
957
|
+
b2Vec2 normal = b2Sub( v21, v11 );
|
|
958
|
+
B2_ASSERT( result.distanceSquared > 0.0f );
|
|
959
|
+
float distance = sqrtf( result.distanceSquared );
|
|
960
|
+
if ( distance > B2_SPECULATIVE_DISTANCE + radius )
|
|
961
|
+
{
|
|
962
|
+
return manifold;
|
|
963
|
+
}
|
|
964
|
+
float invDistance = 1.0f / distance;
|
|
965
|
+
normal.x *= invDistance;
|
|
966
|
+
normal.y *= invDistance;
|
|
967
|
+
|
|
968
|
+
b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
|
|
969
|
+
b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
|
|
970
|
+
|
|
971
|
+
manifold.normal = normal;
|
|
972
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
973
|
+
manifold.points[0].separation = distance - radius;
|
|
974
|
+
manifold.points[0].id = B2_MAKE_ID( i11, i21 );
|
|
975
|
+
manifold.pointCount = 1;
|
|
976
|
+
}
|
|
977
|
+
else if ( result.fraction1 == 0.0f && result.fraction2 == 1.0f )
|
|
978
|
+
{
|
|
979
|
+
// v11 - v22
|
|
980
|
+
b2Vec2 normal = b2Sub( v22, v11 );
|
|
981
|
+
B2_ASSERT( result.distanceSquared > 0.0f );
|
|
982
|
+
float distance = sqrtf( result.distanceSquared );
|
|
983
|
+
if ( distance > B2_SPECULATIVE_DISTANCE + radius )
|
|
984
|
+
{
|
|
985
|
+
return manifold;
|
|
986
|
+
}
|
|
987
|
+
float invDistance = 1.0f / distance;
|
|
988
|
+
normal.x *= invDistance;
|
|
989
|
+
normal.y *= invDistance;
|
|
990
|
+
|
|
991
|
+
b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
|
|
992
|
+
b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
|
|
993
|
+
|
|
994
|
+
manifold.normal = normal;
|
|
995
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
996
|
+
manifold.points[0].separation = distance - radius;
|
|
997
|
+
manifold.points[0].id = B2_MAKE_ID( i11, i22 );
|
|
998
|
+
manifold.pointCount = 1;
|
|
999
|
+
}
|
|
1000
|
+
else if ( result.fraction1 == 1.0f && result.fraction2 == 0.0f )
|
|
1001
|
+
{
|
|
1002
|
+
// v12 - v21
|
|
1003
|
+
b2Vec2 normal = b2Sub( v21, v12 );
|
|
1004
|
+
B2_ASSERT( result.distanceSquared > 0.0f );
|
|
1005
|
+
float distance = sqrtf( result.distanceSquared );
|
|
1006
|
+
if ( distance > B2_SPECULATIVE_DISTANCE + radius )
|
|
1007
|
+
{
|
|
1008
|
+
return manifold;
|
|
1009
|
+
}
|
|
1010
|
+
float invDistance = 1.0f / distance;
|
|
1011
|
+
normal.x *= invDistance;
|
|
1012
|
+
normal.y *= invDistance;
|
|
1013
|
+
|
|
1014
|
+
b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
|
|
1015
|
+
b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
|
|
1016
|
+
|
|
1017
|
+
manifold.normal = normal;
|
|
1018
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
1019
|
+
manifold.points[0].separation = distance - radius;
|
|
1020
|
+
manifold.points[0].id = B2_MAKE_ID( i12, i21 );
|
|
1021
|
+
manifold.pointCount = 1;
|
|
1022
|
+
}
|
|
1023
|
+
else if ( result.fraction1 == 1.0f && result.fraction2 == 1.0f )
|
|
1024
|
+
{
|
|
1025
|
+
// v12 - v22
|
|
1026
|
+
b2Vec2 normal = b2Sub( v22, v12 );
|
|
1027
|
+
B2_ASSERT( result.distanceSquared > 0.0f );
|
|
1028
|
+
float distance = sqrtf( result.distanceSquared );
|
|
1029
|
+
if ( distance > B2_SPECULATIVE_DISTANCE + radius )
|
|
1030
|
+
{
|
|
1031
|
+
return manifold;
|
|
1032
|
+
}
|
|
1033
|
+
float invDistance = 1.0f / distance;
|
|
1034
|
+
normal.x *= invDistance;
|
|
1035
|
+
normal.y *= invDistance;
|
|
1036
|
+
|
|
1037
|
+
b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
|
|
1038
|
+
b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
|
|
1039
|
+
|
|
1040
|
+
manifold.normal = normal;
|
|
1041
|
+
manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
|
|
1042
|
+
manifold.points[0].separation = distance - radius;
|
|
1043
|
+
manifold.points[0].id = B2_MAKE_ID( i12, i22 );
|
|
1044
|
+
manifold.pointCount = 1;
|
|
1045
|
+
}
|
|
1046
|
+
else
|
|
1047
|
+
{
|
|
1048
|
+
// Edge region
|
|
1049
|
+
manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );
|
|
1050
|
+
}
|
|
1051
|
+
#endif
|
|
1052
|
+
}
|
|
1053
|
+
else
|
|
1054
|
+
{
|
|
1055
|
+
// Polygons overlap
|
|
1056
|
+
manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// Convert manifold to world space
|
|
1060
|
+
if ( manifold.pointCount > 0 )
|
|
1061
|
+
{
|
|
1062
|
+
manifold.normal = b2RotateVector( xfA.q, manifold.normal );
|
|
1063
|
+
for ( int i = 0; i < manifold.pointCount; ++i )
|
|
1064
|
+
{
|
|
1065
|
+
b2ManifoldPoint* mp = manifold.points + i;
|
|
1066
|
+
|
|
1067
|
+
// anchor points relative to shape origin in world space
|
|
1068
|
+
mp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) );
|
|
1069
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
1070
|
+
mp->point = b2Add( xfA.p, mp->anchorA );
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
return manifold;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
|
|
1078
|
+
{
|
|
1079
|
+
b2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f };
|
|
1080
|
+
return b2CollideCapsuleAndCircle( &capsuleA, xfA, circleB, xfB );
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB )
|
|
1084
|
+
{
|
|
1085
|
+
b2Polygon polygonA = b2MakeCapsule( segmentA->point1, segmentA->point2, 0.0f );
|
|
1086
|
+
return b2CollidePolygons( &polygonA, xfA, polygonB, xfB );
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB,
|
|
1090
|
+
b2Transform xfB )
|
|
1091
|
+
{
|
|
1092
|
+
b2Manifold manifold = { 0 };
|
|
1093
|
+
|
|
1094
|
+
b2Transform xf = b2InvMulTransforms( xfA, xfB );
|
|
1095
|
+
|
|
1096
|
+
// Compute circle in frame of segment
|
|
1097
|
+
b2Vec2 pB = b2TransformPoint( xf, circleB->center );
|
|
1098
|
+
|
|
1099
|
+
b2Vec2 p1 = segmentA->segment.point1;
|
|
1100
|
+
b2Vec2 p2 = segmentA->segment.point2;
|
|
1101
|
+
b2Vec2 e = b2Sub( p2, p1 );
|
|
1102
|
+
|
|
1103
|
+
// Normal points to the right
|
|
1104
|
+
float offset = b2Dot( b2RightPerp( e ), b2Sub( pB, p1 ) );
|
|
1105
|
+
if ( offset < 0.0f )
|
|
1106
|
+
{
|
|
1107
|
+
// collision is one-sided
|
|
1108
|
+
return manifold;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// Barycentric coordinates
|
|
1112
|
+
float u = b2Dot( e, b2Sub( p2, pB ) );
|
|
1113
|
+
float v = b2Dot( e, b2Sub( pB, p1 ) );
|
|
1114
|
+
|
|
1115
|
+
b2Vec2 pA;
|
|
1116
|
+
|
|
1117
|
+
if ( v <= 0.0f )
|
|
1118
|
+
{
|
|
1119
|
+
// Behind point1?
|
|
1120
|
+
// Is pB in the Voronoi region of the previous edge?
|
|
1121
|
+
b2Vec2 prevEdge = b2Sub( p1, segmentA->ghost1 );
|
|
1122
|
+
float uPrev = b2Dot( prevEdge, b2Sub( pB, p1 ) );
|
|
1123
|
+
if ( uPrev <= 0.0f )
|
|
1124
|
+
{
|
|
1125
|
+
return manifold;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
pA = p1;
|
|
1129
|
+
}
|
|
1130
|
+
else if ( u <= 0.0f )
|
|
1131
|
+
{
|
|
1132
|
+
// Ahead of point2?
|
|
1133
|
+
b2Vec2 nextEdge = b2Sub( segmentA->ghost2, p2 );
|
|
1134
|
+
float vNext = b2Dot( nextEdge, b2Sub( pB, p2 ) );
|
|
1135
|
+
|
|
1136
|
+
// Is pB in the Voronoi region of the next edge?
|
|
1137
|
+
if ( vNext > 0.0f )
|
|
1138
|
+
{
|
|
1139
|
+
return manifold;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
pA = p2;
|
|
1143
|
+
}
|
|
1144
|
+
else
|
|
1145
|
+
{
|
|
1146
|
+
float ee = b2Dot( e, e );
|
|
1147
|
+
pA = (b2Vec2){ u * p1.x + v * p2.x, u * p1.y + v * p2.y };
|
|
1148
|
+
pA = ee > 0.0f ? b2MulSV( 1.0f / ee, pA ) : p1;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
float distance;
|
|
1152
|
+
b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) );
|
|
1153
|
+
|
|
1154
|
+
float radius = circleB->radius;
|
|
1155
|
+
float separation = distance - radius;
|
|
1156
|
+
if ( separation > B2_SPECULATIVE_DISTANCE )
|
|
1157
|
+
{
|
|
1158
|
+
return manifold;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
b2Vec2 cA = pA;
|
|
1162
|
+
b2Vec2 cB = b2MulAdd( pB, -radius, normal );
|
|
1163
|
+
b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
|
|
1164
|
+
|
|
1165
|
+
manifold.normal = b2RotateVector( xfA.q, normal );
|
|
1166
|
+
|
|
1167
|
+
b2ManifoldPoint* mp = manifold.points + 0;
|
|
1168
|
+
mp->anchorA = b2RotateVector( xfA.q, contactPointA );
|
|
1169
|
+
mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
1170
|
+
mp->point = b2Add( xfA.p, mp->anchorA );
|
|
1171
|
+
mp->separation = separation;
|
|
1172
|
+
mp->id = 0;
|
|
1173
|
+
manifold.pointCount = 1;
|
|
1174
|
+
return manifold;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,
|
|
1178
|
+
b2Transform xfB, b2SimplexCache* cache )
|
|
1179
|
+
{
|
|
1180
|
+
b2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius );
|
|
1181
|
+
return b2CollideChainSegmentAndPolygon( segmentA, xfA, &polyB, xfB, cache );
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
static b2Manifold b2ClipSegments( b2Vec2 a1, b2Vec2 a2, b2Vec2 b1, b2Vec2 b2, b2Vec2 normal, float ra, float rb, uint16_t id1,
|
|
1185
|
+
uint16_t id2 )
|
|
1186
|
+
{
|
|
1187
|
+
b2Manifold manifold = { 0 };
|
|
1188
|
+
|
|
1189
|
+
b2Vec2 tangent = b2LeftPerp( normal );
|
|
1190
|
+
|
|
1191
|
+
// Barycentric coordinates of each point relative to a1 along tangent
|
|
1192
|
+
float lower1 = 0.0f;
|
|
1193
|
+
float upper1 = b2Dot( b2Sub( a2, a1 ), tangent );
|
|
1194
|
+
|
|
1195
|
+
// Incident edge points opposite of tangent due to CCW winding
|
|
1196
|
+
float upper2 = b2Dot( b2Sub( b1, a1 ), tangent );
|
|
1197
|
+
float lower2 = b2Dot( b2Sub( b2, a1 ), tangent );
|
|
1198
|
+
|
|
1199
|
+
// Do segments overlap?
|
|
1200
|
+
if ( upper2 < lower1 || upper1 < lower2 )
|
|
1201
|
+
{
|
|
1202
|
+
return manifold;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
b2Vec2 vLower;
|
|
1206
|
+
if ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON )
|
|
1207
|
+
{
|
|
1208
|
+
vLower = b2Lerp( b2, b1, ( lower1 - lower2 ) / ( upper2 - lower2 ) );
|
|
1209
|
+
}
|
|
1210
|
+
else
|
|
1211
|
+
{
|
|
1212
|
+
vLower = b2;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
b2Vec2 vUpper;
|
|
1216
|
+
if ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON )
|
|
1217
|
+
{
|
|
1218
|
+
vUpper = b2Lerp( b2, b1, ( upper1 - lower2 ) / ( upper2 - lower2 ) );
|
|
1219
|
+
}
|
|
1220
|
+
else
|
|
1221
|
+
{
|
|
1222
|
+
vUpper = b1;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// todo vLower can be very close to vUpper, reduce to one point?
|
|
1226
|
+
|
|
1227
|
+
float separationLower = b2Dot( b2Sub( vLower, a1 ), normal );
|
|
1228
|
+
float separationUpper = b2Dot( b2Sub( vUpper, a1 ), normal );
|
|
1229
|
+
|
|
1230
|
+
// Put contact points at midpoint, accounting for radii
|
|
1231
|
+
vLower = b2MulAdd( vLower, 0.5f * ( ra - rb - separationLower ), normal );
|
|
1232
|
+
vUpper = b2MulAdd( vUpper, 0.5f * ( ra - rb - separationUpper ), normal );
|
|
1233
|
+
|
|
1234
|
+
float radius = ra + rb;
|
|
1235
|
+
|
|
1236
|
+
manifold.normal = normal;
|
|
1237
|
+
{
|
|
1238
|
+
b2ManifoldPoint* cp = manifold.points + 0;
|
|
1239
|
+
cp->anchorA = vLower;
|
|
1240
|
+
cp->separation = separationLower - radius;
|
|
1241
|
+
cp->id = id1;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
{
|
|
1245
|
+
b2ManifoldPoint* cp = manifold.points + 1;
|
|
1246
|
+
cp->anchorA = vUpper;
|
|
1247
|
+
cp->separation = separationUpper - radius;
|
|
1248
|
+
cp->id = id2;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
manifold.pointCount = 2;
|
|
1252
|
+
|
|
1253
|
+
return manifold;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
enum b2NormalType
|
|
1257
|
+
{
|
|
1258
|
+
// This means the normal points in a direction that is non-smooth relative to a convex vertex and should be skipped
|
|
1259
|
+
b2_normalSkip,
|
|
1260
|
+
|
|
1261
|
+
// This means the normal points in a direction that is smooth relative to a convex vertex and should be used for collision
|
|
1262
|
+
b2_normalAdmit,
|
|
1263
|
+
|
|
1264
|
+
// This means the normal is in a region of a concave vertex and should be snapped to the segment normal
|
|
1265
|
+
b2_normalSnap
|
|
1266
|
+
};
|
|
1267
|
+
|
|
1268
|
+
struct b2ChainSegmentParams
|
|
1269
|
+
{
|
|
1270
|
+
b2Vec2 edge1;
|
|
1271
|
+
b2Vec2 normal0;
|
|
1272
|
+
b2Vec2 normal2;
|
|
1273
|
+
bool convex1;
|
|
1274
|
+
bool convex2;
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
// Evaluate Gauss map
|
|
1278
|
+
// See https://box2d.org/posts/2020/06/ghost-collisions/
|
|
1279
|
+
static enum b2NormalType b2ClassifyNormal( struct b2ChainSegmentParams params, b2Vec2 normal )
|
|
1280
|
+
{
|
|
1281
|
+
const float sinTol = 0.01f;
|
|
1282
|
+
|
|
1283
|
+
if ( b2Dot( normal, params.edge1 ) <= 0.0f )
|
|
1284
|
+
{
|
|
1285
|
+
// Normal points towards the segment tail
|
|
1286
|
+
if ( params.convex1 )
|
|
1287
|
+
{
|
|
1288
|
+
if ( b2Cross( normal, params.normal0 ) > sinTol )
|
|
1289
|
+
{
|
|
1290
|
+
return b2_normalSkip;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
return b2_normalAdmit;
|
|
1294
|
+
}
|
|
1295
|
+
else
|
|
1296
|
+
{
|
|
1297
|
+
return b2_normalSnap;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
else
|
|
1301
|
+
{
|
|
1302
|
+
// Normal points towards segment head
|
|
1303
|
+
if ( params.convex2 )
|
|
1304
|
+
{
|
|
1305
|
+
if ( b2Cross( params.normal2, normal ) > sinTol )
|
|
1306
|
+
{
|
|
1307
|
+
return b2_normalSkip;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
return b2_normalAdmit;
|
|
1311
|
+
}
|
|
1312
|
+
else
|
|
1313
|
+
{
|
|
1314
|
+
return b2_normalSnap;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB,
|
|
1320
|
+
b2Transform xfB, b2SimplexCache* cache )
|
|
1321
|
+
{
|
|
1322
|
+
b2Manifold manifold = { 0 };
|
|
1323
|
+
|
|
1324
|
+
b2Transform xf = b2InvMulTransforms( xfA, xfB );
|
|
1325
|
+
|
|
1326
|
+
b2Vec2 centroidB = b2TransformPoint( xf, polygonB->centroid );
|
|
1327
|
+
float radiusB = polygonB->radius;
|
|
1328
|
+
|
|
1329
|
+
b2Vec2 p1 = segmentA->segment.point1;
|
|
1330
|
+
b2Vec2 p2 = segmentA->segment.point2;
|
|
1331
|
+
|
|
1332
|
+
b2Vec2 edge1 = b2Normalize( b2Sub( p2, p1 ) );
|
|
1333
|
+
|
|
1334
|
+
struct b2ChainSegmentParams smoothParams = { 0 };
|
|
1335
|
+
smoothParams.edge1 = edge1;
|
|
1336
|
+
|
|
1337
|
+
const float convexTol = 0.01f;
|
|
1338
|
+
b2Vec2 edge0 = b2Normalize( b2Sub( p1, segmentA->ghost1 ) );
|
|
1339
|
+
smoothParams.normal0 = b2RightPerp( edge0 );
|
|
1340
|
+
smoothParams.convex1 = b2Cross( edge0, edge1 ) >= convexTol;
|
|
1341
|
+
|
|
1342
|
+
b2Vec2 edge2 = b2Normalize( b2Sub( segmentA->ghost2, p2 ) );
|
|
1343
|
+
smoothParams.normal2 = b2RightPerp( edge2 );
|
|
1344
|
+
smoothParams.convex2 = b2Cross( edge1, edge2 ) >= convexTol;
|
|
1345
|
+
|
|
1346
|
+
// Normal points to the right
|
|
1347
|
+
b2Vec2 normal1 = b2RightPerp( edge1 );
|
|
1348
|
+
bool behind1 = b2Dot( normal1, b2Sub( centroidB, p1 ) ) < 0.0f;
|
|
1349
|
+
bool behind0 = true;
|
|
1350
|
+
bool behind2 = true;
|
|
1351
|
+
if ( smoothParams.convex1 )
|
|
1352
|
+
{
|
|
1353
|
+
behind0 = b2Dot( smoothParams.normal0, b2Sub( centroidB, p1 ) ) < 0.0f;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
if ( smoothParams.convex2 )
|
|
1357
|
+
{
|
|
1358
|
+
behind2 = b2Dot( smoothParams.normal2, b2Sub( centroidB, p2 ) ) < 0.0f;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
if ( behind1 && behind0 && behind2 )
|
|
1362
|
+
{
|
|
1363
|
+
// one-sided collision
|
|
1364
|
+
return manifold;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Get polygonB in frameA
|
|
1368
|
+
int count = polygonB->count;
|
|
1369
|
+
b2Vec2 vertices[B2_MAX_POLYGON_VERTICES];
|
|
1370
|
+
b2Vec2 normals[B2_MAX_POLYGON_VERTICES];
|
|
1371
|
+
for ( int i = 0; i < count; ++i )
|
|
1372
|
+
{
|
|
1373
|
+
vertices[i] = b2TransformPoint( xf, polygonB->vertices[i] );
|
|
1374
|
+
normals[i] = b2RotateVector( xf.q, polygonB->normals[i] );
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Distance doesn't work correctly with partial polygons
|
|
1378
|
+
b2DistanceInput input;
|
|
1379
|
+
input.proxyA = b2MakeProxy( &segmentA->segment.point1, 2, 0.0f );
|
|
1380
|
+
input.proxyB = b2MakeProxy( vertices, count, 0.0f );
|
|
1381
|
+
input.transformA = b2Transform_identity;
|
|
1382
|
+
input.transformB = b2Transform_identity;
|
|
1383
|
+
input.useRadii = false;
|
|
1384
|
+
|
|
1385
|
+
b2DistanceOutput output = b2ShapeDistance( &input, cache, NULL, 0 );
|
|
1386
|
+
|
|
1387
|
+
if ( output.distance > radiusB + B2_SPECULATIVE_DISTANCE )
|
|
1388
|
+
{
|
|
1389
|
+
return manifold;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// Snap concave normals for partial polygon
|
|
1393
|
+
b2Vec2 n0 = smoothParams.convex1 ? smoothParams.normal0 : normal1;
|
|
1394
|
+
b2Vec2 n2 = smoothParams.convex2 ? smoothParams.normal2 : normal1;
|
|
1395
|
+
|
|
1396
|
+
// Index of incident vertex on polygon
|
|
1397
|
+
int incidentIndex = -1;
|
|
1398
|
+
int incidentNormal = -1;
|
|
1399
|
+
|
|
1400
|
+
if ( behind1 == false && output.distance > 0.1f * B2_LINEAR_SLOP )
|
|
1401
|
+
{
|
|
1402
|
+
// The closest features may be two vertices or an edge and a vertex even when there should
|
|
1403
|
+
// be face contact
|
|
1404
|
+
|
|
1405
|
+
if ( cache->count == 1 )
|
|
1406
|
+
{
|
|
1407
|
+
// vertex-vertex collision
|
|
1408
|
+
b2Vec2 pA = output.pointA;
|
|
1409
|
+
b2Vec2 pB = output.pointB;
|
|
1410
|
+
|
|
1411
|
+
b2Vec2 normal = b2Normalize( b2Sub( pB, pA ) );
|
|
1412
|
+
|
|
1413
|
+
enum b2NormalType type = b2ClassifyNormal( smoothParams, normal );
|
|
1414
|
+
if ( type == b2_normalSkip )
|
|
1415
|
+
{
|
|
1416
|
+
return manifold;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
if ( type == b2_normalAdmit )
|
|
1420
|
+
{
|
|
1421
|
+
manifold.normal = b2RotateVector( xfA.q, normal );
|
|
1422
|
+
b2ManifoldPoint* cp = manifold.points + 0;
|
|
1423
|
+
cp->anchorA = b2RotateVector( xfA.q, pA );
|
|
1424
|
+
cp->anchorB = b2Add( cp->anchorA, b2Sub( xfA.p, xfB.p ) );
|
|
1425
|
+
cp->point = b2Add( xfA.p, cp->anchorA );
|
|
1426
|
+
cp->separation = output.distance - radiusB;
|
|
1427
|
+
cp->id = B2_MAKE_ID( cache->indexA[0], cache->indexB[0] );
|
|
1428
|
+
manifold.pointCount = 1;
|
|
1429
|
+
return manifold;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// fall through b2_normalSnap
|
|
1433
|
+
incidentIndex = cache->indexB[0];
|
|
1434
|
+
}
|
|
1435
|
+
else
|
|
1436
|
+
{
|
|
1437
|
+
// vertex-edge collision
|
|
1438
|
+
B2_ASSERT( cache->count == 2 );
|
|
1439
|
+
|
|
1440
|
+
int ia1 = cache->indexA[0];
|
|
1441
|
+
int ia2 = cache->indexA[1];
|
|
1442
|
+
int ib1 = cache->indexB[0];
|
|
1443
|
+
int ib2 = cache->indexB[1];
|
|
1444
|
+
|
|
1445
|
+
if ( ia1 == ia2 )
|
|
1446
|
+
{
|
|
1447
|
+
// 1 point on A, expect 2 points on B
|
|
1448
|
+
B2_ASSERT( ib1 != ib2 );
|
|
1449
|
+
|
|
1450
|
+
// Find polygon normal most aligned with vector between closest points.
|
|
1451
|
+
// This effectively sorts ib1 and ib2
|
|
1452
|
+
b2Vec2 normalB = b2Sub( output.pointA, output.pointB );
|
|
1453
|
+
float dot1 = b2Dot( normalB, normals[ib1] );
|
|
1454
|
+
float dot2 = b2Dot( normalB, normals[ib2] );
|
|
1455
|
+
int ib = dot1 > dot2 ? ib1 : ib2;
|
|
1456
|
+
|
|
1457
|
+
// Use accurate normal
|
|
1458
|
+
normalB = normals[ib];
|
|
1459
|
+
|
|
1460
|
+
enum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( normalB ) );
|
|
1461
|
+
if ( type == b2_normalSkip )
|
|
1462
|
+
{
|
|
1463
|
+
return manifold;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
if ( type == b2_normalAdmit )
|
|
1467
|
+
{
|
|
1468
|
+
// Get polygon edge associated with normal
|
|
1469
|
+
ib1 = ib;
|
|
1470
|
+
ib2 = ib < count - 1 ? ib + 1 : 0;
|
|
1471
|
+
|
|
1472
|
+
b2Vec2 b1 = vertices[ib1];
|
|
1473
|
+
b2Vec2 b2 = vertices[ib2];
|
|
1474
|
+
|
|
1475
|
+
// Find incident segment vertex
|
|
1476
|
+
dot1 = b2Dot( normalB, b2Sub( p1, b1 ) );
|
|
1477
|
+
dot2 = b2Dot( normalB, b2Sub( p2, b1 ) );
|
|
1478
|
+
|
|
1479
|
+
if ( dot1 < dot2 )
|
|
1480
|
+
{
|
|
1481
|
+
if ( b2Dot( n0, normalB ) < b2Dot( normal1, normalB ) )
|
|
1482
|
+
{
|
|
1483
|
+
// Neighbor is incident
|
|
1484
|
+
return manifold;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
else
|
|
1488
|
+
{
|
|
1489
|
+
if ( b2Dot( n2, normalB ) < b2Dot( normal1, normalB ) )
|
|
1490
|
+
{
|
|
1491
|
+
// Neighbor is incident
|
|
1492
|
+
return manifold;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
manifold =
|
|
1497
|
+
b2ClipSegments( b1, b2, p1, p2, normalB, radiusB, 0.0f, B2_MAKE_ID( ib1, 1 ), B2_MAKE_ID( ib2, 0 ) );
|
|
1498
|
+
|
|
1499
|
+
B2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );
|
|
1500
|
+
if ( manifold.pointCount == 2 )
|
|
1501
|
+
{
|
|
1502
|
+
manifold.normal = b2RotateVector( xfA.q, b2Neg( normalB ) );
|
|
1503
|
+
manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );
|
|
1504
|
+
manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );
|
|
1505
|
+
b2Vec2 pAB = b2Sub( xfA.p, xfB.p );
|
|
1506
|
+
manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );
|
|
1507
|
+
manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );
|
|
1508
|
+
manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );
|
|
1509
|
+
manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );
|
|
1510
|
+
}
|
|
1511
|
+
return manifold;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// fall through b2_normalSnap
|
|
1515
|
+
incidentNormal = ib;
|
|
1516
|
+
}
|
|
1517
|
+
else
|
|
1518
|
+
{
|
|
1519
|
+
// Get index of incident polygonB vertex
|
|
1520
|
+
float dot1 = b2Dot( normal1, b2Sub( vertices[ib1], p1 ) );
|
|
1521
|
+
float dot2 = b2Dot( normal1, b2Sub( vertices[ib2], p2 ) );
|
|
1522
|
+
incidentIndex = dot1 < dot2 ? ib1 : ib2;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
else
|
|
1527
|
+
{
|
|
1528
|
+
// SAT edge normal
|
|
1529
|
+
float edgeSeparation = FLT_MAX;
|
|
1530
|
+
|
|
1531
|
+
for ( int i = 0; i < count; ++i )
|
|
1532
|
+
{
|
|
1533
|
+
float s = b2Dot( normal1, b2Sub( vertices[i], p1 ) );
|
|
1534
|
+
if ( s < edgeSeparation )
|
|
1535
|
+
{
|
|
1536
|
+
edgeSeparation = s;
|
|
1537
|
+
incidentIndex = i;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// Check convex neighbor for edge separation
|
|
1542
|
+
if ( smoothParams.convex1 )
|
|
1543
|
+
{
|
|
1544
|
+
float s0 = FLT_MAX;
|
|
1545
|
+
|
|
1546
|
+
for ( int i = 0; i < count; ++i )
|
|
1547
|
+
{
|
|
1548
|
+
float s = b2Dot( smoothParams.normal0, b2Sub( vertices[i], p1 ) );
|
|
1549
|
+
if ( s < s0 )
|
|
1550
|
+
{
|
|
1551
|
+
s0 = s;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
if ( s0 > edgeSeparation )
|
|
1556
|
+
{
|
|
1557
|
+
edgeSeparation = s0;
|
|
1558
|
+
|
|
1559
|
+
// Indicate neighbor owns edge separation
|
|
1560
|
+
incidentIndex = -1;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// Check convex neighbor for edge separation
|
|
1565
|
+
if ( smoothParams.convex2 )
|
|
1566
|
+
{
|
|
1567
|
+
float s2 = FLT_MAX;
|
|
1568
|
+
|
|
1569
|
+
for ( int i = 0; i < count; ++i )
|
|
1570
|
+
{
|
|
1571
|
+
float s = b2Dot( smoothParams.normal2, b2Sub( vertices[i], p2 ) );
|
|
1572
|
+
if ( s < s2 )
|
|
1573
|
+
{
|
|
1574
|
+
s2 = s;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
if ( s2 > edgeSeparation )
|
|
1579
|
+
{
|
|
1580
|
+
edgeSeparation = s2;
|
|
1581
|
+
|
|
1582
|
+
// Indicate neighbor owns edge separation
|
|
1583
|
+
incidentIndex = -1;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// SAT polygon normals
|
|
1588
|
+
float polygonSeparation = -FLT_MAX;
|
|
1589
|
+
int referenceIndex = -1;
|
|
1590
|
+
|
|
1591
|
+
for ( int i = 0; i < count; ++i )
|
|
1592
|
+
{
|
|
1593
|
+
b2Vec2 n = normals[i];
|
|
1594
|
+
|
|
1595
|
+
enum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( n ) );
|
|
1596
|
+
if ( type != b2_normalAdmit )
|
|
1597
|
+
{
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// Check the infinite sides of the partial polygon
|
|
1602
|
+
// if ((smoothParams.convex1 && b2Cross(n0, n) > 0.0f) || (smoothParams.convex2 && b2Cross(n, n2) > 0.0f))
|
|
1603
|
+
//{
|
|
1604
|
+
// continue;
|
|
1605
|
+
//}
|
|
1606
|
+
|
|
1607
|
+
b2Vec2 p = vertices[i];
|
|
1608
|
+
float s = b2MinFloat( b2Dot( n, b2Sub( p2, p ) ), b2Dot( n, b2Sub( p1, p ) ) );
|
|
1609
|
+
|
|
1610
|
+
if ( s > polygonSeparation )
|
|
1611
|
+
{
|
|
1612
|
+
polygonSeparation = s;
|
|
1613
|
+
referenceIndex = i;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
if ( polygonSeparation > edgeSeparation )
|
|
1618
|
+
{
|
|
1619
|
+
int ia1 = referenceIndex;
|
|
1620
|
+
int ia2 = ia1 < count - 1 ? ia1 + 1 : 0;
|
|
1621
|
+
b2Vec2 a1 = vertices[ia1];
|
|
1622
|
+
b2Vec2 a2 = vertices[ia2];
|
|
1623
|
+
|
|
1624
|
+
b2Vec2 n = normals[ia1];
|
|
1625
|
+
|
|
1626
|
+
float dot1 = b2Dot( n, b2Sub( p1, a1 ) );
|
|
1627
|
+
float dot2 = b2Dot( n, b2Sub( p2, a1 ) );
|
|
1628
|
+
|
|
1629
|
+
if ( dot1 < dot2 )
|
|
1630
|
+
{
|
|
1631
|
+
if ( b2Dot( n0, n ) < b2Dot( normal1, n ) )
|
|
1632
|
+
{
|
|
1633
|
+
// Neighbor is incident
|
|
1634
|
+
return manifold;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
else
|
|
1638
|
+
{
|
|
1639
|
+
if ( b2Dot( n2, n ) < b2Dot( normal1, n ) )
|
|
1640
|
+
{
|
|
1641
|
+
// Neighbor is incident
|
|
1642
|
+
return manifold;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
manifold = b2ClipSegments( a1, a2, p1, p2, normals[ia1], radiusB, 0.0f, B2_MAKE_ID( ia1, 1 ), B2_MAKE_ID( ia2, 0 ) );
|
|
1647
|
+
|
|
1648
|
+
B2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );
|
|
1649
|
+
if ( manifold.pointCount == 2 )
|
|
1650
|
+
{
|
|
1651
|
+
|
|
1652
|
+
manifold.normal = b2RotateVector( xfA.q, b2Neg( normals[ia1] ) );
|
|
1653
|
+
manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );
|
|
1654
|
+
manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );
|
|
1655
|
+
b2Vec2 pAB = b2Sub( xfA.p, xfB.p );
|
|
1656
|
+
manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );
|
|
1657
|
+
manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );
|
|
1658
|
+
manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );
|
|
1659
|
+
manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
return manifold;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
if ( incidentIndex == -1 )
|
|
1666
|
+
{
|
|
1667
|
+
// neighboring segment is the separating axis
|
|
1668
|
+
return manifold;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
// fall through segment normal axis
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
B2_ASSERT( incidentNormal != -1 || incidentIndex != -1 );
|
|
1675
|
+
|
|
1676
|
+
// Segment normal
|
|
1677
|
+
|
|
1678
|
+
// Find incident polygon normal: normal adjacent to deepest vertex that is most anti-parallel to segment normal
|
|
1679
|
+
b2Vec2 b1, b2;
|
|
1680
|
+
int ib1, ib2;
|
|
1681
|
+
|
|
1682
|
+
if ( incidentNormal != -1 )
|
|
1683
|
+
{
|
|
1684
|
+
ib1 = incidentNormal;
|
|
1685
|
+
ib2 = ib1 < count - 1 ? ib1 + 1 : 0;
|
|
1686
|
+
b1 = vertices[ib1];
|
|
1687
|
+
b2 = vertices[ib2];
|
|
1688
|
+
}
|
|
1689
|
+
else
|
|
1690
|
+
{
|
|
1691
|
+
int i2 = incidentIndex;
|
|
1692
|
+
int i1 = i2 > 0 ? i2 - 1 : count - 1;
|
|
1693
|
+
float d1 = b2Dot( normal1, normals[i1] );
|
|
1694
|
+
float d2 = b2Dot( normal1, normals[i2] );
|
|
1695
|
+
if ( d1 < d2 )
|
|
1696
|
+
{
|
|
1697
|
+
ib1 = i1, ib2 = i2;
|
|
1698
|
+
b1 = vertices[ib1];
|
|
1699
|
+
b2 = vertices[ib2];
|
|
1700
|
+
}
|
|
1701
|
+
else
|
|
1702
|
+
{
|
|
1703
|
+
ib1 = i2, ib2 = i2 < count - 1 ? i2 + 1 : 0;
|
|
1704
|
+
b1 = vertices[ib1];
|
|
1705
|
+
b2 = vertices[ib2];
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
manifold = b2ClipSegments( p1, p2, b1, b2, normal1, 0.0f, radiusB, B2_MAKE_ID( 0, ib2 ), B2_MAKE_ID( 1, ib1 ) );
|
|
1710
|
+
|
|
1711
|
+
B2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );
|
|
1712
|
+
if ( manifold.pointCount == 2 )
|
|
1713
|
+
{
|
|
1714
|
+
// There may be no points c
|
|
1715
|
+
manifold.normal = b2RotateVector( xfA.q, manifold.normal );
|
|
1716
|
+
manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );
|
|
1717
|
+
manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );
|
|
1718
|
+
b2Vec2 pAB = b2Sub( xfA.p, xfB.p );
|
|
1719
|
+
manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );
|
|
1720
|
+
manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );
|
|
1721
|
+
manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );
|
|
1722
|
+
manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
return manifold;
|
|
1726
|
+
}
|