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,2038 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2023 Erin Catto
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
#include "solver.h"
|
|
5
|
+
|
|
6
|
+
#include "array.h"
|
|
7
|
+
#include "atomic.h"
|
|
8
|
+
#include "bitset.h"
|
|
9
|
+
#include "body.h"
|
|
10
|
+
#include "contact.h"
|
|
11
|
+
#include "contact_solver.h"
|
|
12
|
+
#include "core.h"
|
|
13
|
+
#include "ctz.h"
|
|
14
|
+
#include "island.h"
|
|
15
|
+
#include "joint.h"
|
|
16
|
+
#include "shape.h"
|
|
17
|
+
#include "solver_set.h"
|
|
18
|
+
#include "arena_allocator.h"
|
|
19
|
+
#include "world.h"
|
|
20
|
+
|
|
21
|
+
#include <limits.h>
|
|
22
|
+
#include <stdbool.h>
|
|
23
|
+
#include <stddef.h>
|
|
24
|
+
|
|
25
|
+
// Compare to SDL_CPUPauseInstruction
|
|
26
|
+
#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __i386__ ) || defined( __x86_64__ ) )
|
|
27
|
+
static inline void b2Pause( void )
|
|
28
|
+
{
|
|
29
|
+
__asm__ __volatile__( "pause\n" );
|
|
30
|
+
}
|
|
31
|
+
#elif ( defined( __arm__ ) && defined( __ARM_ARCH ) && __ARM_ARCH >= 7 ) || defined( __aarch64__ )
|
|
32
|
+
static inline void b2Pause( void )
|
|
33
|
+
{
|
|
34
|
+
__asm__ __volatile__( "yield" ::: "memory" );
|
|
35
|
+
}
|
|
36
|
+
#elif defined( _MSC_VER ) && ( defined( _M_IX86 ) || defined( _M_X64 ) )
|
|
37
|
+
//#include <immintrin.h>
|
|
38
|
+
static inline void b2Pause( void )
|
|
39
|
+
{
|
|
40
|
+
_mm_pause();
|
|
41
|
+
}
|
|
42
|
+
#elif defined( _MSC_VER ) && ( defined( _M_ARM ) || defined( _M_ARM64 ) )
|
|
43
|
+
static inline void b2Pause( void )
|
|
44
|
+
{
|
|
45
|
+
__yield();
|
|
46
|
+
}
|
|
47
|
+
#else
|
|
48
|
+
static inline void b2Pause( void )
|
|
49
|
+
{
|
|
50
|
+
}
|
|
51
|
+
#endif
|
|
52
|
+
|
|
53
|
+
typedef struct b2WorkerContext
|
|
54
|
+
{
|
|
55
|
+
b2StepContext* context;
|
|
56
|
+
int workerIndex;
|
|
57
|
+
void* userTask;
|
|
58
|
+
} b2WorkerContext;
|
|
59
|
+
|
|
60
|
+
// Integrate velocities and apply damping
|
|
61
|
+
static void b2IntegrateVelocitiesTask( int startIndex, int endIndex, b2StepContext* context )
|
|
62
|
+
{
|
|
63
|
+
b2TracyCZoneNC( integrate_velocity, "IntVel", b2_colorDeepPink, true );
|
|
64
|
+
|
|
65
|
+
b2BodyState* states = context->states;
|
|
66
|
+
b2BodySim* sims = context->sims;
|
|
67
|
+
|
|
68
|
+
b2Vec2 gravity = context->world->gravity;
|
|
69
|
+
float h = context->h;
|
|
70
|
+
float maxLinearSpeed = context->maxLinearVelocity;
|
|
71
|
+
float maxAngularSpeed = B2_MAX_ROTATION * context->inv_dt;
|
|
72
|
+
float maxLinearSpeedSquared = maxLinearSpeed * maxLinearSpeed;
|
|
73
|
+
float maxAngularSpeedSquared = maxAngularSpeed * maxAngularSpeed;
|
|
74
|
+
|
|
75
|
+
for ( int i = startIndex; i < endIndex; ++i )
|
|
76
|
+
{
|
|
77
|
+
b2BodySim* sim = sims + i;
|
|
78
|
+
b2BodyState* state = states + i;
|
|
79
|
+
|
|
80
|
+
b2Vec2 v = state->linearVelocity;
|
|
81
|
+
float w = state->angularVelocity;
|
|
82
|
+
|
|
83
|
+
// Apply forces, torque, gravity, and damping
|
|
84
|
+
// Apply damping.
|
|
85
|
+
// Differential equation: dv/dt + c * v = 0
|
|
86
|
+
// Solution: v(t) = v0 * exp(-c * t)
|
|
87
|
+
// Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v(t) * exp(-c * dt)
|
|
88
|
+
// v2 = exp(-c * dt) * v1
|
|
89
|
+
// Pade approximation:
|
|
90
|
+
// v2 = v1 * 1 / (1 + c * dt)
|
|
91
|
+
float linearDamping = 1.0f / ( 1.0f + h * sim->linearDamping );
|
|
92
|
+
float angularDamping = 1.0f / ( 1.0f + h * sim->angularDamping );
|
|
93
|
+
|
|
94
|
+
// Gravity scale will be zero for kinematic bodies
|
|
95
|
+
float gravityScale = sim->invMass > 0.0f ? sim->gravityScale : 0.0f;
|
|
96
|
+
|
|
97
|
+
// lvd = h * im * f + h * g
|
|
98
|
+
b2Vec2 linearVelocityDelta = b2Add( b2MulSV( h * sim->invMass, sim->force ), b2MulSV( h * gravityScale, gravity ) );
|
|
99
|
+
float angularVelocityDelta = h * sim->invInertia * sim->torque;
|
|
100
|
+
|
|
101
|
+
v = b2MulAdd( linearVelocityDelta, linearDamping, v );
|
|
102
|
+
w = angularVelocityDelta + angularDamping * w;
|
|
103
|
+
|
|
104
|
+
// Clamp to max linear speed
|
|
105
|
+
if ( b2Dot( v, v ) > maxLinearSpeedSquared )
|
|
106
|
+
{
|
|
107
|
+
float ratio = maxLinearSpeed / b2Length( v );
|
|
108
|
+
v = b2MulSV( ratio, v );
|
|
109
|
+
sim->isSpeedCapped = true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Clamp to max angular speed
|
|
113
|
+
if ( w * w > maxAngularSpeedSquared && sim->allowFastRotation == false )
|
|
114
|
+
{
|
|
115
|
+
float ratio = maxAngularSpeed / b2AbsFloat( w );
|
|
116
|
+
w *= ratio;
|
|
117
|
+
sim->isSpeedCapped = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
state->linearVelocity = v;
|
|
121
|
+
state->angularVelocity = w;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
b2TracyCZoneEnd( integrate_velocity );
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static void b2PrepareJointsTask( int startIndex, int endIndex, b2StepContext* context )
|
|
128
|
+
{
|
|
129
|
+
b2TracyCZoneNC( prepare_joints, "PrepJoints", b2_colorOldLace, true );
|
|
130
|
+
|
|
131
|
+
b2JointSim** joints = context->joints;
|
|
132
|
+
|
|
133
|
+
for ( int i = startIndex; i < endIndex; ++i )
|
|
134
|
+
{
|
|
135
|
+
b2JointSim* joint = joints[i];
|
|
136
|
+
b2PrepareJoint( joint, context );
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
b2TracyCZoneEnd( prepare_joints );
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static void b2WarmStartJointsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex )
|
|
143
|
+
{
|
|
144
|
+
b2TracyCZoneNC( warm_joints, "WarmJoints", b2_colorGold, true );
|
|
145
|
+
|
|
146
|
+
b2GraphColor* color = context->graph->colors + colorIndex;
|
|
147
|
+
b2JointSim* joints = color->jointSims.data;
|
|
148
|
+
B2_ASSERT( 0 <= startIndex && startIndex < color->jointSims.count );
|
|
149
|
+
B2_ASSERT( startIndex <= endIndex && endIndex <= color->jointSims.count );
|
|
150
|
+
|
|
151
|
+
for ( int i = startIndex; i < endIndex; ++i )
|
|
152
|
+
{
|
|
153
|
+
b2JointSim* joint = joints + i;
|
|
154
|
+
b2WarmStartJoint( joint, context );
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
b2TracyCZoneEnd( warm_joints );
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static void b2SolveJointsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias )
|
|
161
|
+
{
|
|
162
|
+
b2TracyCZoneNC( solve_joints, "SolveJoints", b2_colorLemonChiffon, true );
|
|
163
|
+
|
|
164
|
+
b2GraphColor* color = context->graph->colors + colorIndex;
|
|
165
|
+
b2JointSim* joints = color->jointSims.data;
|
|
166
|
+
B2_ASSERT( 0 <= startIndex && startIndex < color->jointSims.count );
|
|
167
|
+
B2_ASSERT( startIndex <= endIndex && endIndex <= color->jointSims.count );
|
|
168
|
+
|
|
169
|
+
for ( int i = startIndex; i < endIndex; ++i )
|
|
170
|
+
{
|
|
171
|
+
b2JointSim* joint = joints + i;
|
|
172
|
+
b2SolveJoint( joint, context, useBias );
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
b2TracyCZoneEnd( solve_joints );
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static void b2IntegratePositionsTask( int startIndex, int endIndex, b2StepContext* context )
|
|
179
|
+
{
|
|
180
|
+
b2TracyCZoneNC( integrate_positions, "IntPos", b2_colorDarkSeaGreen, true );
|
|
181
|
+
|
|
182
|
+
b2BodyState* states = context->states;
|
|
183
|
+
float h = context->h;
|
|
184
|
+
|
|
185
|
+
B2_ASSERT( startIndex <= endIndex );
|
|
186
|
+
|
|
187
|
+
for ( int i = startIndex; i < endIndex; ++i )
|
|
188
|
+
{
|
|
189
|
+
b2BodyState* state = states + i;
|
|
190
|
+
state->deltaRotation = b2IntegrateRotation( state->deltaRotation, h * state->angularVelocity );
|
|
191
|
+
state->deltaPosition = b2MulAdd( state->deltaPosition, h, state->linearVelocity );
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
b2TracyCZoneEnd( integrate_positions );
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
struct b2ContinuousContext
|
|
198
|
+
{
|
|
199
|
+
b2World* world;
|
|
200
|
+
b2BodySim* fastBodySim;
|
|
201
|
+
b2Shape* fastShape;
|
|
202
|
+
b2Vec2 centroid1, centroid2;
|
|
203
|
+
b2Sweep sweep;
|
|
204
|
+
float fraction;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// This is called from b2DynamicTree_Query for continuous collision
|
|
208
|
+
static bool b2ContinuousQueryCallback( int proxyId, uint64_t userData, void* context )
|
|
209
|
+
{
|
|
210
|
+
B2_UNUSED( proxyId );
|
|
211
|
+
|
|
212
|
+
int shapeId = (int)userData;
|
|
213
|
+
|
|
214
|
+
struct b2ContinuousContext* continuousContext = context;
|
|
215
|
+
b2Shape* fastShape = continuousContext->fastShape;
|
|
216
|
+
b2BodySim* fastBodySim = continuousContext->fastBodySim;
|
|
217
|
+
|
|
218
|
+
// Skip same shape
|
|
219
|
+
if ( shapeId == fastShape->id )
|
|
220
|
+
{
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
b2World* world = continuousContext->world;
|
|
225
|
+
|
|
226
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
227
|
+
|
|
228
|
+
// Skip same body
|
|
229
|
+
if ( shape->bodyId == fastShape->bodyId )
|
|
230
|
+
{
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Skip sensors
|
|
235
|
+
if ( shape->sensorIndex != B2_NULL_INDEX )
|
|
236
|
+
{
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Skip filtered shapes
|
|
241
|
+
bool canCollide = b2ShouldShapesCollide( fastShape->filter, shape->filter );
|
|
242
|
+
if ( canCollide == false )
|
|
243
|
+
{
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
248
|
+
|
|
249
|
+
b2BodySim* bodySim = b2GetBodySim( world, body );
|
|
250
|
+
B2_ASSERT( body->type == b2_staticBody || fastBodySim->isBullet );
|
|
251
|
+
|
|
252
|
+
// Skip bullets
|
|
253
|
+
if ( bodySim->isBullet )
|
|
254
|
+
{
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Skip filtered bodies
|
|
259
|
+
b2Body* fastBody = b2BodyArray_Get( &world->bodies, fastBodySim->bodyId );
|
|
260
|
+
canCollide = b2ShouldBodiesCollide( world, fastBody, body );
|
|
261
|
+
if ( canCollide == false )
|
|
262
|
+
{
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Custom user filtering
|
|
267
|
+
b2CustomFilterFcn* customFilterFcn = world->customFilterFcn;
|
|
268
|
+
if ( customFilterFcn != NULL )
|
|
269
|
+
{
|
|
270
|
+
b2ShapeId idA = { shape->id + 1, world->worldId, shape->generation };
|
|
271
|
+
b2ShapeId idB = { fastShape->id + 1, world->worldId, fastShape->generation };
|
|
272
|
+
canCollide = customFilterFcn( idA, idB, world->customFilterContext );
|
|
273
|
+
if ( canCollide == false )
|
|
274
|
+
{
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Prevent pausing on chain segment junctions
|
|
280
|
+
if ( shape->type == b2_chainSegmentShape )
|
|
281
|
+
{
|
|
282
|
+
b2Transform transform = bodySim->transform;
|
|
283
|
+
b2Vec2 p1 = b2TransformPoint( transform, shape->chainSegment.segment.point1 );
|
|
284
|
+
b2Vec2 p2 = b2TransformPoint( transform, shape->chainSegment.segment.point2 );
|
|
285
|
+
b2Vec2 e = b2Sub( p2, p1 );
|
|
286
|
+
float length;
|
|
287
|
+
e = b2GetLengthAndNormalize( &length, e );
|
|
288
|
+
if (length > B2_LINEAR_SLOP)
|
|
289
|
+
{
|
|
290
|
+
b2Vec2 c1 = continuousContext->centroid1;
|
|
291
|
+
float offset1 = b2Cross( b2Sub( c1, p1 ), e );
|
|
292
|
+
b2Vec2 c2 = continuousContext->centroid2;
|
|
293
|
+
float offset2 = b2Cross( b2Sub( c2, p1 ), e );
|
|
294
|
+
|
|
295
|
+
// todo this should use the min extent of the fast shape, not the body
|
|
296
|
+
const float allowedFraction = 0.25f;
|
|
297
|
+
if ( offset1 < 0.0f || offset1 - offset2 < allowedFraction * fastBodySim->minExtent )
|
|
298
|
+
{
|
|
299
|
+
// Minimal clipping
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// todo_erin testing early out for segments
|
|
306
|
+
#if 0
|
|
307
|
+
if ( shape->type == b2_segmentShape )
|
|
308
|
+
{
|
|
309
|
+
b2Transform transform = bodySim->transform;
|
|
310
|
+
b2Vec2 p1 = b2TransformPoint( transform, shape->segment.point1 );
|
|
311
|
+
b2Vec2 p2 = b2TransformPoint( transform, shape->segment.point2 );
|
|
312
|
+
b2Vec2 e = b2Sub( p2, p1 );
|
|
313
|
+
b2Vec2 c1 = continuousContext->centroid1;
|
|
314
|
+
b2Vec2 c2 = continuousContext->centroid2;
|
|
315
|
+
float offset1 = b2Cross( b2Sub( c1, p1 ), e );
|
|
316
|
+
float offset2 = b2Cross( b2Sub( c2, p1 ), e );
|
|
317
|
+
|
|
318
|
+
if ( offset1 > 0.0f && offset2 > 0.0f )
|
|
319
|
+
{
|
|
320
|
+
// Started behind or finished in front
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if ( offset1 < 0.0f && offset2 < 0.0f )
|
|
325
|
+
{
|
|
326
|
+
// Started behind or finished in front
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
#endif
|
|
331
|
+
|
|
332
|
+
b2TOIInput input;
|
|
333
|
+
input.proxyA = b2MakeShapeDistanceProxy( shape );
|
|
334
|
+
input.proxyB = b2MakeShapeDistanceProxy( fastShape );
|
|
335
|
+
input.sweepA = b2MakeSweep( bodySim );
|
|
336
|
+
input.sweepB = continuousContext->sweep;
|
|
337
|
+
input.maxFraction = continuousContext->fraction;
|
|
338
|
+
|
|
339
|
+
float hitFraction = continuousContext->fraction;
|
|
340
|
+
|
|
341
|
+
bool didHit = false;
|
|
342
|
+
b2TOIOutput output = b2TimeOfImpact( &input );
|
|
343
|
+
if ( 0.0f < output.fraction && output.fraction < continuousContext->fraction )
|
|
344
|
+
{
|
|
345
|
+
hitFraction = output.fraction;
|
|
346
|
+
didHit = true;
|
|
347
|
+
}
|
|
348
|
+
else if ( 0.0f == output.fraction )
|
|
349
|
+
{
|
|
350
|
+
// fallback to TOI of a small circle around the fast shape centroid
|
|
351
|
+
b2Vec2 centroid = b2GetShapeCentroid( fastShape );
|
|
352
|
+
b2ShapeExtent extent = b2ComputeShapeExtent( fastShape, centroid );
|
|
353
|
+
float radius = 0.25f * extent.minExtent;
|
|
354
|
+
input.proxyB = b2MakeProxy( ¢roid, 1, radius );
|
|
355
|
+
output = b2TimeOfImpact( &input );
|
|
356
|
+
if ( 0.0f < output.fraction && output.fraction < continuousContext->fraction )
|
|
357
|
+
{
|
|
358
|
+
hitFraction = output.fraction;
|
|
359
|
+
didHit = true;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if ( didHit && ( shape->enablePreSolveEvents || fastShape->enablePreSolveEvents ) )
|
|
364
|
+
{
|
|
365
|
+
// Pre-solve is expensive because I need to compute a temporary manifold
|
|
366
|
+
b2Transform transformA = b2GetSweepTransform( &input.sweepA, hitFraction );
|
|
367
|
+
b2Transform transformB = b2GetSweepTransform( &input.sweepB, hitFraction );
|
|
368
|
+
b2Manifold manifold = b2ComputeManifold( shape, transformA, fastShape, transformB );
|
|
369
|
+
b2ShapeId shapeIdA = { shape->id + 1, world->worldId, shape->generation };
|
|
370
|
+
b2ShapeId shapeIdB = { fastShape->id + 1, world->worldId, fastShape->generation };
|
|
371
|
+
|
|
372
|
+
// The user may modify the temporary manifold here but it doesn't matter. They will be able to
|
|
373
|
+
// modify the real manifold in the discrete solver.
|
|
374
|
+
didHit = world->preSolveFcn( shapeIdA, shapeIdB, &manifold, world->preSolveContext );
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if ( didHit )
|
|
378
|
+
{
|
|
379
|
+
continuousContext->fraction = hitFraction;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Continuous collision of dynamic versus static
|
|
386
|
+
static void b2SolveContinuous( b2World* world, int bodySimIndex )
|
|
387
|
+
{
|
|
388
|
+
b2TracyCZoneNC( ccd, "CCD", b2_colorDarkGoldenRod, true );
|
|
389
|
+
|
|
390
|
+
b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
|
|
391
|
+
b2BodySim* fastBodySim = b2BodySimArray_Get( &awakeSet->bodySims, bodySimIndex );
|
|
392
|
+
B2_ASSERT( fastBodySim->isFast );
|
|
393
|
+
|
|
394
|
+
b2Sweep sweep = b2MakeSweep( fastBodySim );
|
|
395
|
+
|
|
396
|
+
b2Transform xf1;
|
|
397
|
+
xf1.q = sweep.q1;
|
|
398
|
+
xf1.p = b2Sub( sweep.c1, b2RotateVector( sweep.q1, sweep.localCenter ) );
|
|
399
|
+
|
|
400
|
+
b2Transform xf2;
|
|
401
|
+
xf2.q = sweep.q2;
|
|
402
|
+
xf2.p = b2Sub( sweep.c2, b2RotateVector( sweep.q2, sweep.localCenter ) );
|
|
403
|
+
|
|
404
|
+
b2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody;
|
|
405
|
+
b2DynamicTree* kinematicTree = world->broadPhase.trees + b2_kinematicBody;
|
|
406
|
+
b2DynamicTree* dynamicTree = world->broadPhase.trees + b2_dynamicBody;
|
|
407
|
+
b2Body* fastBody = b2BodyArray_Get( &world->bodies, fastBodySim->bodyId );
|
|
408
|
+
|
|
409
|
+
struct b2ContinuousContext context;
|
|
410
|
+
context.world = world;
|
|
411
|
+
context.sweep = sweep;
|
|
412
|
+
context.fastBodySim = fastBodySim;
|
|
413
|
+
context.fraction = 1.0f;
|
|
414
|
+
|
|
415
|
+
bool isBullet = fastBodySim->isBullet;
|
|
416
|
+
|
|
417
|
+
int shapeId = fastBody->headShapeId;
|
|
418
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
419
|
+
{
|
|
420
|
+
b2Shape* fastShape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
421
|
+
shapeId = fastShape->nextShapeId;
|
|
422
|
+
|
|
423
|
+
context.fastShape = fastShape;
|
|
424
|
+
context.centroid1 = b2TransformPoint( xf1, fastShape->localCentroid );
|
|
425
|
+
context.centroid2 = b2TransformPoint( xf2, fastShape->localCentroid );
|
|
426
|
+
|
|
427
|
+
b2AABB box1 = fastShape->aabb;
|
|
428
|
+
b2AABB box2 = b2ComputeShapeAABB( fastShape, xf2 );
|
|
429
|
+
b2AABB box = b2AABB_Union( box1, box2 );
|
|
430
|
+
|
|
431
|
+
// Store this to avoid double computation in the case there is no impact event
|
|
432
|
+
fastShape->aabb = box2;
|
|
433
|
+
|
|
434
|
+
// No continuous collision for sensors (but still need the updated bounds)
|
|
435
|
+
if ( fastShape->sensorIndex != B2_NULL_INDEX )
|
|
436
|
+
{
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
b2DynamicTree_Query( staticTree, box, B2_DEFAULT_MASK_BITS, b2ContinuousQueryCallback, &context );
|
|
441
|
+
|
|
442
|
+
if ( isBullet )
|
|
443
|
+
{
|
|
444
|
+
b2DynamicTree_Query( kinematicTree, box, B2_DEFAULT_MASK_BITS, b2ContinuousQueryCallback, &context );
|
|
445
|
+
b2DynamicTree_Query( dynamicTree, box, B2_DEFAULT_MASK_BITS, b2ContinuousQueryCallback, &context );
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const float speculativeDistance = B2_SPECULATIVE_DISTANCE;
|
|
450
|
+
const float aabbMargin = B2_AABB_MARGIN;
|
|
451
|
+
|
|
452
|
+
if ( context.fraction < 1.0f )
|
|
453
|
+
{
|
|
454
|
+
// Handle time of impact event
|
|
455
|
+
b2Rot q = b2NLerp( sweep.q1, sweep.q2, context.fraction );
|
|
456
|
+
b2Vec2 c = b2Lerp( sweep.c1, sweep.c2, context.fraction );
|
|
457
|
+
b2Vec2 origin = b2Sub( c, b2RotateVector( q, sweep.localCenter ) );
|
|
458
|
+
|
|
459
|
+
// Advance body
|
|
460
|
+
b2Transform transform = { origin, q };
|
|
461
|
+
fastBodySim->transform = transform;
|
|
462
|
+
fastBodySim->center = c;
|
|
463
|
+
fastBodySim->rotation0 = q;
|
|
464
|
+
fastBodySim->center0 = c;
|
|
465
|
+
|
|
466
|
+
// Update body move event
|
|
467
|
+
b2BodyMoveEvent* event = b2BodyMoveEventArray_Get( &world->bodyMoveEvents, bodySimIndex );
|
|
468
|
+
event->transform = transform;
|
|
469
|
+
|
|
470
|
+
// Prepare AABBs for broad-phase.
|
|
471
|
+
// Even though a body is fast, it may not move much. So the
|
|
472
|
+
// AABB may not need enlargement.
|
|
473
|
+
|
|
474
|
+
shapeId = fastBody->headShapeId;
|
|
475
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
476
|
+
{
|
|
477
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
478
|
+
|
|
479
|
+
// Must recompute aabb at the interpolated transform
|
|
480
|
+
b2AABB aabb = b2ComputeShapeAABB( shape, transform );
|
|
481
|
+
aabb.lowerBound.x -= speculativeDistance;
|
|
482
|
+
aabb.lowerBound.y -= speculativeDistance;
|
|
483
|
+
aabb.upperBound.x += speculativeDistance;
|
|
484
|
+
aabb.upperBound.y += speculativeDistance;
|
|
485
|
+
shape->aabb = aabb;
|
|
486
|
+
|
|
487
|
+
if ( b2AABB_Contains( shape->fatAABB, aabb ) == false )
|
|
488
|
+
{
|
|
489
|
+
b2AABB fatAABB;
|
|
490
|
+
fatAABB.lowerBound.x = aabb.lowerBound.x - aabbMargin;
|
|
491
|
+
fatAABB.lowerBound.y = aabb.lowerBound.y - aabbMargin;
|
|
492
|
+
fatAABB.upperBound.x = aabb.upperBound.x + aabbMargin;
|
|
493
|
+
fatAABB.upperBound.y = aabb.upperBound.y + aabbMargin;
|
|
494
|
+
shape->fatAABB = fatAABB;
|
|
495
|
+
|
|
496
|
+
shape->enlargedAABB = true;
|
|
497
|
+
fastBodySim->enlargeAABB = true;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
shapeId = shape->nextShapeId;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
else
|
|
504
|
+
{
|
|
505
|
+
// No time of impact event
|
|
506
|
+
|
|
507
|
+
// Advance body
|
|
508
|
+
fastBodySim->rotation0 = fastBodySim->transform.q;
|
|
509
|
+
fastBodySim->center0 = fastBodySim->center;
|
|
510
|
+
|
|
511
|
+
// Prepare AABBs for broad-phase
|
|
512
|
+
shapeId = fastBody->headShapeId;
|
|
513
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
514
|
+
{
|
|
515
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
516
|
+
|
|
517
|
+
// shape->aabb is still valid from above
|
|
518
|
+
|
|
519
|
+
if ( b2AABB_Contains( shape->fatAABB, shape->aabb ) == false )
|
|
520
|
+
{
|
|
521
|
+
b2AABB fatAABB;
|
|
522
|
+
fatAABB.lowerBound.x = shape->aabb.lowerBound.x - aabbMargin;
|
|
523
|
+
fatAABB.lowerBound.y = shape->aabb.lowerBound.y - aabbMargin;
|
|
524
|
+
fatAABB.upperBound.x = shape->aabb.upperBound.x + aabbMargin;
|
|
525
|
+
fatAABB.upperBound.y = shape->aabb.upperBound.y + aabbMargin;
|
|
526
|
+
shape->fatAABB = fatAABB;
|
|
527
|
+
|
|
528
|
+
shape->enlargedAABB = true;
|
|
529
|
+
fastBodySim->enlargeAABB = true;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
shapeId = shape->nextShapeId;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
b2TracyCZoneEnd( ccd );
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
static void b2FinalizeBodiesTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )
|
|
540
|
+
{
|
|
541
|
+
b2TracyCZoneNC( finalize_transfprms, "Transforms", b2_colorMediumSeaGreen, true );
|
|
542
|
+
|
|
543
|
+
b2StepContext* stepContext = context;
|
|
544
|
+
b2World* world = stepContext->world;
|
|
545
|
+
bool enableSleep = world->enableSleep;
|
|
546
|
+
b2BodyState* states = stepContext->states;
|
|
547
|
+
b2BodySim* sims = stepContext->sims;
|
|
548
|
+
b2Body* bodies = world->bodies.data;
|
|
549
|
+
float timeStep = stepContext->dt;
|
|
550
|
+
float invTimeStep = stepContext->inv_dt;
|
|
551
|
+
|
|
552
|
+
uint16_t worldId = world->worldId;
|
|
553
|
+
|
|
554
|
+
// The body move event array should already have the correct size
|
|
555
|
+
B2_ASSERT( endIndex <= world->bodyMoveEvents.count );
|
|
556
|
+
b2BodyMoveEvent* moveEvents = world->bodyMoveEvents.data;
|
|
557
|
+
|
|
558
|
+
b2BitSet* enlargedSimBitSet = &world->taskContexts.data[threadIndex].enlargedSimBitSet;
|
|
559
|
+
b2BitSet* awakeIslandBitSet = &world->taskContexts.data[threadIndex].awakeIslandBitSet;
|
|
560
|
+
b2TaskContext* taskContext = world->taskContexts.data + threadIndex;
|
|
561
|
+
|
|
562
|
+
bool enableContinuous = world->enableContinuous;
|
|
563
|
+
|
|
564
|
+
const float speculativeDistance = B2_SPECULATIVE_DISTANCE;
|
|
565
|
+
const float aabbMargin = B2_AABB_MARGIN;
|
|
566
|
+
|
|
567
|
+
B2_ASSERT( startIndex <= endIndex );
|
|
568
|
+
|
|
569
|
+
for ( int simIndex = startIndex; simIndex < endIndex; ++simIndex )
|
|
570
|
+
{
|
|
571
|
+
b2BodyState* state = states + simIndex;
|
|
572
|
+
b2BodySim* sim = sims + simIndex;
|
|
573
|
+
|
|
574
|
+
b2Vec2 v = state->linearVelocity;
|
|
575
|
+
float w = state->angularVelocity;
|
|
576
|
+
|
|
577
|
+
B2_ASSERT( b2IsValidVec2( v ) );
|
|
578
|
+
B2_ASSERT( b2IsValidFloat( w ) );
|
|
579
|
+
|
|
580
|
+
sim->center = b2Add( sim->center, state->deltaPosition );
|
|
581
|
+
sim->transform.q = b2NormalizeRot( b2MulRot( state->deltaRotation, sim->transform.q ) );
|
|
582
|
+
|
|
583
|
+
// Use the velocity of the farthest point on the body to account for rotation.
|
|
584
|
+
float maxVelocity = b2Length( v ) + b2AbsFloat( w ) * sim->maxExtent;
|
|
585
|
+
|
|
586
|
+
// Sleep needs to observe position correction as well as true velocity.
|
|
587
|
+
float maxDeltaPosition = b2Length( state->deltaPosition ) + b2AbsFloat( state->deltaRotation.s ) * sim->maxExtent;
|
|
588
|
+
|
|
589
|
+
// Position correction is not as important for sleep as true velocity.
|
|
590
|
+
float positionSleepFactor = 0.5f;
|
|
591
|
+
|
|
592
|
+
float sleepVelocity = b2MaxFloat( maxVelocity, positionSleepFactor * invTimeStep * maxDeltaPosition );
|
|
593
|
+
|
|
594
|
+
// reset state deltas
|
|
595
|
+
state->deltaPosition = b2Vec2_zero;
|
|
596
|
+
state->deltaRotation = b2Rot_identity;
|
|
597
|
+
|
|
598
|
+
sim->transform.p = b2Sub( sim->center, b2RotateVector( sim->transform.q, sim->localCenter ) );
|
|
599
|
+
|
|
600
|
+
// cache miss here, however I need the shape list below
|
|
601
|
+
b2Body* body = bodies + sim->bodyId;
|
|
602
|
+
body->bodyMoveIndex = simIndex;
|
|
603
|
+
moveEvents[simIndex].transform = sim->transform;
|
|
604
|
+
moveEvents[simIndex].bodyId = ( b2BodyId ){ sim->bodyId + 1, worldId, body->generation };
|
|
605
|
+
moveEvents[simIndex].userData = body->userData;
|
|
606
|
+
moveEvents[simIndex].fellAsleep = false;
|
|
607
|
+
|
|
608
|
+
// reset applied force and torque
|
|
609
|
+
sim->force = b2Vec2_zero;
|
|
610
|
+
sim->torque = 0.0f;
|
|
611
|
+
|
|
612
|
+
body->isSpeedCapped = sim->isSpeedCapped;
|
|
613
|
+
sim->isSpeedCapped = false;
|
|
614
|
+
|
|
615
|
+
sim->isFast = false;
|
|
616
|
+
|
|
617
|
+
if ( enableSleep == false || body->enableSleep == false || sleepVelocity > body->sleepThreshold )
|
|
618
|
+
{
|
|
619
|
+
// Body is not sleepy
|
|
620
|
+
body->sleepTime = 0.0f;
|
|
621
|
+
|
|
622
|
+
if ( body->type == b2_dynamicBody && enableContinuous && maxVelocity * timeStep > 0.5f * sim->minExtent )
|
|
623
|
+
{
|
|
624
|
+
// This flag is only retained for debug draw
|
|
625
|
+
sim->isFast = true;
|
|
626
|
+
|
|
627
|
+
// Store in fast array for the continuous collision stage
|
|
628
|
+
// This is deterministic because the order of TOI sweeps doesn't matter
|
|
629
|
+
if ( sim->isBullet )
|
|
630
|
+
{
|
|
631
|
+
int bulletIndex = b2AtomicFetchAddInt( &stepContext->bulletBodyCount, 1 );
|
|
632
|
+
stepContext->bulletBodies[bulletIndex] = simIndex;
|
|
633
|
+
}
|
|
634
|
+
else
|
|
635
|
+
{
|
|
636
|
+
b2SolveContinuous( world, simIndex );
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
else
|
|
640
|
+
{
|
|
641
|
+
// Body is safe to advance
|
|
642
|
+
sim->center0 = sim->center;
|
|
643
|
+
sim->rotation0 = sim->transform.q;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
else
|
|
647
|
+
{
|
|
648
|
+
// Body is safe to advance and is falling asleep
|
|
649
|
+
sim->center0 = sim->center;
|
|
650
|
+
sim->rotation0 = sim->transform.q;
|
|
651
|
+
body->sleepTime += timeStep;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Any single body in an island can keep it awake
|
|
655
|
+
b2Island* island = b2IslandArray_Get( &world->islands, body->islandId );
|
|
656
|
+
if ( body->sleepTime < B2_TIME_TO_SLEEP )
|
|
657
|
+
{
|
|
658
|
+
// keep island awake
|
|
659
|
+
int islandIndex = island->localIndex;
|
|
660
|
+
b2SetBit( awakeIslandBitSet, islandIndex );
|
|
661
|
+
}
|
|
662
|
+
else if ( island->constraintRemoveCount > 0 )
|
|
663
|
+
{
|
|
664
|
+
// body wants to sleep but its island needs splitting first
|
|
665
|
+
if ( body->sleepTime > taskContext->splitSleepTime )
|
|
666
|
+
{
|
|
667
|
+
// pick the sleepiest candidate
|
|
668
|
+
taskContext->splitIslandId = body->islandId;
|
|
669
|
+
taskContext->splitSleepTime = body->sleepTime;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Update shapes AABBs
|
|
674
|
+
b2Transform transform = sim->transform;
|
|
675
|
+
bool isFast = sim->isFast;
|
|
676
|
+
int shapeId = body->headShapeId;
|
|
677
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
678
|
+
{
|
|
679
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
680
|
+
|
|
681
|
+
if ( isFast )
|
|
682
|
+
{
|
|
683
|
+
// For fast non-bullet bodies the AABB has already been updated in b2SolveContinuous
|
|
684
|
+
// For fast bullet bodies the AABB will be updated at a later stage
|
|
685
|
+
|
|
686
|
+
// Add to enlarged shapes regardless of AABB changes.
|
|
687
|
+
// Bit-set to keep the move array sorted
|
|
688
|
+
b2SetBit( enlargedSimBitSet, simIndex );
|
|
689
|
+
}
|
|
690
|
+
else
|
|
691
|
+
{
|
|
692
|
+
b2AABB aabb = b2ComputeShapeAABB( shape, transform );
|
|
693
|
+
aabb.lowerBound.x -= speculativeDistance;
|
|
694
|
+
aabb.lowerBound.y -= speculativeDistance;
|
|
695
|
+
aabb.upperBound.x += speculativeDistance;
|
|
696
|
+
aabb.upperBound.y += speculativeDistance;
|
|
697
|
+
shape->aabb = aabb;
|
|
698
|
+
|
|
699
|
+
B2_ASSERT( shape->enlargedAABB == false );
|
|
700
|
+
|
|
701
|
+
if ( b2AABB_Contains( shape->fatAABB, aabb ) == false )
|
|
702
|
+
{
|
|
703
|
+
b2AABB fatAABB;
|
|
704
|
+
fatAABB.lowerBound.x = aabb.lowerBound.x - aabbMargin;
|
|
705
|
+
fatAABB.lowerBound.y = aabb.lowerBound.y - aabbMargin;
|
|
706
|
+
fatAABB.upperBound.x = aabb.upperBound.x + aabbMargin;
|
|
707
|
+
fatAABB.upperBound.y = aabb.upperBound.y + aabbMargin;
|
|
708
|
+
shape->fatAABB = fatAABB;
|
|
709
|
+
|
|
710
|
+
shape->enlargedAABB = true;
|
|
711
|
+
|
|
712
|
+
// Bit-set to keep the move array sorted
|
|
713
|
+
b2SetBit( enlargedSimBitSet, simIndex );
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
shapeId = shape->nextShapeId;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
b2TracyCZoneEnd( finalize_transfprms );
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/*
|
|
725
|
+
typedef enum b2SolverStageType
|
|
726
|
+
{
|
|
727
|
+
b2_stagePrepareJoints,
|
|
728
|
+
b2_stagePrepareContacts,
|
|
729
|
+
b2_stageIntegrateVelocities,
|
|
730
|
+
b2_stageWarmStart,
|
|
731
|
+
b2_stageSolve,
|
|
732
|
+
b2_stageIntegratePositions,
|
|
733
|
+
b2_stageRelax,
|
|
734
|
+
b2_stageRestitution,
|
|
735
|
+
b2_stageStoreImpulses
|
|
736
|
+
} b2SolverStageType;
|
|
737
|
+
|
|
738
|
+
typedef enum b2SolverBlockType
|
|
739
|
+
{
|
|
740
|
+
b2_bodyBlock,
|
|
741
|
+
b2_jointBlock,
|
|
742
|
+
b2_contactBlock,
|
|
743
|
+
b2_graphJointBlock,
|
|
744
|
+
b2_graphContactBlock
|
|
745
|
+
} b2SolverBlockType;
|
|
746
|
+
*/
|
|
747
|
+
|
|
748
|
+
static void b2ExecuteBlock( b2SolverStage* stage, b2StepContext* context, b2SolverBlock* block )
|
|
749
|
+
{
|
|
750
|
+
b2SolverStageType stageType = stage->type;
|
|
751
|
+
b2SolverBlockType blockType = block->blockType;
|
|
752
|
+
int startIndex = block->startIndex;
|
|
753
|
+
int endIndex = startIndex + block->count;
|
|
754
|
+
|
|
755
|
+
switch ( stageType )
|
|
756
|
+
{
|
|
757
|
+
case b2_stagePrepareJoints:
|
|
758
|
+
b2PrepareJointsTask( startIndex, endIndex, context );
|
|
759
|
+
break;
|
|
760
|
+
|
|
761
|
+
case b2_stagePrepareContacts:
|
|
762
|
+
b2PrepareContactsTask( startIndex, endIndex, context );
|
|
763
|
+
break;
|
|
764
|
+
|
|
765
|
+
case b2_stageIntegrateVelocities:
|
|
766
|
+
b2IntegrateVelocitiesTask( startIndex, endIndex, context );
|
|
767
|
+
break;
|
|
768
|
+
|
|
769
|
+
case b2_stageWarmStart:
|
|
770
|
+
if ( context->world->enableWarmStarting )
|
|
771
|
+
{
|
|
772
|
+
if ( blockType == b2_graphContactBlock )
|
|
773
|
+
{
|
|
774
|
+
b2WarmStartContactsTask( startIndex, endIndex, context, stage->colorIndex );
|
|
775
|
+
}
|
|
776
|
+
else if ( blockType == b2_graphJointBlock )
|
|
777
|
+
{
|
|
778
|
+
b2WarmStartJointsTask( startIndex, endIndex, context, stage->colorIndex );
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
break;
|
|
782
|
+
|
|
783
|
+
case b2_stageSolve:
|
|
784
|
+
if ( blockType == b2_graphContactBlock )
|
|
785
|
+
{
|
|
786
|
+
b2SolveContactsTask( startIndex, endIndex, context, stage->colorIndex, true );
|
|
787
|
+
}
|
|
788
|
+
else if ( blockType == b2_graphJointBlock )
|
|
789
|
+
{
|
|
790
|
+
b2SolveJointsTask( startIndex, endIndex, context, stage->colorIndex, true );
|
|
791
|
+
}
|
|
792
|
+
break;
|
|
793
|
+
|
|
794
|
+
case b2_stageIntegratePositions:
|
|
795
|
+
b2IntegratePositionsTask( startIndex, endIndex, context );
|
|
796
|
+
break;
|
|
797
|
+
|
|
798
|
+
case b2_stageRelax:
|
|
799
|
+
if ( blockType == b2_graphContactBlock )
|
|
800
|
+
{
|
|
801
|
+
b2SolveContactsTask( startIndex, endIndex, context, stage->colorIndex, false );
|
|
802
|
+
}
|
|
803
|
+
else if ( blockType == b2_graphJointBlock )
|
|
804
|
+
{
|
|
805
|
+
b2SolveJointsTask( startIndex, endIndex, context, stage->colorIndex, false );
|
|
806
|
+
}
|
|
807
|
+
break;
|
|
808
|
+
|
|
809
|
+
case b2_stageRestitution:
|
|
810
|
+
if ( blockType == b2_graphContactBlock )
|
|
811
|
+
{
|
|
812
|
+
b2ApplyRestitutionTask( startIndex, endIndex, context, stage->colorIndex );
|
|
813
|
+
}
|
|
814
|
+
break;
|
|
815
|
+
|
|
816
|
+
case b2_stageStoreImpulses:
|
|
817
|
+
b2StoreImpulsesTask( startIndex, endIndex, context );
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
static inline int GetWorkerStartIndex( int workerIndex, int blockCount, int workerCount )
|
|
823
|
+
{
|
|
824
|
+
if ( blockCount <= workerCount )
|
|
825
|
+
{
|
|
826
|
+
return workerIndex < blockCount ? workerIndex : B2_NULL_INDEX;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
int blocksPerWorker = blockCount / workerCount;
|
|
830
|
+
int remainder = blockCount - blocksPerWorker * workerCount;
|
|
831
|
+
return blocksPerWorker * workerIndex + b2MinInt( remainder, workerIndex );
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
static void b2ExecuteStage( b2SolverStage* stage, b2StepContext* context, int previousSyncIndex, int syncIndex, int workerIndex )
|
|
835
|
+
{
|
|
836
|
+
int completedCount = 0;
|
|
837
|
+
b2SolverBlock* blocks = stage->blocks;
|
|
838
|
+
int blockCount = stage->blockCount;
|
|
839
|
+
|
|
840
|
+
int expectedSyncIndex = previousSyncIndex;
|
|
841
|
+
|
|
842
|
+
int startIndex = GetWorkerStartIndex( workerIndex, blockCount, context->workerCount );
|
|
843
|
+
if ( startIndex == B2_NULL_INDEX )
|
|
844
|
+
{
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
B2_ASSERT( 0 <= startIndex && startIndex < blockCount );
|
|
849
|
+
|
|
850
|
+
int blockIndex = startIndex;
|
|
851
|
+
|
|
852
|
+
while ( b2AtomicCompareExchangeInt( &blocks[blockIndex].syncIndex, expectedSyncIndex, syncIndex ) == true )
|
|
853
|
+
{
|
|
854
|
+
B2_ASSERT( stage->type != b2_stagePrepareContacts || syncIndex < 2 );
|
|
855
|
+
|
|
856
|
+
B2_ASSERT( completedCount < blockCount );
|
|
857
|
+
|
|
858
|
+
b2ExecuteBlock( stage, context, blocks + blockIndex );
|
|
859
|
+
|
|
860
|
+
completedCount += 1;
|
|
861
|
+
blockIndex += 1;
|
|
862
|
+
if ( blockIndex >= blockCount )
|
|
863
|
+
{
|
|
864
|
+
// Keep looking for work
|
|
865
|
+
blockIndex = 0;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
expectedSyncIndex = previousSyncIndex;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Search backwards for blocks
|
|
872
|
+
blockIndex = startIndex - 1;
|
|
873
|
+
while ( true )
|
|
874
|
+
{
|
|
875
|
+
if ( blockIndex < 0 )
|
|
876
|
+
{
|
|
877
|
+
blockIndex = blockCount - 1;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
expectedSyncIndex = previousSyncIndex;
|
|
881
|
+
|
|
882
|
+
if ( b2AtomicCompareExchangeInt( &blocks[blockIndex].syncIndex, expectedSyncIndex, syncIndex ) == false )
|
|
883
|
+
{
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
b2ExecuteBlock( stage, context, blocks + blockIndex );
|
|
888
|
+
completedCount += 1;
|
|
889
|
+
blockIndex -= 1;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
(void)b2AtomicFetchAddInt( &stage->completionCount, completedCount );
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
static void b2ExecuteMainStage( b2SolverStage* stage, b2StepContext* context, uint32_t syncBits )
|
|
896
|
+
{
|
|
897
|
+
int blockCount = stage->blockCount;
|
|
898
|
+
if ( blockCount == 0 )
|
|
899
|
+
{
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if ( blockCount == 1 )
|
|
904
|
+
{
|
|
905
|
+
b2ExecuteBlock( stage, context, stage->blocks );
|
|
906
|
+
}
|
|
907
|
+
else
|
|
908
|
+
{
|
|
909
|
+
b2AtomicStoreU32( &context->atomicSyncBits, syncBits );
|
|
910
|
+
|
|
911
|
+
int syncIndex = ( syncBits >> 16 ) & 0xFFFF;
|
|
912
|
+
B2_ASSERT( syncIndex > 0 );
|
|
913
|
+
int previousSyncIndex = syncIndex - 1;
|
|
914
|
+
|
|
915
|
+
b2ExecuteStage( stage, context, previousSyncIndex, syncIndex, 0 );
|
|
916
|
+
|
|
917
|
+
// todo consider using the cycle counter as well
|
|
918
|
+
while ( b2AtomicLoadInt( &stage->completionCount ) != blockCount )
|
|
919
|
+
{
|
|
920
|
+
b2Pause();
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// This should not use the thread index because thread 0 can be called twice by enkiTS.
|
|
928
|
+
static void b2SolverTask( int startIndex, int endIndex, uint32_t threadIndexIgnore, void* taskContext )
|
|
929
|
+
{
|
|
930
|
+
B2_UNUSED( startIndex, endIndex, threadIndexIgnore );
|
|
931
|
+
|
|
932
|
+
b2WorkerContext* workerContext = taskContext;
|
|
933
|
+
int workerIndex = workerContext->workerIndex;
|
|
934
|
+
b2StepContext* context = workerContext->context;
|
|
935
|
+
int activeColorCount = context->activeColorCount;
|
|
936
|
+
b2SolverStage* stages = context->stages;
|
|
937
|
+
b2Profile* profile = &context->world->profile;
|
|
938
|
+
|
|
939
|
+
if ( workerIndex == 0 )
|
|
940
|
+
{
|
|
941
|
+
// Main thread synchronizes the workers and does work itself.
|
|
942
|
+
//
|
|
943
|
+
// Stages are re-used by loops so that I don't need more stages for large iteration counts.
|
|
944
|
+
// The sync indices grow monotonically for the body/graph/constraint groupings because they share solver blocks.
|
|
945
|
+
// The stage index and sync indices are combined in to sync bits for atomic synchronization.
|
|
946
|
+
// The workers need to compute the previous sync index for a given stage so that CAS works correctly. This
|
|
947
|
+
// setup makes this easy to do.
|
|
948
|
+
|
|
949
|
+
/*
|
|
950
|
+
b2_stagePrepareJoints,
|
|
951
|
+
b2_stagePrepareContacts,
|
|
952
|
+
b2_stageIntegrateVelocities,
|
|
953
|
+
b2_stageWarmStart,
|
|
954
|
+
b2_stageSolve,
|
|
955
|
+
b2_stageIntegratePositions,
|
|
956
|
+
b2_stageRelax,
|
|
957
|
+
b2_stageRestitution,
|
|
958
|
+
b2_stageStoreImpulses
|
|
959
|
+
*/
|
|
960
|
+
|
|
961
|
+
uint64_t ticks = b2GetTicks();
|
|
962
|
+
|
|
963
|
+
int bodySyncIndex = 1;
|
|
964
|
+
int stageIndex = 0;
|
|
965
|
+
|
|
966
|
+
// This stage loops over all awake joints
|
|
967
|
+
uint32_t jointSyncIndex = 1;
|
|
968
|
+
uint32_t syncBits = ( jointSyncIndex << 16 ) | stageIndex;
|
|
969
|
+
B2_ASSERT( stages[stageIndex].type == b2_stagePrepareJoints );
|
|
970
|
+
b2ExecuteMainStage( stages + stageIndex, context, syncBits );
|
|
971
|
+
stageIndex += 1;
|
|
972
|
+
jointSyncIndex += 1;
|
|
973
|
+
|
|
974
|
+
// This stage loops over all contact constraints
|
|
975
|
+
uint32_t contactSyncIndex = 1;
|
|
976
|
+
syncBits = ( contactSyncIndex << 16 ) | stageIndex;
|
|
977
|
+
B2_ASSERT( stages[stageIndex].type == b2_stagePrepareContacts );
|
|
978
|
+
b2ExecuteMainStage( stages + stageIndex, context, syncBits );
|
|
979
|
+
stageIndex += 1;
|
|
980
|
+
contactSyncIndex += 1;
|
|
981
|
+
|
|
982
|
+
int graphSyncIndex = 1;
|
|
983
|
+
|
|
984
|
+
// Single-threaded overflow work. These constraints don't fit in the graph coloring.
|
|
985
|
+
b2PrepareOverflowJoints( context );
|
|
986
|
+
b2PrepareOverflowContacts( context );
|
|
987
|
+
|
|
988
|
+
profile->prepareConstraints += b2GetMillisecondsAndReset( &ticks );
|
|
989
|
+
|
|
990
|
+
int subStepCount = context->subStepCount;
|
|
991
|
+
for ( int i = 0; i < subStepCount; ++i )
|
|
992
|
+
{
|
|
993
|
+
// stage index restarted each iteration
|
|
994
|
+
// syncBits still increases monotonically because the upper bits increase each iteration
|
|
995
|
+
int iterStageIndex = stageIndex;
|
|
996
|
+
|
|
997
|
+
// integrate velocities
|
|
998
|
+
syncBits = ( bodySyncIndex << 16 ) | iterStageIndex;
|
|
999
|
+
B2_ASSERT( stages[iterStageIndex].type == b2_stageIntegrateVelocities );
|
|
1000
|
+
b2ExecuteMainStage( stages + iterStageIndex, context, syncBits );
|
|
1001
|
+
iterStageIndex += 1;
|
|
1002
|
+
bodySyncIndex += 1;
|
|
1003
|
+
|
|
1004
|
+
profile->integrateVelocities += b2GetMillisecondsAndReset( &ticks );
|
|
1005
|
+
|
|
1006
|
+
// warm start constraints
|
|
1007
|
+
b2WarmStartOverflowJoints( context );
|
|
1008
|
+
b2WarmStartOverflowContacts( context );
|
|
1009
|
+
|
|
1010
|
+
for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )
|
|
1011
|
+
{
|
|
1012
|
+
syncBits = ( graphSyncIndex << 16 ) | iterStageIndex;
|
|
1013
|
+
B2_ASSERT( stages[iterStageIndex].type == b2_stageWarmStart );
|
|
1014
|
+
b2ExecuteMainStage( stages + iterStageIndex, context, syncBits );
|
|
1015
|
+
iterStageIndex += 1;
|
|
1016
|
+
}
|
|
1017
|
+
graphSyncIndex += 1;
|
|
1018
|
+
|
|
1019
|
+
profile->warmStart += b2GetMillisecondsAndReset( &ticks );
|
|
1020
|
+
|
|
1021
|
+
// solve constraints
|
|
1022
|
+
bool useBias = true;
|
|
1023
|
+
b2SolveOverflowJoints( context, useBias );
|
|
1024
|
+
b2SolveOverflowContacts( context, useBias );
|
|
1025
|
+
|
|
1026
|
+
for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )
|
|
1027
|
+
{
|
|
1028
|
+
syncBits = ( graphSyncIndex << 16 ) | iterStageIndex;
|
|
1029
|
+
B2_ASSERT( stages[iterStageIndex].type == b2_stageSolve );
|
|
1030
|
+
b2ExecuteMainStage( stages + iterStageIndex, context, syncBits );
|
|
1031
|
+
iterStageIndex += 1;
|
|
1032
|
+
}
|
|
1033
|
+
graphSyncIndex += 1;
|
|
1034
|
+
|
|
1035
|
+
profile->solveImpulses += b2GetMillisecondsAndReset( &ticks );
|
|
1036
|
+
|
|
1037
|
+
// integrate positions
|
|
1038
|
+
B2_ASSERT( stages[iterStageIndex].type == b2_stageIntegratePositions );
|
|
1039
|
+
syncBits = ( bodySyncIndex << 16 ) | iterStageIndex;
|
|
1040
|
+
b2ExecuteMainStage( stages + iterStageIndex, context, syncBits );
|
|
1041
|
+
iterStageIndex += 1;
|
|
1042
|
+
bodySyncIndex += 1;
|
|
1043
|
+
|
|
1044
|
+
profile->integratePositions += b2GetMillisecondsAndReset( &ticks );
|
|
1045
|
+
|
|
1046
|
+
// relax constraints
|
|
1047
|
+
useBias = false;
|
|
1048
|
+
b2SolveOverflowJoints( context, useBias );
|
|
1049
|
+
b2SolveOverflowContacts( context, useBias );
|
|
1050
|
+
|
|
1051
|
+
for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )
|
|
1052
|
+
{
|
|
1053
|
+
syncBits = ( graphSyncIndex << 16 ) | iterStageIndex;
|
|
1054
|
+
B2_ASSERT( stages[iterStageIndex].type == b2_stageRelax );
|
|
1055
|
+
b2ExecuteMainStage( stages + iterStageIndex, context, syncBits );
|
|
1056
|
+
iterStageIndex += 1;
|
|
1057
|
+
}
|
|
1058
|
+
graphSyncIndex += 1;
|
|
1059
|
+
|
|
1060
|
+
profile->relaxImpulses += b2GetMillisecondsAndReset( &ticks );
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// advance the stage according to the sub-stepping tasks just completed
|
|
1064
|
+
// integrate velocities / warm start / solve / integrate positions / relax
|
|
1065
|
+
stageIndex += 1 + activeColorCount + activeColorCount + 1 + activeColorCount;
|
|
1066
|
+
|
|
1067
|
+
// Restitution
|
|
1068
|
+
{
|
|
1069
|
+
b2ApplyOverflowRestitution( context );
|
|
1070
|
+
|
|
1071
|
+
int iterStageIndex = stageIndex;
|
|
1072
|
+
for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex )
|
|
1073
|
+
{
|
|
1074
|
+
syncBits = ( graphSyncIndex << 16 ) | iterStageIndex;
|
|
1075
|
+
B2_ASSERT( stages[iterStageIndex].type == b2_stageRestitution );
|
|
1076
|
+
b2ExecuteMainStage( stages + iterStageIndex, context, syncBits );
|
|
1077
|
+
iterStageIndex += 1;
|
|
1078
|
+
}
|
|
1079
|
+
// graphSyncIndex += 1;
|
|
1080
|
+
stageIndex += activeColorCount;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
profile->applyRestitution += b2GetMillisecondsAndReset( &ticks );
|
|
1084
|
+
|
|
1085
|
+
b2StoreOverflowImpulses( context );
|
|
1086
|
+
|
|
1087
|
+
syncBits = ( contactSyncIndex << 16 ) | stageIndex;
|
|
1088
|
+
B2_ASSERT( stages[stageIndex].type == b2_stageStoreImpulses );
|
|
1089
|
+
b2ExecuteMainStage( stages + stageIndex, context, syncBits );
|
|
1090
|
+
|
|
1091
|
+
profile->storeImpulses += b2GetMillisecondsAndReset( &ticks );
|
|
1092
|
+
|
|
1093
|
+
// Signal workers to finish
|
|
1094
|
+
b2AtomicStoreU32( &context->atomicSyncBits, UINT_MAX );
|
|
1095
|
+
|
|
1096
|
+
B2_ASSERT( stageIndex + 1 == context->stageCount );
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// Worker spins and waits for work
|
|
1101
|
+
uint32_t lastSyncBits = 0;
|
|
1102
|
+
// uint64_t maxSpinTime = 10;
|
|
1103
|
+
while ( true )
|
|
1104
|
+
{
|
|
1105
|
+
// Spin until main thread bumps changes the sync bits. This can waste significant time overall, but it is necessary for
|
|
1106
|
+
// parallel simulation with graph coloring.
|
|
1107
|
+
uint32_t syncBits;
|
|
1108
|
+
int spinCount = 0;
|
|
1109
|
+
while ( ( syncBits = b2AtomicLoadU32( &context->atomicSyncBits ) ) == lastSyncBits )
|
|
1110
|
+
{
|
|
1111
|
+
if ( spinCount > 5 )
|
|
1112
|
+
{
|
|
1113
|
+
b2Yield();
|
|
1114
|
+
spinCount = 0;
|
|
1115
|
+
}
|
|
1116
|
+
else
|
|
1117
|
+
{
|
|
1118
|
+
// Using the cycle counter helps to account for variation in mm_pause timing across different
|
|
1119
|
+
// CPUs. However, this is X64 only.
|
|
1120
|
+
// uint64_t prev = __rdtsc();
|
|
1121
|
+
// do
|
|
1122
|
+
//{
|
|
1123
|
+
// b2Pause();
|
|
1124
|
+
//}
|
|
1125
|
+
// while ((__rdtsc() - prev) < maxSpinTime);
|
|
1126
|
+
// maxSpinTime += 10;
|
|
1127
|
+
b2Pause();
|
|
1128
|
+
b2Pause();
|
|
1129
|
+
spinCount += 1;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if ( syncBits == UINT_MAX )
|
|
1134
|
+
{
|
|
1135
|
+
// sentinel hit
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
int stageIndex = syncBits & 0xFFFF;
|
|
1140
|
+
B2_ASSERT( stageIndex < context->stageCount );
|
|
1141
|
+
|
|
1142
|
+
int syncIndex = ( syncBits >> 16 ) & 0xFFFF;
|
|
1143
|
+
B2_ASSERT( syncIndex > 0 );
|
|
1144
|
+
|
|
1145
|
+
int previousSyncIndex = syncIndex - 1;
|
|
1146
|
+
|
|
1147
|
+
b2SolverStage* stage = stages + stageIndex;
|
|
1148
|
+
b2ExecuteStage( stage, context, previousSyncIndex, syncIndex, workerIndex );
|
|
1149
|
+
|
|
1150
|
+
lastSyncBits = syncBits;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
static void b2BulletBodyTask( int startIndex, int endIndex, uint32_t threadIndex, void* taskContext )
|
|
1155
|
+
{
|
|
1156
|
+
B2_UNUSED( threadIndex );
|
|
1157
|
+
|
|
1158
|
+
b2TracyCZoneNC( bullet_body_task, "Bullet", b2_colorLightSkyBlue, true );
|
|
1159
|
+
|
|
1160
|
+
b2StepContext* stepContext = taskContext;
|
|
1161
|
+
|
|
1162
|
+
B2_ASSERT( startIndex <= endIndex );
|
|
1163
|
+
|
|
1164
|
+
for ( int i = startIndex; i < endIndex; ++i )
|
|
1165
|
+
{
|
|
1166
|
+
int simIndex = stepContext->bulletBodies[i];
|
|
1167
|
+
b2SolveContinuous( stepContext->world, simIndex );
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
b2TracyCZoneEnd( bullet_body_task );
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
#if B2_SIMD_WIDTH == 8
|
|
1174
|
+
#define B2_SIMD_SHIFT 3
|
|
1175
|
+
#elif B2_SIMD_WIDTH == 4
|
|
1176
|
+
#define B2_SIMD_SHIFT 2
|
|
1177
|
+
#else
|
|
1178
|
+
#define B2_SIMD_SHIFT 0
|
|
1179
|
+
#endif
|
|
1180
|
+
|
|
1181
|
+
// Solve with graph coloring
|
|
1182
|
+
void b2Solve( b2World* world, b2StepContext* stepContext )
|
|
1183
|
+
{
|
|
1184
|
+
world->stepIndex += 1;
|
|
1185
|
+
|
|
1186
|
+
// Merge islands
|
|
1187
|
+
{
|
|
1188
|
+
b2TracyCZoneNC( merge, "Merge", b2_colorLightGoldenRodYellow, true );
|
|
1189
|
+
uint64_t mergeTicks = b2GetTicks();
|
|
1190
|
+
|
|
1191
|
+
b2MergeAwakeIslands( world );
|
|
1192
|
+
|
|
1193
|
+
world->profile.mergeIslands = b2GetMilliseconds( mergeTicks );
|
|
1194
|
+
b2TracyCZoneEnd( merge );
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// Are there any awake bodies? This scenario should not be important for profiling.
|
|
1198
|
+
b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
|
|
1199
|
+
int awakeBodyCount = awakeSet->bodySims.count;
|
|
1200
|
+
if ( awakeBodyCount == 0 )
|
|
1201
|
+
{
|
|
1202
|
+
// Nothing to simulate, however the tree rebuild must be finished.
|
|
1203
|
+
if ( world->userTreeTask != NULL )
|
|
1204
|
+
{
|
|
1205
|
+
world->finishTaskFcn( world->userTreeTask, world->userTaskContext );
|
|
1206
|
+
world->userTreeTask = NULL;
|
|
1207
|
+
world->activeTaskCount -= 1;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
b2ValidateNoEnlarged( &world->broadPhase );
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// Solve constraints using graph coloring
|
|
1215
|
+
{
|
|
1216
|
+
// Prepare buffers for bullets
|
|
1217
|
+
b2AtomicStoreInt(&stepContext->bulletBodyCount, 0);
|
|
1218
|
+
stepContext->bulletBodies = b2AllocateArenaItem( &world->arena, awakeBodyCount * sizeof( int ), "bullet bodies" );
|
|
1219
|
+
|
|
1220
|
+
b2TracyCZoneNC( prepare_stages, "Prepare Stages", b2_colorDarkOrange, true );
|
|
1221
|
+
uint64_t prepareTicks = b2GetTicks();
|
|
1222
|
+
|
|
1223
|
+
b2ConstraintGraph* graph = &world->constraintGraph;
|
|
1224
|
+
b2GraphColor* colors = graph->colors;
|
|
1225
|
+
|
|
1226
|
+
stepContext->sims = awakeSet->bodySims.data;
|
|
1227
|
+
stepContext->states = awakeSet->bodyStates.data;
|
|
1228
|
+
|
|
1229
|
+
// count contacts, joints, and colors
|
|
1230
|
+
int awakeJointCount = 0;
|
|
1231
|
+
int activeColorCount = 0;
|
|
1232
|
+
for ( int i = 0; i < B2_GRAPH_COLOR_COUNT - 1; ++i )
|
|
1233
|
+
{
|
|
1234
|
+
int perColorContactCount = colors[i].contactSims.count;
|
|
1235
|
+
int perColorJointCount = colors[i].jointSims.count;
|
|
1236
|
+
int occupancyCount = perColorContactCount + perColorJointCount;
|
|
1237
|
+
activeColorCount += occupancyCount > 0 ? 1 : 0;
|
|
1238
|
+
awakeJointCount += perColorJointCount;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// prepare for move events
|
|
1242
|
+
b2BodyMoveEventArray_Resize( &world->bodyMoveEvents, awakeBodyCount );
|
|
1243
|
+
|
|
1244
|
+
// Each worker receives at most M blocks of work. The workers may receive less blocks if there is not sufficient work.
|
|
1245
|
+
// Each block of work has a minimum number of elements (block size). This in turn may limit the number of blocks.
|
|
1246
|
+
// If there are many elements then the block size is increased so there are still at most M blocks of work per worker.
|
|
1247
|
+
// M is a tunable number that has two goals:
|
|
1248
|
+
// 1. keep M small to reduce overhead
|
|
1249
|
+
// 2. keep M large enough for other workers to be able to steal work
|
|
1250
|
+
// The block size is a power of two to make math efficient.
|
|
1251
|
+
|
|
1252
|
+
int workerCount = world->workerCount;
|
|
1253
|
+
const int blocksPerWorker = 4;
|
|
1254
|
+
const int maxBlockCount = blocksPerWorker * workerCount;
|
|
1255
|
+
|
|
1256
|
+
// Configure blocks for tasks that parallel-for bodies
|
|
1257
|
+
int bodyBlockSize = 1 << 5;
|
|
1258
|
+
int bodyBlockCount;
|
|
1259
|
+
if ( awakeBodyCount > bodyBlockSize * maxBlockCount )
|
|
1260
|
+
{
|
|
1261
|
+
// Too many blocks, increase block size
|
|
1262
|
+
bodyBlockSize = awakeBodyCount / maxBlockCount;
|
|
1263
|
+
bodyBlockCount = maxBlockCount;
|
|
1264
|
+
}
|
|
1265
|
+
else
|
|
1266
|
+
{
|
|
1267
|
+
bodyBlockCount = ( ( awakeBodyCount - 1 ) >> 5 ) + 1;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// Configure blocks for tasks parallel-for each active graph color
|
|
1271
|
+
// The blocks are a mix of SIMD contact blocks and joint blocks
|
|
1272
|
+
int activeColorIndices[B2_GRAPH_COLOR_COUNT];
|
|
1273
|
+
|
|
1274
|
+
int colorContactCounts[B2_GRAPH_COLOR_COUNT];
|
|
1275
|
+
int colorContactBlockSizes[B2_GRAPH_COLOR_COUNT];
|
|
1276
|
+
int colorContactBlockCounts[B2_GRAPH_COLOR_COUNT];
|
|
1277
|
+
|
|
1278
|
+
int colorJointCounts[B2_GRAPH_COLOR_COUNT];
|
|
1279
|
+
int colorJointBlockSizes[B2_GRAPH_COLOR_COUNT];
|
|
1280
|
+
int colorJointBlockCounts[B2_GRAPH_COLOR_COUNT];
|
|
1281
|
+
|
|
1282
|
+
int graphBlockCount = 0;
|
|
1283
|
+
|
|
1284
|
+
// c is the active color index
|
|
1285
|
+
int simdContactCount = 0;
|
|
1286
|
+
int c = 0;
|
|
1287
|
+
for ( int i = 0; i < B2_GRAPH_COLOR_COUNT - 1; ++i )
|
|
1288
|
+
{
|
|
1289
|
+
int colorContactCount = colors[i].contactSims.count;
|
|
1290
|
+
int colorJointCount = colors[i].jointSims.count;
|
|
1291
|
+
|
|
1292
|
+
if ( colorContactCount + colorJointCount > 0 )
|
|
1293
|
+
{
|
|
1294
|
+
activeColorIndices[c] = i;
|
|
1295
|
+
|
|
1296
|
+
// 4/8-way SIMD
|
|
1297
|
+
int colorContactCountSIMD = colorContactCount > 0 ? ( ( colorContactCount - 1 ) >> B2_SIMD_SHIFT ) + 1 : 0;
|
|
1298
|
+
|
|
1299
|
+
colorContactCounts[c] = colorContactCountSIMD;
|
|
1300
|
+
|
|
1301
|
+
// determine the number of contact work blocks for this color
|
|
1302
|
+
if ( colorContactCountSIMD > blocksPerWorker * maxBlockCount )
|
|
1303
|
+
{
|
|
1304
|
+
// too many contact blocks
|
|
1305
|
+
colorContactBlockSizes[c] = colorContactCountSIMD / maxBlockCount;
|
|
1306
|
+
colorContactBlockCounts[c] = maxBlockCount;
|
|
1307
|
+
}
|
|
1308
|
+
else if ( colorContactCountSIMD > 0 )
|
|
1309
|
+
{
|
|
1310
|
+
// dividing by blocksPerWorker (4)
|
|
1311
|
+
colorContactBlockSizes[c] = blocksPerWorker;
|
|
1312
|
+
colorContactBlockCounts[c] = ( ( colorContactCountSIMD - 1 ) >> 2 ) + 1;
|
|
1313
|
+
}
|
|
1314
|
+
else
|
|
1315
|
+
{
|
|
1316
|
+
// no contacts in this color
|
|
1317
|
+
colorContactBlockSizes[c] = 0;
|
|
1318
|
+
colorContactBlockCounts[c] = 0;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
colorJointCounts[c] = colorJointCount;
|
|
1322
|
+
|
|
1323
|
+
// determine number of joint work blocks for this color
|
|
1324
|
+
if ( colorJointCount > blocksPerWorker * maxBlockCount )
|
|
1325
|
+
{
|
|
1326
|
+
// too many joint blocks
|
|
1327
|
+
colorJointBlockSizes[c] = colorJointCount / maxBlockCount;
|
|
1328
|
+
colorJointBlockCounts[c] = maxBlockCount;
|
|
1329
|
+
}
|
|
1330
|
+
else if ( colorJointCount > 0 )
|
|
1331
|
+
{
|
|
1332
|
+
// dividing by blocksPerWorker (4)
|
|
1333
|
+
colorJointBlockSizes[c] = blocksPerWorker;
|
|
1334
|
+
colorJointBlockCounts[c] = ( ( colorJointCount - 1 ) >> 2 ) + 1;
|
|
1335
|
+
}
|
|
1336
|
+
else
|
|
1337
|
+
{
|
|
1338
|
+
colorJointBlockSizes[c] = 0;
|
|
1339
|
+
colorJointBlockCounts[c] = 0;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
graphBlockCount += colorContactBlockCounts[c] + colorJointBlockCounts[c];
|
|
1343
|
+
simdContactCount += colorContactCountSIMD;
|
|
1344
|
+
c += 1;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
activeColorCount = c;
|
|
1348
|
+
|
|
1349
|
+
// Gather contact pointers for easy parallel-for traversal. Some may be NULL due to SIMD remainders.
|
|
1350
|
+
b2ContactSim** contacts = b2AllocateArenaItem(
|
|
1351
|
+
&world->arena, B2_SIMD_WIDTH * simdContactCount * sizeof( b2ContactSim* ), "contact pointers" );
|
|
1352
|
+
|
|
1353
|
+
// Gather joint pointers for easy parallel-for traversal.
|
|
1354
|
+
b2JointSim** joints =
|
|
1355
|
+
b2AllocateArenaItem( &world->arena, awakeJointCount * sizeof( b2JointSim* ), "joint pointers" );
|
|
1356
|
+
|
|
1357
|
+
int simdConstraintSize = b2GetContactConstraintSIMDByteCount();
|
|
1358
|
+
b2ContactConstraintSIMD* simdContactConstraints =
|
|
1359
|
+
b2AllocateArenaItem( &world->arena, simdContactCount * simdConstraintSize, "contact constraint" );
|
|
1360
|
+
|
|
1361
|
+
int overflowContactCount = colors[B2_OVERFLOW_INDEX].contactSims.count;
|
|
1362
|
+
b2ContactConstraint* overflowContactConstraints = b2AllocateArenaItem(
|
|
1363
|
+
&world->arena, overflowContactCount * sizeof( b2ContactConstraint ), "overflow contact constraint" );
|
|
1364
|
+
|
|
1365
|
+
graph->colors[B2_OVERFLOW_INDEX].overflowConstraints = overflowContactConstraints;
|
|
1366
|
+
|
|
1367
|
+
// Distribute transient constraints to each graph color and build flat arrays of contact and joint pointers
|
|
1368
|
+
{
|
|
1369
|
+
int contactBase = 0;
|
|
1370
|
+
int jointBase = 0;
|
|
1371
|
+
for ( int i = 0; i < activeColorCount; ++i )
|
|
1372
|
+
{
|
|
1373
|
+
int j = activeColorIndices[i];
|
|
1374
|
+
b2GraphColor* color = colors + j;
|
|
1375
|
+
|
|
1376
|
+
int colorContactCount = color->contactSims.count;
|
|
1377
|
+
|
|
1378
|
+
if ( colorContactCount == 0 )
|
|
1379
|
+
{
|
|
1380
|
+
color->simdConstraints = NULL;
|
|
1381
|
+
}
|
|
1382
|
+
else
|
|
1383
|
+
{
|
|
1384
|
+
color->simdConstraints =
|
|
1385
|
+
(b2ContactConstraintSIMD*)( (uint8_t*)simdContactConstraints + contactBase * simdConstraintSize );
|
|
1386
|
+
|
|
1387
|
+
for ( int k = 0; k < colorContactCount; ++k )
|
|
1388
|
+
{
|
|
1389
|
+
contacts[B2_SIMD_WIDTH * contactBase + k] = color->contactSims.data + k;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// remainder
|
|
1393
|
+
int colorContactCountSIMD = ( ( colorContactCount - 1 ) >> B2_SIMD_SHIFT ) + 1;
|
|
1394
|
+
for ( int k = colorContactCount; k < B2_SIMD_WIDTH * colorContactCountSIMD; ++k )
|
|
1395
|
+
{
|
|
1396
|
+
contacts[B2_SIMD_WIDTH * contactBase + k] = NULL;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
contactBase += colorContactCountSIMD;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
int colorJointCount = color->jointSims.count;
|
|
1403
|
+
for ( int k = 0; k < colorJointCount; ++k )
|
|
1404
|
+
{
|
|
1405
|
+
joints[jointBase + k] = color->jointSims.data + k;
|
|
1406
|
+
}
|
|
1407
|
+
jointBase += colorJointCount;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
B2_ASSERT( contactBase == simdContactCount );
|
|
1411
|
+
B2_ASSERT( jointBase == awakeJointCount );
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// Define work blocks for preparing contacts and storing contact impulses
|
|
1415
|
+
int contactBlockSize = blocksPerWorker;
|
|
1416
|
+
int contactBlockCount = simdContactCount > 0 ? ( ( simdContactCount - 1 ) >> 2 ) + 1 : 0;
|
|
1417
|
+
if ( simdContactCount > contactBlockSize * maxBlockCount )
|
|
1418
|
+
{
|
|
1419
|
+
// Too many blocks, increase block size
|
|
1420
|
+
contactBlockSize = simdContactCount / maxBlockCount;
|
|
1421
|
+
contactBlockCount = maxBlockCount;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// Define work blocks for preparing joints
|
|
1425
|
+
int jointBlockSize = blocksPerWorker;
|
|
1426
|
+
int jointBlockCount = awakeJointCount > 0 ? ( ( awakeJointCount - 1 ) >> 2 ) + 1 : 0;
|
|
1427
|
+
if ( awakeJointCount > jointBlockSize * maxBlockCount )
|
|
1428
|
+
{
|
|
1429
|
+
// Too many blocks, increase block size
|
|
1430
|
+
jointBlockSize = awakeJointCount / maxBlockCount;
|
|
1431
|
+
jointBlockCount = maxBlockCount;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
int stageCount = 0;
|
|
1435
|
+
|
|
1436
|
+
// b2_stagePrepareJoints
|
|
1437
|
+
stageCount += 1;
|
|
1438
|
+
// b2_stagePrepareContacts
|
|
1439
|
+
stageCount += 1;
|
|
1440
|
+
// b2_stageIntegrateVelocities
|
|
1441
|
+
stageCount += 1;
|
|
1442
|
+
// b2_stageWarmStart
|
|
1443
|
+
stageCount += activeColorCount;
|
|
1444
|
+
// b2_stageSolve
|
|
1445
|
+
stageCount += activeColorCount;
|
|
1446
|
+
// b2_stageIntegratePositions
|
|
1447
|
+
stageCount += 1;
|
|
1448
|
+
// b2_stageRelax
|
|
1449
|
+
stageCount += activeColorCount;
|
|
1450
|
+
// b2_stageRestitution
|
|
1451
|
+
stageCount += activeColorCount;
|
|
1452
|
+
// b2_stageStoreImpulses
|
|
1453
|
+
stageCount += 1;
|
|
1454
|
+
|
|
1455
|
+
b2SolverStage* stages = b2AllocateArenaItem( &world->arena, stageCount * sizeof( b2SolverStage ), "stages" );
|
|
1456
|
+
b2SolverBlock* bodyBlocks =
|
|
1457
|
+
b2AllocateArenaItem( &world->arena, bodyBlockCount * sizeof( b2SolverBlock ), "body blocks" );
|
|
1458
|
+
b2SolverBlock* contactBlocks =
|
|
1459
|
+
b2AllocateArenaItem( &world->arena, contactBlockCount * sizeof( b2SolverBlock ), "contact blocks" );
|
|
1460
|
+
b2SolverBlock* jointBlocks =
|
|
1461
|
+
b2AllocateArenaItem( &world->arena, jointBlockCount * sizeof( b2SolverBlock ), "joint blocks" );
|
|
1462
|
+
b2SolverBlock* graphBlocks =
|
|
1463
|
+
b2AllocateArenaItem( &world->arena, graphBlockCount * sizeof( b2SolverBlock ), "graph blocks" );
|
|
1464
|
+
|
|
1465
|
+
// Split an awake island. This modifies:
|
|
1466
|
+
// - stack allocator
|
|
1467
|
+
// - world island array and solver set
|
|
1468
|
+
// - island indices on bodies, contacts, and joints
|
|
1469
|
+
// I'm squeezing this task in here because it may be expensive and this is a safe place to put it.
|
|
1470
|
+
// Note: cannot split islands in parallel with FinalizeBodies
|
|
1471
|
+
void* splitIslandTask = NULL;
|
|
1472
|
+
if ( world->splitIslandId != B2_NULL_INDEX )
|
|
1473
|
+
{
|
|
1474
|
+
splitIslandTask = world->enqueueTaskFcn( &b2SplitIslandTask, 1, 1, world, world->userTaskContext );
|
|
1475
|
+
world->taskCount += 1;
|
|
1476
|
+
world->activeTaskCount += splitIslandTask == NULL ? 0 : 1;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// Prepare body work blocks
|
|
1480
|
+
for ( int i = 0; i < bodyBlockCount; ++i )
|
|
1481
|
+
{
|
|
1482
|
+
b2SolverBlock* block = bodyBlocks + i;
|
|
1483
|
+
block->startIndex = i * bodyBlockSize;
|
|
1484
|
+
block->count = (int16_t)bodyBlockSize;
|
|
1485
|
+
block->blockType = b2_bodyBlock;
|
|
1486
|
+
b2AtomicStoreInt(&block->syncIndex, 0);
|
|
1487
|
+
}
|
|
1488
|
+
bodyBlocks[bodyBlockCount - 1].count = (int16_t)( awakeBodyCount - ( bodyBlockCount - 1 ) * bodyBlockSize );
|
|
1489
|
+
|
|
1490
|
+
// Prepare joint work blocks
|
|
1491
|
+
for ( int i = 0; i < jointBlockCount; ++i )
|
|
1492
|
+
{
|
|
1493
|
+
b2SolverBlock* block = jointBlocks + i;
|
|
1494
|
+
block->startIndex = i * jointBlockSize;
|
|
1495
|
+
block->count = (int16_t)jointBlockSize;
|
|
1496
|
+
block->blockType = b2_jointBlock;
|
|
1497
|
+
b2AtomicStoreInt( &block->syncIndex, 0 );
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
if ( jointBlockCount > 0 )
|
|
1501
|
+
{
|
|
1502
|
+
jointBlocks[jointBlockCount - 1].count = (int16_t)( awakeJointCount - ( jointBlockCount - 1 ) * jointBlockSize );
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// Prepare contact work blocks
|
|
1506
|
+
for ( int i = 0; i < contactBlockCount; ++i )
|
|
1507
|
+
{
|
|
1508
|
+
b2SolverBlock* block = contactBlocks + i;
|
|
1509
|
+
block->startIndex = i * contactBlockSize;
|
|
1510
|
+
block->count = (int16_t)contactBlockSize;
|
|
1511
|
+
block->blockType = b2_contactBlock;
|
|
1512
|
+
b2AtomicStoreInt( &block->syncIndex, 0 );
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
if ( contactBlockCount > 0 )
|
|
1516
|
+
{
|
|
1517
|
+
contactBlocks[contactBlockCount - 1].count =
|
|
1518
|
+
(int16_t)( simdContactCount - ( contactBlockCount - 1 ) * contactBlockSize );
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// Prepare graph work blocks
|
|
1522
|
+
b2SolverBlock* graphColorBlocks[B2_GRAPH_COLOR_COUNT];
|
|
1523
|
+
b2SolverBlock* baseGraphBlock = graphBlocks;
|
|
1524
|
+
|
|
1525
|
+
for ( int i = 0; i < activeColorCount; ++i )
|
|
1526
|
+
{
|
|
1527
|
+
graphColorBlocks[i] = baseGraphBlock;
|
|
1528
|
+
|
|
1529
|
+
int colorJointBlockCount = colorJointBlockCounts[i];
|
|
1530
|
+
int colorJointBlockSize = colorJointBlockSizes[i];
|
|
1531
|
+
for ( int j = 0; j < colorJointBlockCount; ++j )
|
|
1532
|
+
{
|
|
1533
|
+
b2SolverBlock* block = baseGraphBlock + j;
|
|
1534
|
+
block->startIndex = j * colorJointBlockSize;
|
|
1535
|
+
block->count = (int16_t)colorJointBlockSize;
|
|
1536
|
+
block->blockType = b2_graphJointBlock;
|
|
1537
|
+
b2AtomicStoreInt( &block->syncIndex, 0 );
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
if ( colorJointBlockCount > 0 )
|
|
1541
|
+
{
|
|
1542
|
+
baseGraphBlock[colorJointBlockCount - 1].count =
|
|
1543
|
+
(int16_t)( colorJointCounts[i] - ( colorJointBlockCount - 1 ) * colorJointBlockSize );
|
|
1544
|
+
baseGraphBlock += colorJointBlockCount;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
int colorContactBlockCount = colorContactBlockCounts[i];
|
|
1548
|
+
int colorContactBlockSize = colorContactBlockSizes[i];
|
|
1549
|
+
for ( int j = 0; j < colorContactBlockCount; ++j )
|
|
1550
|
+
{
|
|
1551
|
+
b2SolverBlock* block = baseGraphBlock + j;
|
|
1552
|
+
block->startIndex = j * colorContactBlockSize;
|
|
1553
|
+
block->count = (int16_t)colorContactBlockSize;
|
|
1554
|
+
block->blockType = b2_graphContactBlock;
|
|
1555
|
+
b2AtomicStoreInt( &block->syncIndex, 0 );
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
if ( colorContactBlockCount > 0 )
|
|
1559
|
+
{
|
|
1560
|
+
baseGraphBlock[colorContactBlockCount - 1].count =
|
|
1561
|
+
(int16_t)( colorContactCounts[i] - ( colorContactBlockCount - 1 ) * colorContactBlockSize );
|
|
1562
|
+
baseGraphBlock += colorContactBlockCount;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
B2_ASSERT( (ptrdiff_t)(baseGraphBlock - graphBlocks) == graphBlockCount );
|
|
1567
|
+
|
|
1568
|
+
b2SolverStage* stage = stages;
|
|
1569
|
+
|
|
1570
|
+
// Prepare joints
|
|
1571
|
+
stage->type = b2_stagePrepareJoints;
|
|
1572
|
+
stage->blocks = jointBlocks;
|
|
1573
|
+
stage->blockCount = jointBlockCount;
|
|
1574
|
+
stage->colorIndex = -1;
|
|
1575
|
+
b2AtomicStoreInt(&stage->completionCount, 0);
|
|
1576
|
+
stage += 1;
|
|
1577
|
+
|
|
1578
|
+
// Prepare contacts
|
|
1579
|
+
stage->type = b2_stagePrepareContacts;
|
|
1580
|
+
stage->blocks = contactBlocks;
|
|
1581
|
+
stage->blockCount = contactBlockCount;
|
|
1582
|
+
stage->colorIndex = -1;
|
|
1583
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1584
|
+
stage += 1;
|
|
1585
|
+
|
|
1586
|
+
// Integrate velocities
|
|
1587
|
+
stage->type = b2_stageIntegrateVelocities;
|
|
1588
|
+
stage->blocks = bodyBlocks;
|
|
1589
|
+
stage->blockCount = bodyBlockCount;
|
|
1590
|
+
stage->colorIndex = -1;
|
|
1591
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1592
|
+
stage += 1;
|
|
1593
|
+
|
|
1594
|
+
// Warm start
|
|
1595
|
+
for ( int i = 0; i < activeColorCount; ++i )
|
|
1596
|
+
{
|
|
1597
|
+
stage->type = b2_stageWarmStart;
|
|
1598
|
+
stage->blocks = graphColorBlocks[i];
|
|
1599
|
+
stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];
|
|
1600
|
+
stage->colorIndex = activeColorIndices[i];
|
|
1601
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1602
|
+
stage += 1;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
// Solve graph
|
|
1606
|
+
for ( int i = 0; i < activeColorCount; ++i )
|
|
1607
|
+
{
|
|
1608
|
+
stage->type = b2_stageSolve;
|
|
1609
|
+
stage->blocks = graphColorBlocks[i];
|
|
1610
|
+
stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];
|
|
1611
|
+
stage->colorIndex = activeColorIndices[i];
|
|
1612
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1613
|
+
stage += 1;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// Integrate positions
|
|
1617
|
+
stage->type = b2_stageIntegratePositions;
|
|
1618
|
+
stage->blocks = bodyBlocks;
|
|
1619
|
+
stage->blockCount = bodyBlockCount;
|
|
1620
|
+
stage->colorIndex = -1;
|
|
1621
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1622
|
+
stage += 1;
|
|
1623
|
+
|
|
1624
|
+
// Relax constraints
|
|
1625
|
+
for ( int i = 0; i < activeColorCount; ++i )
|
|
1626
|
+
{
|
|
1627
|
+
stage->type = b2_stageRelax;
|
|
1628
|
+
stage->blocks = graphColorBlocks[i];
|
|
1629
|
+
stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];
|
|
1630
|
+
stage->colorIndex = activeColorIndices[i];
|
|
1631
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1632
|
+
stage += 1;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
// Restitution
|
|
1636
|
+
// Note: joint blocks mixed in, could have joint limit restitution
|
|
1637
|
+
for ( int i = 0; i < activeColorCount; ++i )
|
|
1638
|
+
{
|
|
1639
|
+
stage->type = b2_stageRestitution;
|
|
1640
|
+
stage->blocks = graphColorBlocks[i];
|
|
1641
|
+
stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i];
|
|
1642
|
+
stage->colorIndex = activeColorIndices[i];
|
|
1643
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1644
|
+
stage += 1;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Store impulses
|
|
1648
|
+
stage->type = b2_stageStoreImpulses;
|
|
1649
|
+
stage->blocks = contactBlocks;
|
|
1650
|
+
stage->blockCount = contactBlockCount;
|
|
1651
|
+
stage->colorIndex = -1;
|
|
1652
|
+
b2AtomicStoreInt( &stage->completionCount, 0 );
|
|
1653
|
+
stage += 1;
|
|
1654
|
+
|
|
1655
|
+
B2_ASSERT( (int)( stage - stages ) == stageCount );
|
|
1656
|
+
|
|
1657
|
+
B2_ASSERT( workerCount <= B2_MAX_WORKERS );
|
|
1658
|
+
b2WorkerContext workerContext[B2_MAX_WORKERS];
|
|
1659
|
+
|
|
1660
|
+
stepContext->graph = graph;
|
|
1661
|
+
stepContext->joints = joints;
|
|
1662
|
+
stepContext->contacts = contacts;
|
|
1663
|
+
stepContext->simdContactConstraints = simdContactConstraints;
|
|
1664
|
+
stepContext->activeColorCount = activeColorCount;
|
|
1665
|
+
stepContext->workerCount = workerCount;
|
|
1666
|
+
stepContext->stageCount = stageCount;
|
|
1667
|
+
stepContext->stages = stages;
|
|
1668
|
+
b2AtomicStoreU32(&stepContext->atomicSyncBits, 0);
|
|
1669
|
+
|
|
1670
|
+
world->profile.prepareStages = b2GetMillisecondsAndReset( &prepareTicks );
|
|
1671
|
+
b2TracyCZoneEnd( prepare_stages );
|
|
1672
|
+
|
|
1673
|
+
b2TracyCZoneNC( solve_constraints, "Solve Constraints", b2_colorIndigo, true );
|
|
1674
|
+
uint64_t constraintTicks = b2GetTicks();
|
|
1675
|
+
|
|
1676
|
+
// Must use worker index because thread 0 can be assigned multiple tasks by enkiTS
|
|
1677
|
+
for ( int i = 0; i < workerCount; ++i )
|
|
1678
|
+
{
|
|
1679
|
+
workerContext[i].context = stepContext;
|
|
1680
|
+
workerContext[i].workerIndex = i;
|
|
1681
|
+
workerContext[i].userTask = world->enqueueTaskFcn( b2SolverTask, 1, 1, workerContext + i, world->userTaskContext );
|
|
1682
|
+
world->taskCount += 1;
|
|
1683
|
+
world->activeTaskCount += workerContext[i].userTask == NULL ? 0 : 1;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// Finish island split
|
|
1687
|
+
if ( splitIslandTask != NULL )
|
|
1688
|
+
{
|
|
1689
|
+
world->finishTaskFcn( splitIslandTask, world->userTaskContext );
|
|
1690
|
+
world->activeTaskCount -= 1;
|
|
1691
|
+
}
|
|
1692
|
+
world->splitIslandId = B2_NULL_INDEX;
|
|
1693
|
+
|
|
1694
|
+
// Finish constraint solve
|
|
1695
|
+
for ( int i = 0; i < workerCount; ++i )
|
|
1696
|
+
{
|
|
1697
|
+
if ( workerContext[i].userTask != NULL )
|
|
1698
|
+
{
|
|
1699
|
+
world->finishTaskFcn( workerContext[i].userTask, world->userTaskContext );
|
|
1700
|
+
world->activeTaskCount -= 1;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
world->profile.solveConstraints = b2GetMillisecondsAndReset( &constraintTicks );
|
|
1705
|
+
b2TracyCZoneEnd( solve_constraints );
|
|
1706
|
+
|
|
1707
|
+
b2TracyCZoneNC( update_transforms, "Update Transforms", b2_colorMediumSeaGreen, true );
|
|
1708
|
+
uint64_t transformTicks = b2GetTicks();
|
|
1709
|
+
|
|
1710
|
+
// Prepare contact, enlarged body, and island bit sets used in body finalization.
|
|
1711
|
+
int awakeIslandCount = awakeSet->islandSims.count;
|
|
1712
|
+
for ( int i = 0; i < world->workerCount; ++i )
|
|
1713
|
+
{
|
|
1714
|
+
b2TaskContext* taskContext = world->taskContexts.data + i;
|
|
1715
|
+
b2SetBitCountAndClear( &taskContext->enlargedSimBitSet, awakeBodyCount );
|
|
1716
|
+
b2SetBitCountAndClear( &taskContext->awakeIslandBitSet, awakeIslandCount );
|
|
1717
|
+
taskContext->splitIslandId = B2_NULL_INDEX;
|
|
1718
|
+
taskContext->splitSleepTime = 0.0f;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// Finalize bodies. Must happen after the constraint solver and after island splitting.
|
|
1722
|
+
void* finalizeBodiesTask =
|
|
1723
|
+
world->enqueueTaskFcn( b2FinalizeBodiesTask, awakeBodyCount, 64, stepContext, world->userTaskContext );
|
|
1724
|
+
world->taskCount += 1;
|
|
1725
|
+
if ( finalizeBodiesTask != NULL )
|
|
1726
|
+
{
|
|
1727
|
+
world->finishTaskFcn( finalizeBodiesTask, world->userTaskContext );
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
b2FreeArenaItem( &world->arena, graphBlocks );
|
|
1731
|
+
b2FreeArenaItem( &world->arena, jointBlocks );
|
|
1732
|
+
b2FreeArenaItem( &world->arena, contactBlocks );
|
|
1733
|
+
b2FreeArenaItem( &world->arena, bodyBlocks );
|
|
1734
|
+
b2FreeArenaItem( &world->arena, stages );
|
|
1735
|
+
b2FreeArenaItem( &world->arena, overflowContactConstraints );
|
|
1736
|
+
b2FreeArenaItem( &world->arena, simdContactConstraints );
|
|
1737
|
+
b2FreeArenaItem( &world->arena, joints );
|
|
1738
|
+
b2FreeArenaItem( &world->arena, contacts );
|
|
1739
|
+
|
|
1740
|
+
world->profile.transforms = b2GetMilliseconds( transformTicks );
|
|
1741
|
+
b2TracyCZoneEnd( update_transforms );
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// Report hit events
|
|
1745
|
+
// todo_erin perhaps optimize this with a bitset
|
|
1746
|
+
// todo_erin perhaps do this in parallel with other work below
|
|
1747
|
+
{
|
|
1748
|
+
b2TracyCZoneNC( hit_events, "Hit Events", b2_colorRosyBrown, true );
|
|
1749
|
+
uint64_t hitTicks = b2GetTicks();
|
|
1750
|
+
|
|
1751
|
+
B2_ASSERT( world->contactHitEvents.count == 0 );
|
|
1752
|
+
|
|
1753
|
+
float threshold = world->hitEventThreshold;
|
|
1754
|
+
b2GraphColor* colors = world->constraintGraph.colors;
|
|
1755
|
+
for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )
|
|
1756
|
+
{
|
|
1757
|
+
b2GraphColor* color = colors + i;
|
|
1758
|
+
int contactCount = color->contactSims.count;
|
|
1759
|
+
b2ContactSim* contactSims = color->contactSims.data;
|
|
1760
|
+
for ( int j = 0; j < contactCount; ++j )
|
|
1761
|
+
{
|
|
1762
|
+
b2ContactSim* contactSim = contactSims + j;
|
|
1763
|
+
if ( ( contactSim->simFlags & b2_simEnableHitEvent ) == 0 )
|
|
1764
|
+
{
|
|
1765
|
+
continue;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
b2ContactHitEvent event = { 0 };
|
|
1769
|
+
event.approachSpeed = threshold;
|
|
1770
|
+
|
|
1771
|
+
bool hit = false;
|
|
1772
|
+
int pointCount = contactSim->manifold.pointCount;
|
|
1773
|
+
for ( int k = 0; k < pointCount; ++k )
|
|
1774
|
+
{
|
|
1775
|
+
b2ManifoldPoint* mp = contactSim->manifold.points + k;
|
|
1776
|
+
float approachSpeed = -mp->normalVelocity;
|
|
1777
|
+
|
|
1778
|
+
// Need to check total impulse because the point may be speculative and not colliding
|
|
1779
|
+
if ( approachSpeed > event.approachSpeed && mp->totalNormalImpulse > 0.0f )
|
|
1780
|
+
{
|
|
1781
|
+
event.approachSpeed = approachSpeed;
|
|
1782
|
+
event.point = mp->point;
|
|
1783
|
+
hit = true;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
if ( hit == true )
|
|
1788
|
+
{
|
|
1789
|
+
event.normal = contactSim->manifold.normal;
|
|
1790
|
+
|
|
1791
|
+
b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contactSim->shapeIdA );
|
|
1792
|
+
b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contactSim->shapeIdB );
|
|
1793
|
+
|
|
1794
|
+
event.shapeIdA = ( b2ShapeId ){ shapeA->id + 1, world->worldId, shapeA->generation };
|
|
1795
|
+
event.shapeIdB = ( b2ShapeId ){ shapeB->id + 1, world->worldId, shapeB->generation };
|
|
1796
|
+
|
|
1797
|
+
b2ContactHitEventArray_Push( &world->contactHitEvents, event );
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
world->profile.hitEvents = b2GetMilliseconds( hitTicks );
|
|
1803
|
+
b2TracyCZoneEnd( hit_events );
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
{
|
|
1807
|
+
b2TracyCZoneNC( refit_bvh, "Refit BVH", b2_colorFireBrick, true );
|
|
1808
|
+
uint64_t refitTicks = b2GetTicks();
|
|
1809
|
+
|
|
1810
|
+
// Finish the user tree task that was queued earlier in the time step. This must be complete before touching the
|
|
1811
|
+
// broad-phase.
|
|
1812
|
+
if ( world->userTreeTask != NULL )
|
|
1813
|
+
{
|
|
1814
|
+
world->finishTaskFcn( world->userTreeTask, world->userTaskContext );
|
|
1815
|
+
world->userTreeTask = NULL;
|
|
1816
|
+
world->activeTaskCount -= 1;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
b2ValidateNoEnlarged( &world->broadPhase );
|
|
1820
|
+
|
|
1821
|
+
// Gather bits for all sim bodies that have enlarged AABBs
|
|
1822
|
+
b2BitSet* enlargedBodyBitSet = &world->taskContexts.data[0].enlargedSimBitSet;
|
|
1823
|
+
for ( int i = 1; i < world->workerCount; ++i )
|
|
1824
|
+
{
|
|
1825
|
+
b2InPlaceUnion( enlargedBodyBitSet, &world->taskContexts.data[i].enlargedSimBitSet );
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// Enlarge broad-phase proxies and build move array
|
|
1829
|
+
// Apply shape AABB changes to broad-phase. This also create the move array which must be
|
|
1830
|
+
// in deterministic order. I'm tracking sim bodies because the number of shape ids can be huge.
|
|
1831
|
+
// This has to happen before bullets are processed.
|
|
1832
|
+
{
|
|
1833
|
+
b2BroadPhase* broadPhase = &world->broadPhase;
|
|
1834
|
+
uint32_t wordCount = enlargedBodyBitSet->blockCount;
|
|
1835
|
+
uint64_t* bits = enlargedBodyBitSet->bits;
|
|
1836
|
+
|
|
1837
|
+
// Fast array access is important here
|
|
1838
|
+
b2Body* bodyArray = world->bodies.data;
|
|
1839
|
+
b2BodySim* bodySimArray = awakeSet->bodySims.data;
|
|
1840
|
+
b2Shape* shapeArray = world->shapes.data;
|
|
1841
|
+
|
|
1842
|
+
for ( uint32_t k = 0; k < wordCount; ++k )
|
|
1843
|
+
{
|
|
1844
|
+
uint64_t word = bits[k];
|
|
1845
|
+
while ( word != 0 )
|
|
1846
|
+
{
|
|
1847
|
+
uint32_t ctz = b2CTZ64( word );
|
|
1848
|
+
uint32_t bodySimIndex = 64 * k + ctz;
|
|
1849
|
+
|
|
1850
|
+
b2BodySim* bodySim = bodySimArray + bodySimIndex;
|
|
1851
|
+
|
|
1852
|
+
b2Body* body = bodyArray + bodySim->bodyId;
|
|
1853
|
+
|
|
1854
|
+
int shapeId = body->headShapeId;
|
|
1855
|
+
if ( bodySim->isBullet && bodySim->isFast )
|
|
1856
|
+
{
|
|
1857
|
+
// Fast bullet bodies don't have their final AABB yet
|
|
1858
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
1859
|
+
{
|
|
1860
|
+
b2Shape* shape = shapeArray + shapeId;
|
|
1861
|
+
|
|
1862
|
+
// Shape is fast. It's aabb will be enlarged in continuous collision.
|
|
1863
|
+
// Update the move array here for determinism because bullets are processed
|
|
1864
|
+
// below in non-deterministic order.
|
|
1865
|
+
b2BufferMove( broadPhase, shape->proxyKey );
|
|
1866
|
+
|
|
1867
|
+
shapeId = shape->nextShapeId;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
else
|
|
1871
|
+
{
|
|
1872
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
1873
|
+
{
|
|
1874
|
+
b2Shape* shape = shapeArray + shapeId;
|
|
1875
|
+
|
|
1876
|
+
// The AABB may not have been enlarged, despite the body being flagged as enlarged.
|
|
1877
|
+
// For example, a body with multiple shapes may have not have all shapes enlarged.
|
|
1878
|
+
// A fast body may have been flagged as enlarged despite having no shapes enlarged.
|
|
1879
|
+
if ( shape->enlargedAABB )
|
|
1880
|
+
{
|
|
1881
|
+
b2BroadPhase_EnlargeProxy( broadPhase, shape->proxyKey, shape->fatAABB );
|
|
1882
|
+
shape->enlargedAABB = false;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
shapeId = shape->nextShapeId;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// Clear the smallest set bit
|
|
1890
|
+
word = word & ( word - 1 );
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
b2ValidateBroadphase( &world->broadPhase );
|
|
1896
|
+
|
|
1897
|
+
world->profile.refit = b2GetMilliseconds( refitTicks );
|
|
1898
|
+
b2TracyCZoneEnd( refit_bvh );
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
int bulletBodyCount = b2AtomicLoadInt( &stepContext->bulletBodyCount );
|
|
1902
|
+
if ( bulletBodyCount > 0 )
|
|
1903
|
+
{
|
|
1904
|
+
b2TracyCZoneNC( bullets, "Bullets", b2_colorLightYellow, true );
|
|
1905
|
+
uint64_t bulletTicks = b2GetTicks();
|
|
1906
|
+
|
|
1907
|
+
// Fast bullet bodies
|
|
1908
|
+
// Note: a bullet body may be moving slow
|
|
1909
|
+
int minRange = 8;
|
|
1910
|
+
void* userBulletBodyTask = world->enqueueTaskFcn( &b2BulletBodyTask, bulletBodyCount, minRange, stepContext,
|
|
1911
|
+
world->userTaskContext );
|
|
1912
|
+
world->taskCount += 1;
|
|
1913
|
+
if ( userBulletBodyTask != NULL )
|
|
1914
|
+
{
|
|
1915
|
+
world->finishTaskFcn( userBulletBodyTask, world->userTaskContext );
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// Serially enlarge broad-phase proxies for bullet shapes
|
|
1919
|
+
b2BroadPhase* broadPhase = &world->broadPhase;
|
|
1920
|
+
b2DynamicTree* dynamicTree = broadPhase->trees + b2_dynamicBody;
|
|
1921
|
+
|
|
1922
|
+
// Fast array access is important here
|
|
1923
|
+
b2Body* bodyArray = world->bodies.data;
|
|
1924
|
+
b2BodySim* bodySimArray = awakeSet->bodySims.data;
|
|
1925
|
+
b2Shape* shapeArray = world->shapes.data;
|
|
1926
|
+
|
|
1927
|
+
// Serially enlarge broad-phase proxies for bullet shapes
|
|
1928
|
+
int* bulletBodySimIndices = stepContext->bulletBodies;
|
|
1929
|
+
|
|
1930
|
+
// This loop has non-deterministic order but it shouldn't affect the result
|
|
1931
|
+
for ( int i = 0; i < bulletBodyCount; ++i )
|
|
1932
|
+
{
|
|
1933
|
+
b2BodySim* bulletBodySim = bodySimArray + bulletBodySimIndices[i];
|
|
1934
|
+
if ( bulletBodySim->enlargeAABB == false )
|
|
1935
|
+
{
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// clear flag
|
|
1940
|
+
bulletBodySim->enlargeAABB = false;
|
|
1941
|
+
|
|
1942
|
+
int bodyId = bulletBodySim->bodyId;
|
|
1943
|
+
B2_ASSERT( 0 <= bodyId && bodyId < world->bodies.count );
|
|
1944
|
+
b2Body* bulletBody = bodyArray + bodyId;
|
|
1945
|
+
|
|
1946
|
+
int shapeId = bulletBody->headShapeId;
|
|
1947
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
1948
|
+
{
|
|
1949
|
+
b2Shape* shape = shapeArray + shapeId;
|
|
1950
|
+
if ( shape->enlargedAABB == false )
|
|
1951
|
+
{
|
|
1952
|
+
shapeId = shape->nextShapeId;
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
// clear flag
|
|
1957
|
+
shape->enlargedAABB = false;
|
|
1958
|
+
|
|
1959
|
+
int proxyKey = shape->proxyKey;
|
|
1960
|
+
int proxyId = B2_PROXY_ID( proxyKey );
|
|
1961
|
+
B2_ASSERT( B2_PROXY_TYPE( proxyKey ) == b2_dynamicBody );
|
|
1962
|
+
|
|
1963
|
+
// all fast bullet shapes should already be in the move buffer
|
|
1964
|
+
B2_ASSERT( b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 ) );
|
|
1965
|
+
|
|
1966
|
+
b2DynamicTree_EnlargeProxy( dynamicTree, proxyId, shape->fatAABB );
|
|
1967
|
+
|
|
1968
|
+
shapeId = shape->nextShapeId;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
world->profile.bullets = b2GetMilliseconds( bulletTicks );
|
|
1973
|
+
b2TracyCZoneEnd( bullets );
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// Need to free this even if no bullets got processed.
|
|
1977
|
+
b2FreeArenaItem( &world->arena, stepContext->bulletBodies );
|
|
1978
|
+
stepContext->bulletBodies = NULL;
|
|
1979
|
+
b2AtomicStoreInt(&stepContext->bulletBodyCount, 0);
|
|
1980
|
+
|
|
1981
|
+
// Island sleeping
|
|
1982
|
+
// This must be done last because putting islands to sleep invalidates the enlarged body bits.
|
|
1983
|
+
// todo_erin figure out how to do this in parallel with tree refit
|
|
1984
|
+
if ( world->enableSleep == true )
|
|
1985
|
+
{
|
|
1986
|
+
b2TracyCZoneNC( sleep_islands, "Island Sleep", b2_colorLightSlateGray, true );
|
|
1987
|
+
uint64_t sleepTicks = b2GetTicks();
|
|
1988
|
+
|
|
1989
|
+
// Collect split island candidate for the next time step. No need to split if sleeping is disabled.
|
|
1990
|
+
B2_ASSERT( world->splitIslandId == B2_NULL_INDEX );
|
|
1991
|
+
float splitSleepTimer = 0.0f;
|
|
1992
|
+
for ( int i = 0; i < world->workerCount; ++i )
|
|
1993
|
+
{
|
|
1994
|
+
b2TaskContext* taskContext = world->taskContexts.data + i;
|
|
1995
|
+
if ( taskContext->splitIslandId != B2_NULL_INDEX && taskContext->splitSleepTime >= splitSleepTimer )
|
|
1996
|
+
{
|
|
1997
|
+
B2_ASSERT( taskContext->splitSleepTime > 0.0f );
|
|
1998
|
+
|
|
1999
|
+
// Tie breaking for determinism. Largest island id wins. Needed due to work stealing.
|
|
2000
|
+
if ( taskContext->splitSleepTime == splitSleepTimer && taskContext->splitIslandId < world->splitIslandId )
|
|
2001
|
+
{
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
world->splitIslandId = taskContext->splitIslandId;
|
|
2006
|
+
splitSleepTimer = taskContext->splitSleepTime;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
b2BitSet* awakeIslandBitSet = &world->taskContexts.data[0].awakeIslandBitSet;
|
|
2011
|
+
for ( int i = 1; i < world->workerCount; ++i )
|
|
2012
|
+
{
|
|
2013
|
+
b2InPlaceUnion( awakeIslandBitSet, &world->taskContexts.data[i].awakeIslandBitSet );
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
// Need to process in reverse because this moves islands to sleeping solver sets.
|
|
2017
|
+
b2IslandSim* islands = awakeSet->islandSims.data;
|
|
2018
|
+
int count = awakeSet->islandSims.count;
|
|
2019
|
+
for ( int islandIndex = count - 1; islandIndex >= 0; islandIndex -= 1 )
|
|
2020
|
+
{
|
|
2021
|
+
if ( b2GetBit( awakeIslandBitSet, islandIndex ) == true )
|
|
2022
|
+
{
|
|
2023
|
+
// this island is still awake
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
b2IslandSim* island = islands + islandIndex;
|
|
2028
|
+
int islandId = island->islandId;
|
|
2029
|
+
|
|
2030
|
+
b2TrySleepIsland( world, islandId );
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
b2ValidateSolverSets( world );
|
|
2034
|
+
|
|
2035
|
+
world->profile.sleepIslands = b2GetMilliseconds( sleepTicks );
|
|
2036
|
+
b2TracyCZoneEnd( sleep_islands );
|
|
2037
|
+
}
|
|
2038
|
+
}
|