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,1415 @@
|
|
|
1
|
+
|
|
2
|
+
// SPDX-FileCopyrightText: 2023 Erin Catto
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
#include "constants.h"
|
|
6
|
+
#include "core.h"
|
|
7
|
+
|
|
8
|
+
#include "box2d/collision.h"
|
|
9
|
+
#include "box2d/math_functions.h"
|
|
10
|
+
|
|
11
|
+
#include <float.h>
|
|
12
|
+
#include <stddef.h>
|
|
13
|
+
|
|
14
|
+
b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time )
|
|
15
|
+
{
|
|
16
|
+
// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
|
|
17
|
+
b2Transform xf;
|
|
18
|
+
xf.p = b2Add( b2MulSV( 1.0f - time, sweep->c1 ), b2MulSV( time, sweep->c2 ) );
|
|
19
|
+
|
|
20
|
+
b2Rot q = {
|
|
21
|
+
( 1.0f - time ) * sweep->q1.c + time * sweep->q2.c,
|
|
22
|
+
( 1.0f - time ) * sweep->q1.s + time * sweep->q2.s,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
xf.q = b2NormalizeRot( q );
|
|
26
|
+
|
|
27
|
+
// Shift to origin
|
|
28
|
+
xf.p = b2Sub( xf.p, b2RotateVector( xf.q, sweep->localCenter ) );
|
|
29
|
+
return xf;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Follows Ericson 5.1.9 Closest Points of Two Line Segments
|
|
33
|
+
b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 )
|
|
34
|
+
{
|
|
35
|
+
b2SegmentDistanceResult result = { 0 };
|
|
36
|
+
|
|
37
|
+
b2Vec2 d1 = b2Sub( q1, p1 );
|
|
38
|
+
b2Vec2 d2 = b2Sub( q2, p2 );
|
|
39
|
+
b2Vec2 r = b2Sub( p1, p2 );
|
|
40
|
+
float dd1 = b2Dot( d1, d1 );
|
|
41
|
+
float dd2 = b2Dot( d2, d2 );
|
|
42
|
+
float rd1 = b2Dot( r, d1 );
|
|
43
|
+
float rd2 = b2Dot( r, d2 );
|
|
44
|
+
|
|
45
|
+
const float epsSqr = FLT_EPSILON * FLT_EPSILON;
|
|
46
|
+
|
|
47
|
+
if ( dd1 < epsSqr || dd2 < epsSqr )
|
|
48
|
+
{
|
|
49
|
+
// Handle all degeneracies
|
|
50
|
+
if ( dd1 >= epsSqr )
|
|
51
|
+
{
|
|
52
|
+
// Segment 2 is degenerate
|
|
53
|
+
result.fraction1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );
|
|
54
|
+
result.fraction2 = 0.0f;
|
|
55
|
+
}
|
|
56
|
+
else if ( dd2 >= epsSqr )
|
|
57
|
+
{
|
|
58
|
+
// Segment 1 is degenerate
|
|
59
|
+
result.fraction1 = 0.0f;
|
|
60
|
+
result.fraction2 = b2ClampFloat( rd2 / dd2, 0.0f, 1.0f );
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
{
|
|
64
|
+
result.fraction1 = 0.0f;
|
|
65
|
+
result.fraction2 = 0.0f;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
{
|
|
70
|
+
// Non-degenerate segments
|
|
71
|
+
float d12 = b2Dot( d1, d2 );
|
|
72
|
+
|
|
73
|
+
float denominator = dd1 * dd2 - d12 * d12;
|
|
74
|
+
|
|
75
|
+
// Fraction on segment 1
|
|
76
|
+
float f1 = 0.0f;
|
|
77
|
+
if ( denominator != 0.0f )
|
|
78
|
+
{
|
|
79
|
+
// not parallel
|
|
80
|
+
f1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denominator, 0.0f, 1.0f );
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Compute point on segment 2 closest to p1 + f1 * d1
|
|
84
|
+
float f2 = ( d12 * f1 + rd2 ) / dd2;
|
|
85
|
+
|
|
86
|
+
// Clamping of segment 2 requires a do over on segment 1
|
|
87
|
+
if ( f2 < 0.0f )
|
|
88
|
+
{
|
|
89
|
+
f2 = 0.0f;
|
|
90
|
+
f1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );
|
|
91
|
+
}
|
|
92
|
+
else if ( f2 > 1.0f )
|
|
93
|
+
{
|
|
94
|
+
f2 = 1.0f;
|
|
95
|
+
f1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f );
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
result.fraction1 = f1;
|
|
99
|
+
result.fraction2 = f2;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
result.closest1 = b2MulAdd( p1, result.fraction1, d1 );
|
|
103
|
+
result.closest2 = b2MulAdd( p2, result.fraction2, d2 );
|
|
104
|
+
result.distanceSquared = b2DistanceSquared( result.closest1, result.closest2 );
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
b2ShapeProxy b2MakeProxy( const b2Vec2* points, int count, float radius )
|
|
109
|
+
{
|
|
110
|
+
count = b2MinInt( count, B2_MAX_POLYGON_VERTICES );
|
|
111
|
+
b2ShapeProxy proxy;
|
|
112
|
+
for ( int i = 0; i < count; ++i )
|
|
113
|
+
{
|
|
114
|
+
proxy.points[i] = points[i];
|
|
115
|
+
}
|
|
116
|
+
proxy.count = count;
|
|
117
|
+
proxy.radius = radius;
|
|
118
|
+
return proxy;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
b2ShapeProxy b2MakeOffsetProxy( const b2Vec2* points, int count, float radius, b2Vec2 position, b2Rot rotation )
|
|
122
|
+
{
|
|
123
|
+
count = b2MinInt( count, B2_MAX_POLYGON_VERTICES );
|
|
124
|
+
b2Transform transform = {
|
|
125
|
+
.p = position,
|
|
126
|
+
.q = rotation,
|
|
127
|
+
};
|
|
128
|
+
b2ShapeProxy proxy;
|
|
129
|
+
for ( int i = 0; i < count; ++i )
|
|
130
|
+
{
|
|
131
|
+
proxy.points[i] = b2TransformPoint( transform, points[i] );
|
|
132
|
+
}
|
|
133
|
+
proxy.count = count;
|
|
134
|
+
proxy.radius = radius;
|
|
135
|
+
return proxy;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static inline b2Vec2 b2Weight2( float a1, b2Vec2 w1, float a2, b2Vec2 w2 )
|
|
139
|
+
{
|
|
140
|
+
return (b2Vec2){ a1 * w1.x + a2 * w2.x, a1 * w1.y + a2 * w2.y };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static inline b2Vec2 b2Weight3( float a1, b2Vec2 w1, float a2, b2Vec2 w2, float a3, b2Vec2 w3 )
|
|
144
|
+
{
|
|
145
|
+
return (b2Vec2){ a1 * w1.x + a2 * w2.x + a3 * w3.x, a1 * w1.y + a2 * w2.y + a3 * w3.y };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static inline int b2FindSupport( const b2ShapeProxy* proxy, b2Vec2 direction )
|
|
149
|
+
{
|
|
150
|
+
const b2Vec2* points = proxy->points;
|
|
151
|
+
int count = proxy->count;
|
|
152
|
+
|
|
153
|
+
int bestIndex = 0;
|
|
154
|
+
float bestValue = b2Dot( points[0], direction );
|
|
155
|
+
for ( int i = 1; i < count; ++i )
|
|
156
|
+
{
|
|
157
|
+
float value = b2Dot( points[i], direction );
|
|
158
|
+
if ( value > bestValue )
|
|
159
|
+
{
|
|
160
|
+
bestIndex = i;
|
|
161
|
+
bestValue = value;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return bestIndex;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static b2Simplex b2MakeSimplexFromCache( const b2SimplexCache* cache, const b2ShapeProxy* proxyA, const b2ShapeProxy* proxyB )
|
|
169
|
+
{
|
|
170
|
+
B2_ASSERT( cache->count <= 3 );
|
|
171
|
+
b2Simplex s;
|
|
172
|
+
|
|
173
|
+
// Copy data from cache.
|
|
174
|
+
s.count = cache->count;
|
|
175
|
+
|
|
176
|
+
b2SimplexVertex* vertices[] = { &s.v1, &s.v2, &s.v3 };
|
|
177
|
+
for ( int i = 0; i < s.count; ++i )
|
|
178
|
+
{
|
|
179
|
+
b2SimplexVertex* v = vertices[i];
|
|
180
|
+
v->indexA = cache->indexA[i];
|
|
181
|
+
v->indexB = cache->indexB[i];
|
|
182
|
+
v->wA = proxyA->points[v->indexA];
|
|
183
|
+
v->wB = proxyB->points[v->indexB];
|
|
184
|
+
v->w = b2Sub( v->wA, v->wB );
|
|
185
|
+
|
|
186
|
+
// invalid
|
|
187
|
+
v->a = -1.0f;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// If the cache is empty or invalid ...
|
|
191
|
+
if ( s.count == 0 )
|
|
192
|
+
{
|
|
193
|
+
b2SimplexVertex* v = vertices[0];
|
|
194
|
+
v->indexA = 0;
|
|
195
|
+
v->indexB = 0;
|
|
196
|
+
v->wA = proxyA->points[0];
|
|
197
|
+
v->wB = proxyB->points[0];
|
|
198
|
+
v->w = b2Sub( v->wA, v->wB );
|
|
199
|
+
v->a = 1.0f;
|
|
200
|
+
s.count = 1;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return s;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static void b2MakeSimplexCache( b2SimplexCache* cache, const b2Simplex* simplex )
|
|
207
|
+
{
|
|
208
|
+
cache->count = (uint16_t)simplex->count;
|
|
209
|
+
const b2SimplexVertex* vertices[] = { &simplex->v1, &simplex->v2, &simplex->v3 };
|
|
210
|
+
for ( int i = 0; i < simplex->count; ++i )
|
|
211
|
+
{
|
|
212
|
+
cache->indexA[i] = (uint8_t)vertices[i]->indexA;
|
|
213
|
+
cache->indexB[i] = (uint8_t)vertices[i]->indexB;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
static void b2ComputeSimplexWitnessPoints( b2Vec2* a, b2Vec2* b, const b2Simplex* s )
|
|
218
|
+
{
|
|
219
|
+
switch ( s->count )
|
|
220
|
+
{
|
|
221
|
+
case 0:
|
|
222
|
+
B2_ASSERT( false );
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case 1:
|
|
226
|
+
*a = s->v1.wA;
|
|
227
|
+
*b = s->v1.wB;
|
|
228
|
+
break;
|
|
229
|
+
|
|
230
|
+
case 2:
|
|
231
|
+
*a = b2Weight2( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA );
|
|
232
|
+
*b = b2Weight2( s->v1.a, s->v1.wB, s->v2.a, s->v2.wB );
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
case 3:
|
|
236
|
+
*a = b2Weight3( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA, s->v3.a, s->v3.wA );
|
|
237
|
+
// todo why are these not equal?
|
|
238
|
+
//*b = b2Weight3(s->v1.a, s->v1.wB, s->v2.a, s->v2.wB, s->v3.a, s->v3.wB);
|
|
239
|
+
*b = *a;
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
default:
|
|
243
|
+
B2_ASSERT( false );
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Solve a line segment using barycentric coordinates.
|
|
249
|
+
//
|
|
250
|
+
// p = a1 * w1 + a2 * w2
|
|
251
|
+
// a1 + a2 = 1
|
|
252
|
+
//
|
|
253
|
+
// The vector from the origin to the closest point on the line is
|
|
254
|
+
// perpendicular to the line.
|
|
255
|
+
// e12 = w2 - w1
|
|
256
|
+
// dot(p, e) = 0
|
|
257
|
+
// a1 * dot(w1, e) + a2 * dot(w2, e) = 0
|
|
258
|
+
//
|
|
259
|
+
// 2-by-2 linear system
|
|
260
|
+
// [1 1 ][a1] = [1]
|
|
261
|
+
// [w1.e12 w2.e12][a2] = [0]
|
|
262
|
+
//
|
|
263
|
+
// Define
|
|
264
|
+
// d12_1 = dot(w2, e12)
|
|
265
|
+
// d12_2 = -dot(w1, e12)
|
|
266
|
+
// d12 = d12_1 + d12_2
|
|
267
|
+
//
|
|
268
|
+
// Solution
|
|
269
|
+
// a1 = d12_1 / d12
|
|
270
|
+
// a2 = d12_2 / d12
|
|
271
|
+
//
|
|
272
|
+
// returns a vector that points towards the origin
|
|
273
|
+
static b2Vec2 b2SolveSimplex2( b2Simplex* s )
|
|
274
|
+
{
|
|
275
|
+
b2Vec2 w1 = s->v1.w;
|
|
276
|
+
b2Vec2 w2 = s->v2.w;
|
|
277
|
+
b2Vec2 e12 = b2Sub( w2, w1 );
|
|
278
|
+
|
|
279
|
+
// w1 region
|
|
280
|
+
float d12_2 = -b2Dot( w1, e12 );
|
|
281
|
+
if ( d12_2 <= 0.0f )
|
|
282
|
+
{
|
|
283
|
+
// a2 <= 0, so we clamp it to 0
|
|
284
|
+
s->v1.a = 1.0f;
|
|
285
|
+
s->count = 1;
|
|
286
|
+
return b2Neg( w1 );
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// w2 region
|
|
290
|
+
float d12_1 = b2Dot( w2, e12 );
|
|
291
|
+
if ( d12_1 <= 0.0f )
|
|
292
|
+
{
|
|
293
|
+
// a1 <= 0, so we clamp it to 0
|
|
294
|
+
s->v2.a = 1.0f;
|
|
295
|
+
s->count = 1;
|
|
296
|
+
s->v1 = s->v2;
|
|
297
|
+
return b2Neg( w2 );
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Must be in e12 region.
|
|
301
|
+
float inv_d12 = 1.0f / ( d12_1 + d12_2 );
|
|
302
|
+
s->v1.a = d12_1 * inv_d12;
|
|
303
|
+
s->v2.a = d12_2 * inv_d12;
|
|
304
|
+
s->count = 2;
|
|
305
|
+
return b2CrossSV( b2Cross( b2Add( w1, w2 ), e12 ), e12 );
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
static b2Vec2 b2SolveSimplex3( b2Simplex* s )
|
|
309
|
+
{
|
|
310
|
+
b2Vec2 w1 = s->v1.w;
|
|
311
|
+
b2Vec2 w2 = s->v2.w;
|
|
312
|
+
b2Vec2 w3 = s->v3.w;
|
|
313
|
+
|
|
314
|
+
// Edge12
|
|
315
|
+
// [1 1 ][a1] = [1]
|
|
316
|
+
// [w1.e12 w2.e12][a2] = [0]
|
|
317
|
+
// a3 = 0
|
|
318
|
+
b2Vec2 e12 = b2Sub( w2, w1 );
|
|
319
|
+
float w1e12 = b2Dot( w1, e12 );
|
|
320
|
+
float w2e12 = b2Dot( w2, e12 );
|
|
321
|
+
float d12_1 = w2e12;
|
|
322
|
+
float d12_2 = -w1e12;
|
|
323
|
+
|
|
324
|
+
// Edge13
|
|
325
|
+
// [1 1 ][a1] = [1]
|
|
326
|
+
// [w1.e13 w3.e13][a3] = [0]
|
|
327
|
+
// a2 = 0
|
|
328
|
+
b2Vec2 e13 = b2Sub( w3, w1 );
|
|
329
|
+
float w1e13 = b2Dot( w1, e13 );
|
|
330
|
+
float w3e13 = b2Dot( w3, e13 );
|
|
331
|
+
float d13_1 = w3e13;
|
|
332
|
+
float d13_2 = -w1e13;
|
|
333
|
+
|
|
334
|
+
// Edge23
|
|
335
|
+
// [1 1 ][a2] = [1]
|
|
336
|
+
// [w2.e23 w3.e23][a3] = [0]
|
|
337
|
+
// a1 = 0
|
|
338
|
+
b2Vec2 e23 = b2Sub( w3, w2 );
|
|
339
|
+
float w2e23 = b2Dot( w2, e23 );
|
|
340
|
+
float w3e23 = b2Dot( w3, e23 );
|
|
341
|
+
float d23_1 = w3e23;
|
|
342
|
+
float d23_2 = -w2e23;
|
|
343
|
+
|
|
344
|
+
// Triangle123
|
|
345
|
+
float n123 = b2Cross( e12, e13 );
|
|
346
|
+
|
|
347
|
+
float d123_1 = n123 * b2Cross( w2, w3 );
|
|
348
|
+
float d123_2 = n123 * b2Cross( w3, w1 );
|
|
349
|
+
float d123_3 = n123 * b2Cross( w1, w2 );
|
|
350
|
+
|
|
351
|
+
// w1 region
|
|
352
|
+
if ( d12_2 <= 0.0f && d13_2 <= 0.0f )
|
|
353
|
+
{
|
|
354
|
+
s->v1.a = 1.0f;
|
|
355
|
+
s->count = 1;
|
|
356
|
+
return b2Neg( w1 );
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// e12
|
|
360
|
+
if ( d12_1 > 0.0f && d12_2 > 0.0f && d123_3 <= 0.0f )
|
|
361
|
+
{
|
|
362
|
+
float inv_d12 = 1.0f / ( d12_1 + d12_2 );
|
|
363
|
+
s->v1.a = d12_1 * inv_d12;
|
|
364
|
+
s->v2.a = d12_2 * inv_d12;
|
|
365
|
+
s->count = 2;
|
|
366
|
+
return b2CrossSV( b2Cross( b2Add( w1, w2 ), e12 ), e12 );
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// e13
|
|
370
|
+
if ( d13_1 > 0.0f && d13_2 > 0.0f && d123_2 <= 0.0f )
|
|
371
|
+
{
|
|
372
|
+
float inv_d13 = 1.0f / ( d13_1 + d13_2 );
|
|
373
|
+
s->v1.a = d13_1 * inv_d13;
|
|
374
|
+
s->v3.a = d13_2 * inv_d13;
|
|
375
|
+
s->count = 2;
|
|
376
|
+
s->v2 = s->v3;
|
|
377
|
+
return b2CrossSV( b2Cross( b2Add( w1, w3 ), e13 ), e13 );
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// w2 region
|
|
381
|
+
if ( d12_1 <= 0.0f && d23_2 <= 0.0f )
|
|
382
|
+
{
|
|
383
|
+
s->v2.a = 1.0f;
|
|
384
|
+
s->count = 1;
|
|
385
|
+
s->v1 = s->v2;
|
|
386
|
+
return b2Neg( w2 );
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// w3 region
|
|
390
|
+
if ( d13_1 <= 0.0f && d23_1 <= 0.0f )
|
|
391
|
+
{
|
|
392
|
+
s->v3.a = 1.0f;
|
|
393
|
+
s->count = 1;
|
|
394
|
+
s->v1 = s->v3;
|
|
395
|
+
return b2Neg( w3 );
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// e23
|
|
399
|
+
if ( d23_1 > 0.0f && d23_2 > 0.0f && d123_1 <= 0.0f )
|
|
400
|
+
{
|
|
401
|
+
float inv_d23 = 1.0f / ( d23_1 + d23_2 );
|
|
402
|
+
s->v2.a = d23_1 * inv_d23;
|
|
403
|
+
s->v3.a = d23_2 * inv_d23;
|
|
404
|
+
s->count = 2;
|
|
405
|
+
s->v1 = s->v3;
|
|
406
|
+
return b2CrossSV( b2Cross( b2Add( w2, w3 ), e23 ), e23 );
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Must be in triangle123
|
|
410
|
+
float inv_d123 = 1.0f / ( d123_1 + d123_2 + d123_3 );
|
|
411
|
+
s->v1.a = d123_1 * inv_d123;
|
|
412
|
+
s->v2.a = d123_2 * inv_d123;
|
|
413
|
+
s->v3.a = d123_3 * inv_d123;
|
|
414
|
+
s->count = 3;
|
|
415
|
+
|
|
416
|
+
// No search direction
|
|
417
|
+
return b2Vec2_zero;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Uses GJK for computing the distance between convex shapes.
|
|
421
|
+
// https://box2d.org/files/ErinCatto_GJK_GDC2010.pdf
|
|
422
|
+
// I spent time optimizing this and could find no further significant gains 3/30/2025
|
|
423
|
+
b2DistanceOutput b2ShapeDistance( const b2DistanceInput* input, b2SimplexCache* cache, b2Simplex* simplexes, int simplexCapacity )
|
|
424
|
+
{
|
|
425
|
+
B2_UNUSED( simplexes, simplexCapacity );
|
|
426
|
+
B2_ASSERT( input->proxyA.count > 0 && input->proxyB.count > 0 );
|
|
427
|
+
B2_ASSERT( input->proxyA.radius >= 0.0f );
|
|
428
|
+
B2_ASSERT( input->proxyB.radius >= 0.0f );
|
|
429
|
+
|
|
430
|
+
b2DistanceOutput output = { 0 };
|
|
431
|
+
|
|
432
|
+
const b2ShapeProxy* proxyA = &input->proxyA;
|
|
433
|
+
|
|
434
|
+
// Get proxyB in frame A to avoid further transforms in the main loop.
|
|
435
|
+
// This is still a performance gain at 8 points.
|
|
436
|
+
b2ShapeProxy localProxyB;
|
|
437
|
+
{
|
|
438
|
+
b2Transform transform = b2InvMulTransforms( input->transformA, input->transformB );
|
|
439
|
+
localProxyB.count = input->proxyB.count;
|
|
440
|
+
localProxyB.radius = input->proxyB.radius;
|
|
441
|
+
for ( int i = 0; i < localProxyB.count; ++i )
|
|
442
|
+
{
|
|
443
|
+
localProxyB.points[i] = b2TransformPoint( transform, input->proxyB.points[i] );
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Initialize the simplex.
|
|
448
|
+
b2Simplex simplex = b2MakeSimplexFromCache( cache, proxyA, &localProxyB );
|
|
449
|
+
|
|
450
|
+
int simplexIndex = 0;
|
|
451
|
+
if ( simplexes != NULL && simplexIndex < simplexCapacity )
|
|
452
|
+
{
|
|
453
|
+
simplexes[simplexIndex] = simplex;
|
|
454
|
+
simplexIndex += 1;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Get simplex vertices as an array.
|
|
458
|
+
b2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 };
|
|
459
|
+
|
|
460
|
+
b2Vec2 nonUnitNormal = b2Vec2_zero;
|
|
461
|
+
|
|
462
|
+
// These store the vertices of the last simplex so that we can check for duplicates and prevent cycling.
|
|
463
|
+
int saveA[3], saveB[3];
|
|
464
|
+
|
|
465
|
+
// Main iteration loop. All computations are done in frame A.
|
|
466
|
+
const int maxIterations = 20;
|
|
467
|
+
int iteration = 0;
|
|
468
|
+
while ( iteration < maxIterations )
|
|
469
|
+
{
|
|
470
|
+
// Copy simplex so we can identify duplicates.
|
|
471
|
+
int saveCount = simplex.count;
|
|
472
|
+
for ( int i = 0; i < saveCount; ++i )
|
|
473
|
+
{
|
|
474
|
+
saveA[i] = vertices[i]->indexA;
|
|
475
|
+
saveB[i] = vertices[i]->indexB;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
b2Vec2 d = { 0 };
|
|
479
|
+
switch ( simplex.count )
|
|
480
|
+
{
|
|
481
|
+
case 1:
|
|
482
|
+
d = b2Neg( simplex.v1.w );
|
|
483
|
+
break;
|
|
484
|
+
|
|
485
|
+
case 2:
|
|
486
|
+
d = b2SolveSimplex2( &simplex );
|
|
487
|
+
break;
|
|
488
|
+
|
|
489
|
+
case 3:
|
|
490
|
+
d = b2SolveSimplex3( &simplex );
|
|
491
|
+
break;
|
|
492
|
+
|
|
493
|
+
default:
|
|
494
|
+
B2_ASSERT( false );
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// If we have 3 points, then the origin is in the corresponding triangle.
|
|
498
|
+
if ( simplex.count == 3 )
|
|
499
|
+
{
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
#ifndef NDEBUG
|
|
504
|
+
if ( simplexes != NULL && simplexIndex < simplexCapacity )
|
|
505
|
+
{
|
|
506
|
+
simplexes[simplexIndex] = simplex;
|
|
507
|
+
simplexIndex += 1;
|
|
508
|
+
}
|
|
509
|
+
#endif
|
|
510
|
+
|
|
511
|
+
// Save the normal
|
|
512
|
+
nonUnitNormal = d;
|
|
513
|
+
|
|
514
|
+
// Ensure the search direction is numerically fit.
|
|
515
|
+
if ( b2Dot( d, d ) < FLT_EPSILON * FLT_EPSILON )
|
|
516
|
+
{
|
|
517
|
+
// This is unlikely but could lead to bad cycling.
|
|
518
|
+
// The branch predictor seems to make this check have low cost.
|
|
519
|
+
|
|
520
|
+
// The origin is probably contained by a line segment
|
|
521
|
+
// or triangle. Thus the shapes are overlapped.
|
|
522
|
+
|
|
523
|
+
// We can't return zero here even though there may be overlap.
|
|
524
|
+
// In case the simplex is a point, segment, or triangle it is difficult
|
|
525
|
+
// to determine if the origin is contained in the CSO or very close to it.
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Compute a tentative new simplex vertex using support points.
|
|
530
|
+
// support = support(a, d) - support(b, -d)
|
|
531
|
+
b2SimplexVertex* vertex = vertices[simplex.count];
|
|
532
|
+
vertex->indexA = b2FindSupport( proxyA, d );
|
|
533
|
+
vertex->wA = proxyA->points[vertex->indexA];
|
|
534
|
+
vertex->indexB = b2FindSupport( &localProxyB, b2Neg( d ) );
|
|
535
|
+
vertex->wB = localProxyB.points[vertex->indexB];
|
|
536
|
+
vertex->w = b2Sub( vertex->wA, vertex->wB );
|
|
537
|
+
|
|
538
|
+
// Iteration count is equated to the number of support point calls.
|
|
539
|
+
++iteration;
|
|
540
|
+
|
|
541
|
+
// Check for duplicate support points. This is the main termination criteria.
|
|
542
|
+
bool duplicate = false;
|
|
543
|
+
for ( int i = 0; i < saveCount; ++i )
|
|
544
|
+
{
|
|
545
|
+
if ( vertex->indexA == saveA[i] && vertex->indexB == saveB[i] )
|
|
546
|
+
{
|
|
547
|
+
duplicate = true;
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// If we found a duplicate support point we must exit to avoid cycling.
|
|
553
|
+
if ( duplicate )
|
|
554
|
+
{
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// New vertex is valid and needed.
|
|
559
|
+
simplex.count += 1;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
#ifndef NDEBUG
|
|
563
|
+
if ( simplexes != NULL && simplexIndex < simplexCapacity )
|
|
564
|
+
{
|
|
565
|
+
simplexes[simplexIndex] = simplex;
|
|
566
|
+
simplexIndex += 1;
|
|
567
|
+
}
|
|
568
|
+
#endif
|
|
569
|
+
|
|
570
|
+
// Prepare output
|
|
571
|
+
b2Vec2 normal = b2Normalize( nonUnitNormal );
|
|
572
|
+
normal = b2RotateVector( input->transformA.q, normal );
|
|
573
|
+
|
|
574
|
+
b2Vec2 localPointA, localPointB;
|
|
575
|
+
b2ComputeSimplexWitnessPoints( &localPointA, &localPointB, &simplex );
|
|
576
|
+
output.normal = normal;
|
|
577
|
+
output.distance = b2Distance( localPointA, localPointB );
|
|
578
|
+
output.pointA = b2TransformPoint( input->transformA, localPointA );
|
|
579
|
+
output.pointB = b2TransformPoint( input->transformA, localPointB );
|
|
580
|
+
output.iterations = iteration;
|
|
581
|
+
output.simplexCount = simplexIndex;
|
|
582
|
+
|
|
583
|
+
// Cache the simplex
|
|
584
|
+
b2MakeSimplexCache( cache, &simplex );
|
|
585
|
+
|
|
586
|
+
// Apply radii if requested
|
|
587
|
+
if ( input->useRadii && output.distance > 0.1f * B2_LINEAR_SLOP )
|
|
588
|
+
{
|
|
589
|
+
float radiusA = input->proxyA.radius;
|
|
590
|
+
float radiusB = input->proxyB.radius;
|
|
591
|
+
output.distance = b2MaxFloat( 0.0f, output.distance - radiusA - radiusB );
|
|
592
|
+
|
|
593
|
+
// Keep closest points on perimeter even if overlapped, this way the points move smoothly.
|
|
594
|
+
output.pointA = b2MulAdd( output.pointA, radiusA, normal );
|
|
595
|
+
output.pointB = b2MulSub( output.pointB, radiusB, normal );
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return output;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Shape cast using conservative advancement
|
|
602
|
+
b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input )
|
|
603
|
+
{
|
|
604
|
+
// Compute tolerance
|
|
605
|
+
float linearSlop = B2_LINEAR_SLOP;
|
|
606
|
+
float totalRadius = input->proxyA.radius + input->proxyB.radius;
|
|
607
|
+
float target = b2MaxFloat( linearSlop, totalRadius - linearSlop );
|
|
608
|
+
float tolerance = 0.25f * linearSlop;
|
|
609
|
+
|
|
610
|
+
B2_ASSERT( target > tolerance );
|
|
611
|
+
|
|
612
|
+
// Prepare input for distance query
|
|
613
|
+
b2SimplexCache cache = { 0 };
|
|
614
|
+
|
|
615
|
+
float alpha = 0.0f;
|
|
616
|
+
|
|
617
|
+
b2DistanceInput distanceInput = { 0 };
|
|
618
|
+
distanceInput.proxyA = input->proxyA;
|
|
619
|
+
distanceInput.proxyB = input->proxyB;
|
|
620
|
+
distanceInput.transformA = input->transformA;
|
|
621
|
+
distanceInput.transformB = input->transformB;
|
|
622
|
+
distanceInput.useRadii = false;
|
|
623
|
+
|
|
624
|
+
b2Vec2 delta2 = input->translationB;
|
|
625
|
+
b2CastOutput output = { 0 };
|
|
626
|
+
|
|
627
|
+
int iteration = 0;
|
|
628
|
+
int maxIterations = 20;
|
|
629
|
+
for ( ; iteration < maxIterations; ++iteration )
|
|
630
|
+
{
|
|
631
|
+
output.iterations += 1;
|
|
632
|
+
|
|
633
|
+
b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
|
|
634
|
+
|
|
635
|
+
if ( distanceOutput.distance < target + tolerance )
|
|
636
|
+
{
|
|
637
|
+
if ( iteration == 0 )
|
|
638
|
+
{
|
|
639
|
+
if ( input->canEncroach && distanceOutput.distance > 2.0f * linearSlop )
|
|
640
|
+
{
|
|
641
|
+
target = distanceOutput.distance - linearSlop;
|
|
642
|
+
}
|
|
643
|
+
else
|
|
644
|
+
{
|
|
645
|
+
if ( distanceOutput.distance == 0.0f )
|
|
646
|
+
{
|
|
647
|
+
// Normal may be invalid
|
|
648
|
+
return output;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Initial overlap but distance is non-zero due to radius
|
|
652
|
+
B2_ASSERT( b2IsNormalized( distanceOutput.normal ) );
|
|
653
|
+
output.fraction = alpha;
|
|
654
|
+
output.point = b2MulAdd( distanceOutput.pointA, input->proxyA.radius, distanceOutput.normal );
|
|
655
|
+
output.normal = distanceOutput.normal;
|
|
656
|
+
output.hit = true;
|
|
657
|
+
return output;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
else
|
|
661
|
+
{
|
|
662
|
+
// Regular hit
|
|
663
|
+
B2_ASSERT( distanceOutput.distance > 0.0f && b2IsNormalized( distanceOutput.normal ) );
|
|
664
|
+
output.fraction = alpha;
|
|
665
|
+
output.point = b2MulAdd( distanceOutput.pointA, input->proxyA.radius, distanceOutput.normal );
|
|
666
|
+
output.normal = distanceOutput.normal;
|
|
667
|
+
output.hit = true;
|
|
668
|
+
return output;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
B2_ASSERT( distanceOutput.distance > 0.0f );
|
|
673
|
+
B2_ASSERT( b2IsNormalized( distanceOutput.normal ) );
|
|
674
|
+
|
|
675
|
+
// Check if shapes are approaching each other
|
|
676
|
+
float denominator = b2Dot( delta2, distanceOutput.normal );
|
|
677
|
+
if ( denominator >= 0.0f )
|
|
678
|
+
{
|
|
679
|
+
// Miss
|
|
680
|
+
output.fraction = 1.0f;
|
|
681
|
+
return output;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Advance sweep
|
|
685
|
+
alpha += ( target - distanceOutput.distance ) / denominator;
|
|
686
|
+
if ( alpha >= input->maxFraction )
|
|
687
|
+
{
|
|
688
|
+
// Miss
|
|
689
|
+
output.fraction = 1.0f;
|
|
690
|
+
return output;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
distanceInput.transformB.p = b2MulAdd( input->transformB.p, alpha, delta2 );
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Failure!
|
|
697
|
+
return output;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
#if 0
|
|
701
|
+
static inline b2Vec2 b2ComputeSimplexClosestPoint( const b2Simplex* s )
|
|
702
|
+
{
|
|
703
|
+
if ( s->count == 1 )
|
|
704
|
+
{
|
|
705
|
+
return s->v1.w;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if ( s->count == 2 )
|
|
709
|
+
{
|
|
710
|
+
return b2Weight2( s->v1.a, s->v1.w, s->v2.a, s->v2.w );
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
return b2Vec2_zero;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
typedef struct b2ShapeCastData
|
|
717
|
+
{
|
|
718
|
+
b2Simplex simplex;
|
|
719
|
+
b2Vec2 closestA, closestB;
|
|
720
|
+
b2Vec2 normal;
|
|
721
|
+
b2Vec2 p0;
|
|
722
|
+
float fraction;
|
|
723
|
+
} b2ShapeCastData;
|
|
724
|
+
|
|
725
|
+
// GJK-raycast
|
|
726
|
+
// Algorithm by Gino van den Bergen.
|
|
727
|
+
// "Smooth Mesh Contacts with GJK" in Game Physics Pearls. 2010
|
|
728
|
+
// This needs the simplex of A - B because the translation is for B and this
|
|
729
|
+
// is how the relative motion works out when both shapes are translating.
|
|
730
|
+
// This is similar to ray vs polygon and involves plane clipping. See b2RayCastPolygon.
|
|
731
|
+
// In this case the polygon is just points and there are no planes. This uses a modified
|
|
732
|
+
// version of GJK to generate planes for clipping.
|
|
733
|
+
// The algorithm works by incrementally building clipping planes using GJK. Once a valid
|
|
734
|
+
// clip plane is found the simplex origin is moved to the current fraction on the ray.
|
|
735
|
+
// This resets the simplex after every clip. Later I should compare performance.
|
|
736
|
+
// However, adapting this to work with encroachment is tricky and confusing because encroachment
|
|
737
|
+
// needs distance.
|
|
738
|
+
// Note: this algorithm is difficult to debug and not worth the effort in my opinion 4/1/2025
|
|
739
|
+
b2CastOutput b2ShapeCastMerged( const b2ShapeCastPairInput* input, b2ShapeCastData* debugData, int debugCapacity )
|
|
740
|
+
{
|
|
741
|
+
B2_UNUSED( debugData, debugCapacity );
|
|
742
|
+
|
|
743
|
+
b2CastOutput output = { 0 };
|
|
744
|
+
output.fraction = input->maxFraction;
|
|
745
|
+
|
|
746
|
+
b2ShapeProxy proxyA = input->proxyA;
|
|
747
|
+
|
|
748
|
+
b2Transform xf = b2InvMulTransforms( input->transformA, input->transformB );
|
|
749
|
+
|
|
750
|
+
// Put proxyB in proxyA's frame to reduce round-off error
|
|
751
|
+
b2ShapeProxy proxyB;
|
|
752
|
+
proxyB.count = input->proxyB.count;
|
|
753
|
+
proxyB.radius = input->proxyB.radius;
|
|
754
|
+
B2_ASSERT( proxyB.count <= B2_MAX_POLYGON_VERTICES );
|
|
755
|
+
|
|
756
|
+
for ( int i = 0; i < proxyB.count; ++i )
|
|
757
|
+
{
|
|
758
|
+
proxyB.points[i] = b2TransformPoint( xf, input->proxyB.points[i] );
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
float radius = proxyA.radius + proxyB.radius;
|
|
762
|
+
|
|
763
|
+
b2Vec2 r = b2RotateVector( xf.q, input->translationB );
|
|
764
|
+
float lambda = 0.0f;
|
|
765
|
+
float maxFraction = input->maxFraction;
|
|
766
|
+
|
|
767
|
+
// Initial simplex
|
|
768
|
+
b2Simplex simplex;
|
|
769
|
+
simplex.count = 0;
|
|
770
|
+
|
|
771
|
+
// Get simplex vertices as an array.
|
|
772
|
+
b2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 };
|
|
773
|
+
|
|
774
|
+
// Get an initial point in A - B
|
|
775
|
+
b2Vec2 wA = proxyA.points[0];
|
|
776
|
+
b2Vec2 wB = proxyB.points[0];
|
|
777
|
+
b2Vec2 v = b2Sub( wA, wB );
|
|
778
|
+
b2Vec2 d = b2Neg( v );
|
|
779
|
+
|
|
780
|
+
// Sigma is the target distance between proxies
|
|
781
|
+
const float linearSlop = B2_LINEAR_SLOP;
|
|
782
|
+
const float sigma = b2MaxFloat( linearSlop, radius - linearSlop );
|
|
783
|
+
float tolerance = 0.5f * linearSlop;
|
|
784
|
+
float stolSquared = ( sigma + tolerance ) * ( sigma + tolerance );
|
|
785
|
+
|
|
786
|
+
// Main iteration loop.
|
|
787
|
+
const int maxIterations = 20;
|
|
788
|
+
int iteration = 0;
|
|
789
|
+
while ( iteration < maxIterations && b2LengthSquared( v ) > stolSquared )
|
|
790
|
+
{
|
|
791
|
+
B2_ASSERT( simplex.count < 3 );
|
|
792
|
+
|
|
793
|
+
// Support in direction d (A - B)
|
|
794
|
+
int indexA = b2FindSupport( &proxyA, d );
|
|
795
|
+
wA = proxyA.points[indexA];
|
|
796
|
+
int indexB = b2FindSupport( &proxyB, b2Neg( d ) );
|
|
797
|
+
wB = proxyB.points[indexB];
|
|
798
|
+
b2Vec2 p0 = b2Sub( wA, wB );
|
|
799
|
+
|
|
800
|
+
// d is a normal at p, normalize to work with sigma
|
|
801
|
+
b2Vec2 normal = b2Normalize( d );
|
|
802
|
+
|
|
803
|
+
// Intersect ray with plane
|
|
804
|
+
// p = origin + t * r
|
|
805
|
+
// dot(n, p - p0) = sigma
|
|
806
|
+
// dot(n, origin - p0) + t * dot(n, r) = sigma
|
|
807
|
+
// t = ( dot(n, p0) + sigma) / dot(n, r)
|
|
808
|
+
// if t < (dot(n, p0) + sigma) / dot(n, r) then t can be increased
|
|
809
|
+
// or (flipping sign because dot(n,r) < 0)
|
|
810
|
+
// dot(n, p0) + sigma < t * dot(n, r) && dot(n, r) < 0
|
|
811
|
+
float np0 = b2Dot( normal, p0 );
|
|
812
|
+
float nr = b2Dot( normal, r );
|
|
813
|
+
if ( np0 + sigma < lambda * nr )
|
|
814
|
+
{
|
|
815
|
+
if ( nr >= 0.0f )
|
|
816
|
+
{
|
|
817
|
+
// miss
|
|
818
|
+
return output;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
lambda = ( np0 + sigma ) / nr;
|
|
822
|
+
if ( lambda > maxFraction )
|
|
823
|
+
{
|
|
824
|
+
// too far
|
|
825
|
+
return output;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// reset the simplex
|
|
829
|
+
simplex.count = 0;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Shift by lambda * r because we want the closest point to the current clip point.
|
|
833
|
+
// Note that the support point p is not shifted because we want the plane equation
|
|
834
|
+
// to be formed in un-shifted space.
|
|
835
|
+
b2SimplexVertex* vertex = vertices[simplex.count];
|
|
836
|
+
vertex->indexA = indexB;
|
|
837
|
+
vertex->wA = wA;
|
|
838
|
+
vertex->indexB = indexA;
|
|
839
|
+
vertex->wB = (b2Vec2){ wB.x + lambda * r.x, wB.y + lambda * r.y };
|
|
840
|
+
vertex->w = b2Sub( vertex->wA, vertex->wB );
|
|
841
|
+
vertex->a = 1.0f;
|
|
842
|
+
simplex.count += 1;
|
|
843
|
+
|
|
844
|
+
switch ( simplex.count )
|
|
845
|
+
{
|
|
846
|
+
case 1:
|
|
847
|
+
d = b2Neg( simplex.v1.w );
|
|
848
|
+
break;
|
|
849
|
+
|
|
850
|
+
case 2:
|
|
851
|
+
d = b2SolveSimplex2( &simplex );
|
|
852
|
+
break;
|
|
853
|
+
|
|
854
|
+
case 3:
|
|
855
|
+
d = b2SolveSimplex3( &simplex );
|
|
856
|
+
break;
|
|
857
|
+
|
|
858
|
+
default:
|
|
859
|
+
B2_ASSERT( false );
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
#ifndef NDEBUG
|
|
863
|
+
if ( debugData != NULL && output.iterations < debugCapacity )
|
|
864
|
+
{
|
|
865
|
+
debugData[output.iterations].simplex = simplex;
|
|
866
|
+
debugData[output.iterations].normal = normal;
|
|
867
|
+
debugData[output.iterations].p0 = p0;
|
|
868
|
+
b2Vec2 cA, cB;
|
|
869
|
+
b2ComputeSimplexWitnessPoints( &cA, &cB, &simplex );
|
|
870
|
+
debugData[output.iterations].closestA = cA;
|
|
871
|
+
debugData[output.iterations].closestB = cB;
|
|
872
|
+
debugData[output.iterations].fraction = lambda;
|
|
873
|
+
}
|
|
874
|
+
#endif
|
|
875
|
+
|
|
876
|
+
output.iterations += 1;
|
|
877
|
+
|
|
878
|
+
// If we have 3 points, then the origin is in the corresponding triangle.
|
|
879
|
+
if ( simplex.count == 3 )
|
|
880
|
+
{
|
|
881
|
+
// Overlap
|
|
882
|
+
return output;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Get distance vector
|
|
886
|
+
v = b2ComputeSimplexClosestPoint( &simplex );
|
|
887
|
+
|
|
888
|
+
// Iteration count is equated to the number of support point calls.
|
|
889
|
+
++iteration;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if ( iteration == 0 || lambda == 0.0f )
|
|
893
|
+
{
|
|
894
|
+
// Initial overlap
|
|
895
|
+
return output;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Prepare output.
|
|
899
|
+
b2Vec2 pointA, pointB;
|
|
900
|
+
b2ComputeSimplexWitnessPoints( &pointB, &pointA, &simplex );
|
|
901
|
+
|
|
902
|
+
b2Vec2 n = b2Normalize( b2Neg( v ) );
|
|
903
|
+
b2Vec2 point = { pointA.x + proxyA.radius * n.x, pointA.y + proxyA.radius * n.y };
|
|
904
|
+
|
|
905
|
+
output.point = b2TransformPoint( input->transformA, point );
|
|
906
|
+
output.normal = b2RotateVector( input->transformA.q, n );
|
|
907
|
+
output.fraction = lambda;
|
|
908
|
+
output.iterations = iteration;
|
|
909
|
+
output.hit = true;
|
|
910
|
+
return output;
|
|
911
|
+
}
|
|
912
|
+
#endif
|
|
913
|
+
|
|
914
|
+
// Warning: writing to these globals significantly slows multithreading performance
|
|
915
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
916
|
+
float b2_toiTime, b2_toiMaxTime;
|
|
917
|
+
int b2_toiCalls, b2_toiDistanceIterations, b2_toiMaxDistanceIterations;
|
|
918
|
+
int b2_toiRootIterations, b2_toiMaxRootIterations;
|
|
919
|
+
int b2_toiFailedCount;
|
|
920
|
+
int b2_toiOverlappedCount;
|
|
921
|
+
int b2_toiHitCount;
|
|
922
|
+
int b2_toiSeparatedCount;
|
|
923
|
+
#endif
|
|
924
|
+
|
|
925
|
+
typedef enum b2SeparationType
|
|
926
|
+
{
|
|
927
|
+
b2_pointsType,
|
|
928
|
+
b2_faceAType,
|
|
929
|
+
b2_faceBType
|
|
930
|
+
} b2SeparationType;
|
|
931
|
+
|
|
932
|
+
typedef struct b2SeparationFunction
|
|
933
|
+
{
|
|
934
|
+
const b2ShapeProxy* proxyA;
|
|
935
|
+
const b2ShapeProxy* proxyB;
|
|
936
|
+
b2Sweep sweepA, sweepB;
|
|
937
|
+
b2Vec2 localPoint;
|
|
938
|
+
b2Vec2 axis;
|
|
939
|
+
b2SeparationType type;
|
|
940
|
+
} b2SeparationFunction;
|
|
941
|
+
|
|
942
|
+
static b2SeparationFunction b2MakeSeparationFunction( const b2SimplexCache* cache, const b2ShapeProxy* proxyA,
|
|
943
|
+
const b2Sweep* sweepA, const b2ShapeProxy* proxyB, const b2Sweep* sweepB,
|
|
944
|
+
float t1 )
|
|
945
|
+
{
|
|
946
|
+
b2SeparationFunction f;
|
|
947
|
+
|
|
948
|
+
f.proxyA = proxyA;
|
|
949
|
+
f.proxyB = proxyB;
|
|
950
|
+
int count = cache->count;
|
|
951
|
+
B2_ASSERT( 0 < count && count < 3 );
|
|
952
|
+
|
|
953
|
+
f.sweepA = *sweepA;
|
|
954
|
+
f.sweepB = *sweepB;
|
|
955
|
+
|
|
956
|
+
b2Transform xfA = b2GetSweepTransform( sweepA, t1 );
|
|
957
|
+
b2Transform xfB = b2GetSweepTransform( sweepB, t1 );
|
|
958
|
+
|
|
959
|
+
if ( count == 1 )
|
|
960
|
+
{
|
|
961
|
+
f.type = b2_pointsType;
|
|
962
|
+
b2Vec2 localPointA = proxyA->points[cache->indexA[0]];
|
|
963
|
+
b2Vec2 localPointB = proxyB->points[cache->indexB[0]];
|
|
964
|
+
b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
|
|
965
|
+
b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
|
|
966
|
+
f.axis = b2Normalize( b2Sub( pointB, pointA ) );
|
|
967
|
+
f.localPoint = b2Vec2_zero;
|
|
968
|
+
return f;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
if ( cache->indexA[0] == cache->indexA[1] )
|
|
972
|
+
{
|
|
973
|
+
// Two points on B and one on A.
|
|
974
|
+
f.type = b2_faceBType;
|
|
975
|
+
b2Vec2 localPointB1 = proxyB->points[cache->indexB[0]];
|
|
976
|
+
b2Vec2 localPointB2 = proxyB->points[cache->indexB[1]];
|
|
977
|
+
|
|
978
|
+
f.axis = b2CrossVS( b2Sub( localPointB2, localPointB1 ), 1.0f );
|
|
979
|
+
f.axis = b2Normalize( f.axis );
|
|
980
|
+
b2Vec2 normal = b2RotateVector( xfB.q, f.axis );
|
|
981
|
+
|
|
982
|
+
f.localPoint = (b2Vec2){ 0.5f * ( localPointB1.x + localPointB2.x ), 0.5f * ( localPointB1.y + localPointB2.y ) };
|
|
983
|
+
b2Vec2 pointB = b2TransformPoint( xfB, f.localPoint );
|
|
984
|
+
|
|
985
|
+
b2Vec2 localPointA = proxyA->points[cache->indexA[0]];
|
|
986
|
+
b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
|
|
987
|
+
|
|
988
|
+
float s = b2Dot( b2Sub( pointA, pointB ), normal );
|
|
989
|
+
if ( s < 0.0f )
|
|
990
|
+
{
|
|
991
|
+
f.axis = b2Neg( f.axis );
|
|
992
|
+
}
|
|
993
|
+
return f;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// Two points on A and one or two points on B.
|
|
997
|
+
f.type = b2_faceAType;
|
|
998
|
+
b2Vec2 localPointA1 = proxyA->points[cache->indexA[0]];
|
|
999
|
+
b2Vec2 localPointA2 = proxyA->points[cache->indexA[1]];
|
|
1000
|
+
|
|
1001
|
+
f.axis = b2CrossVS( b2Sub( localPointA2, localPointA1 ), 1.0f );
|
|
1002
|
+
f.axis = b2Normalize( f.axis );
|
|
1003
|
+
b2Vec2 normal = b2RotateVector( xfA.q, f.axis );
|
|
1004
|
+
|
|
1005
|
+
f.localPoint = (b2Vec2){ 0.5f * ( localPointA1.x + localPointA2.x ), 0.5f * ( localPointA1.y + localPointA2.y ) };
|
|
1006
|
+
b2Vec2 pointA = b2TransformPoint( xfA, f.localPoint );
|
|
1007
|
+
|
|
1008
|
+
b2Vec2 localPointB = proxyB->points[cache->indexB[0]];
|
|
1009
|
+
b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
|
|
1010
|
+
|
|
1011
|
+
float s = b2Dot( b2Sub( pointB, pointA ), normal );
|
|
1012
|
+
if ( s < 0.0f )
|
|
1013
|
+
{
|
|
1014
|
+
f.axis = b2Neg( f.axis );
|
|
1015
|
+
}
|
|
1016
|
+
return f;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
static float b2FindMinSeparation( const b2SeparationFunction* f, int* indexA, int* indexB, float t )
|
|
1020
|
+
{
|
|
1021
|
+
b2Transform xfA = b2GetSweepTransform( &f->sweepA, t );
|
|
1022
|
+
b2Transform xfB = b2GetSweepTransform( &f->sweepB, t );
|
|
1023
|
+
|
|
1024
|
+
switch ( f->type )
|
|
1025
|
+
{
|
|
1026
|
+
case b2_pointsType:
|
|
1027
|
+
{
|
|
1028
|
+
b2Vec2 axisA = b2InvRotateVector( xfA.q, f->axis );
|
|
1029
|
+
b2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( f->axis ) );
|
|
1030
|
+
|
|
1031
|
+
*indexA = b2FindSupport( f->proxyA, axisA );
|
|
1032
|
+
*indexB = b2FindSupport( f->proxyB, axisB );
|
|
1033
|
+
|
|
1034
|
+
b2Vec2 localPointA = f->proxyA->points[*indexA];
|
|
1035
|
+
b2Vec2 localPointB = f->proxyB->points[*indexB];
|
|
1036
|
+
|
|
1037
|
+
b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
|
|
1038
|
+
b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
|
|
1039
|
+
|
|
1040
|
+
float separation = b2Dot( b2Sub( pointB, pointA ), f->axis );
|
|
1041
|
+
return separation;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
case b2_faceAType:
|
|
1045
|
+
{
|
|
1046
|
+
b2Vec2 normal = b2RotateVector( xfA.q, f->axis );
|
|
1047
|
+
b2Vec2 pointA = b2TransformPoint( xfA, f->localPoint );
|
|
1048
|
+
|
|
1049
|
+
b2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( normal ) );
|
|
1050
|
+
|
|
1051
|
+
*indexA = -1;
|
|
1052
|
+
*indexB = b2FindSupport( f->proxyB, axisB );
|
|
1053
|
+
|
|
1054
|
+
b2Vec2 localPointB = f->proxyB->points[*indexB];
|
|
1055
|
+
b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
|
|
1056
|
+
|
|
1057
|
+
float separation = b2Dot( b2Sub( pointB, pointA ), normal );
|
|
1058
|
+
return separation;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
case b2_faceBType:
|
|
1062
|
+
{
|
|
1063
|
+
b2Vec2 normal = b2RotateVector( xfB.q, f->axis );
|
|
1064
|
+
b2Vec2 pointB = b2TransformPoint( xfB, f->localPoint );
|
|
1065
|
+
|
|
1066
|
+
b2Vec2 axisA = b2InvRotateVector( xfA.q, b2Neg( normal ) );
|
|
1067
|
+
|
|
1068
|
+
*indexB = -1;
|
|
1069
|
+
*indexA = b2FindSupport( f->proxyA, axisA );
|
|
1070
|
+
|
|
1071
|
+
b2Vec2 localPointA = f->proxyA->points[*indexA];
|
|
1072
|
+
b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
|
|
1073
|
+
|
|
1074
|
+
float separation = b2Dot( b2Sub( pointA, pointB ), normal );
|
|
1075
|
+
return separation;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
default:
|
|
1079
|
+
B2_ASSERT( false );
|
|
1080
|
+
*indexA = -1;
|
|
1081
|
+
*indexB = -1;
|
|
1082
|
+
return 0.0f;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
//
|
|
1087
|
+
static float b2EvaluateSeparation( const b2SeparationFunction* f, int indexA, int indexB, float t )
|
|
1088
|
+
{
|
|
1089
|
+
b2Transform xfA = b2GetSweepTransform( &f->sweepA, t );
|
|
1090
|
+
b2Transform xfB = b2GetSweepTransform( &f->sweepB, t );
|
|
1091
|
+
|
|
1092
|
+
switch ( f->type )
|
|
1093
|
+
{
|
|
1094
|
+
case b2_pointsType:
|
|
1095
|
+
{
|
|
1096
|
+
b2Vec2 localPointA = f->proxyA->points[indexA];
|
|
1097
|
+
b2Vec2 localPointB = f->proxyB->points[indexB];
|
|
1098
|
+
|
|
1099
|
+
b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
|
|
1100
|
+
b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
|
|
1101
|
+
|
|
1102
|
+
float separation = b2Dot( b2Sub( pointB, pointA ), f->axis );
|
|
1103
|
+
return separation;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
case b2_faceAType:
|
|
1107
|
+
{
|
|
1108
|
+
b2Vec2 normal = b2RotateVector( xfA.q, f->axis );
|
|
1109
|
+
b2Vec2 pointA = b2TransformPoint( xfA, f->localPoint );
|
|
1110
|
+
|
|
1111
|
+
b2Vec2 localPointB = f->proxyB->points[indexB];
|
|
1112
|
+
b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
|
|
1113
|
+
|
|
1114
|
+
float separation = b2Dot( b2Sub( pointB, pointA ), normal );
|
|
1115
|
+
return separation;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
case b2_faceBType:
|
|
1119
|
+
{
|
|
1120
|
+
b2Vec2 normal = b2RotateVector( xfB.q, f->axis );
|
|
1121
|
+
b2Vec2 pointB = b2TransformPoint( xfB, f->localPoint );
|
|
1122
|
+
|
|
1123
|
+
b2Vec2 localPointA = f->proxyA->points[indexA];
|
|
1124
|
+
b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
|
|
1125
|
+
|
|
1126
|
+
float separation = b2Dot( b2Sub( pointA, pointB ), normal );
|
|
1127
|
+
return separation;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
default:
|
|
1131
|
+
B2_ASSERT( false );
|
|
1132
|
+
return 0.0f;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// CCD via the local separating axis method. This seeks progression
|
|
1137
|
+
// by computing the largest time at which separation is maintained.
|
|
1138
|
+
b2TOIOutput b2TimeOfImpact( const b2TOIInput* input )
|
|
1139
|
+
{
|
|
1140
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1141
|
+
uint64_t ticks = b2GetTicks();
|
|
1142
|
+
++b2_toiCalls;
|
|
1143
|
+
#endif
|
|
1144
|
+
|
|
1145
|
+
b2TOIOutput output;
|
|
1146
|
+
output.state = b2_toiStateUnknown;
|
|
1147
|
+
output.fraction = input->maxFraction;
|
|
1148
|
+
|
|
1149
|
+
b2Sweep sweepA = input->sweepA;
|
|
1150
|
+
b2Sweep sweepB = input->sweepB;
|
|
1151
|
+
B2_ASSERT( b2IsNormalizedRot( sweepA.q1 ) && b2IsNormalizedRot( sweepA.q2 ) );
|
|
1152
|
+
B2_ASSERT( b2IsNormalizedRot( sweepB.q1 ) && b2IsNormalizedRot( sweepB.q2 ) );
|
|
1153
|
+
|
|
1154
|
+
// todo_erin
|
|
1155
|
+
// c1 can be at the origin yet the points are far away
|
|
1156
|
+
// b2Vec2 origin = b2Add(sweepA.c1, input->proxyA.points[0]);
|
|
1157
|
+
|
|
1158
|
+
const b2ShapeProxy* proxyA = &input->proxyA;
|
|
1159
|
+
const b2ShapeProxy* proxyB = &input->proxyB;
|
|
1160
|
+
|
|
1161
|
+
float tMax = input->maxFraction;
|
|
1162
|
+
|
|
1163
|
+
float totalRadius = proxyA->radius + proxyB->radius;
|
|
1164
|
+
// todo_erin consider different target
|
|
1165
|
+
// float target = b2MaxFloat( B2_LINEAR_SLOP, totalRadius );
|
|
1166
|
+
float target = b2MaxFloat( B2_LINEAR_SLOP, totalRadius - B2_LINEAR_SLOP );
|
|
1167
|
+
float tolerance = 0.25f * B2_LINEAR_SLOP;
|
|
1168
|
+
B2_ASSERT( target > tolerance );
|
|
1169
|
+
|
|
1170
|
+
float t1 = 0.0f;
|
|
1171
|
+
const int k_maxIterations = 20;
|
|
1172
|
+
int distanceIterations = 0;
|
|
1173
|
+
|
|
1174
|
+
// Prepare input for distance query.
|
|
1175
|
+
b2SimplexCache cache = { 0 };
|
|
1176
|
+
b2DistanceInput distanceInput;
|
|
1177
|
+
distanceInput.proxyA = input->proxyA;
|
|
1178
|
+
distanceInput.proxyB = input->proxyB;
|
|
1179
|
+
distanceInput.useRadii = false;
|
|
1180
|
+
|
|
1181
|
+
// The outer loop progressively attempts to compute new separating axes.
|
|
1182
|
+
// This loop terminates when an axis is repeated (no progress is made).
|
|
1183
|
+
for ( ;; )
|
|
1184
|
+
{
|
|
1185
|
+
b2Transform xfA = b2GetSweepTransform( &sweepA, t1 );
|
|
1186
|
+
b2Transform xfB = b2GetSweepTransform( &sweepB, t1 );
|
|
1187
|
+
|
|
1188
|
+
// Get the distance between shapes. We can also use the results
|
|
1189
|
+
// to get a separating axis.
|
|
1190
|
+
distanceInput.transformA = xfA;
|
|
1191
|
+
distanceInput.transformB = xfB;
|
|
1192
|
+
b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
|
|
1193
|
+
|
|
1194
|
+
// Progressive time of impact. This handles slender geometry well but introduces
|
|
1195
|
+
// significant time loss.
|
|
1196
|
+
// if (distanceIterations == 0)
|
|
1197
|
+
//{
|
|
1198
|
+
// if ( distanceOutput.distance > totalRadius + B2_SPECULATIVE_DISTANCE )
|
|
1199
|
+
// {
|
|
1200
|
+
// target = totalRadius + B2_SPECULATIVE_DISTANCE - tolerance;
|
|
1201
|
+
// }
|
|
1202
|
+
// else
|
|
1203
|
+
// {
|
|
1204
|
+
// target = distanceOutput.distance - 1.5f * tolerance;
|
|
1205
|
+
// target = b2MaxFloat( target, 2.0f * tolerance );
|
|
1206
|
+
// }
|
|
1207
|
+
//}
|
|
1208
|
+
|
|
1209
|
+
distanceIterations += 1;
|
|
1210
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1211
|
+
b2_toiDistanceIterations += 1;
|
|
1212
|
+
#endif
|
|
1213
|
+
|
|
1214
|
+
// If the shapes are overlapped, we give up on continuous collision.
|
|
1215
|
+
if ( distanceOutput.distance <= 0.0f )
|
|
1216
|
+
{
|
|
1217
|
+
// Failure!
|
|
1218
|
+
output.state = b2_toiStateOverlapped;
|
|
1219
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1220
|
+
b2_toiOverlappedCount += 1;
|
|
1221
|
+
#endif
|
|
1222
|
+
output.fraction = 0.0f;
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if ( distanceOutput.distance <= target + tolerance )
|
|
1227
|
+
{
|
|
1228
|
+
// Victory!
|
|
1229
|
+
output.state = b2_toiStateHit;
|
|
1230
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1231
|
+
b2_toiHitCount += 1;
|
|
1232
|
+
#endif
|
|
1233
|
+
output.fraction = t1;
|
|
1234
|
+
break;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// Initialize the separating axis.
|
|
1238
|
+
b2SeparationFunction fcn = b2MakeSeparationFunction( &cache, proxyA, &sweepA, proxyB, &sweepB, t1 );
|
|
1239
|
+
#if 0
|
|
1240
|
+
// Dump the curve seen by the root finder
|
|
1241
|
+
{
|
|
1242
|
+
const int N = 100;
|
|
1243
|
+
float dx = 1.0f / N;
|
|
1244
|
+
float xs[N + 1];
|
|
1245
|
+
float fs[N + 1];
|
|
1246
|
+
|
|
1247
|
+
float x = 0.0f;
|
|
1248
|
+
|
|
1249
|
+
for (int i = 0; i <= N; ++i)
|
|
1250
|
+
{
|
|
1251
|
+
sweepA.GetTransform(&xfA, x);
|
|
1252
|
+
sweepB.GetTransform(&xfB, x);
|
|
1253
|
+
float f = fcn.Evaluate(xfA, xfB) - target;
|
|
1254
|
+
|
|
1255
|
+
printf("%g %g\n", x, f);
|
|
1256
|
+
|
|
1257
|
+
xs[i] = x;
|
|
1258
|
+
fs[i] = f;
|
|
1259
|
+
|
|
1260
|
+
x += dx;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
#endif
|
|
1264
|
+
|
|
1265
|
+
// Compute the TOI on the separating axis. We do this by successively
|
|
1266
|
+
// resolving the deepest point. This loop is bounded by the number of vertices.
|
|
1267
|
+
bool done = false;
|
|
1268
|
+
float t2 = tMax;
|
|
1269
|
+
int pushBackIterations = 0;
|
|
1270
|
+
for ( ;; )
|
|
1271
|
+
{
|
|
1272
|
+
// Find the deepest point at t2. Store the witness point indices.
|
|
1273
|
+
int indexA, indexB;
|
|
1274
|
+
float s2 = b2FindMinSeparation( &fcn, &indexA, &indexB, t2 );
|
|
1275
|
+
|
|
1276
|
+
// Is the final configuration separated?
|
|
1277
|
+
if ( s2 > target + tolerance )
|
|
1278
|
+
{
|
|
1279
|
+
// Victory!
|
|
1280
|
+
output.state = b2_toiStateSeparated;
|
|
1281
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1282
|
+
b2_toiSeparatedCount += 1;
|
|
1283
|
+
#endif
|
|
1284
|
+
output.fraction = tMax;
|
|
1285
|
+
done = true;
|
|
1286
|
+
break;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// Has the separation reached tolerance?
|
|
1290
|
+
if ( s2 > target - tolerance )
|
|
1291
|
+
{
|
|
1292
|
+
// Advance the sweeps
|
|
1293
|
+
t1 = t2;
|
|
1294
|
+
break;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// Compute the initial separation of the witness points.
|
|
1298
|
+
float s1 = b2EvaluateSeparation( &fcn, indexA, indexB, t1 );
|
|
1299
|
+
|
|
1300
|
+
// Check for initial overlap. This might happen if the root finder
|
|
1301
|
+
// runs out of iterations.
|
|
1302
|
+
if ( s1 < target - tolerance )
|
|
1303
|
+
{
|
|
1304
|
+
output.state = b2_toiStateFailed;
|
|
1305
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1306
|
+
b2_toiFailedCount += 1;
|
|
1307
|
+
#endif
|
|
1308
|
+
output.fraction = t1;
|
|
1309
|
+
done = true;
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Check for touching
|
|
1314
|
+
if ( s1 <= target + tolerance )
|
|
1315
|
+
{
|
|
1316
|
+
// Victory! t1 should hold the TOI (could be 0.0).
|
|
1317
|
+
output.state = b2_toiStateHit;
|
|
1318
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1319
|
+
b2_toiHitCount += 1;
|
|
1320
|
+
#endif
|
|
1321
|
+
output.fraction = t1;
|
|
1322
|
+
done = true;
|
|
1323
|
+
break;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// Compute 1D root of: f(x) - target = 0
|
|
1327
|
+
int rootIterationCount = 0;
|
|
1328
|
+
float a1 = t1, a2 = t2;
|
|
1329
|
+
for ( ;; )
|
|
1330
|
+
{
|
|
1331
|
+
// Use a mix of the secant rule and bisection.
|
|
1332
|
+
float t;
|
|
1333
|
+
if ( rootIterationCount & 1 )
|
|
1334
|
+
{
|
|
1335
|
+
// Secant rule to improve convergence.
|
|
1336
|
+
t = a1 + ( target - s1 ) * ( a2 - a1 ) / ( s2 - s1 );
|
|
1337
|
+
}
|
|
1338
|
+
else
|
|
1339
|
+
{
|
|
1340
|
+
// Bisection to guarantee progress.
|
|
1341
|
+
t = 0.5f * ( a1 + a2 );
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
rootIterationCount += 1;
|
|
1345
|
+
|
|
1346
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1347
|
+
++b2_toiRootIterations;
|
|
1348
|
+
#endif
|
|
1349
|
+
|
|
1350
|
+
float s = b2EvaluateSeparation( &fcn, indexA, indexB, t );
|
|
1351
|
+
|
|
1352
|
+
if ( b2AbsFloat( s - target ) < tolerance )
|
|
1353
|
+
{
|
|
1354
|
+
// t2 holds a tentative value for t1
|
|
1355
|
+
t2 = t;
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// Ensure we continue to bracket the root.
|
|
1360
|
+
if ( s > target )
|
|
1361
|
+
{
|
|
1362
|
+
a1 = t;
|
|
1363
|
+
s1 = s;
|
|
1364
|
+
}
|
|
1365
|
+
else
|
|
1366
|
+
{
|
|
1367
|
+
a2 = t;
|
|
1368
|
+
s2 = s;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
if ( rootIterationCount == 50 )
|
|
1372
|
+
{
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1378
|
+
b2_toiMaxRootIterations = b2MaxInt( b2_toiMaxRootIterations, rootIterationCount );
|
|
1379
|
+
#endif
|
|
1380
|
+
|
|
1381
|
+
pushBackIterations += 1;
|
|
1382
|
+
|
|
1383
|
+
if ( pushBackIterations == B2_MAX_POLYGON_VERTICES )
|
|
1384
|
+
{
|
|
1385
|
+
break;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
if ( done )
|
|
1390
|
+
{
|
|
1391
|
+
break;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
if ( distanceIterations == k_maxIterations )
|
|
1395
|
+
{
|
|
1396
|
+
// Root finder got stuck. Semi-victory.
|
|
1397
|
+
output.state = b2_toiStateFailed;
|
|
1398
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1399
|
+
b2_toiFailedCount += 1;
|
|
1400
|
+
#endif
|
|
1401
|
+
output.fraction = t1;
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
#if B2_SNOOP_TOI_COUNTERS
|
|
1407
|
+
b2_toiMaxDistanceIterations = b2MaxInt( b2_toiMaxDistanceIterations, distanceIterations );
|
|
1408
|
+
|
|
1409
|
+
float time = b2GetMilliseconds( ticks );
|
|
1410
|
+
b2_toiMaxTime = b2MaxFloat( b2_toiMaxTime, time );
|
|
1411
|
+
b2_toiTime += time;
|
|
1412
|
+
#endif
|
|
1413
|
+
|
|
1414
|
+
return output;
|
|
1415
|
+
}
|