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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +336 -0
  4. data/examples/contact_events.rb +22 -0
  5. data/examples/debug_draw.rb +15 -0
  6. data/examples/falling_ball.rb +19 -0
  7. data/examples/rugl_debug_draw.rb +68 -0
  8. data/examples/stagecraft_debug_draw.rb +64 -0
  9. data/examples/support/box2d_debug_lines.rb +125 -0
  10. data/ext/box2d/CMakeLists.txt +35 -0
  11. data/ext/box2d/extconf.rb +42 -0
  12. data/ext/box2d/native.c +9 -0
  13. data/ext/box2d/vendor/box2d/LICENSE +21 -0
  14. data/ext/box2d/vendor/box2d/VERSION +1 -0
  15. data/ext/box2d/vendor/box2d/include/box2d/base.h +131 -0
  16. data/ext/box2d/vendor/box2d/include/box2d/box2d.h +1222 -0
  17. data/ext/box2d/vendor/box2d/include/box2d/collision.h +830 -0
  18. data/ext/box2d/vendor/box2d/include/box2d/id.h +144 -0
  19. data/ext/box2d/vendor/box2d/include/box2d/math_functions.h +761 -0
  20. data/ext/box2d/vendor/box2d/include/box2d/types.h +1457 -0
  21. data/ext/box2d/vendor/box2d/src/CMakeLists.txt +223 -0
  22. data/ext/box2d/vendor/box2d/src/aabb.c +132 -0
  23. data/ext/box2d/vendor/box2d/src/aabb.h +56 -0
  24. data/ext/box2d/vendor/box2d/src/arena_allocator.c +112 -0
  25. data/ext/box2d/vendor/box2d/src/arena_allocator.h +48 -0
  26. data/ext/box2d/vendor/box2d/src/array.c +8 -0
  27. data/ext/box2d/vendor/box2d/src/array.h +179 -0
  28. data/ext/box2d/vendor/box2d/src/atomic.h +79 -0
  29. data/ext/box2d/vendor/box2d/src/bitset.c +67 -0
  30. data/ext/box2d/vendor/box2d/src/bitset.h +65 -0
  31. data/ext/box2d/vendor/box2d/src/body.c +1884 -0
  32. data/ext/box2d/vendor/box2d/src/body.h +194 -0
  33. data/ext/box2d/vendor/box2d/src/box2d.natvis +41 -0
  34. data/ext/box2d/vendor/box2d/src/broad_phase.c +524 -0
  35. data/ext/box2d/vendor/box2d/src/broad_phase.h +83 -0
  36. data/ext/box2d/vendor/box2d/src/constants.h +54 -0
  37. data/ext/box2d/vendor/box2d/src/constraint_graph.c +322 -0
  38. data/ext/box2d/vendor/box2d/src/constraint_graph.h +58 -0
  39. data/ext/box2d/vendor/box2d/src/contact.c +650 -0
  40. data/ext/box2d/vendor/box2d/src/contact.h +148 -0
  41. data/ext/box2d/vendor/box2d/src/contact_solver.c +2120 -0
  42. data/ext/box2d/vendor/box2d/src/contact_solver.h +54 -0
  43. data/ext/box2d/vendor/box2d/src/core.c +178 -0
  44. data/ext/box2d/vendor/box2d/src/core.h +149 -0
  45. data/ext/box2d/vendor/box2d/src/ctz.h +112 -0
  46. data/ext/box2d/vendor/box2d/src/distance.c +1415 -0
  47. data/ext/box2d/vendor/box2d/src/distance_joint.c +556 -0
  48. data/ext/box2d/vendor/box2d/src/dynamic_tree.c +1989 -0
  49. data/ext/box2d/vendor/box2d/src/geometry.c +1028 -0
  50. data/ext/box2d/vendor/box2d/src/hull.c +328 -0
  51. data/ext/box2d/vendor/box2d/src/id_pool.c +79 -0
  52. data/ext/box2d/vendor/box2d/src/id_pool.h +35 -0
  53. data/ext/box2d/vendor/box2d/src/island.c +977 -0
  54. data/ext/box2d/vendor/box2d/src/island.h +89 -0
  55. data/ext/box2d/vendor/box2d/src/joint.c +1272 -0
  56. data/ext/box2d/vendor/box2d/src/joint.h +335 -0
  57. data/ext/box2d/vendor/box2d/src/manifold.c +1726 -0
  58. data/ext/box2d/vendor/box2d/src/math_functions.c +159 -0
  59. data/ext/box2d/vendor/box2d/src/motor_joint.c +283 -0
  60. data/ext/box2d/vendor/box2d/src/mouse_joint.c +214 -0
  61. data/ext/box2d/vendor/box2d/src/mover.c +73 -0
  62. data/ext/box2d/vendor/box2d/src/prismatic_joint.c +656 -0
  63. data/ext/box2d/vendor/box2d/src/revolute_joint.c +534 -0
  64. data/ext/box2d/vendor/box2d/src/sensor.c +389 -0
  65. data/ext/box2d/vendor/box2d/src/sensor.h +36 -0
  66. data/ext/box2d/vendor/box2d/src/shape.c +1714 -0
  67. data/ext/box2d/vendor/box2d/src/shape.h +123 -0
  68. data/ext/box2d/vendor/box2d/src/solver.c +2038 -0
  69. data/ext/box2d/vendor/box2d/src/solver.h +155 -0
  70. data/ext/box2d/vendor/box2d/src/solver_set.c +613 -0
  71. data/ext/box2d/vendor/box2d/src/solver_set.h +57 -0
  72. data/ext/box2d/vendor/box2d/src/table.c +238 -0
  73. data/ext/box2d/vendor/box2d/src/table.h +37 -0
  74. data/ext/box2d/vendor/box2d/src/timer.c +185 -0
  75. data/ext/box2d/vendor/box2d/src/types.c +151 -0
  76. data/ext/box2d/vendor/box2d/src/weld_joint.c +310 -0
  77. data/ext/box2d/vendor/box2d/src/wheel_joint.c +551 -0
  78. data/ext/box2d/vendor/box2d/src/world.c +3301 -0
  79. data/ext/box2d/vendor/box2d/src/world.h +192 -0
  80. data/generator/generate.rb +316 -0
  81. data/generator/layout_probe.c +507 -0
  82. data/generator/verify_layouts.rb +32 -0
  83. data/lib/box2d/body.rb +172 -0
  84. data/lib/box2d/body_definition.rb +38 -0
  85. data/lib/box2d/body_shapes.rb +135 -0
  86. data/lib/box2d/chain.rb +61 -0
  87. data/lib/box2d/debug_draw.rb +118 -0
  88. data/lib/box2d/events.rb +103 -0
  89. data/lib/box2d/fixed_stepper.rb +39 -0
  90. data/lib/box2d/handle.rb +46 -0
  91. data/lib/box2d/hit.rb +5 -0
  92. data/lib/box2d/joint.rb +197 -0
  93. data/lib/box2d/native.rb +1462 -0
  94. data/lib/box2d/native_loader.rb +45 -0
  95. data/lib/box2d/pixel_scale.rb +29 -0
  96. data/lib/box2d/shape.rb +109 -0
  97. data/lib/box2d/shape_definition.rb +44 -0
  98. data/lib/box2d/value_conversion.rb +80 -0
  99. data/lib/box2d/version.rb +7 -0
  100. data/lib/box2d/world.rb +135 -0
  101. data/lib/box2d/world_joints.rb +156 -0
  102. data/lib/box2d/world_queries.rb +122 -0
  103. data/lib/box2d/world_registry.rb +95 -0
  104. data/lib/box2d.rb +31 -0
  105. data/script/build_platform_gem.rb +24 -0
  106. data/script/deterministic_scene.rb +56 -0
  107. data/script/update_deterministic_snapshot.rb +11 -0
  108. data/script/verify_platform_gem.rb +24 -0
  109. metadata +164 -0
