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,1028 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2023 Erin Catto
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
#include "constants.h"
|
|
5
|
+
#include "shape.h"
|
|
6
|
+
|
|
7
|
+
#include "box2d/collision.h"
|
|
8
|
+
#include "box2d/math_functions.h"
|
|
9
|
+
|
|
10
|
+
#include <float.h>
|
|
11
|
+
#include <stddef.h>
|
|
12
|
+
|
|
13
|
+
_Static_assert( B2_MAX_POLYGON_VERTICES > 2, "must be 3 or more" );
|
|
14
|
+
|
|
15
|
+
bool b2IsValidRay( const b2RayCastInput* input )
|
|
16
|
+
{
|
|
17
|
+
bool isValid = b2IsValidVec2( input->origin ) && b2IsValidVec2( input->translation ) &&
|
|
18
|
+
b2IsValidFloat( input->maxFraction ) && 0.0f <= input->maxFraction && input->maxFraction < B2_HUGE;
|
|
19
|
+
return isValid;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static b2Vec2 b2ComputePolygonCentroid( const b2Vec2* vertices, int count )
|
|
23
|
+
{
|
|
24
|
+
b2Vec2 center = { 0.0f, 0.0f };
|
|
25
|
+
float area = 0.0f;
|
|
26
|
+
|
|
27
|
+
// Get a reference point for forming triangles.
|
|
28
|
+
// Use the first vertex to reduce round-off errors.
|
|
29
|
+
b2Vec2 origin = vertices[0];
|
|
30
|
+
|
|
31
|
+
const float inv3 = 1.0f / 3.0f;
|
|
32
|
+
|
|
33
|
+
for ( int i = 1; i < count - 1; ++i )
|
|
34
|
+
{
|
|
35
|
+
// Triangle edges
|
|
36
|
+
b2Vec2 e1 = b2Sub( vertices[i], origin );
|
|
37
|
+
b2Vec2 e2 = b2Sub( vertices[i + 1], origin );
|
|
38
|
+
float a = 0.5f * b2Cross( e1, e2 );
|
|
39
|
+
|
|
40
|
+
// Area weighted centroid
|
|
41
|
+
center = b2MulAdd( center, a * inv3, b2Add( e1, e2 ) );
|
|
42
|
+
area += a;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
B2_ASSERT( area > FLT_EPSILON );
|
|
46
|
+
float invArea = 1.0f / area;
|
|
47
|
+
center.x *= invArea;
|
|
48
|
+
center.y *= invArea;
|
|
49
|
+
|
|
50
|
+
// Restore offset
|
|
51
|
+
center = b2Add( origin, center );
|
|
52
|
+
|
|
53
|
+
return center;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
b2Polygon b2MakePolygon( const b2Hull* hull, float radius )
|
|
57
|
+
{
|
|
58
|
+
B2_ASSERT( b2ValidateHull( hull ) );
|
|
59
|
+
|
|
60
|
+
if ( hull->count < 3 )
|
|
61
|
+
{
|
|
62
|
+
// Handle a bad hull when assertions are disabled
|
|
63
|
+
return b2MakeSquare( 0.5f );
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
b2Polygon shape = { 0 };
|
|
67
|
+
shape.count = hull->count;
|
|
68
|
+
shape.radius = radius;
|
|
69
|
+
|
|
70
|
+
// Copy vertices
|
|
71
|
+
for ( int i = 0; i < shape.count; ++i )
|
|
72
|
+
{
|
|
73
|
+
shape.vertices[i] = hull->points[i];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Compute normals. Ensure the edges have non-zero length.
|
|
77
|
+
for ( int i = 0; i < shape.count; ++i )
|
|
78
|
+
{
|
|
79
|
+
int i1 = i;
|
|
80
|
+
int i2 = i + 1 < shape.count ? i + 1 : 0;
|
|
81
|
+
b2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] );
|
|
82
|
+
B2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON );
|
|
83
|
+
shape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) );
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
shape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count );
|
|
87
|
+
|
|
88
|
+
return shape;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation )
|
|
92
|
+
{
|
|
93
|
+
return b2MakeOffsetRoundedPolygon( hull, position, rotation, 0.0f );
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius )
|
|
97
|
+
{
|
|
98
|
+
B2_ASSERT( b2ValidateHull( hull ) );
|
|
99
|
+
|
|
100
|
+
if ( hull->count < 3 )
|
|
101
|
+
{
|
|
102
|
+
// Handle a bad hull when assertions are disabled
|
|
103
|
+
return b2MakeSquare( 0.5f );
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
b2Transform transform = { position, rotation };
|
|
107
|
+
|
|
108
|
+
b2Polygon shape = { 0 };
|
|
109
|
+
shape.count = hull->count;
|
|
110
|
+
shape.radius = radius;
|
|
111
|
+
|
|
112
|
+
// Copy vertices
|
|
113
|
+
for ( int i = 0; i < shape.count; ++i )
|
|
114
|
+
{
|
|
115
|
+
shape.vertices[i] = b2TransformPoint( transform, hull->points[i] );
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Compute normals. Ensure the edges have non-zero length.
|
|
119
|
+
for ( int i = 0; i < shape.count; ++i )
|
|
120
|
+
{
|
|
121
|
+
int i1 = i;
|
|
122
|
+
int i2 = i + 1 < shape.count ? i + 1 : 0;
|
|
123
|
+
b2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] );
|
|
124
|
+
B2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON );
|
|
125
|
+
shape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) );
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
shape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count );
|
|
129
|
+
|
|
130
|
+
return shape;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
b2Polygon b2MakeSquare( float halfWidth )
|
|
134
|
+
{
|
|
135
|
+
return b2MakeBox( halfWidth, halfWidth );
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
b2Polygon b2MakeBox( float halfWidth, float halfHeight )
|
|
139
|
+
{
|
|
140
|
+
B2_ASSERT( b2IsValidFloat( halfWidth ) && halfWidth > 0.0f );
|
|
141
|
+
B2_ASSERT( b2IsValidFloat( halfHeight ) && halfHeight > 0.0f );
|
|
142
|
+
|
|
143
|
+
b2Polygon shape = { 0 };
|
|
144
|
+
shape.count = 4;
|
|
145
|
+
shape.vertices[0] = (b2Vec2){ -halfWidth, -halfHeight };
|
|
146
|
+
shape.vertices[1] = (b2Vec2){ halfWidth, -halfHeight };
|
|
147
|
+
shape.vertices[2] = (b2Vec2){ halfWidth, halfHeight };
|
|
148
|
+
shape.vertices[3] = (b2Vec2){ -halfWidth, halfHeight };
|
|
149
|
+
shape.normals[0] = (b2Vec2){ 0.0f, -1.0f };
|
|
150
|
+
shape.normals[1] = (b2Vec2){ 1.0f, 0.0f };
|
|
151
|
+
shape.normals[2] = (b2Vec2){ 0.0f, 1.0f };
|
|
152
|
+
shape.normals[3] = (b2Vec2){ -1.0f, 0.0f };
|
|
153
|
+
shape.radius = 0.0f;
|
|
154
|
+
shape.centroid = b2Vec2_zero;
|
|
155
|
+
return shape;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius )
|
|
159
|
+
{
|
|
160
|
+
B2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );
|
|
161
|
+
b2Polygon shape = b2MakeBox( halfWidth, halfHeight );
|
|
162
|
+
shape.radius = radius;
|
|
163
|
+
return shape;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation )
|
|
167
|
+
{
|
|
168
|
+
b2Transform xf = { center, rotation };
|
|
169
|
+
|
|
170
|
+
b2Polygon shape = { 0 };
|
|
171
|
+
shape.count = 4;
|
|
172
|
+
shape.vertices[0] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, -halfHeight } );
|
|
173
|
+
shape.vertices[1] = b2TransformPoint( xf, (b2Vec2){ halfWidth, -halfHeight } );
|
|
174
|
+
shape.vertices[2] = b2TransformPoint( xf, (b2Vec2){ halfWidth, halfHeight } );
|
|
175
|
+
shape.vertices[3] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, halfHeight } );
|
|
176
|
+
shape.normals[0] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, -1.0f } );
|
|
177
|
+
shape.normals[1] = b2RotateVector( xf.q, (b2Vec2){ 1.0f, 0.0f } );
|
|
178
|
+
shape.normals[2] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, 1.0f } );
|
|
179
|
+
shape.normals[3] = b2RotateVector( xf.q, (b2Vec2){ -1.0f, 0.0f } );
|
|
180
|
+
shape.radius = 0.0f;
|
|
181
|
+
shape.centroid = xf.p;
|
|
182
|
+
return shape;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius )
|
|
186
|
+
{
|
|
187
|
+
B2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );
|
|
188
|
+
b2Transform xf = { center, rotation };
|
|
189
|
+
|
|
190
|
+
b2Polygon shape = { 0 };
|
|
191
|
+
shape.count = 4;
|
|
192
|
+
shape.vertices[0] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, -halfHeight } );
|
|
193
|
+
shape.vertices[1] = b2TransformPoint( xf, (b2Vec2){ halfWidth, -halfHeight } );
|
|
194
|
+
shape.vertices[2] = b2TransformPoint( xf, (b2Vec2){ halfWidth, halfHeight } );
|
|
195
|
+
shape.vertices[3] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, halfHeight } );
|
|
196
|
+
shape.normals[0] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, -1.0f } );
|
|
197
|
+
shape.normals[1] = b2RotateVector( xf.q, (b2Vec2){ 1.0f, 0.0f } );
|
|
198
|
+
shape.normals[2] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, 1.0f } );
|
|
199
|
+
shape.normals[3] = b2RotateVector( xf.q, (b2Vec2){ -1.0f, 0.0f } );
|
|
200
|
+
shape.radius = radius;
|
|
201
|
+
shape.centroid = xf.p;
|
|
202
|
+
return shape;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon )
|
|
206
|
+
{
|
|
207
|
+
b2Polygon p = *polygon;
|
|
208
|
+
|
|
209
|
+
for ( int i = 0; i < p.count; ++i )
|
|
210
|
+
{
|
|
211
|
+
p.vertices[i] = b2TransformPoint( transform, p.vertices[i] );
|
|
212
|
+
p.normals[i] = b2RotateVector( transform.q, p.normals[i] );
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
p.centroid = b2TransformPoint( transform, p.centroid );
|
|
216
|
+
|
|
217
|
+
return p;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
b2MassData b2ComputeCircleMass( const b2Circle* shape, float density )
|
|
221
|
+
{
|
|
222
|
+
float rr = shape->radius * shape->radius;
|
|
223
|
+
|
|
224
|
+
b2MassData massData;
|
|
225
|
+
massData.mass = density * B2_PI * rr;
|
|
226
|
+
massData.center = shape->center;
|
|
227
|
+
|
|
228
|
+
// inertia about the local origin
|
|
229
|
+
massData.rotationalInertia = massData.mass * ( 0.5f * rr + b2Dot( shape->center, shape->center ) );
|
|
230
|
+
|
|
231
|
+
return massData;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density )
|
|
235
|
+
{
|
|
236
|
+
float radius = shape->radius;
|
|
237
|
+
float rr = radius * radius;
|
|
238
|
+
b2Vec2 p1 = shape->center1;
|
|
239
|
+
b2Vec2 p2 = shape->center2;
|
|
240
|
+
float length = b2Length( b2Sub( p2, p1 ) );
|
|
241
|
+
float ll = length * length;
|
|
242
|
+
|
|
243
|
+
float circleMass = density * ( B2_PI * radius * radius );
|
|
244
|
+
float boxMass = density * ( 2.0f * radius * length );
|
|
245
|
+
|
|
246
|
+
b2MassData massData;
|
|
247
|
+
massData.mass = circleMass + boxMass;
|
|
248
|
+
massData.center.x = 0.5f * ( p1.x + p2.x );
|
|
249
|
+
massData.center.y = 0.5f * ( p1.y + p2.y );
|
|
250
|
+
|
|
251
|
+
// two offset half circles, both halves add up to full circle and each half is offset by half length
|
|
252
|
+
// semi-circle centroid = 4 r / 3 pi
|
|
253
|
+
// Need to apply parallel-axis theorem twice:
|
|
254
|
+
// 1. shift semi-circle centroid to origin
|
|
255
|
+
// 2. shift semi-circle to box end
|
|
256
|
+
// m * ((h + lc)^2 - lc^2) = m * (h^2 + 2 * h * lc)
|
|
257
|
+
// See: https://en.wikipedia.org/wiki/Parallel_axis_theorem
|
|
258
|
+
// I verified this formula by computing the convex hull of a 128 vertex capsule
|
|
259
|
+
|
|
260
|
+
// half circle centroid
|
|
261
|
+
float lc = 4.0f * radius / ( 3.0f * B2_PI );
|
|
262
|
+
|
|
263
|
+
// half length of rectangular portion of capsule
|
|
264
|
+
float h = 0.5f * length;
|
|
265
|
+
|
|
266
|
+
float circleInertia = circleMass * ( 0.5f * rr + h * h + 2.0f * h * lc );
|
|
267
|
+
float boxInertia = boxMass * ( 4.0f * rr + ll ) / 12.0f;
|
|
268
|
+
massData.rotationalInertia = circleInertia + boxInertia;
|
|
269
|
+
|
|
270
|
+
// inertia about the local origin
|
|
271
|
+
massData.rotationalInertia += massData.mass * b2Dot( massData.center, massData.center );
|
|
272
|
+
|
|
273
|
+
return massData;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density )
|
|
277
|
+
{
|
|
278
|
+
// Polygon mass, centroid, and inertia.
|
|
279
|
+
// Let rho be the polygon density in mass per unit area.
|
|
280
|
+
// Then:
|
|
281
|
+
// mass = rho * int(dA)
|
|
282
|
+
// centroid.x = (1/mass) * rho * int(x * dA)
|
|
283
|
+
// centroid.y = (1/mass) * rho * int(y * dA)
|
|
284
|
+
// I = rho * int((x*x + y*y) * dA)
|
|
285
|
+
//
|
|
286
|
+
// We can compute these integrals by summing all the integrals
|
|
287
|
+
// for each triangle of the polygon. To evaluate the integral
|
|
288
|
+
// for a single triangle, we make a change of variables to
|
|
289
|
+
// the (u,v) coordinates of the triangle:
|
|
290
|
+
// x = x0 + e1x * u + e2x * v
|
|
291
|
+
// y = y0 + e1y * u + e2y * v
|
|
292
|
+
// where 0 <= u && 0 <= v && u + v <= 1.
|
|
293
|
+
//
|
|
294
|
+
// We integrate u from [0,1-v] and then v from [0,1].
|
|
295
|
+
// We also need to use the Jacobian of the transformation:
|
|
296
|
+
// D = cross(e1, e2)
|
|
297
|
+
//
|
|
298
|
+
// Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)
|
|
299
|
+
//
|
|
300
|
+
// The rest of the derivation is handled by computer algebra.
|
|
301
|
+
|
|
302
|
+
B2_ASSERT( shape->count > 0 );
|
|
303
|
+
|
|
304
|
+
if ( shape->count == 1 )
|
|
305
|
+
{
|
|
306
|
+
b2Circle circle;
|
|
307
|
+
circle.center = shape->vertices[0];
|
|
308
|
+
circle.radius = shape->radius;
|
|
309
|
+
return b2ComputeCircleMass( &circle, density );
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if ( shape->count == 2 )
|
|
313
|
+
{
|
|
314
|
+
b2Capsule capsule;
|
|
315
|
+
capsule.center1 = shape->vertices[0];
|
|
316
|
+
capsule.center2 = shape->vertices[1];
|
|
317
|
+
capsule.radius = shape->radius;
|
|
318
|
+
return b2ComputeCapsuleMass( &capsule, density );
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
b2Vec2 vertices[B2_MAX_POLYGON_VERTICES] = { 0 };
|
|
322
|
+
int count = shape->count;
|
|
323
|
+
float radius = shape->radius;
|
|
324
|
+
|
|
325
|
+
if ( radius > 0.0f )
|
|
326
|
+
{
|
|
327
|
+
// Approximate mass of rounded polygons by pushing out the vertices.
|
|
328
|
+
float sqrt2 = 1.412f;
|
|
329
|
+
for ( int i = 0; i < count; ++i )
|
|
330
|
+
{
|
|
331
|
+
int j = i == 0 ? count - 1 : i - 1;
|
|
332
|
+
b2Vec2 n1 = shape->normals[j];
|
|
333
|
+
b2Vec2 n2 = shape->normals[i];
|
|
334
|
+
|
|
335
|
+
b2Vec2 mid = b2Normalize( b2Add( n1, n2 ) );
|
|
336
|
+
vertices[i] = b2MulAdd( shape->vertices[i], sqrt2 * radius, mid );
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else
|
|
340
|
+
{
|
|
341
|
+
for ( int i = 0; i < count; ++i )
|
|
342
|
+
{
|
|
343
|
+
vertices[i] = shape->vertices[i];
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
b2Vec2 center = { 0.0f, 0.0f };
|
|
348
|
+
float area = 0.0f;
|
|
349
|
+
float rotationalInertia = 0.0f;
|
|
350
|
+
|
|
351
|
+
// Get a reference point for forming triangles.
|
|
352
|
+
// Use the first vertex to reduce round-off errors.
|
|
353
|
+
b2Vec2 r = vertices[0];
|
|
354
|
+
|
|
355
|
+
const float inv3 = 1.0f / 3.0f;
|
|
356
|
+
|
|
357
|
+
for ( int i = 1; i < count - 1; ++i )
|
|
358
|
+
{
|
|
359
|
+
// Triangle edges
|
|
360
|
+
b2Vec2 e1 = b2Sub( vertices[i], r );
|
|
361
|
+
b2Vec2 e2 = b2Sub( vertices[i + 1], r );
|
|
362
|
+
|
|
363
|
+
float D = b2Cross( e1, e2 );
|
|
364
|
+
|
|
365
|
+
float triangleArea = 0.5f * D;
|
|
366
|
+
area += triangleArea;
|
|
367
|
+
|
|
368
|
+
// Area weighted centroid, r at origin
|
|
369
|
+
center = b2MulAdd( center, triangleArea * inv3, b2Add( e1, e2 ) );
|
|
370
|
+
|
|
371
|
+
float ex1 = e1.x, ey1 = e1.y;
|
|
372
|
+
float ex2 = e2.x, ey2 = e2.y;
|
|
373
|
+
|
|
374
|
+
float intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;
|
|
375
|
+
float inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;
|
|
376
|
+
|
|
377
|
+
rotationalInertia += ( 0.25f * inv3 * D ) * ( intx2 + inty2 );
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
b2MassData massData;
|
|
381
|
+
|
|
382
|
+
// Total mass
|
|
383
|
+
massData.mass = density * area;
|
|
384
|
+
|
|
385
|
+
// Center of mass, shift back from origin at r
|
|
386
|
+
B2_ASSERT( area > FLT_EPSILON );
|
|
387
|
+
float invArea = 1.0f / area;
|
|
388
|
+
center.x *= invArea;
|
|
389
|
+
center.y *= invArea;
|
|
390
|
+
massData.center = b2Add( r, center );
|
|
391
|
+
|
|
392
|
+
// Inertia tensor relative to the local origin (point s).
|
|
393
|
+
massData.rotationalInertia = density * rotationalInertia;
|
|
394
|
+
|
|
395
|
+
// Shift to center of mass then to original body origin.
|
|
396
|
+
massData.rotationalInertia += massData.mass * ( b2Dot( massData.center, massData.center ) - b2Dot( center, center ) );
|
|
397
|
+
|
|
398
|
+
return massData;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform xf )
|
|
402
|
+
{
|
|
403
|
+
b2Vec2 p = b2TransformPoint( xf, shape->center );
|
|
404
|
+
float r = shape->radius;
|
|
405
|
+
|
|
406
|
+
b2AABB aabb = { { p.x - r, p.y - r }, { p.x + r, p.y + r } };
|
|
407
|
+
return aabb;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform xf )
|
|
411
|
+
{
|
|
412
|
+
b2Vec2 v1 = b2TransformPoint( xf, shape->center1 );
|
|
413
|
+
b2Vec2 v2 = b2TransformPoint( xf, shape->center2 );
|
|
414
|
+
|
|
415
|
+
b2Vec2 r = { shape->radius, shape->radius };
|
|
416
|
+
b2Vec2 lower = b2Sub( b2Min( v1, v2 ), r );
|
|
417
|
+
b2Vec2 upper = b2Add( b2Max( v1, v2 ), r );
|
|
418
|
+
|
|
419
|
+
b2AABB aabb = { lower, upper };
|
|
420
|
+
return aabb;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform xf )
|
|
424
|
+
{
|
|
425
|
+
B2_ASSERT( shape->count > 0 );
|
|
426
|
+
b2Vec2 lower = b2TransformPoint( xf, shape->vertices[0] );
|
|
427
|
+
b2Vec2 upper = lower;
|
|
428
|
+
|
|
429
|
+
for ( int i = 1; i < shape->count; ++i )
|
|
430
|
+
{
|
|
431
|
+
b2Vec2 v = b2TransformPoint( xf, shape->vertices[i] );
|
|
432
|
+
lower = b2Min( lower, v );
|
|
433
|
+
upper = b2Max( upper, v );
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
b2Vec2 r = { shape->radius, shape->radius };
|
|
437
|
+
lower = b2Sub( lower, r );
|
|
438
|
+
upper = b2Add( upper, r );
|
|
439
|
+
|
|
440
|
+
b2AABB aabb = { lower, upper };
|
|
441
|
+
return aabb;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform xf )
|
|
445
|
+
{
|
|
446
|
+
b2Vec2 v1 = b2TransformPoint( xf, shape->point1 );
|
|
447
|
+
b2Vec2 v2 = b2TransformPoint( xf, shape->point2 );
|
|
448
|
+
|
|
449
|
+
b2Vec2 lower = b2Min( v1, v2 );
|
|
450
|
+
b2Vec2 upper = b2Max( v1, v2 );
|
|
451
|
+
|
|
452
|
+
b2AABB aabb = { lower, upper };
|
|
453
|
+
return aabb;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
bool b2PointInCircle( b2Vec2 point, const b2Circle* shape )
|
|
457
|
+
{
|
|
458
|
+
b2Vec2 center = shape->center;
|
|
459
|
+
return b2DistanceSquared( point, center ) <= shape->radius * shape->radius;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
bool b2PointInCapsule( b2Vec2 point, const b2Capsule* shape )
|
|
463
|
+
{
|
|
464
|
+
float rr = shape->radius * shape->radius;
|
|
465
|
+
b2Vec2 p1 = shape->center1;
|
|
466
|
+
b2Vec2 p2 = shape->center2;
|
|
467
|
+
|
|
468
|
+
b2Vec2 d = b2Sub( p2, p1 );
|
|
469
|
+
float dd = b2Dot( d, d );
|
|
470
|
+
if ( dd == 0.0f )
|
|
471
|
+
{
|
|
472
|
+
// Capsule is really a circle
|
|
473
|
+
return b2DistanceSquared( point, p1 ) <= rr;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Get closest point on capsule segment
|
|
477
|
+
// c = p1 + t * d
|
|
478
|
+
// dot(point - c, d) = 0
|
|
479
|
+
// dot(point - p1 - t * d, d) = 0
|
|
480
|
+
// t = dot(point - p1, d) / dot(d, d)
|
|
481
|
+
float t = b2Dot( b2Sub( point, p1 ), d ) / dd;
|
|
482
|
+
t = b2ClampFloat( t, 0.0f, 1.0f );
|
|
483
|
+
b2Vec2 c = b2MulAdd( p1, t, d );
|
|
484
|
+
|
|
485
|
+
// Is query point within radius around closest point?
|
|
486
|
+
return b2DistanceSquared( point, c ) <= rr;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
bool b2PointInPolygon( b2Vec2 point, const b2Polygon* shape )
|
|
490
|
+
{
|
|
491
|
+
b2DistanceInput input = { 0 };
|
|
492
|
+
input.proxyA = b2MakeProxy( shape->vertices, shape->count, 0.0f );
|
|
493
|
+
input.proxyB = b2MakeProxy( &point, 1, 0.0f );
|
|
494
|
+
input.transformA = b2Transform_identity;
|
|
495
|
+
input.transformB = b2Transform_identity;
|
|
496
|
+
input.useRadii = false;
|
|
497
|
+
|
|
498
|
+
b2SimplexCache cache = { 0 };
|
|
499
|
+
b2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );
|
|
500
|
+
|
|
501
|
+
return output.distance <= shape->radius;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Precision Improvements for Ray / Sphere Intersection - Ray Tracing Gems 2019
|
|
505
|
+
// http://www.codercorner.com/blog/?p=321
|
|
506
|
+
b2CastOutput b2RayCastCircle( const b2RayCastInput* input, const b2Circle* shape )
|
|
507
|
+
{
|
|
508
|
+
B2_ASSERT( b2IsValidRay( input ) );
|
|
509
|
+
|
|
510
|
+
b2Vec2 p = shape->center;
|
|
511
|
+
|
|
512
|
+
b2CastOutput output = { 0 };
|
|
513
|
+
|
|
514
|
+
// Shift ray so circle center is the origin
|
|
515
|
+
b2Vec2 s = b2Sub( input->origin, p );
|
|
516
|
+
float length;
|
|
517
|
+
b2Vec2 d = b2GetLengthAndNormalize( &length, input->translation );
|
|
518
|
+
if ( length == 0.0f )
|
|
519
|
+
{
|
|
520
|
+
// zero length ray
|
|
521
|
+
return output;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Find closest point on ray to origin
|
|
525
|
+
|
|
526
|
+
// solve: dot(s + t * d, d) = 0
|
|
527
|
+
float t = -b2Dot( s, d );
|
|
528
|
+
|
|
529
|
+
// c is the closest point on the line to the origin
|
|
530
|
+
b2Vec2 c = b2MulAdd( s, t, d );
|
|
531
|
+
|
|
532
|
+
float cc = b2Dot( c, c );
|
|
533
|
+
float r = shape->radius;
|
|
534
|
+
float rr = r * r;
|
|
535
|
+
|
|
536
|
+
if ( cc > rr )
|
|
537
|
+
{
|
|
538
|
+
// closest point is outside the circle
|
|
539
|
+
return output;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Pythagoras
|
|
543
|
+
float h = sqrtf( rr - cc );
|
|
544
|
+
|
|
545
|
+
float fraction = t - h;
|
|
546
|
+
|
|
547
|
+
if ( fraction < 0.0f || input->maxFraction * length < fraction )
|
|
548
|
+
{
|
|
549
|
+
// outside the range of the ray segment
|
|
550
|
+
return output;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// hit point relative to center
|
|
554
|
+
b2Vec2 hitPoint = b2MulAdd( s, fraction, d );
|
|
555
|
+
|
|
556
|
+
output.fraction = fraction / length;
|
|
557
|
+
output.normal = b2Normalize( hitPoint );
|
|
558
|
+
output.point = b2MulAdd( p, shape->radius, output.normal );
|
|
559
|
+
output.hit = true;
|
|
560
|
+
|
|
561
|
+
return output;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
b2CastOutput b2RayCastCapsule( const b2RayCastInput* input, const b2Capsule* shape )
|
|
565
|
+
{
|
|
566
|
+
B2_ASSERT( b2IsValidRay( input ) );
|
|
567
|
+
|
|
568
|
+
b2CastOutput output = { 0 };
|
|
569
|
+
|
|
570
|
+
b2Vec2 v1 = shape->center1;
|
|
571
|
+
b2Vec2 v2 = shape->center2;
|
|
572
|
+
|
|
573
|
+
b2Vec2 e = b2Sub( v2, v1 );
|
|
574
|
+
|
|
575
|
+
float capsuleLength;
|
|
576
|
+
b2Vec2 a = b2GetLengthAndNormalize( &capsuleLength, e );
|
|
577
|
+
|
|
578
|
+
if ( capsuleLength < FLT_EPSILON )
|
|
579
|
+
{
|
|
580
|
+
// Capsule is really a circle
|
|
581
|
+
b2Circle circle = { v1, shape->radius };
|
|
582
|
+
return b2RayCastCircle( input, &circle );
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
b2Vec2 p1 = input->origin;
|
|
586
|
+
b2Vec2 d = input->translation;
|
|
587
|
+
|
|
588
|
+
// Ray from capsule start to ray start
|
|
589
|
+
b2Vec2 q = b2Sub( p1, v1 );
|
|
590
|
+
float qa = b2Dot( q, a );
|
|
591
|
+
|
|
592
|
+
// Vector to ray start that is perpendicular to capsule axis
|
|
593
|
+
b2Vec2 qp = b2MulAdd( q, -qa, a );
|
|
594
|
+
|
|
595
|
+
float radius = shape->radius;
|
|
596
|
+
|
|
597
|
+
// Does the ray start within the infinite length capsule?
|
|
598
|
+
if ( b2Dot( qp, qp ) < radius * radius )
|
|
599
|
+
{
|
|
600
|
+
if ( qa < 0.0f )
|
|
601
|
+
{
|
|
602
|
+
// start point behind capsule segment
|
|
603
|
+
b2Circle circle = { v1, shape->radius };
|
|
604
|
+
return b2RayCastCircle( input, &circle );
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if ( qa > 1.0f )
|
|
608
|
+
{
|
|
609
|
+
// start point ahead of capsule segment
|
|
610
|
+
b2Circle circle = { v2, shape->radius };
|
|
611
|
+
return b2RayCastCircle( input, &circle );
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// ray starts inside capsule -> no hit
|
|
615
|
+
return output;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Perpendicular to capsule axis, pointing right
|
|
619
|
+
b2Vec2 n = { a.y, -a.x };
|
|
620
|
+
|
|
621
|
+
float rayLength;
|
|
622
|
+
b2Vec2 u = b2GetLengthAndNormalize( &rayLength, d );
|
|
623
|
+
|
|
624
|
+
// Intersect ray with infinite length capsule
|
|
625
|
+
// v1 + radius * n + s1 * a = p1 + s2 * u
|
|
626
|
+
// v1 - radius * n + s1 * a = p1 + s2 * u
|
|
627
|
+
|
|
628
|
+
// s1 * a - s2 * u = b
|
|
629
|
+
// b = q - radius * ap
|
|
630
|
+
// or
|
|
631
|
+
// b = q + radius * ap
|
|
632
|
+
|
|
633
|
+
// Cramer's rule [a -u]
|
|
634
|
+
float den = -a.x * u.y + u.x * a.y;
|
|
635
|
+
if ( -FLT_EPSILON < den && den < FLT_EPSILON )
|
|
636
|
+
{
|
|
637
|
+
// Ray is parallel to capsule and outside infinite length capsule
|
|
638
|
+
return output;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
b2Vec2 b1 = b2MulSub( q, radius, n );
|
|
642
|
+
b2Vec2 b2 = b2MulAdd( q, radius, n );
|
|
643
|
+
|
|
644
|
+
float invDen = 1.0f / den;
|
|
645
|
+
|
|
646
|
+
// Cramer's rule [a b1]
|
|
647
|
+
float s21 = ( a.x * b1.y - b1.x * a.y ) * invDen;
|
|
648
|
+
|
|
649
|
+
// Cramer's rule [a b2]
|
|
650
|
+
float s22 = ( a.x * b2.y - b2.x * a.y ) * invDen;
|
|
651
|
+
|
|
652
|
+
float s2;
|
|
653
|
+
b2Vec2 b;
|
|
654
|
+
if ( s21 < s22 )
|
|
655
|
+
{
|
|
656
|
+
s2 = s21;
|
|
657
|
+
b = b1;
|
|
658
|
+
}
|
|
659
|
+
else
|
|
660
|
+
{
|
|
661
|
+
s2 = s22;
|
|
662
|
+
b = b2;
|
|
663
|
+
n = b2Neg( n );
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if ( s2 < 0.0f || input->maxFraction * rayLength < s2 )
|
|
667
|
+
{
|
|
668
|
+
return output;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Cramer's rule [b -u]
|
|
672
|
+
float s1 = ( -b.x * u.y + u.x * b.y ) * invDen;
|
|
673
|
+
|
|
674
|
+
if ( s1 < 0.0f )
|
|
675
|
+
{
|
|
676
|
+
// ray passes behind capsule segment
|
|
677
|
+
b2Circle circle = { v1, shape->radius };
|
|
678
|
+
return b2RayCastCircle( input, &circle );
|
|
679
|
+
}
|
|
680
|
+
else if ( capsuleLength < s1 )
|
|
681
|
+
{
|
|
682
|
+
// ray passes ahead of capsule segment
|
|
683
|
+
b2Circle circle = { v2, shape->radius };
|
|
684
|
+
return b2RayCastCircle( input, &circle );
|
|
685
|
+
}
|
|
686
|
+
else
|
|
687
|
+
{
|
|
688
|
+
// ray hits capsule side
|
|
689
|
+
output.fraction = s2 / rayLength;
|
|
690
|
+
output.point = b2Add( b2Lerp( v1, v2, s1 / capsuleLength ), b2MulSV( shape->radius, n ) );
|
|
691
|
+
output.normal = n;
|
|
692
|
+
output.hit = true;
|
|
693
|
+
return output;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Ray vs line segment
|
|
698
|
+
b2CastOutput b2RayCastSegment( const b2RayCastInput* input, const b2Segment* shape, bool oneSided )
|
|
699
|
+
{
|
|
700
|
+
if ( oneSided )
|
|
701
|
+
{
|
|
702
|
+
// Skip left-side collision
|
|
703
|
+
float offset = b2Cross( b2Sub( input->origin, shape->point1 ), b2Sub( shape->point2, shape->point1 ) );
|
|
704
|
+
if ( offset < 0.0f )
|
|
705
|
+
{
|
|
706
|
+
b2CastOutput output = { 0 };
|
|
707
|
+
return output;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Put the ray into the edge's frame of reference.
|
|
712
|
+
b2Vec2 p1 = input->origin;
|
|
713
|
+
b2Vec2 d = input->translation;
|
|
714
|
+
|
|
715
|
+
b2Vec2 v1 = shape->point1;
|
|
716
|
+
b2Vec2 v2 = shape->point2;
|
|
717
|
+
b2Vec2 e = b2Sub( v2, v1 );
|
|
718
|
+
|
|
719
|
+
b2CastOutput output = { 0 };
|
|
720
|
+
|
|
721
|
+
float length;
|
|
722
|
+
b2Vec2 eUnit = b2GetLengthAndNormalize( &length, e );
|
|
723
|
+
if ( length == 0.0f )
|
|
724
|
+
{
|
|
725
|
+
return output;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Normal points to the right, looking from v1 towards v2
|
|
729
|
+
b2Vec2 normal = b2RightPerp( eUnit );
|
|
730
|
+
|
|
731
|
+
// Intersect ray with infinite segment using normal
|
|
732
|
+
// Similar to intersecting a ray with an infinite plane
|
|
733
|
+
// p = p1 + t * d
|
|
734
|
+
// dot(normal, p - v1) = 0
|
|
735
|
+
// dot(normal, p1 - v1) + t * dot(normal, d) = 0
|
|
736
|
+
float numerator = b2Dot( normal, b2Sub( v1, p1 ) );
|
|
737
|
+
float denominator = b2Dot( normal, d );
|
|
738
|
+
|
|
739
|
+
if ( denominator == 0.0f )
|
|
740
|
+
{
|
|
741
|
+
// parallel
|
|
742
|
+
return output;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
float t = numerator / denominator;
|
|
746
|
+
if ( t < 0.0f || input->maxFraction < t )
|
|
747
|
+
{
|
|
748
|
+
// out of ray range
|
|
749
|
+
return output;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Intersection point on infinite segment
|
|
753
|
+
b2Vec2 p = b2MulAdd( p1, t, d );
|
|
754
|
+
|
|
755
|
+
// Compute position of p along segment
|
|
756
|
+
// p = v1 + s * e
|
|
757
|
+
// s = dot(p - v1, e) / dot(e, e)
|
|
758
|
+
|
|
759
|
+
float s = b2Dot( b2Sub( p, v1 ), eUnit );
|
|
760
|
+
if ( s < 0.0f || length < s )
|
|
761
|
+
{
|
|
762
|
+
// out of segment range
|
|
763
|
+
return output;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if ( numerator > 0.0f )
|
|
767
|
+
{
|
|
768
|
+
normal = b2Neg( normal );
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
output.fraction = t;
|
|
772
|
+
output.point = p;
|
|
773
|
+
output.normal = normal;
|
|
774
|
+
output.hit = true;
|
|
775
|
+
|
|
776
|
+
return output;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
b2CastOutput b2RayCastPolygon( const b2RayCastInput* input, const b2Polygon* shape )
|
|
780
|
+
{
|
|
781
|
+
B2_ASSERT( b2IsValidRay( input ) );
|
|
782
|
+
|
|
783
|
+
if ( shape->radius == 0.0f )
|
|
784
|
+
{
|
|
785
|
+
// Put the ray into the polygon's frame of reference.
|
|
786
|
+
b2Vec2 p1 = input->origin;
|
|
787
|
+
b2Vec2 d = input->translation;
|
|
788
|
+
|
|
789
|
+
float lower = 0.0f, upper = input->maxFraction;
|
|
790
|
+
|
|
791
|
+
int index = -1;
|
|
792
|
+
|
|
793
|
+
b2CastOutput output = { 0 };
|
|
794
|
+
|
|
795
|
+
for ( int i = 0; i < shape->count; ++i )
|
|
796
|
+
{
|
|
797
|
+
// p = p1 + a * d
|
|
798
|
+
// dot(normal, p - v) = 0
|
|
799
|
+
// dot(normal, p1 - v) + a * dot(normal, d) = 0
|
|
800
|
+
float numerator = b2Dot( shape->normals[i], b2Sub( shape->vertices[i], p1 ) );
|
|
801
|
+
float denominator = b2Dot( shape->normals[i], d );
|
|
802
|
+
|
|
803
|
+
if ( denominator == 0.0f )
|
|
804
|
+
{
|
|
805
|
+
if ( numerator < 0.0f )
|
|
806
|
+
{
|
|
807
|
+
return output;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
else
|
|
811
|
+
{
|
|
812
|
+
// Note: we want this predicate without division:
|
|
813
|
+
// lower < numerator / denominator, where denominator < 0
|
|
814
|
+
// Since denominator < 0, we have to flip the inequality:
|
|
815
|
+
// lower < numerator / denominator <==> denominator * lower > numerator.
|
|
816
|
+
if ( denominator < 0.0f && numerator < lower * denominator )
|
|
817
|
+
{
|
|
818
|
+
// Increase lower.
|
|
819
|
+
// The segment enters this half-space.
|
|
820
|
+
lower = numerator / denominator;
|
|
821
|
+
index = i;
|
|
822
|
+
}
|
|
823
|
+
else if ( denominator > 0.0f && numerator < upper * denominator )
|
|
824
|
+
{
|
|
825
|
+
// Decrease upper.
|
|
826
|
+
// The segment exits this half-space.
|
|
827
|
+
upper = numerator / denominator;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// The use of epsilon here causes the B2_ASSERT on lower to trip
|
|
832
|
+
// in some cases. Apparently the use of epsilon was to make edge
|
|
833
|
+
// shapes work, but now those are handled separately.
|
|
834
|
+
// if (upper < lower - b2_epsilon)
|
|
835
|
+
if ( upper < lower )
|
|
836
|
+
{
|
|
837
|
+
return output;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
B2_ASSERT( 0.0f <= lower && lower <= input->maxFraction );
|
|
842
|
+
|
|
843
|
+
if ( index >= 0 )
|
|
844
|
+
{
|
|
845
|
+
output.fraction = lower;
|
|
846
|
+
output.normal = shape->normals[index];
|
|
847
|
+
output.point = b2MulAdd( p1, lower, d );
|
|
848
|
+
output.hit = true;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return output;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// TODO_ERIN this is not working for ray vs box (zero radii)
|
|
855
|
+
b2ShapeCastPairInput castInput;
|
|
856
|
+
castInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );
|
|
857
|
+
castInput.proxyB = b2MakeProxy( &input->origin, 1, 0.0f );
|
|
858
|
+
castInput.transformA = b2Transform_identity;
|
|
859
|
+
castInput.transformB = b2Transform_identity;
|
|
860
|
+
castInput.translationB = input->translation;
|
|
861
|
+
castInput.maxFraction = input->maxFraction;
|
|
862
|
+
castInput.canEncroach = false;
|
|
863
|
+
return b2ShapeCast( &castInput );
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
b2CastOutput b2ShapeCastCircle( const b2ShapeCastInput* input, const b2Circle* shape )
|
|
867
|
+
{
|
|
868
|
+
b2ShapeCastPairInput pairInput;
|
|
869
|
+
pairInput.proxyA = b2MakeProxy( &shape->center, 1, shape->radius );
|
|
870
|
+
pairInput.proxyB = input->proxy;
|
|
871
|
+
pairInput.transformA = b2Transform_identity;
|
|
872
|
+
pairInput.transformB = b2Transform_identity;
|
|
873
|
+
pairInput.translationB = input->translation;
|
|
874
|
+
pairInput.maxFraction = input->maxFraction;
|
|
875
|
+
pairInput.canEncroach = input->canEncroach;
|
|
876
|
+
|
|
877
|
+
b2CastOutput output = b2ShapeCast( &pairInput );
|
|
878
|
+
return output;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
b2CastOutput b2ShapeCastCapsule( const b2ShapeCastInput* input, const b2Capsule* shape )
|
|
882
|
+
{
|
|
883
|
+
b2ShapeCastPairInput pairInput;
|
|
884
|
+
pairInput.proxyA = b2MakeProxy( &shape->center1, 2, shape->radius );
|
|
885
|
+
pairInput.proxyB = input->proxy;
|
|
886
|
+
pairInput.transformA = b2Transform_identity;
|
|
887
|
+
pairInput.transformB = b2Transform_identity;
|
|
888
|
+
pairInput.translationB = input->translation;
|
|
889
|
+
pairInput.maxFraction = input->maxFraction;
|
|
890
|
+
pairInput.canEncroach = input->canEncroach;
|
|
891
|
+
|
|
892
|
+
b2CastOutput output = b2ShapeCast( &pairInput );
|
|
893
|
+
return output;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
b2CastOutput b2ShapeCastSegment( const b2ShapeCastInput* input, const b2Segment* shape )
|
|
897
|
+
{
|
|
898
|
+
b2ShapeCastPairInput pairInput;
|
|
899
|
+
pairInput.proxyA = b2MakeProxy( &shape->point1, 2, 0.0f );
|
|
900
|
+
pairInput.proxyB = input->proxy;
|
|
901
|
+
pairInput.transformA = b2Transform_identity;
|
|
902
|
+
pairInput.transformB = b2Transform_identity;
|
|
903
|
+
pairInput.translationB = input->translation;
|
|
904
|
+
pairInput.maxFraction = input->maxFraction;
|
|
905
|
+
pairInput.canEncroach = input->canEncroach;
|
|
906
|
+
|
|
907
|
+
b2CastOutput output = b2ShapeCast( &pairInput );
|
|
908
|
+
return output;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
b2CastOutput b2ShapeCastPolygon( const b2ShapeCastInput* input, const b2Polygon* shape )
|
|
912
|
+
{
|
|
913
|
+
b2ShapeCastPairInput pairInput;
|
|
914
|
+
pairInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );
|
|
915
|
+
pairInput.proxyB = input->proxy;
|
|
916
|
+
pairInput.transformA = b2Transform_identity;
|
|
917
|
+
pairInput.transformB = b2Transform_identity;
|
|
918
|
+
pairInput.translationB = input->translation;
|
|
919
|
+
pairInput.maxFraction = input->maxFraction;
|
|
920
|
+
pairInput.canEncroach = input->canEncroach;
|
|
921
|
+
|
|
922
|
+
b2CastOutput output = b2ShapeCast( &pairInput );
|
|
923
|
+
return output;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
b2PlaneResult b2CollideMoverAndCircle( const b2Circle* shape, const b2Capsule* mover )
|
|
927
|
+
{
|
|
928
|
+
b2DistanceInput distanceInput;
|
|
929
|
+
distanceInput.proxyA = b2MakeProxy( &shape->center, 1, 0.0f );
|
|
930
|
+
distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
|
|
931
|
+
distanceInput.transformA = b2Transform_identity;
|
|
932
|
+
distanceInput.transformB = b2Transform_identity;
|
|
933
|
+
distanceInput.useRadii = false;
|
|
934
|
+
|
|
935
|
+
float totalRadius = mover->radius + shape->radius;
|
|
936
|
+
|
|
937
|
+
b2SimplexCache cache = { 0 };
|
|
938
|
+
b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
|
|
939
|
+
|
|
940
|
+
if ( distanceOutput.distance <= totalRadius )
|
|
941
|
+
{
|
|
942
|
+
b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
|
|
943
|
+
return (b2PlaneResult){
|
|
944
|
+
.plane = plane,
|
|
945
|
+
.hit = true,
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
return (b2PlaneResult){ 0 };
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
b2PlaneResult b2CollideMoverAndCapsule( const b2Capsule* shape, const b2Capsule* mover )
|
|
953
|
+
{
|
|
954
|
+
b2DistanceInput distanceInput;
|
|
955
|
+
distanceInput.proxyA = b2MakeProxy( &shape->center1, 2, 0.0f );
|
|
956
|
+
distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
|
|
957
|
+
distanceInput.transformA = b2Transform_identity;
|
|
958
|
+
distanceInput.transformB = b2Transform_identity;
|
|
959
|
+
distanceInput.useRadii = false;
|
|
960
|
+
|
|
961
|
+
float totalRadius = mover->radius + shape->radius;
|
|
962
|
+
|
|
963
|
+
b2SimplexCache cache = { 0 };
|
|
964
|
+
b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
|
|
965
|
+
|
|
966
|
+
if ( distanceOutput.distance <= totalRadius )
|
|
967
|
+
{
|
|
968
|
+
b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
|
|
969
|
+
return (b2PlaneResult){
|
|
970
|
+
.plane = plane,
|
|
971
|
+
.hit = true,
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
return (b2PlaneResult){ 0 };
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
b2PlaneResult b2CollideMoverAndPolygon( const b2Polygon* shape, const b2Capsule* mover )
|
|
979
|
+
{
|
|
980
|
+
b2DistanceInput distanceInput;
|
|
981
|
+
distanceInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );
|
|
982
|
+
distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
|
|
983
|
+
distanceInput.transformA = b2Transform_identity;
|
|
984
|
+
distanceInput.transformB = b2Transform_identity;
|
|
985
|
+
distanceInput.useRadii = false;
|
|
986
|
+
|
|
987
|
+
float totalRadius = mover->radius + shape->radius;
|
|
988
|
+
|
|
989
|
+
b2SimplexCache cache = { 0 };
|
|
990
|
+
b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
|
|
991
|
+
|
|
992
|
+
if ( distanceOutput.distance <= totalRadius )
|
|
993
|
+
{
|
|
994
|
+
b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
|
|
995
|
+
return (b2PlaneResult){
|
|
996
|
+
.plane = plane,
|
|
997
|
+
.hit = true,
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return (b2PlaneResult){ 0 };
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
b2PlaneResult b2CollideMoverAndSegment( const b2Segment* shape, const b2Capsule* mover )
|
|
1005
|
+
{
|
|
1006
|
+
b2DistanceInput distanceInput;
|
|
1007
|
+
distanceInput.proxyA = b2MakeProxy( &shape->point1, 2, 0.0f );
|
|
1008
|
+
distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
|
|
1009
|
+
distanceInput.transformA = b2Transform_identity;
|
|
1010
|
+
distanceInput.transformB = b2Transform_identity;
|
|
1011
|
+
distanceInput.useRadii = false;
|
|
1012
|
+
|
|
1013
|
+
float totalRadius = mover->radius;
|
|
1014
|
+
|
|
1015
|
+
b2SimplexCache cache = { 0 };
|
|
1016
|
+
b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
|
|
1017
|
+
|
|
1018
|
+
if ( distanceOutput.distance <= totalRadius )
|
|
1019
|
+
{
|
|
1020
|
+
b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
|
|
1021
|
+
return (b2PlaneResult){
|
|
1022
|
+
.plane = plane,
|
|
1023
|
+
.hit = true,
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
return (b2PlaneResult){ 0 };
|
|
1028
|
+
}
|