@@ -0,0 +1,2120 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "contact_solver.h"
5
+
6
+ #include "body.h"
7
+ #include "constraint_graph.h"
8
+ #include "contact.h"
9
+ #include "core.h"
10
+ #include "solver_set.h"
11
+ #include "world.h"
12
+
13
+ #include <stddef.h>
14
+
15
+ // contact separation for sub-stepping
16
+ // s = s0 + dot(cB + rB - cA - rA, normal)
17
+ // normal is held constant
18
+ // body positions c can translation and anchors r can rotate
19
+ // s(t) = s0 + dot(cB(t) + rB(t) - cA(t) - rA(t), normal)
20
+ // s(t) = s0 + dot(cB0 + dpB + rot(dqB, rB0) - cA0 - dpA - rot(dqA, rA0), normal)
21
+ // s(t) = s0 + dot(cB0 - cA0, normal) + dot(dpB - dpA + rot(dqB, rB0) - rot(dqA, rA0), normal)
22
+ // s_base = s0 + dot(cB0 - cA0, normal)
23
+
24
+ void b2PrepareOverflowContacts( b2StepContext* context )
25
+ {
26
+ b2TracyCZoneNC( prepare_overflow_contact, "Prepare Overflow Contact", b2_colorYellow, true );
27
+
28
+ b2World* world = context->world;
29
+ b2ConstraintGraph* graph = context->graph;
30
+ b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
31
+ b2ContactConstraint* constraints = color->overflowConstraints;
32
+ int contactCount = color->contactSims.count;
33
+ b2ContactSim* contacts = color->contactSims.data;
34
+ b2BodyState* awakeStates = context->states;
35
+
36
+ #if B2_VALIDATE
37
+ b2Body* bodies = world->bodies.data;
38
+ #endif
39
+
40
+ // Stiffer for static contacts to avoid bodies getting pushed through the ground
41
+ b2Softness contactSoftness = context->contactSoftness;
42
+ b2Softness staticSoftness = context->staticSoftness;
43
+
44
+ float warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f;
45
+
46
+ for ( int i = 0; i < contactCount; ++i )
47
+ {
48
+ b2ContactSim* contactSim = contacts + i;
49
+
50
+ const b2Manifold* manifold = &contactSim->manifold;
51
+ int pointCount = manifold->pointCount;
52
+
53
+ B2_ASSERT( 0 < pointCount && pointCount <= 2 );
54
+
55
+ int indexA = contactSim->bodySimIndexA;
56
+ int indexB = contactSim->bodySimIndexB;
57
+
58
+ #if B2_VALIDATE
59
+ b2Body* bodyA = bodies + contactSim->bodyIdA;
60
+ int validIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX;
61
+ B2_ASSERT( indexA == validIndexA );
62
+
63
+ b2Body* bodyB = bodies + contactSim->bodyIdB;
64
+ int validIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX;
65
+ B2_ASSERT( indexB == validIndexB );
66
+ #endif
67
+
68
+ b2ContactConstraint* constraint = constraints + i;
69
+ constraint->indexA = indexA;
70
+ constraint->indexB = indexB;
71
+ constraint->normal = manifold->normal;
72
+ constraint->friction = contactSim->friction;
73
+ constraint->restitution = contactSim->restitution;
74
+ constraint->rollingResistance = contactSim->rollingResistance;
75
+ constraint->rollingImpulse = warmStartScale * manifold->rollingImpulse;
76
+ constraint->tangentSpeed = contactSim->tangentSpeed;
77
+ constraint->pointCount = pointCount;
78
+
79
+ b2Vec2 vA = b2Vec2_zero;
80
+ float wA = 0.0f;
81
+ float mA = contactSim->invMassA;
82
+ float iA = contactSim->invIA;
83
+ if ( indexA != B2_NULL_INDEX )
84
+ {
85
+ b2BodyState* stateA = awakeStates + indexA;
86
+ vA = stateA->linearVelocity;
87
+ wA = stateA->angularVelocity;
88
+ }
89
+
90
+ b2Vec2 vB = b2Vec2_zero;
91
+ float wB = 0.0f;
92
+ float mB = contactSim->invMassB;
93
+ float iB = contactSim->invIB;
94
+ if ( indexB != B2_NULL_INDEX )
95
+ {
96
+ b2BodyState* stateB = awakeStates + indexB;
97
+ vB = stateB->linearVelocity;
98
+ wB = stateB->angularVelocity;
99
+ }
100
+
101
+ if ( indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX )
102
+ {
103
+ constraint->softness = staticSoftness;
104
+ }
105
+ else
106
+ {
107
+ constraint->softness = contactSoftness;
108
+ }
109
+
110
+ // copy mass into constraint to avoid cache misses during sub-stepping
111
+ constraint->invMassA = mA;
112
+ constraint->invIA = iA;
113
+ constraint->invMassB = mB;
114
+ constraint->invIB = iB;
115
+
116
+ {
117
+ float k = iA + iB;
118
+ constraint->rollingMass = k > 0.0f ? 1.0f / k : 0.0f;
119
+ }
120
+
121
+ b2Vec2 normal = constraint->normal;
122
+ b2Vec2 tangent = b2RightPerp( constraint->normal );
123
+
124
+ for ( int j = 0; j < pointCount; ++j )
125
+ {
126
+ const b2ManifoldPoint* mp = manifold->points + j;
127
+ b2ContactConstraintPoint* cp = constraint->points + j;
128
+
129
+ cp->normalImpulse = warmStartScale * mp->normalImpulse;
130
+ cp->tangentImpulse = warmStartScale * mp->tangentImpulse;
131
+ cp->totalNormalImpulse = 0.0f;
132
+
133
+ b2Vec2 rA = mp->anchorA;
134
+ b2Vec2 rB = mp->anchorB;
135
+
136
+ cp->anchorA = rA;
137
+ cp->anchorB = rB;
138
+ cp->baseSeparation = mp->separation - b2Dot( b2Sub( rB, rA ), normal );
139
+
140
+ float rnA = b2Cross( rA, normal );
141
+ float rnB = b2Cross( rB, normal );
142
+ float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
143
+ cp->normalMass = kNormal > 0.0f ? 1.0f / kNormal : 0.0f;
144
+
145
+ float rtA = b2Cross( rA, tangent );
146
+ float rtB = b2Cross( rB, tangent );
147
+ float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;
148
+ cp->tangentMass = kTangent > 0.0f ? 1.0f / kTangent : 0.0f;
149
+
150
+ // Save relative velocity for restitution
151
+ b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
152
+ b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
153
+ cp->relativeVelocity = b2Dot( normal, b2Sub( vrB, vrA ) );
154
+ }
155
+ }
156
+
157
+ b2TracyCZoneEnd( prepare_overflow_contact );
158
+ }
159
+
160
+ void b2WarmStartOverflowContacts( b2StepContext* context )
161
+ {
162
+ b2TracyCZoneNC( warmstart_overflow_contact, "WarmStart Overflow Contact", b2_colorDarkOrange, true );
163
+
164
+ b2ConstraintGraph* graph = context->graph;
165
+ b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
166
+ b2ContactConstraint* constraints = color->overflowConstraints;
167
+ int contactCount = color->contactSims.count;
168
+ b2World* world = context->world;
169
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
170
+ b2BodyState* states = awakeSet->bodyStates.data;
171
+
172
+ // This is a dummy state to represent a static body because static bodies don't have a solver body.
173
+ b2BodyState dummyState = b2_identityBodyState;
174
+
175
+ for ( int i = 0; i < contactCount; ++i )
176
+ {
177
+ const b2ContactConstraint* constraint = constraints + i;
178
+
179
+ int indexA = constraint->indexA;
180
+ int indexB = constraint->indexB;
181
+
182
+ b2BodyState* stateA = indexA == B2_NULL_INDEX ? &dummyState : states + indexA;
183
+ b2BodyState* stateB = indexB == B2_NULL_INDEX ? &dummyState : states + indexB;
184
+
185
+ b2Vec2 vA = stateA->linearVelocity;
186
+ float wA = stateA->angularVelocity;
187
+ b2Vec2 vB = stateB->linearVelocity;
188
+ float wB = stateB->angularVelocity;
189
+
190
+ float mA = constraint->invMassA;
191
+ float iA = constraint->invIA;
192
+ float mB = constraint->invMassB;
193
+ float iB = constraint->invIB;
194
+
195
+ // Stiffer for static contacts to avoid bodies getting pushed through the ground
196
+ b2Vec2 normal = constraint->normal;
197
+ b2Vec2 tangent = b2RightPerp( constraint->normal );
198
+ int pointCount = constraint->pointCount;
199
+
200
+ for ( int j = 0; j < pointCount; ++j )
201
+ {
202
+ const b2ContactConstraintPoint* cp = constraint->points + j;
203
+
204
+ // fixed anchors
205
+ b2Vec2 rA = cp->anchorA;
206
+ b2Vec2 rB = cp->anchorB;
207
+
208
+ b2Vec2 P = b2Add( b2MulSV( cp->normalImpulse, normal ), b2MulSV( cp->tangentImpulse, tangent ) );
209
+ wA -= iA * b2Cross( rA, P );
210
+ vA = b2MulAdd( vA, -mA, P );
211
+ wB += iB * b2Cross( rB, P );
212
+ vB = b2MulAdd( vB, mB, P );
213
+ }
214
+
215
+ wA -= iA * constraint->rollingImpulse;
216
+ wB += iB * constraint->rollingImpulse;
217
+
218
+ stateA->linearVelocity = vA;
219
+ stateA->angularVelocity = wA;
220
+ stateB->linearVelocity = vB;
221
+ stateB->angularVelocity = wB;
222
+ }
223
+
224
+ b2TracyCZoneEnd( warmstart_overflow_contact );
225
+ }
226
+
227
+ void b2SolveOverflowContacts( b2StepContext* context, bool useBias )
228
+ {
229
+ b2TracyCZoneNC( solve_contact, "Solve Contact", b2_colorAliceBlue, true );
230
+
231
+ b2ConstraintGraph* graph = context->graph;
232
+ b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
233
+ b2ContactConstraint* constraints = color->overflowConstraints;
234
+ int contactCount = color->contactSims.count;
235
+ b2World* world = context->world;
236
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
237
+ b2BodyState* states = awakeSet->bodyStates.data;
238
+
239
+ float inv_h = context->inv_h;
240
+ const float pushout = context->world->maxContactPushSpeed;
241
+
242
+ // This is a dummy body to represent a static body since static bodies don't have a solver body.
243
+ b2BodyState dummyState = b2_identityBodyState;
244
+
245
+ for ( int i = 0; i < contactCount; ++i )
246
+ {
247
+ b2ContactConstraint* constraint = constraints + i;
248
+ float mA = constraint->invMassA;
249
+ float iA = constraint->invIA;
250
+ float mB = constraint->invMassB;
251
+ float iB = constraint->invIB;
252
+
253
+ b2BodyState* stateA = constraint->indexA == B2_NULL_INDEX ? &dummyState : states + constraint->indexA;
254
+ b2Vec2 vA = stateA->linearVelocity;
255
+ float wA = stateA->angularVelocity;
256
+ b2Rot dqA = stateA->deltaRotation;
257
+
258
+ b2BodyState* stateB = constraint->indexB == B2_NULL_INDEX ? &dummyState : states + constraint->indexB;
259
+ b2Vec2 vB = stateB->linearVelocity;
260
+ float wB = stateB->angularVelocity;
261
+ b2Rot dqB = stateB->deltaRotation;
262
+
263
+ b2Vec2 dp = b2Sub( stateB->deltaPosition, stateA->deltaPosition );
264
+
265
+ b2Vec2 normal = constraint->normal;
266
+ b2Vec2 tangent = b2RightPerp( normal );
267
+ float friction = constraint->friction;
268
+ b2Softness softness = constraint->softness;
269
+
270
+ int pointCount = constraint->pointCount;
271
+ float totalNormalImpulse = 0.0f;
272
+
273
+ // Non-penetration
274
+ for ( int j = 0; j < pointCount; ++j )
275
+ {
276
+ b2ContactConstraintPoint* cp = constraint->points + j;
277
+
278
+ // fixed anchor points
279
+ b2Vec2 rA = cp->anchorA;
280
+ b2Vec2 rB = cp->anchorB;
281
+
282
+ // compute current separation
283
+ // this is subject to round-off error if the anchor is far from the body center of mass
284
+ b2Vec2 ds = b2Add( dp, b2Sub( b2RotateVector( dqB, rB ), b2RotateVector( dqA, rA ) ) );
285
+ float s = cp->baseSeparation + b2Dot( ds, normal );
286
+
287
+ float velocityBias = 0.0f;
288
+ float massScale = 1.0f;
289
+ float impulseScale = 0.0f;
290
+ if ( s > 0.0f )
291
+ {
292
+ // speculative bias
293
+ velocityBias = s * inv_h;
294
+ }
295
+ else if ( useBias )
296
+ {
297
+ velocityBias = b2MaxFloat( softness.biasRate * s, -pushout );
298
+ massScale = softness.massScale;
299
+ impulseScale = softness.impulseScale;
300
+ }
301
+
302
+ // relative normal velocity at contact
303
+ b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
304
+ b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
305
+ float vn = b2Dot( b2Sub( vrB, vrA ), normal );
306
+
307
+ // incremental normal impulse
308
+ float impulse = -cp->normalMass * massScale * ( vn + velocityBias ) - impulseScale * cp->normalImpulse;
309
+
310
+ // clamp the accumulated impulse
311
+ float newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f );
312
+ impulse = newImpulse - cp->normalImpulse;
313
+ cp->normalImpulse = newImpulse;
314
+ cp->totalNormalImpulse += newImpulse;
315
+ totalNormalImpulse += newImpulse;
316
+
317
+ // apply normal impulse
318
+ b2Vec2 P = b2MulSV( impulse, normal );
319
+ vA = b2MulSub( vA, mA, P );
320
+ wA -= iA * b2Cross( rA, P );
321
+
322
+ vB = b2MulAdd( vB, mB, P );
323
+ wB += iB * b2Cross( rB, P );
324
+ }
325
+
326
+ // Friction
327
+ for ( int j = 0; j < pointCount; ++j )
328
+ {
329
+ b2ContactConstraintPoint* cp = constraint->points + j;
330
+
331
+ // fixed anchor points
332
+ b2Vec2 rA = cp->anchorA;
333
+ b2Vec2 rB = cp->anchorB;
334
+
335
+ // relative tangent velocity at contact
336
+ b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
337
+ b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
338
+
339
+ // vt = dot(vrB - sB * tangent - (vrA + sA * tangent), tangent)
340
+ // = dot(vrB - vrA, tangent) - (sA + sB)
341
+
342
+ float vt = b2Dot( b2Sub( vrB, vrA ), tangent ) - constraint->tangentSpeed;
343
+
344
+ // incremental tangent impulse
345
+ float impulse = cp->tangentMass * ( -vt );
346
+
347
+ // clamp the accumulated force
348
+ float maxFriction = friction * cp->normalImpulse;
349
+ float newImpulse = b2ClampFloat( cp->tangentImpulse + impulse, -maxFriction, maxFriction );
350
+ impulse = newImpulse - cp->tangentImpulse;
351
+ cp->tangentImpulse = newImpulse;
352
+
353
+ // apply tangent impulse
354
+ b2Vec2 P = b2MulSV( impulse, tangent );
355
+ vA = b2MulSub( vA, mA, P );
356
+ wA -= iA * b2Cross( rA, P );
357
+ vB = b2MulAdd( vB, mB, P );
358
+ wB += iB * b2Cross( rB, P );
359
+ }
360
+
361
+ // Rolling resistance
362
+ {
363
+ float deltaLambda = -constraint->rollingMass * ( wB - wA );
364
+ float lambda = constraint->rollingImpulse;
365
+ float maxLambda = constraint->rollingResistance * totalNormalImpulse;
366
+ constraint->rollingImpulse = b2ClampFloat( lambda + deltaLambda, -maxLambda, maxLambda );
367
+ deltaLambda = constraint->rollingImpulse - lambda;
368
+
369
+ wA -= iA * deltaLambda;
370
+ wB += iB * deltaLambda;
371
+ }
372
+
373
+ stateA->linearVelocity = vA;
374
+ stateA->angularVelocity = wA;
375
+ stateB->linearVelocity = vB;
376
+ stateB->angularVelocity = wB;
377
+ }
378
+
379
+ b2TracyCZoneEnd( solve_contact );
380
+ }
381
+
382
+ void b2ApplyOverflowRestitution( b2StepContext* context )
383
+ {
384
+ b2TracyCZoneNC( overflow_resitution, "Overflow Restitution", b2_colorViolet, true );
385
+
386
+ b2ConstraintGraph* graph = context->graph;
387
+ b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
388
+ b2ContactConstraint* constraints = color->overflowConstraints;
389
+ int contactCount = color->contactSims.count;
390
+ b2World* world = context->world;
391
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
392
+ b2BodyState* states = awakeSet->bodyStates.data;
393
+
394
+ float threshold = context->world->restitutionThreshold;
395
+
396
+ // dummy state to represent a static body
397
+ b2BodyState dummyState = b2_identityBodyState;
398
+
399
+ for ( int i = 0; i < contactCount; ++i )
400
+ {
401
+ b2ContactConstraint* constraint = constraints + i;
402
+
403
+ float restitution = constraint->restitution;
404
+ if ( restitution == 0.0f )
405
+ {
406
+ continue;
407
+ }
408
+
409
+ float mA = constraint->invMassA;
410
+ float iA = constraint->invIA;
411
+ float mB = constraint->invMassB;
412
+ float iB = constraint->invIB;
413
+
414
+ b2BodyState* stateA = constraint->indexA == B2_NULL_INDEX ? &dummyState : states + constraint->indexA;
415
+ b2Vec2 vA = stateA->linearVelocity;
416
+ float wA = stateA->angularVelocity;
417
+
418
+ b2BodyState* stateB = constraint->indexB == B2_NULL_INDEX ? &dummyState : states + constraint->indexB;
419
+ b2Vec2 vB = stateB->linearVelocity;
420
+ float wB = stateB->angularVelocity;
421
+
422
+ b2Vec2 normal = constraint->normal;
423
+ int pointCount = constraint->pointCount;
424
+
425
+ // it is possible to get more accurate restitution by iterating
426
+ // this only makes a difference if there are two contact points
427
+ // for (int iter = 0; iter < 10; ++iter)
428
+ {
429
+ for ( int j = 0; j < pointCount; ++j )
430
+ {
431
+ b2ContactConstraintPoint* cp = constraint->points + j;
432
+
433
+ // if the normal impulse is zero then there was no collision
434
+ // this skips speculative contact points that didn't generate an impulse
435
+ // The max normal impulse is used in case there was a collision that moved away within the sub-step process
436
+ if ( cp->relativeVelocity > -threshold || cp->totalNormalImpulse == 0.0f )
437
+ {
438
+ continue;
439
+ }
440
+
441
+ // fixed anchor points
442
+ b2Vec2 rA = cp->anchorA;
443
+ b2Vec2 rB = cp->anchorB;
444
+
445
+ // relative normal velocity at contact
446
+ b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
447
+ b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
448
+ float vn = b2Dot( b2Sub( vrB, vrA ), normal );
449
+
450
+ // compute normal impulse
451
+ float impulse = -cp->normalMass * ( vn + restitution * cp->relativeVelocity );
452
+
453
+ // clamp the accumulated impulse
454
+ // todo should this be stored?
455
+ float newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f );
456
+ impulse = newImpulse - cp->normalImpulse;
457
+ cp->normalImpulse = newImpulse;
458
+
459
+ // Add the incremental impulse rather than the full impulse because this is not a sub-step
460
+ cp->totalNormalImpulse += impulse;
461
+
462
+ // apply contact impulse
463
+ b2Vec2 P = b2MulSV( impulse, normal );
464
+ vA = b2MulSub( vA, mA, P );
465
+ wA -= iA * b2Cross( rA, P );
466
+ vB = b2MulAdd( vB, mB, P );
467
+ wB += iB * b2Cross( rB, P );
468
+ }
469
+ }
470
+
471
+ stateA->linearVelocity = vA;
472
+ stateA->angularVelocity = wA;
473
+ stateB->linearVelocity = vB;
474
+ stateB->angularVelocity = wB;
475
+ }
476
+
477
+ b2TracyCZoneEnd( overflow_resitution );
478
+ }
479
+
480
+ void b2StoreOverflowImpulses( b2StepContext* context )
481
+ {
482
+ b2TracyCZoneNC( store_impulses, "Store", b2_colorFireBrick, true );
483
+
484
+ b2ConstraintGraph* graph = context->graph;
485
+ b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX;
486
+ b2ContactConstraint* constraints = color->overflowConstraints;
487
+ b2ContactSim* contacts = color->contactSims.data;
488
+ int contactCount = color->contactSims.count;
489
+
490
+ for ( int i = 0; i < contactCount; ++i )
491
+ {
492
+ const b2ContactConstraint* constraint = constraints + i;
493
+ b2ContactSim* contact = contacts + i;
494
+ b2Manifold* manifold = &contact->manifold;
495
+ int pointCount = manifold->pointCount;
496
+
497
+ for ( int j = 0; j < pointCount; ++j )
498
+ {
499
+ manifold->points[j].normalImpulse = constraint->points[j].normalImpulse;
500
+ manifold->points[j].tangentImpulse = constraint->points[j].tangentImpulse;
501
+ manifold->points[j].totalNormalImpulse = constraint->points[j].totalNormalImpulse;
502
+ manifold->points[j].normalVelocity = constraint->points[j].relativeVelocity;
503
+ }
504
+
505
+ manifold->rollingImpulse = constraint->rollingImpulse;
506
+ }
507
+
508
+ b2TracyCZoneEnd( store_impulses );
509
+ }
510
+
511
+ #if defined( B2_SIMD_AVX2 )
512
+
513
+ #include <immintrin.h>
514
+
515
+ // wide float holds 8 numbers
516
+ typedef __m256 b2FloatW;
517
+
518
+ #elif defined( B2_SIMD_NEON )
519
+
520
+ #include <arm_neon.h>
521
+
522
+ // wide float holds 4 numbers
523
+ typedef float32x4_t b2FloatW;
524
+
525
+ #elif defined( B2_SIMD_SSE2 )
526
+
527
+ #include <emmintrin.h>
528
+
529
+ // wide float holds 4 numbers
530
+ typedef __m128 b2FloatW;
531
+
532
+ #else
533
+
534
+ // scalar math
535
+ typedef struct b2FloatW
536
+ {
537
+ float x, y, z, w;
538
+ } b2FloatW;
539
+
540
+ #endif
541
+
542
+ // Wide vec2
543
+ typedef struct b2Vec2W
544
+ {
545
+ b2FloatW X, Y;
546
+ } b2Vec2W;
547
+
548
+ // Wide rotation
549
+ typedef struct b2RotW
550
+ {
551
+ b2FloatW C, S;
552
+ } b2RotW;
553
+
554
+ #if defined( B2_SIMD_AVX2 )
555
+
556
+ static inline b2FloatW b2ZeroW( void )
557
+ {
558
+ return _mm256_setzero_ps();
559
+ }
560
+
561
+ static inline b2FloatW b2SplatW( float scalar )
562
+ {
563
+ return _mm256_set1_ps( scalar );
564
+ }
565
+
566
+ static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )
567
+ {
568
+ return _mm256_add_ps( a, b );
569
+ }
570
+
571
+ static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )
572
+ {
573
+ return _mm256_sub_ps( a, b );
574
+ }
575
+
576
+ static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )
577
+ {
578
+ return _mm256_mul_ps( a, b );
579
+ }
580
+
581
+ static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )
582
+ {
583
+ // FMA can be emulated: https://github.com/lattera/glibc/blob/master/sysdeps/ieee754/dbl-64/s_fmaf.c#L34
584
+ // return _mm256_fmadd_ps( b, c, a );
585
+ return _mm256_add_ps( _mm256_mul_ps( b, c ), a );
586
+ }
587
+
588
+ static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )
589
+ {
590
+ // return _mm256_fnmadd_ps(b, c, a);
591
+ return _mm256_sub_ps( a, _mm256_mul_ps( b, c ) );
592
+ }
593
+
594
+ static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )
595
+ {
596
+ return _mm256_min_ps( a, b );
597
+ }
598
+
599
+ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )
600
+ {
601
+ return _mm256_max_ps( a, b );
602
+ }
603
+
604
+ // a = clamp(a, -b, b)
605
+ static inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )
606
+ {
607
+ b2FloatW nb = _mm256_sub_ps( _mm256_setzero_ps(), b );
608
+ return _mm256_max_ps( nb, _mm256_min_ps( a, b ) );
609
+ }
610
+
611
+ static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )
612
+ {
613
+ return _mm256_or_ps( a, b );
614
+ }
615
+
616
+ static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )
617
+ {
618
+ return _mm256_cmp_ps( a, b, _CMP_GT_OQ );
619
+ }
620
+
621
+ static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )
622
+ {
623
+ return _mm256_cmp_ps( a, b, _CMP_EQ_OQ );
624
+ }
625
+
626
+ static inline bool b2AllZeroW( b2FloatW a )
627
+ {
628
+ // Compare each element with zero
629
+ b2FloatW zero = _mm256_setzero_ps();
630
+ b2FloatW cmp = _mm256_cmp_ps( a, zero, _CMP_EQ_OQ );
631
+
632
+ // Create a mask from the comparison results
633
+ int mask = _mm256_movemask_ps( cmp );
634
+
635
+ // If all elements are zero, the mask will be 0xFF (11111111 in binary)
636
+ return mask == 0xFF;
637
+ }
638
+
639
+ // component-wise returns mask ? b : a
640
+ static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )
641
+ {
642
+ return _mm256_blendv_ps( a, b, mask );
643
+ }
644
+
645
+ #elif defined( B2_SIMD_NEON )
646
+
647
+ static inline b2FloatW b2ZeroW( void )
648
+ {
649
+ return vdupq_n_f32( 0.0f );
650
+ }
651
+
652
+ static inline b2FloatW b2SplatW( float scalar )
653
+ {
654
+ return vdupq_n_f32( scalar );
655
+ }
656
+
657
+ static inline b2FloatW b2SetW( float a, float b, float c, float d )
658
+ {
659
+ float32_t array[4] = { a, b, c, d };
660
+ return vld1q_f32( array );
661
+ }
662
+
663
+ static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )
664
+ {
665
+ return vaddq_f32( a, b );
666
+ }
667
+
668
+ static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )
669
+ {
670
+ return vsubq_f32( a, b );
671
+ }
672
+
673
+ static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )
674
+ {
675
+ return vmulq_f32( a, b );
676
+ }
677
+
678
+ static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )
679
+ {
680
+ return vmlaq_f32( a, b, c );
681
+ }
682
+
683
+ static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )
684
+ {
685
+ return vmlsq_f32( a, b, c );
686
+ }
687
+
688
+ static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )
689
+ {
690
+ return vminq_f32( a, b );
691
+ }
692
+
693
+ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )
694
+ {
695
+ return vmaxq_f32( a, b );
696
+ }
697
+
698
+ // a = clamp(a, -b, b)
699
+ static inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )
700
+ {
701
+ b2FloatW nb = vnegq_f32( b );
702
+ return vmaxq_f32( nb, vminq_f32( a, b ) );
703
+ }
704
+
705
+ static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )
706
+ {
707
+ return vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32( a ), vreinterpretq_u32_f32( b ) ) );
708
+ }
709
+
710
+ static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )
711
+ {
712
+ return vreinterpretq_f32_u32( vcgtq_f32( a, b ) );
713
+ }
714
+
715
+ static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )
716
+ {
717
+ return vreinterpretq_f32_u32( vceqq_f32( a, b ) );
718
+ }
719
+
720
+ static inline bool b2AllZeroW( b2FloatW a )
721
+ {
722
+ // Create a zero vector for comparison
723
+ b2FloatW zero = vdupq_n_f32( 0.0f );
724
+
725
+ // Compare the input vector with zero
726
+ uint32x4_t cmp_result = vceqq_f32( a, zero );
727
+
728
+ // Check if all comparison results are non-zero using vminvq
729
+ #ifdef __ARM_FEATURE_SVE
730
+ // ARM v8.2+ has horizontal minimum instruction
731
+ return vminvq_u32( cmp_result ) != 0;
732
+ #else
733
+ // For older ARM architectures, we need to manually check all lanes
734
+ return vgetq_lane_u32( cmp_result, 0 ) != 0 && vgetq_lane_u32( cmp_result, 1 ) != 0 && vgetq_lane_u32( cmp_result, 2 ) != 0 &&
735
+ vgetq_lane_u32( cmp_result, 3 ) != 0;
736
+ #endif
737
+ }
738
+
739
+ // component-wise returns mask ? b : a
740
+ static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )
741
+ {
742
+ uint32x4_t mask32 = vreinterpretq_u32_f32( mask );
743
+ return vbslq_f32( mask32, b, a );
744
+ }
745
+
746
+ static inline b2FloatW b2LoadW( const float32_t* data )
747
+ {
748
+ return vld1q_f32( data );
749
+ }
750
+
751
+ static inline void b2StoreW( float32_t* data, b2FloatW a )
752
+ {
753
+ vst1q_f32( data, a );
754
+ }
755
+
756
+ static inline b2FloatW b2UnpackLoW( b2FloatW a, b2FloatW b )
757
+ {
758
+ #if defined( __aarch64__ )
759
+ return vzip1q_f32( a, b );
760
+ #else
761
+ float32x2_t a1 = vget_low_f32( a );
762
+ float32x2_t b1 = vget_low_f32( b );
763
+ float32x2x2_t result = vzip_f32( a1, b1 );
764
+ return vcombine_f32( result.val[0], result.val[1] );
765
+ #endif
766
+ }
767
+
768
+ static inline b2FloatW b2UnpackHiW( b2FloatW a, b2FloatW b )
769
+ {
770
+ #if defined( __aarch64__ )
771
+ return vzip2q_f32( a, b );
772
+ #else
773
+ float32x2_t a1 = vget_high_f32( a );
774
+ float32x2_t b1 = vget_high_f32( b );
775
+ float32x2x2_t result = vzip_f32( a1, b1 );
776
+ return vcombine_f32( result.val[0], result.val[1] );
777
+ #endif
778
+ }
779
+
780
+ #elif defined( B2_SIMD_SSE2 )
781
+
782
+ static inline b2FloatW b2ZeroW( void )
783
+ {
784
+ return _mm_setzero_ps();
785
+ }
786
+
787
+ static inline b2FloatW b2SplatW( float scalar )
788
+ {
789
+ return _mm_set1_ps( scalar );
790
+ }
791
+
792
+ static inline b2FloatW b2SetW( float a, float b, float c, float d )
793
+ {
794
+ return _mm_setr_ps( a, b, c, d );
795
+ }
796
+
797
+ static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )
798
+ {
799
+ return _mm_add_ps( a, b );
800
+ }
801
+
802
+ static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )
803
+ {
804
+ return _mm_sub_ps( a, b );
805
+ }
806
+
807
+ static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )
808
+ {
809
+ return _mm_mul_ps( a, b );
810
+ }
811
+
812
+ static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )
813
+ {
814
+ return _mm_add_ps( a, _mm_mul_ps( b, c ) );
815
+ }
816
+
817
+ static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )
818
+ {
819
+ return _mm_sub_ps( a, _mm_mul_ps( b, c ) );
820
+ }
821
+
822
+ static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )
823
+ {
824
+ return _mm_min_ps( a, b );
825
+ }
826
+
827
+ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )
828
+ {
829
+ return _mm_max_ps( a, b );
830
+ }
831
+
832
+ // a = clamp(a, -b, b)
833
+ static inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )
834
+ {
835
+ // Create a mask with the sign bit set for each element
836
+ __m128 mask = _mm_set1_ps( -0.0f );
837
+
838
+ // XOR the input with the mask to negate each element
839
+ __m128 nb = _mm_xor_ps( b, mask );
840
+
841
+ return _mm_max_ps( nb, _mm_min_ps( a, b ) );
842
+ }
843
+
844
+ static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )
845
+ {
846
+ return _mm_or_ps( a, b );
847
+ }
848
+
849
+ static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )
850
+ {
851
+ return _mm_cmpgt_ps( a, b );
852
+ }
853
+
854
+ static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )
855
+ {
856
+ return _mm_cmpeq_ps( a, b );
857
+ }
858
+
859
+ static inline bool b2AllZeroW( b2FloatW a )
860
+ {
861
+ // Compare each element with zero
862
+ b2FloatW zero = _mm_setzero_ps();
863
+ b2FloatW cmp = _mm_cmpeq_ps( a, zero );
864
+
865
+ // Create a mask from the comparison results
866
+ int mask = _mm_movemask_ps( cmp );
867
+
868
+ // If all elements are zero, the mask will be 0xF (1111 in binary)
869
+ return mask == 0xF;
870
+ }
871
+
872
+ // component-wise returns mask ? b : a
873
+ static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )
874
+ {
875
+ return _mm_or_ps( _mm_and_ps( mask, b ), _mm_andnot_ps( mask, a ) );
876
+ }
877
+
878
+ static inline b2FloatW b2LoadW( const float* data )
879
+ {
880
+ return _mm_load_ps( data );
881
+ }
882
+
883
+ static inline void b2StoreW( float* data, b2FloatW a )
884
+ {
885
+ _mm_store_ps( data, a );
886
+ }
887
+
888
+ static inline b2FloatW b2UnpackLoW( b2FloatW a, b2FloatW b )
889
+ {
890
+ return _mm_unpacklo_ps( a, b );
891
+ }
892
+
893
+ static inline b2FloatW b2UnpackHiW( b2FloatW a, b2FloatW b )
894
+ {
895
+ return _mm_unpackhi_ps( a, b );
896
+ }
897
+
898
+ #else
899
+
900
+ static inline b2FloatW b2ZeroW( void )
901
+ {
902
+ return (b2FloatW){ 0.0f, 0.0f, 0.0f, 0.0f };
903
+ }
904
+
905
+ static inline b2FloatW b2SplatW( float scalar )
906
+ {
907
+ return (b2FloatW){ scalar, scalar, scalar, scalar };
908
+ }
909
+
910
+ static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b )
911
+ {
912
+ return (b2FloatW){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w };
913
+ }
914
+
915
+ static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b )
916
+ {
917
+ return (b2FloatW){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w };
918
+ }
919
+
920
+ static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b )
921
+ {
922
+ return (b2FloatW){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w };
923
+ }
924
+
925
+ static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c )
926
+ {
927
+ return (b2FloatW){ a.x + b.x * c.x, a.y + b.y * c.y, a.z + b.z * c.z, a.w + b.w * c.w };
928
+ }
929
+
930
+ static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c )
931
+ {
932
+ return (b2FloatW){ a.x - b.x * c.x, a.y - b.y * c.y, a.z - b.z * c.z, a.w - b.w * c.w };
933
+ }
934
+
935
+ static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b )
936
+ {
937
+ b2FloatW r;
938
+ r.x = a.x <= b.x ? a.x : b.x;
939
+ r.y = a.y <= b.y ? a.y : b.y;
940
+ r.z = a.z <= b.z ? a.z : b.z;
941
+ r.w = a.w <= b.w ? a.w : b.w;
942
+ return r;
943
+ }
944
+
945
+ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b )
946
+ {
947
+ b2FloatW r;
948
+ r.x = a.x >= b.x ? a.x : b.x;
949
+ r.y = a.y >= b.y ? a.y : b.y;
950
+ r.z = a.z >= b.z ? a.z : b.z;
951
+ r.w = a.w >= b.w ? a.w : b.w;
952
+ return r;
953
+ }
954
+
955
+ // a = clamp(a, -b, b)
956
+ static inline b2FloatW b2SymClampW( b2FloatW a, b2FloatW b )
957
+ {
958
+ b2FloatW r;
959
+ r.x = b2ClampFloat( a.x, -b.x, b.x );
960
+ r.y = b2ClampFloat( a.y, -b.y, b.y );
961
+ r.z = b2ClampFloat( a.z, -b.z, b.z );
962
+ r.w = b2ClampFloat( a.w, -b.w, b.w );
963
+ return r;
964
+ }
965
+
966
+ static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b )
967
+ {
968
+ b2FloatW r;
969
+ r.x = a.x != 0.0f || b.x != 0.0f ? 1.0f : 0.0f;
970
+ r.y = a.y != 0.0f || b.y != 0.0f ? 1.0f : 0.0f;
971
+ r.z = a.z != 0.0f || b.z != 0.0f ? 1.0f : 0.0f;
972
+ r.w = a.w != 0.0f || b.w != 0.0f ? 1.0f : 0.0f;
973
+ return r;
974
+ }
975
+
976
+ static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b )
977
+ {
978
+ b2FloatW r;
979
+ r.x = a.x > b.x ? 1.0f : 0.0f;
980
+ r.y = a.y > b.y ? 1.0f : 0.0f;
981
+ r.z = a.z > b.z ? 1.0f : 0.0f;
982
+ r.w = a.w > b.w ? 1.0f : 0.0f;
983
+ return r;
984
+ }
985
+
986
+ static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b )
987
+ {
988
+ b2FloatW r;
989
+ r.x = a.x == b.x ? 1.0f : 0.0f;
990
+ r.y = a.y == b.y ? 1.0f : 0.0f;
991
+ r.z = a.z == b.z ? 1.0f : 0.0f;
992
+ r.w = a.w == b.w ? 1.0f : 0.0f;
993
+ return r;
994
+ }
995
+
996
+ static inline bool b2AllZeroW( b2FloatW a )
997
+ {
998
+ return a.x == 0.0f && a.y == 0.0f && a.z == 0.0f && a.w == 0.0f;
999
+ }
1000
+
1001
+ // component-wise returns mask ? b : a
1002
+ static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask )
1003
+ {
1004
+ b2FloatW r;
1005
+ r.x = mask.x != 0.0f ? b.x : a.x;
1006
+ r.y = mask.y != 0.0f ? b.y : a.y;
1007
+ r.z = mask.z != 0.0f ? b.z : a.z;
1008
+ r.w = mask.w != 0.0f ? b.w : a.w;
1009
+ return r;
1010
+ }
1011
+
1012
+ #endif
1013
+
1014
+ static inline b2FloatW b2DotW( b2Vec2W a, b2Vec2W b )
1015
+ {
1016
+ return b2AddW( b2MulW( a.X, b.X ), b2MulW( a.Y, b.Y ) );
1017
+ }
1018
+
1019
+ static inline b2FloatW b2CrossW( b2Vec2W a, b2Vec2W b )
1020
+ {
1021
+ return b2SubW( b2MulW( a.X, b.Y ), b2MulW( a.Y, b.X ) );
1022
+ }
1023
+
1024
+ static inline b2Vec2W b2RotateVectorW( b2RotW q, b2Vec2W v )
1025
+ {
1026
+ return (b2Vec2W){ b2SubW( b2MulW( q.C, v.X ), b2MulW( q.S, v.Y ) ), b2AddW( b2MulW( q.S, v.X ), b2MulW( q.C, v.Y ) ) };
1027
+ }
1028
+
1029
+ // Soft contact constraints with sub-stepping support
1030
+ // Uses fixed anchors for Jacobians for better behavior on rolling shapes (circles & capsules)
1031
+ // http://mmacklin.com/smallsteps.pdf
1032
+ // https://box2d.org/files/ErinCatto_SoftConstraints_GDC2011.pdf
1033
+
1034
+ typedef struct b2ContactConstraintSIMD
1035
+ {
1036
+ int indexA[B2_SIMD_WIDTH];
1037
+ int indexB[B2_SIMD_WIDTH];
1038
+
1039
+ b2FloatW invMassA, invMassB;
1040
+ b2FloatW invIA, invIB;
1041
+ b2Vec2W normal;
1042
+ b2FloatW friction;
1043
+ b2FloatW tangentSpeed;
1044
+ b2FloatW rollingResistance;
1045
+ b2FloatW rollingMass;
1046
+ b2FloatW rollingImpulse;
1047
+ b2FloatW biasRate;
1048
+ b2FloatW massScale;
1049
+ b2FloatW impulseScale;
1050
+ b2Vec2W anchorA1, anchorB1;
1051
+ b2FloatW normalMass1, tangentMass1;
1052
+ b2FloatW baseSeparation1;
1053
+ b2FloatW normalImpulse1;
1054
+ b2FloatW totalNormalImpulse1;
1055
+ b2FloatW tangentImpulse1;
1056
+ b2Vec2W anchorA2, anchorB2;
1057
+ b2FloatW baseSeparation2;
1058
+ b2FloatW normalImpulse2;
1059
+ b2FloatW totalNormalImpulse2;
1060
+ b2FloatW tangentImpulse2;
1061
+ b2FloatW normalMass2, tangentMass2;
1062
+ b2FloatW restitution;
1063
+ b2FloatW relativeVelocity1, relativeVelocity2;
1064
+ } b2ContactConstraintSIMD;
1065
+
1066
+ int b2GetContactConstraintSIMDByteCount( void )
1067
+ {
1068
+ return sizeof( b2ContactConstraintSIMD );
1069
+ }
1070
+
1071
+ // wide version of b2BodyState
1072
+ typedef struct b2BodyStateW
1073
+ {
1074
+ b2Vec2W v;
1075
+ b2FloatW w;
1076
+ b2FloatW flags;
1077
+ b2Vec2W dp;
1078
+ b2RotW dq;
1079
+ } b2BodyStateW;
1080
+
1081
+ // Custom gather/scatter for each SIMD type
1082
+ #if defined( B2_SIMD_AVX2 )
1083
+
1084
+ // This is a load and 8x8 transpose
1085
+ static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )
1086
+ {
1087
+ _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" );
1088
+ B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );
1089
+ // b2BodyState b2_identityBodyState = {{0.0f, 0.0f}, 0.0f, 0, {0.0f, 0.0f}, {1.0f, 0.0f}};
1090
+ b2FloatW identity = _mm256_setr_ps( 0.0f, 0.0f, 0.0f, 0, 0.0f, 0.0f, 1.0f, 0.0f );
1091
+ b2FloatW b0 = indices[0] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[0] ) );
1092
+ b2FloatW b1 = indices[1] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[1] ) );
1093
+ b2FloatW b2 = indices[2] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[2] ) );
1094
+ b2FloatW b3 = indices[3] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[3] ) );
1095
+ b2FloatW b4 = indices[4] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[4] ) );
1096
+ b2FloatW b5 = indices[5] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[5] ) );
1097
+ b2FloatW b6 = indices[6] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[6] ) );
1098
+ b2FloatW b7 = indices[7] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[7] ) );
1099
+
1100
+ b2FloatW t0 = _mm256_unpacklo_ps( b0, b1 );
1101
+ b2FloatW t1 = _mm256_unpackhi_ps( b0, b1 );
1102
+ b2FloatW t2 = _mm256_unpacklo_ps( b2, b3 );
1103
+ b2FloatW t3 = _mm256_unpackhi_ps( b2, b3 );
1104
+ b2FloatW t4 = _mm256_unpacklo_ps( b4, b5 );
1105
+ b2FloatW t5 = _mm256_unpackhi_ps( b4, b5 );
1106
+ b2FloatW t6 = _mm256_unpacklo_ps( b6, b7 );
1107
+ b2FloatW t7 = _mm256_unpackhi_ps( b6, b7 );
1108
+ b2FloatW tt0 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1109
+ b2FloatW tt1 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1110
+ b2FloatW tt2 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1111
+ b2FloatW tt3 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1112
+ b2FloatW tt4 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1113
+ b2FloatW tt5 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1114
+ b2FloatW tt6 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1115
+ b2FloatW tt7 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1116
+
1117
+ b2BodyStateW simdBody;
1118
+ simdBody.v.X = _mm256_permute2f128_ps( tt0, tt4, 0x20 );
1119
+ simdBody.v.Y = _mm256_permute2f128_ps( tt1, tt5, 0x20 );
1120
+ simdBody.w = _mm256_permute2f128_ps( tt2, tt6, 0x20 );
1121
+ simdBody.flags = _mm256_permute2f128_ps( tt3, tt7, 0x20 );
1122
+ simdBody.dp.X = _mm256_permute2f128_ps( tt0, tt4, 0x31 );
1123
+ simdBody.dp.Y = _mm256_permute2f128_ps( tt1, tt5, 0x31 );
1124
+ simdBody.dq.C = _mm256_permute2f128_ps( tt2, tt6, 0x31 );
1125
+ simdBody.dq.S = _mm256_permute2f128_ps( tt3, tt7, 0x31 );
1126
+ return simdBody;
1127
+ }
1128
+
1129
+ // This writes everything back to the solver bodies but only the velocities change
1130
+ static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )
1131
+ {
1132
+ _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" );
1133
+ B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );
1134
+ b2FloatW t0 = _mm256_unpacklo_ps( simdBody->v.X, simdBody->v.Y );
1135
+ b2FloatW t1 = _mm256_unpackhi_ps( simdBody->v.X, simdBody->v.Y );
1136
+ b2FloatW t2 = _mm256_unpacklo_ps( simdBody->w, simdBody->flags );
1137
+ b2FloatW t3 = _mm256_unpackhi_ps( simdBody->w, simdBody->flags );
1138
+ b2FloatW t4 = _mm256_unpacklo_ps( simdBody->dp.X, simdBody->dp.Y );
1139
+ b2FloatW t5 = _mm256_unpackhi_ps( simdBody->dp.X, simdBody->dp.Y );
1140
+ b2FloatW t6 = _mm256_unpacklo_ps( simdBody->dq.C, simdBody->dq.S );
1141
+ b2FloatW t7 = _mm256_unpackhi_ps( simdBody->dq.C, simdBody->dq.S );
1142
+ b2FloatW tt0 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1143
+ b2FloatW tt1 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1144
+ b2FloatW tt2 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1145
+ b2FloatW tt3 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1146
+ b2FloatW tt4 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1147
+ b2FloatW tt5 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1148
+ b2FloatW tt6 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 1, 0, 1, 0 ) );
1149
+ b2FloatW tt7 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 3, 2, 3, 2 ) );
1150
+
1151
+ // I don't use any dummy body in the body array because this will lead to multithreaded sharing and the
1152
+ // associated cache flushing.
1153
+ if ( indices[0] != B2_NULL_INDEX )
1154
+ _mm256_store_ps( (float*)( states + indices[0] ), _mm256_permute2f128_ps( tt0, tt4, 0x20 ) );
1155
+ if ( indices[1] != B2_NULL_INDEX )
1156
+ _mm256_store_ps( (float*)( states + indices[1] ), _mm256_permute2f128_ps( tt1, tt5, 0x20 ) );
1157
+ if ( indices[2] != B2_NULL_INDEX )
1158
+ _mm256_store_ps( (float*)( states + indices[2] ), _mm256_permute2f128_ps( tt2, tt6, 0x20 ) );
1159
+ if ( indices[3] != B2_NULL_INDEX )
1160
+ _mm256_store_ps( (float*)( states + indices[3] ), _mm256_permute2f128_ps( tt3, tt7, 0x20 ) );
1161
+ if ( indices[4] != B2_NULL_INDEX )
1162
+ _mm256_store_ps( (float*)( states + indices[4] ), _mm256_permute2f128_ps( tt0, tt4, 0x31 ) );
1163
+ if ( indices[5] != B2_NULL_INDEX )
1164
+ _mm256_store_ps( (float*)( states + indices[5] ), _mm256_permute2f128_ps( tt1, tt5, 0x31 ) );
1165
+ if ( indices[6] != B2_NULL_INDEX )
1166
+ _mm256_store_ps( (float*)( states + indices[6] ), _mm256_permute2f128_ps( tt2, tt6, 0x31 ) );
1167
+ if ( indices[7] != B2_NULL_INDEX )
1168
+ _mm256_store_ps( (float*)( states + indices[7] ), _mm256_permute2f128_ps( tt3, tt7, 0x31 ) );
1169
+ }
1170
+
1171
+ #elif defined( B2_SIMD_NEON )
1172
+
1173
+ // This is a load and transpose
1174
+ static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )
1175
+ {
1176
+ _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" );
1177
+ B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );
1178
+
1179
+ // [vx vy w flags]
1180
+ b2FloatW identityA = b2ZeroW();
1181
+
1182
+ // [dpx dpy dqc dqs]
1183
+
1184
+ b2FloatW identityB = b2SetW( 0.0f, 0.0f, 1.0f, 0.0f );
1185
+
1186
+ b2FloatW b1a = indices[0] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[0] ) + 0 );
1187
+ b2FloatW b1b = indices[0] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[0] ) + 4 );
1188
+ b2FloatW b2a = indices[1] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[1] ) + 0 );
1189
+ b2FloatW b2b = indices[1] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[1] ) + 4 );
1190
+ b2FloatW b3a = indices[2] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[2] ) + 0 );
1191
+ b2FloatW b3b = indices[2] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[2] ) + 4 );
1192
+ b2FloatW b4a = indices[3] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[3] ) + 0 );
1193
+ b2FloatW b4b = indices[3] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[3] ) + 4 );
1194
+
1195
+ // [vx1 vx3 vy1 vy3]
1196
+ b2FloatW t1a = b2UnpackLoW( b1a, b3a );
1197
+
1198
+ // [vx2 vx4 vy2 vy4]
1199
+ b2FloatW t2a = b2UnpackLoW( b2a, b4a );
1200
+
1201
+ // [w1 w3 f1 f3]
1202
+ b2FloatW t3a = b2UnpackHiW( b1a, b3a );
1203
+
1204
+ // [w2 w4 f2 f4]
1205
+ b2FloatW t4a = b2UnpackHiW( b2a, b4a );
1206
+
1207
+ b2BodyStateW simdBody;
1208
+ simdBody.v.X = b2UnpackLoW( t1a, t2a );
1209
+ simdBody.v.Y = b2UnpackHiW( t1a, t2a );
1210
+ simdBody.w = b2UnpackLoW( t3a, t4a );
1211
+ simdBody.flags = b2UnpackHiW( t3a, t4a );
1212
+
1213
+ b2FloatW t1b = b2UnpackLoW( b1b, b3b );
1214
+ b2FloatW t2b = b2UnpackLoW( b2b, b4b );
1215
+ b2FloatW t3b = b2UnpackHiW( b1b, b3b );
1216
+ b2FloatW t4b = b2UnpackHiW( b2b, b4b );
1217
+
1218
+ simdBody.dp.X = b2UnpackLoW( t1b, t2b );
1219
+ simdBody.dp.Y = b2UnpackHiW( t1b, t2b );
1220
+ simdBody.dq.C = b2UnpackLoW( t3b, t4b );
1221
+ simdBody.dq.S = b2UnpackHiW( t3b, t4b );
1222
+
1223
+ return simdBody;
1224
+ }
1225
+
1226
+ // This writes only the velocities back to the solver bodies
1227
+ // https://developer.arm.com/documentation/102107a/0100/Floating-point-4x4-matrix-transposition
1228
+ static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )
1229
+ {
1230
+ _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" );
1231
+ B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );
1232
+
1233
+ // b2FloatW x = b2SetW(0.0f, 1.0f, 2.0f, 3.0f);
1234
+ // b2FloatW y = b2SetW(4.0f, 5.0f, 6.0f, 7.0f);
1235
+ // b2FloatW z = b2SetW(8.0f, 9.0f, 10.0f, 11.0f);
1236
+ // b2FloatW w = b2SetW(12.0f, 13.0f, 14.0f, 15.0f);
1237
+ //
1238
+ // float32x4x2_t rr1 = vtrnq_f32( x, y );
1239
+ // float32x4x2_t rr2 = vtrnq_f32( z, w );
1240
+ //
1241
+ // float32x4_t b1 = vcombine_f32(vget_low_f32(rr1.val[0]), vget_low_f32(rr2.val[0]));
1242
+ // float32x4_t b2 = vcombine_f32(vget_low_f32(rr1.val[1]), vget_low_f32(rr2.val[1]));
1243
+ // float32x4_t b3 = vcombine_f32(vget_high_f32(rr1.val[0]), vget_high_f32(rr2.val[0]));
1244
+ // float32x4_t b4 = vcombine_f32(vget_high_f32(rr1.val[1]), vget_high_f32(rr2.val[1]));
1245
+
1246
+ // transpose
1247
+ float32x4x2_t r1 = vtrnq_f32( simdBody->v.X, simdBody->v.Y );
1248
+ float32x4x2_t r2 = vtrnq_f32( simdBody->w, simdBody->flags );
1249
+
1250
+ // I don't use any dummy body in the body array because this will lead to multithreaded sharing and the
1251
+ // associated cache flushing.
1252
+ if ( indices[0] != B2_NULL_INDEX )
1253
+ {
1254
+ float32x4_t body1 = vcombine_f32( vget_low_f32( r1.val[0] ), vget_low_f32( r2.val[0] ) );
1255
+ b2StoreW( (float*)( states + indices[0] ), body1 );
1256
+ }
1257
+
1258
+ if ( indices[1] != B2_NULL_INDEX )
1259
+ {
1260
+ float32x4_t body2 = vcombine_f32( vget_low_f32( r1.val[1] ), vget_low_f32( r2.val[1] ) );
1261
+ b2StoreW( (float*)( states + indices[1] ), body2 );
1262
+ }
1263
+
1264
+ if ( indices[2] != B2_NULL_INDEX )
1265
+ {
1266
+ float32x4_t body3 = vcombine_f32( vget_high_f32( r1.val[0] ), vget_high_f32( r2.val[0] ) );
1267
+ b2StoreW( (float*)( states + indices[2] ), body3 );
1268
+ }
1269
+
1270
+ if ( indices[3] != B2_NULL_INDEX )
1271
+ {
1272
+ float32x4_t body4 = vcombine_f32( vget_high_f32( r1.val[1] ), vget_high_f32( r2.val[1] ) );
1273
+ b2StoreW( (float*)( states + indices[3] ), body4 );
1274
+ }
1275
+ }
1276
+
1277
+ #elif defined( B2_SIMD_SSE2 )
1278
+
1279
+ // This is a load and transpose
1280
+ static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )
1281
+ {
1282
+ _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" );
1283
+ B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );
1284
+
1285
+ // [vx vy w flags]
1286
+ b2FloatW identityA = b2ZeroW();
1287
+
1288
+ // [dpx dpy dqc dqs]
1289
+ b2FloatW identityB = b2SetW( 0.0f, 0.0f, 1.0f, 0.0f );
1290
+
1291
+ b2FloatW b1a = indices[0] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[0] ) + 0 );
1292
+ b2FloatW b1b = indices[0] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[0] ) + 4 );
1293
+ b2FloatW b2a = indices[1] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[1] ) + 0 );
1294
+ b2FloatW b2b = indices[1] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[1] ) + 4 );
1295
+ b2FloatW b3a = indices[2] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[2] ) + 0 );
1296
+ b2FloatW b3b = indices[2] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[2] ) + 4 );
1297
+ b2FloatW b4a = indices[3] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[3] ) + 0 );
1298
+ b2FloatW b4b = indices[3] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[3] ) + 4 );
1299
+
1300
+ // [vx1 vx3 vy1 vy3]
1301
+ b2FloatW t1a = b2UnpackLoW( b1a, b3a );
1302
+
1303
+ // [vx2 vx4 vy2 vy4]
1304
+ b2FloatW t2a = b2UnpackLoW( b2a, b4a );
1305
+
1306
+ // [w1 w3 f1 f3]
1307
+ b2FloatW t3a = b2UnpackHiW( b1a, b3a );
1308
+
1309
+ // [w2 w4 f2 f4]
1310
+ b2FloatW t4a = b2UnpackHiW( b2a, b4a );
1311
+
1312
+ b2BodyStateW simdBody;
1313
+ simdBody.v.X = b2UnpackLoW( t1a, t2a );
1314
+ simdBody.v.Y = b2UnpackHiW( t1a, t2a );
1315
+ simdBody.w = b2UnpackLoW( t3a, t4a );
1316
+ simdBody.flags = b2UnpackHiW( t3a, t4a );
1317
+
1318
+ b2FloatW t1b = b2UnpackLoW( b1b, b3b );
1319
+ b2FloatW t2b = b2UnpackLoW( b2b, b4b );
1320
+ b2FloatW t3b = b2UnpackHiW( b1b, b3b );
1321
+ b2FloatW t4b = b2UnpackHiW( b2b, b4b );
1322
+
1323
+ simdBody.dp.X = b2UnpackLoW( t1b, t2b );
1324
+ simdBody.dp.Y = b2UnpackHiW( t1b, t2b );
1325
+ simdBody.dq.C = b2UnpackLoW( t3b, t4b );
1326
+ simdBody.dq.S = b2UnpackHiW( t3b, t4b );
1327
+
1328
+ return simdBody;
1329
+ }
1330
+
1331
+ // This writes only the velocities back to the solver bodies
1332
+ static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )
1333
+ {
1334
+ _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" );
1335
+ B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 );
1336
+
1337
+ // [vx1 vy1 vx2 vy2]
1338
+ b2FloatW t1 = b2UnpackLoW( simdBody->v.X, simdBody->v.Y );
1339
+ // [vx3 vy3 vx4 vy4]
1340
+ b2FloatW t2 = b2UnpackHiW( simdBody->v.X, simdBody->v.Y );
1341
+ // [w1 f1 w2 f2]
1342
+ b2FloatW t3 = b2UnpackLoW( simdBody->w, simdBody->flags );
1343
+ // [w3 f3 w4 f4]
1344
+ b2FloatW t4 = b2UnpackHiW( simdBody->w, simdBody->flags );
1345
+
1346
+ // I don't use any dummy body in the body array because this will lead to multithreaded sharing and the
1347
+ // associated cache flushing.
1348
+ if ( indices[0] != B2_NULL_INDEX )
1349
+ {
1350
+ // [t1.x t1.y t3.x t3.y]
1351
+ b2StoreW( (float*)( states + indices[0] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ) );
1352
+ }
1353
+
1354
+ if ( indices[1] != B2_NULL_INDEX )
1355
+ {
1356
+ // [t1.z t1.w t3.z t3.w]
1357
+ b2StoreW( (float*)( states + indices[1] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ) );
1358
+ }
1359
+
1360
+ if ( indices[2] != B2_NULL_INDEX )
1361
+ {
1362
+ // [t2.x t2.y t4.x t4.y]
1363
+ b2StoreW( (float*)( states + indices[2] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 1, 0, 1, 0 ) ) );
1364
+ }
1365
+
1366
+ if ( indices[3] != B2_NULL_INDEX )
1367
+ {
1368
+ // [t2.z t2.w t4.z t4.w]
1369
+ b2StoreW( (float*)( states + indices[3] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 3, 2, 3, 2 ) ) );
1370
+ }
1371
+ }
1372
+
1373
+ #else
1374
+
1375
+ // This is a load and transpose
1376
+ static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices )
1377
+ {
1378
+ b2BodyState identity = b2_identityBodyState;
1379
+
1380
+ b2BodyState s1 = indices[0] == B2_NULL_INDEX ? identity : states[indices[0]];
1381
+ b2BodyState s2 = indices[1] == B2_NULL_INDEX ? identity : states[indices[1]];
1382
+ b2BodyState s3 = indices[2] == B2_NULL_INDEX ? identity : states[indices[2]];
1383
+ b2BodyState s4 = indices[3] == B2_NULL_INDEX ? identity : states[indices[3]];
1384
+
1385
+ b2BodyStateW simdBody;
1386
+ simdBody.v.X = (b2FloatW){ s1.linearVelocity.x, s2.linearVelocity.x, s3.linearVelocity.x, s4.linearVelocity.x };
1387
+ simdBody.v.Y = (b2FloatW){ s1.linearVelocity.y, s2.linearVelocity.y, s3.linearVelocity.y, s4.linearVelocity.y };
1388
+ simdBody.w = (b2FloatW){ s1.angularVelocity, s2.angularVelocity, s3.angularVelocity, s4.angularVelocity };
1389
+ simdBody.flags = (b2FloatW){ (float)s1.flags, (float)s2.flags, (float)s3.flags, (float)s4.flags };
1390
+ simdBody.dp.X = (b2FloatW){ s1.deltaPosition.x, s2.deltaPosition.x, s3.deltaPosition.x, s4.deltaPosition.x };
1391
+ simdBody.dp.Y = (b2FloatW){ s1.deltaPosition.y, s2.deltaPosition.y, s3.deltaPosition.y, s4.deltaPosition.y };
1392
+ simdBody.dq.C = (b2FloatW){ s1.deltaRotation.c, s2.deltaRotation.c, s3.deltaRotation.c, s4.deltaRotation.c };
1393
+ simdBody.dq.S = (b2FloatW){ s1.deltaRotation.s, s2.deltaRotation.s, s3.deltaRotation.s, s4.deltaRotation.s };
1394
+
1395
+ return simdBody;
1396
+ }
1397
+
1398
+ // This writes only the velocities back to the solver bodies
1399
+ static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody )
1400
+ {
1401
+ // todo somehow skip writing to kinematic bodies
1402
+
1403
+ if ( indices[0] != B2_NULL_INDEX )
1404
+ {
1405
+ b2BodyState* state = states + indices[0];
1406
+ state->linearVelocity.x = simdBody->v.X.x;
1407
+ state->linearVelocity.y = simdBody->v.Y.x;
1408
+ state->angularVelocity = simdBody->w.x;
1409
+ }
1410
+
1411
+ if ( indices[1] != B2_NULL_INDEX )
1412
+ {
1413
+ b2BodyState* state = states + indices[1];
1414
+ state->linearVelocity.x = simdBody->v.X.y;
1415
+ state->linearVelocity.y = simdBody->v.Y.y;
1416
+ state->angularVelocity = simdBody->w.y;
1417
+ }
1418
+
1419
+ if ( indices[2] != B2_NULL_INDEX )
1420
+ {
1421
+ b2BodyState* state = states + indices[2];
1422
+ state->linearVelocity.x = simdBody->v.X.z;
1423
+ state->linearVelocity.y = simdBody->v.Y.z;
1424
+ state->angularVelocity = simdBody->w.z;
1425
+ }
1426
+
1427
+ if ( indices[3] != B2_NULL_INDEX )
1428
+ {
1429
+ b2BodyState* state = states + indices[3];
1430
+ state->linearVelocity.x = simdBody->v.X.w;
1431
+ state->linearVelocity.y = simdBody->v.Y.w;
1432
+ state->angularVelocity = simdBody->w.w;
1433
+ }
1434
+ }
1435
+
1436
+ #endif
1437
+
1438
+ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context )
1439
+ {
1440
+ b2TracyCZoneNC( prepare_contact, "Prepare Contact", b2_colorYellow, true );
1441
+ b2World* world = context->world;
1442
+ b2ContactSim** contacts = context->contacts;
1443
+ b2ContactConstraintSIMD* constraints = context->simdContactConstraints;
1444
+ b2BodyState* awakeStates = context->states;
1445
+ #if B2_VALIDATE
1446
+ b2Body* bodies = world->bodies.data;
1447
+ #endif
1448
+
1449
+ // Stiffer for static contacts to avoid bodies getting pushed through the ground
1450
+ b2Softness contactSoftness = context->contactSoftness;
1451
+ b2Softness staticSoftness = context->staticSoftness;
1452
+
1453
+ float warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f;
1454
+
1455
+ for ( int i = startIndex; i < endIndex; ++i )
1456
+ {
1457
+ b2ContactConstraintSIMD* constraint = constraints + i;
1458
+
1459
+ for ( int j = 0; j < B2_SIMD_WIDTH; ++j )
1460
+ {
1461
+ b2ContactSim* contactSim = contacts[B2_SIMD_WIDTH * i + j];
1462
+
1463
+ if ( contactSim != NULL )
1464
+ {
1465
+ const b2Manifold* manifold = &contactSim->manifold;
1466
+
1467
+ int indexA = contactSim->bodySimIndexA;
1468
+ int indexB = contactSim->bodySimIndexB;
1469
+
1470
+ #if B2_VALIDATE
1471
+ b2Body* bodyA = bodies + contactSim->bodyIdA;
1472
+ int validIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX;
1473
+ b2Body* bodyB = bodies + contactSim->bodyIdB;
1474
+ int validIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX;
1475
+
1476
+ B2_ASSERT( indexA == validIndexA );
1477
+ B2_ASSERT( indexB == validIndexB );
1478
+ #endif
1479
+ constraint->indexA[j] = indexA;
1480
+ constraint->indexB[j] = indexB;
1481
+
1482
+ b2Vec2 vA = b2Vec2_zero;
1483
+ float wA = 0.0f;
1484
+ float mA = contactSim->invMassA;
1485
+ float iA = contactSim->invIA;
1486
+ if ( indexA != B2_NULL_INDEX )
1487
+ {
1488
+ b2BodyState* stateA = awakeStates + indexA;
1489
+ vA = stateA->linearVelocity;
1490
+ wA = stateA->angularVelocity;
1491
+ }
1492
+
1493
+ b2Vec2 vB = b2Vec2_zero;
1494
+ float wB = 0.0f;
1495
+ float mB = contactSim->invMassB;
1496
+ float iB = contactSim->invIB;
1497
+ if ( indexB != B2_NULL_INDEX )
1498
+ {
1499
+ b2BodyState* stateB = awakeStates + indexB;
1500
+ vB = stateB->linearVelocity;
1501
+ wB = stateB->angularVelocity;
1502
+ }
1503
+
1504
+ ( (float*)&constraint->invMassA )[j] = mA;
1505
+ ( (float*)&constraint->invMassB )[j] = mB;
1506
+ ( (float*)&constraint->invIA )[j] = iA;
1507
+ ( (float*)&constraint->invIB )[j] = iB;
1508
+
1509
+ {
1510
+ float k = iA + iB;
1511
+ ( (float*)&constraint->rollingMass )[j] = k > 0.0f ? 1.0f / k : 0.0f;
1512
+ }
1513
+
1514
+ b2Softness soft = ( indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX ) ? staticSoftness : contactSoftness;
1515
+
1516
+ b2Vec2 normal = manifold->normal;
1517
+ ( (float*)&constraint->normal.X )[j] = normal.x;
1518
+ ( (float*)&constraint->normal.Y )[j] = normal.y;
1519
+
1520
+ ( (float*)&constraint->friction )[j] = contactSim->friction;
1521
+ ( (float*)&constraint->tangentSpeed )[j] = contactSim->tangentSpeed;
1522
+ ( (float*)&constraint->restitution )[j] = contactSim->restitution;
1523
+ ( (float*)&constraint->rollingResistance )[j] = contactSim->rollingResistance;
1524
+ ( (float*)&constraint->rollingImpulse )[j] = warmStartScale * manifold->rollingImpulse;
1525
+
1526
+ ( (float*)&constraint->biasRate )[j] = soft.biasRate;
1527
+ ( (float*)&constraint->massScale )[j] = soft.massScale;
1528
+ ( (float*)&constraint->impulseScale )[j] = soft.impulseScale;
1529
+
1530
+ b2Vec2 tangent = b2RightPerp( normal );
1531
+
1532
+ {
1533
+ const b2ManifoldPoint* mp = manifold->points + 0;
1534
+
1535
+ b2Vec2 rA = mp->anchorA;
1536
+ b2Vec2 rB = mp->anchorB;
1537
+
1538
+ ( (float*)&constraint->anchorA1.X )[j] = rA.x;
1539
+ ( (float*)&constraint->anchorA1.Y )[j] = rA.y;
1540
+ ( (float*)&constraint->anchorB1.X )[j] = rB.x;
1541
+ ( (float*)&constraint->anchorB1.Y )[j] = rB.y;
1542
+
1543
+ ( (float*)&constraint->baseSeparation1 )[j] = mp->separation - b2Dot( b2Sub( rB, rA ), normal );
1544
+
1545
+ ( (float*)&constraint->normalImpulse1 )[j] = warmStartScale * mp->normalImpulse;
1546
+ ( (float*)&constraint->tangentImpulse1 )[j] = warmStartScale * mp->tangentImpulse;
1547
+ ( (float*)&constraint->totalNormalImpulse1 )[j] = 0.0f;
1548
+
1549
+ float rnA = b2Cross( rA, normal );
1550
+ float rnB = b2Cross( rB, normal );
1551
+ float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
1552
+ ( (float*)&constraint->normalMass1 )[j] = kNormal > 0.0f ? 1.0f / kNormal : 0.0f;
1553
+
1554
+ float rtA = b2Cross( rA, tangent );
1555
+ float rtB = b2Cross( rB, tangent );
1556
+ float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;
1557
+ ( (float*)&constraint->tangentMass1 )[j] = kTangent > 0.0f ? 1.0f / kTangent : 0.0f;
1558
+
1559
+ // relative velocity for restitution
1560
+ b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
1561
+ b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
1562
+ ( (float*)&constraint->relativeVelocity1 )[j] = b2Dot( normal, b2Sub( vrB, vrA ) );
1563
+ }
1564
+
1565
+ int pointCount = manifold->pointCount;
1566
+ B2_ASSERT( 0 < pointCount && pointCount <= 2 );
1567
+
1568
+ if ( pointCount == 2 )
1569
+ {
1570
+ const b2ManifoldPoint* mp = manifold->points + 1;
1571
+
1572
+ b2Vec2 rA = mp->anchorA;
1573
+ b2Vec2 rB = mp->anchorB;
1574
+
1575
+ ( (float*)&constraint->anchorA2.X )[j] = rA.x;
1576
+ ( (float*)&constraint->anchorA2.Y )[j] = rA.y;
1577
+ ( (float*)&constraint->anchorB2.X )[j] = rB.x;
1578
+ ( (float*)&constraint->anchorB2.Y )[j] = rB.y;
1579
+
1580
+ ( (float*)&constraint->baseSeparation2 )[j] = mp->separation - b2Dot( b2Sub( rB, rA ), normal );
1581
+
1582
+ ( (float*)&constraint->normalImpulse2 )[j] = warmStartScale * mp->normalImpulse;
1583
+ ( (float*)&constraint->tangentImpulse2 )[j] = warmStartScale * mp->tangentImpulse;
1584
+ ( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;
1585
+
1586
+ float rnA = b2Cross( rA, normal );
1587
+ float rnB = b2Cross( rB, normal );
1588
+ float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
1589
+ ( (float*)&constraint->normalMass2 )[j] = kNormal > 0.0f ? 1.0f / kNormal : 0.0f;
1590
+
1591
+ float rtA = b2Cross( rA, tangent );
1592
+ float rtB = b2Cross( rB, tangent );
1593
+ float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;
1594
+ ( (float*)&constraint->tangentMass2 )[j] = kTangent > 0.0f ? 1.0f / kTangent : 0.0f;
1595
+
1596
+ // relative velocity for restitution
1597
+ b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) );
1598
+ b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) );
1599
+ ( (float*)&constraint->relativeVelocity2 )[j] = b2Dot( normal, b2Sub( vrB, vrA ) );
1600
+ }
1601
+ else
1602
+ {
1603
+ // dummy data that has no effect
1604
+ ( (float*)&constraint->baseSeparation2 )[j] = 0.0f;
1605
+ ( (float*)&constraint->normalImpulse2 )[j] = 0.0f;
1606
+ ( (float*)&constraint->tangentImpulse2 )[j] = 0.0f;
1607
+ ( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;
1608
+ ( (float*)&constraint->anchorA2.X )[j] = 0.0f;
1609
+ ( (float*)&constraint->anchorA2.Y )[j] = 0.0f;
1610
+ ( (float*)&constraint->anchorB2.X )[j] = 0.0f;
1611
+ ( (float*)&constraint->anchorB2.Y )[j] = 0.0f;
1612
+ ( (float*)&constraint->normalMass2 )[j] = 0.0f;
1613
+ ( (float*)&constraint->tangentMass2 )[j] = 0.0f;
1614
+ ( (float*)&constraint->relativeVelocity2 )[j] = 0.0f;
1615
+ }
1616
+ }
1617
+ else
1618
+ {
1619
+ // SIMD remainder
1620
+ constraint->indexA[j] = B2_NULL_INDEX;
1621
+ constraint->indexB[j] = B2_NULL_INDEX;
1622
+
1623
+ ( (float*)&constraint->invMassA )[j] = 0.0f;
1624
+ ( (float*)&constraint->invMassB )[j] = 0.0f;
1625
+ ( (float*)&constraint->invIA )[j] = 0.0f;
1626
+ ( (float*)&constraint->invIB )[j] = 0.0f;
1627
+
1628
+ ( (float*)&constraint->normal.X )[j] = 0.0f;
1629
+ ( (float*)&constraint->normal.Y )[j] = 0.0f;
1630
+ ( (float*)&constraint->friction )[j] = 0.0f;
1631
+ ( (float*)&constraint->tangentSpeed )[j] = 0.0f;
1632
+ ( (float*)&constraint->rollingResistance )[j] = 0.0f;
1633
+ ( (float*)&constraint->rollingMass )[j] = 0.0f;
1634
+ ( (float*)&constraint->rollingImpulse )[j] = 0.0f;
1635
+ ( (float*)&constraint->biasRate )[j] = 0.0f;
1636
+ ( (float*)&constraint->massScale )[j] = 0.0f;
1637
+ ( (float*)&constraint->impulseScale )[j] = 0.0f;
1638
+
1639
+ ( (float*)&constraint->anchorA1.X )[j] = 0.0f;
1640
+ ( (float*)&constraint->anchorA1.Y )[j] = 0.0f;
1641
+ ( (float*)&constraint->anchorB1.X )[j] = 0.0f;
1642
+ ( (float*)&constraint->anchorB1.Y )[j] = 0.0f;
1643
+ ( (float*)&constraint->baseSeparation1 )[j] = 0.0f;
1644
+ ( (float*)&constraint->normalImpulse1 )[j] = 0.0f;
1645
+ ( (float*)&constraint->tangentImpulse1 )[j] = 0.0f;
1646
+ ( (float*)&constraint->totalNormalImpulse1 )[j] = 0.0f;
1647
+ ( (float*)&constraint->normalMass1 )[j] = 0.0f;
1648
+ ( (float*)&constraint->tangentMass1 )[j] = 0.0f;
1649
+
1650
+ ( (float*)&constraint->anchorA2.X )[j] = 0.0f;
1651
+ ( (float*)&constraint->anchorA2.Y )[j] = 0.0f;
1652
+ ( (float*)&constraint->anchorB2.X )[j] = 0.0f;
1653
+ ( (float*)&constraint->anchorB2.Y )[j] = 0.0f;
1654
+ ( (float*)&constraint->baseSeparation2 )[j] = 0.0f;
1655
+ ( (float*)&constraint->normalImpulse2 )[j] = 0.0f;
1656
+ ( (float*)&constraint->tangentImpulse2 )[j] = 0.0f;
1657
+ ( (float*)&constraint->totalNormalImpulse2 )[j] = 0.0f;
1658
+ ( (float*)&constraint->normalMass2 )[j] = 0.0f;
1659
+ ( (float*)&constraint->tangentMass2 )[j] = 0.0f;
1660
+
1661
+ ( (float*)&constraint->restitution )[j] = 0.0f;
1662
+ ( (float*)&constraint->relativeVelocity1 )[j] = 0.0f;
1663
+ ( (float*)&constraint->relativeVelocity2 )[j] = 0.0f;
1664
+ }
1665
+ }
1666
+ }
1667
+
1668
+ b2TracyCZoneEnd( prepare_contact );
1669
+ }
1670
+
1671
+ void b2WarmStartContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex )
1672
+ {
1673
+ b2TracyCZoneNC( warm_start_contact, "Warm Start", b2_colorGreen, true );
1674
+
1675
+ b2BodyState* states = context->states;
1676
+ b2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints;
1677
+
1678
+ for ( int i = startIndex; i < endIndex; ++i )
1679
+ {
1680
+ b2ContactConstraintSIMD* c = constraints + i;
1681
+ b2BodyStateW bA = b2GatherBodies( states, c->indexA );
1682
+ b2BodyStateW bB = b2GatherBodies( states, c->indexB );
1683
+
1684
+ b2FloatW tangentX = c->normal.Y;
1685
+ b2FloatW tangentY = b2SubW( b2ZeroW(), c->normal.X );
1686
+
1687
+ {
1688
+ // fixed anchors
1689
+ b2Vec2W rA = c->anchorA1;
1690
+ b2Vec2W rB = c->anchorB1;
1691
+
1692
+ b2Vec2W P;
1693
+ P.X = b2AddW( b2MulW( c->normalImpulse1, c->normal.X ), b2MulW( c->tangentImpulse1, tangentX ) );
1694
+ P.Y = b2AddW( b2MulW( c->normalImpulse1, c->normal.Y ), b2MulW( c->tangentImpulse1, tangentY ) );
1695
+ bA.w = b2MulSubW( bA.w, c->invIA, b2CrossW( rA, P ) );
1696
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, P.X );
1697
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, P.Y );
1698
+ bB.w = b2MulAddW( bB.w, c->invIB, b2CrossW( rB, P ) );
1699
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, P.X );
1700
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, P.Y );
1701
+ }
1702
+
1703
+ {
1704
+ // fixed anchors
1705
+ b2Vec2W rA = c->anchorA2;
1706
+ b2Vec2W rB = c->anchorB2;
1707
+
1708
+ b2Vec2W P;
1709
+ P.X = b2AddW( b2MulW( c->normalImpulse2, c->normal.X ), b2MulW( c->tangentImpulse2, tangentX ) );
1710
+ P.Y = b2AddW( b2MulW( c->normalImpulse2, c->normal.Y ), b2MulW( c->tangentImpulse2, tangentY ) );
1711
+ bA.w = b2MulSubW( bA.w, c->invIA, b2CrossW( rA, P ) );
1712
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, P.X );
1713
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, P.Y );
1714
+ bB.w = b2MulAddW( bB.w, c->invIB, b2CrossW( rB, P ) );
1715
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, P.X );
1716
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, P.Y );
1717
+ }
1718
+
1719
+ bA.w = b2MulSubW( bA.w, c->invIA, c->rollingImpulse );
1720
+ bB.w = b2MulAddW( bB.w, c->invIB, c->rollingImpulse );
1721
+
1722
+ b2ScatterBodies( states, c->indexA, &bA );
1723
+ b2ScatterBodies( states, c->indexB, &bB );
1724
+ }
1725
+
1726
+ b2TracyCZoneEnd( warm_start_contact );
1727
+ }
1728
+
1729
+ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias )
1730
+ {
1731
+ b2TracyCZoneNC( solve_contact, "Solve Contact", b2_colorAliceBlue, true );
1732
+
1733
+ b2BodyState* states = context->states;
1734
+ b2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints;
1735
+ b2FloatW inv_h = b2SplatW( context->inv_h );
1736
+ b2FloatW minBiasVel = b2SplatW( -context->world->maxContactPushSpeed );
1737
+ b2FloatW oneW = b2SplatW( 1.0f );
1738
+
1739
+ for ( int i = startIndex; i < endIndex; ++i )
1740
+ {
1741
+ b2ContactConstraintSIMD* c = constraints + i;
1742
+
1743
+ b2BodyStateW bA = b2GatherBodies( states, c->indexA );
1744
+ b2BodyStateW bB = b2GatherBodies( states, c->indexB );
1745
+
1746
+ b2FloatW biasRate, massScale, impulseScale;
1747
+ if ( useBias )
1748
+ {
1749
+ biasRate = c->biasRate;
1750
+ massScale = c->massScale;
1751
+ impulseScale = c->impulseScale;
1752
+ }
1753
+ else
1754
+ {
1755
+ biasRate = b2ZeroW();
1756
+ massScale = oneW;
1757
+ impulseScale = b2ZeroW();
1758
+ }
1759
+
1760
+ b2FloatW totalNormalImpulse = b2ZeroW();
1761
+
1762
+ b2Vec2W dp = { b2SubW( bB.dp.X, bA.dp.X ), b2SubW( bB.dp.Y, bA.dp.Y ) };
1763
+
1764
+ // point1 non-penetration constraint
1765
+ {
1766
+ // Fixed anchors for impulses
1767
+ b2Vec2W rA = c->anchorA1;
1768
+ b2Vec2W rB = c->anchorB1;
1769
+
1770
+ // Moving anchors for current separation
1771
+ b2Vec2W rsA = b2RotateVectorW( bA.dq, rA );
1772
+ b2Vec2W rsB = b2RotateVectorW( bB.dq, rB );
1773
+
1774
+ // compute current separation
1775
+ // this is subject to round-off error if the anchor is far from the body center of mass
1776
+ b2Vec2W ds = { b2AddW( dp.X, b2SubW( rsB.X, rsA.X ) ), b2AddW( dp.Y, b2SubW( rsB.Y, rsA.Y ) ) };
1777
+ b2FloatW s = b2AddW( b2DotW( c->normal, ds ), c->baseSeparation1 );
1778
+
1779
+ // Apply speculative bias if separation is greater than zero, otherwise apply soft constraint bias
1780
+ // The minBiasVel is meant to limit stiffness, not increase it.
1781
+ b2FloatW mask = b2GreaterThanW( s, b2ZeroW() );
1782
+ b2FloatW specBias = b2MulW( s, inv_h );
1783
+ b2FloatW softBias = b2MaxW( b2MulW( biasRate, s ), minBiasVel );
1784
+
1785
+ // todo try b2MaxW(softBias, specBias);
1786
+ b2FloatW bias = b2BlendW( softBias, specBias, mask );
1787
+
1788
+ b2FloatW pointMassScale = b2BlendW( massScale, oneW, mask );
1789
+ b2FloatW pointImpulseScale = b2BlendW( impulseScale, b2ZeroW(), mask );
1790
+
1791
+ // Relative velocity at contact
1792
+ b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );
1793
+ b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );
1794
+ b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );
1795
+
1796
+ // Compute normal impulse
1797
+ b2FloatW negImpulse = b2AddW( b2MulW( c->normalMass1, b2MulW( pointMassScale, b2AddW( vn, bias ) ) ),
1798
+ b2MulW( pointImpulseScale, c->normalImpulse1 ) );
1799
+
1800
+ // Clamp the accumulated impulse
1801
+ b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse1, negImpulse ), b2ZeroW() );
1802
+ b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse1 );
1803
+ c->normalImpulse1 = newImpulse;
1804
+ c->totalNormalImpulse1 = b2AddW( c->totalNormalImpulse1, newImpulse );
1805
+
1806
+ totalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse );
1807
+
1808
+ // Apply contact impulse
1809
+ b2FloatW Px = b2MulW( impulse, c->normal.X );
1810
+ b2FloatW Py = b2MulW( impulse, c->normal.Y );
1811
+
1812
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );
1813
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );
1814
+ bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );
1815
+
1816
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );
1817
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );
1818
+ bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );
1819
+ }
1820
+
1821
+ // second point non-penetration constraint
1822
+ {
1823
+ // moving anchors for current separation
1824
+ b2Vec2W rsA = b2RotateVectorW( bA.dq, c->anchorA2 );
1825
+ b2Vec2W rsB = b2RotateVectorW( bB.dq, c->anchorB2 );
1826
+
1827
+ // compute current separation
1828
+ b2Vec2W ds = { b2AddW( dp.X, b2SubW( rsB.X, rsA.X ) ), b2AddW( dp.Y, b2SubW( rsB.Y, rsA.Y ) ) };
1829
+ b2FloatW s = b2AddW( b2DotW( c->normal, ds ), c->baseSeparation2 );
1830
+
1831
+ b2FloatW mask = b2GreaterThanW( s, b2ZeroW() );
1832
+ b2FloatW specBias = b2MulW( s, inv_h );
1833
+ b2FloatW softBias = b2MaxW( b2MulW( biasRate, s ), minBiasVel );
1834
+ b2FloatW bias = b2BlendW( softBias, specBias, mask );
1835
+
1836
+ b2FloatW pointMassScale = b2BlendW( massScale, oneW, mask );
1837
+ b2FloatW pointImpulseScale = b2BlendW( impulseScale, b2ZeroW(), mask );
1838
+
1839
+ // fixed anchors for Jacobians
1840
+ b2Vec2W rA = c->anchorA2;
1841
+ b2Vec2W rB = c->anchorB2;
1842
+
1843
+ // Relative velocity at contact
1844
+ b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );
1845
+ b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );
1846
+ b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );
1847
+
1848
+ // Compute normal impulse
1849
+ b2FloatW negImpulse = b2AddW( b2MulW( c->normalMass2, b2MulW( pointMassScale, b2AddW( vn, bias ) ) ),
1850
+ b2MulW( pointImpulseScale, c->normalImpulse2 ) );
1851
+
1852
+ // Clamp the accumulated impulse
1853
+ b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse2, negImpulse ), b2ZeroW() );
1854
+ b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse2 );
1855
+ c->normalImpulse2 = newImpulse;
1856
+ c->totalNormalImpulse2 = b2AddW( c->totalNormalImpulse2, newImpulse );
1857
+
1858
+ totalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse );
1859
+
1860
+ // Apply contact impulse
1861
+ b2FloatW Px = b2MulW( impulse, c->normal.X );
1862
+ b2FloatW Py = b2MulW( impulse, c->normal.Y );
1863
+
1864
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );
1865
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );
1866
+ bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );
1867
+
1868
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );
1869
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );
1870
+ bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );
1871
+ }
1872
+
1873
+ b2FloatW tangentX = c->normal.Y;
1874
+ b2FloatW tangentY = b2SubW( b2ZeroW(), c->normal.X );
1875
+
1876
+ // point 1 friction constraint
1877
+ {
1878
+ // fixed anchors for Jacobians
1879
+ b2Vec2W rA = c->anchorA1;
1880
+ b2Vec2W rB = c->anchorB1;
1881
+
1882
+ // Relative velocity at contact
1883
+ b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );
1884
+ b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );
1885
+ b2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) );
1886
+
1887
+ // Tangent speed (conveyor belt)
1888
+ vt = b2SubW( vt, c->tangentSpeed );
1889
+
1890
+ // Compute tangent force
1891
+ b2FloatW negImpulse = b2MulW( c->tangentMass1, vt );
1892
+
1893
+ // Clamp the accumulated force
1894
+ b2FloatW maxFriction = b2MulW( c->friction, c->normalImpulse1 );
1895
+ b2FloatW newImpulse = b2SubW( c->tangentImpulse1, negImpulse );
1896
+ newImpulse = b2MaxW( b2SubW( b2ZeroW(), maxFriction ), b2MinW( newImpulse, maxFriction ) );
1897
+ b2FloatW impulse = b2SubW( newImpulse, c->tangentImpulse1 );
1898
+ c->tangentImpulse1 = newImpulse;
1899
+
1900
+ // Apply contact impulse
1901
+ b2FloatW Px = b2MulW( impulse, tangentX );
1902
+ b2FloatW Py = b2MulW( impulse, tangentY );
1903
+
1904
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );
1905
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );
1906
+ bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );
1907
+
1908
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );
1909
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );
1910
+ bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );
1911
+ }
1912
+
1913
+ // second point friction constraint
1914
+ {
1915
+ // fixed anchors for Jacobians
1916
+ b2Vec2W rA = c->anchorA2;
1917
+ b2Vec2W rB = c->anchorB2;
1918
+
1919
+ // Relative velocity at contact
1920
+ b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );
1921
+ b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );
1922
+ b2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) );
1923
+
1924
+ // Tangent speed (conveyor belt)
1925
+ vt = b2SubW( vt, c->tangentSpeed );
1926
+
1927
+ // Compute tangent force
1928
+ b2FloatW negImpulse = b2MulW( c->tangentMass2, vt );
1929
+
1930
+ // Clamp the accumulated force
1931
+ b2FloatW maxFriction = b2MulW( c->friction, c->normalImpulse2 );
1932
+ b2FloatW newImpulse = b2SubW( c->tangentImpulse2, negImpulse );
1933
+ newImpulse = b2MaxW( b2SubW( b2ZeroW(), maxFriction ), b2MinW( newImpulse, maxFriction ) );
1934
+ b2FloatW impulse = b2SubW( newImpulse, c->tangentImpulse2 );
1935
+ c->tangentImpulse2 = newImpulse;
1936
+
1937
+ // Apply contact impulse
1938
+ b2FloatW Px = b2MulW( impulse, tangentX );
1939
+ b2FloatW Py = b2MulW( impulse, tangentY );
1940
+
1941
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );
1942
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );
1943
+ bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );
1944
+
1945
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );
1946
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );
1947
+ bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );
1948
+ }
1949
+
1950
+ // Rolling resistance
1951
+ {
1952
+ b2FloatW deltaLambda = b2MulW( c->rollingMass, b2SubW( bA.w, bB.w ) );
1953
+ b2FloatW lambda = c->rollingImpulse;
1954
+ b2FloatW maxLambda = b2MulW( c->rollingResistance, totalNormalImpulse );
1955
+ c->rollingImpulse = b2SymClampW( b2AddW( lambda, deltaLambda ), maxLambda );
1956
+ deltaLambda = b2SubW( c->rollingImpulse, lambda );
1957
+
1958
+ bA.w = b2MulSubW( bA.w, c->invIA, deltaLambda );
1959
+ bB.w = b2MulAddW( bB.w, c->invIB, deltaLambda );
1960
+ }
1961
+
1962
+ b2ScatterBodies( states, c->indexA, &bA );
1963
+ b2ScatterBodies( states, c->indexB, &bB );
1964
+ }
1965
+
1966
+ b2TracyCZoneEnd( solve_contact );
1967
+ }
1968
+
1969
+ void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex )
1970
+ {
1971
+ b2TracyCZoneNC( restitution, "Restitution", b2_colorDodgerBlue, true );
1972
+
1973
+ b2BodyState* states = context->states;
1974
+ b2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints;
1975
+ b2FloatW threshold = b2SplatW( context->world->restitutionThreshold );
1976
+ b2FloatW zero = b2ZeroW();
1977
+
1978
+ for ( int i = startIndex; i < endIndex; ++i )
1979
+ {
1980
+ b2ContactConstraintSIMD* c = constraints + i;
1981
+
1982
+ if ( b2AllZeroW( c->restitution ) )
1983
+ {
1984
+ // No lanes have restitution. Common case.
1985
+ continue;
1986
+ }
1987
+
1988
+ // Create a mask based on restitution so that lanes with no restitution are not affected
1989
+ // by the calculations below.
1990
+ b2FloatW restitutionMask = b2EqualsW( c->restitution, zero );
1991
+
1992
+ b2BodyStateW bA = b2GatherBodies( states, c->indexA );
1993
+ b2BodyStateW bB = b2GatherBodies( states, c->indexB );
1994
+
1995
+ // first point non-penetration constraint
1996
+ {
1997
+ // Set effective mass to zero if restitution should not be applied
1998
+ b2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity1, threshold ), zero );
1999
+ b2FloatW mask2 = b2EqualsW( c->totalNormalImpulse1, zero );
2000
+ b2FloatW mask = b2OrW( b2OrW( mask1, mask2 ), restitutionMask );
2001
+ b2FloatW mass = b2BlendW( c->normalMass1, zero, mask );
2002
+
2003
+ // fixed anchors for Jacobians
2004
+ b2Vec2W rA = c->anchorA1;
2005
+ b2Vec2W rB = c->anchorB1;
2006
+
2007
+ // Relative velocity at contact
2008
+ b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );
2009
+ b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );
2010
+ b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );
2011
+
2012
+ // Compute normal impulse
2013
+ b2FloatW negImpulse = b2MulW( mass, b2AddW( vn, b2MulW( c->restitution, c->relativeVelocity1 ) ) );
2014
+
2015
+ // Clamp the accumulated impulse
2016
+ b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse1, negImpulse ), b2ZeroW() );
2017
+ b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse1 );
2018
+ c->normalImpulse1 = newImpulse;
2019
+
2020
+ // Apply contact impulse
2021
+ b2FloatW Px = b2MulW( impulse, c->normal.X );
2022
+ b2FloatW Py = b2MulW( impulse, c->normal.Y );
2023
+
2024
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );
2025
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );
2026
+ bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );
2027
+
2028
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );
2029
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );
2030
+ bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );
2031
+ }
2032
+
2033
+ // second point non-penetration constraint
2034
+ {
2035
+ // Set effective mass to zero if restitution should not be applied
2036
+ b2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity2, threshold ), zero );
2037
+ b2FloatW mask2 = b2EqualsW( c->totalNormalImpulse2, zero );
2038
+ b2FloatW mask = b2OrW( b2OrW( mask1, mask2 ), restitutionMask );
2039
+ b2FloatW mass = b2BlendW( c->normalMass2, zero, mask );
2040
+
2041
+ // fixed anchors for Jacobians
2042
+ b2Vec2W rA = c->anchorA2;
2043
+ b2Vec2W rB = c->anchorB2;
2044
+
2045
+ // Relative velocity at contact
2046
+ b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) );
2047
+ b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) );
2048
+ b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) );
2049
+
2050
+ // Compute normal impulse
2051
+ b2FloatW negImpulse = b2MulW( mass, b2AddW( vn, b2MulW( c->restitution, c->relativeVelocity2 ) ) );
2052
+
2053
+ // Clamp the accumulated impulse
2054
+ b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse2, negImpulse ), b2ZeroW() );
2055
+ b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse2 );
2056
+ c->normalImpulse2 = newImpulse;
2057
+
2058
+ // Apply contact impulse
2059
+ b2FloatW Px = b2MulW( impulse, c->normal.X );
2060
+ b2FloatW Py = b2MulW( impulse, c->normal.Y );
2061
+
2062
+ bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px );
2063
+ bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py );
2064
+ bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) );
2065
+
2066
+ bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px );
2067
+ bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py );
2068
+ bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) );
2069
+ }
2070
+
2071
+ b2ScatterBodies( states, c->indexA, &bA );
2072
+ b2ScatterBodies( states, c->indexB, &bB );
2073
+ }
2074
+
2075
+ b2TracyCZoneEnd( restitution );
2076
+ }
2077
+
2078
+ void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context )
2079
+ {
2080
+ b2TracyCZoneNC( store_impulses, "Store", b2_colorFireBrick, true );
2081
+
2082
+ b2ContactSim** contacts = context->contacts;
2083
+ const b2ContactConstraintSIMD* constraints = context->simdContactConstraints;
2084
+
2085
+ b2Manifold dummy = { 0 };
2086
+
2087
+ for ( int constraintIndex = startIndex; constraintIndex < endIndex; ++constraintIndex )
2088
+ {
2089
+ const b2ContactConstraintSIMD* c = constraints + constraintIndex;
2090
+ const float* rollingImpulse = (float*)&c->rollingImpulse;
2091
+ const float* normalImpulse1 = (float*)&c->normalImpulse1;
2092
+ const float* normalImpulse2 = (float*)&c->normalImpulse2;
2093
+ const float* tangentImpulse1 = (float*)&c->tangentImpulse1;
2094
+ const float* tangentImpulse2 = (float*)&c->tangentImpulse2;
2095
+ const float* totalNormalImpulse1 = (float*)&c->totalNormalImpulse1;
2096
+ const float* totalNormalImpulse2 = (float*)&c->totalNormalImpulse2;
2097
+ const float* normalVelocity1 = (float*)&c->relativeVelocity1;
2098
+ const float* normalVelocity2 = (float*)&c->relativeVelocity2;
2099
+
2100
+ int baseIndex = B2_SIMD_WIDTH * constraintIndex;
2101
+
2102
+ for ( int laneIndex = 0; laneIndex < B2_SIMD_WIDTH; ++laneIndex )
2103
+ {
2104
+ b2Manifold* m = contacts[baseIndex + laneIndex] == NULL ? &dummy : &contacts[baseIndex + laneIndex]->manifold;
2105
+ m->rollingImpulse = rollingImpulse[laneIndex];
2106
+
2107
+ m->points[0].normalImpulse = normalImpulse1[laneIndex];
2108
+ m->points[0].tangentImpulse = tangentImpulse1[laneIndex];
2109
+ m->points[0].totalNormalImpulse = totalNormalImpulse1[laneIndex];
2110
+ m->points[0].normalVelocity = normalVelocity1[laneIndex];
2111
+
2112
+ m->points[1].normalImpulse = normalImpulse2[laneIndex];
2113
+ m->points[1].tangentImpulse = tangentImpulse2[laneIndex];
2114
+ m->points[1].totalNormalImpulse = totalNormalImpulse2[laneIndex];
2115
+ m->points[1].normalVelocity = normalVelocity2[laneIndex];
2116
+ }
2117
+ }
2118
+
2119
+ b2TracyCZoneEnd( store_impulses );
2120
+ }