Skip to content

Commit 6861e21

Browse files
committed
[upstream] Tunable joint stiffness (box2d #934)
Joint stiffness is now tunable per joint. This allows specific joints to have increased stiffness, which may be important for some games. Removed `b2World_SetJointTuning` Added `b2Joint_SetConstraintTuning` and `b2Joint_GetConstraintTuning`. Added `b2StoreWorldId` and `b2LoadWorldId`. Added `b2Joint_GetReferenceAngle`, `b2Joint_SetReferenceAngle`, `b2Joint_SetLocalAxisA`, and `b2Joint_GetLocalAxisA`. Removed `b2WeldJoint_GetReferenceAngle` and `b2WeldJoint_SetReferenceAngle`. Added `b2SpringDamper` math utility function used by the character sample. Fixed bug in `b2SolvePlanes`.
1 parent 8e1138c commit 6861e21

37 files changed

+659
-230
lines changed

src/Box2D.NET.Samples/SampleApp.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ public byte[] AllocFcn(uint size, int alignment)
417417
long sizeAligned = ((size - 1) | (uint)(alignment - 1)) + 1;
418418
B2_ASSERT((sizeAligned & (alignment - 1)) == 0);
419419

420-
// #if defined( _WIN64 ) || defined( _WIN32 )
420+
// #if defined( _MSC_VER ) || defined( __MINGW32__ ) || defined( __MINGW64__ )
421421
// void* ptr = _aligned_malloc( sizeAligned, alignment );
422422
// #else
423423
// void* ptr = aligned_alloc(alignment, sizeAligned);
@@ -429,7 +429,7 @@ public byte[] AllocFcn(uint size, int alignment)
429429

430430
private void FreeFcn(byte[] mem)
431431
{
432-
// #if defined( _WIN64 ) || defined( _WIN32 )
432+
// #if defined( _MSC_VER ) || defined( __MINGW32__ ) || defined( __MINGW64__ )
433433
// _aligned_free( mem );
434434
// #else
435435
// free(mem);
@@ -737,8 +737,8 @@ private void UpdateUI()
737737
if (ImGui.BeginTabItem("Controls"))
738738
{
739739
ImGui.PushItemWidth(100.0f);
740-
ImGui.SliderInt("Sub-steps", ref _context.settings.subStepCount, 1, 50);
741-
ImGui.SliderFloat("Hertz", ref _context.settings.hertz, 5.0f, 120.0f, "%.0f hz");
740+
ImGui.SliderInt("Sub-steps", ref _context.settings.subStepCount, 1, 32);
741+
ImGui.SliderFloat("Hertz", ref _context.settings.hertz, 5.0f, 240.0f, "%.0f hz");
742742

743743
if (ImGui.SliderInt("Workers", ref _context.settings.workerCount, 1, maxWorkers))
744744
{

src/Box2D.NET.Samples/Samples/Characters/Mover.cs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -397,13 +397,8 @@ void SolveMove(float timeStep, float throttle)
397397
{
398398
float pogoCurrentLength = castResult.fraction * rayLength;
399399

400-
float zeta = m_pogoDampingRatio;
401-
float hertz = m_pogoHertz;
402-
float omega = 2.0f * B2_PI * hertz;
403-
float omegaH = omega * timeStep;
404-
405-
m_pogoVelocity = (m_pogoVelocity - omega * omegaH * (pogoCurrentLength - pogoRestLength)) /
406-
(1.0f + 2.0f * zeta * omegaH + omegaH * omegaH);
400+
float offset = pogoCurrentLength - pogoRestLength;
401+
m_pogoVelocity = b2SpringDamper(m_pogoHertz, m_pogoDampingRatio, offset, m_pogoVelocity, timeStep);
407402

408403
B2Vec2 delta = castResult.fraction * translation;
409404
m_draw.DrawSegment(origin, origin + delta, B2HexColor.b2_colorGray);
@@ -445,15 +440,13 @@ void SolveMove(float timeStep, float throttle)
445440
mover.radius = m_capsule.radius;
446441

447442
b2World_CollideMover(m_worldId, ref mover, collideFilter, PlaneResultFcn, this);
448-
B2PlaneSolverResult solverResult = b2SolvePlanes(target, m_planes, m_planeCount);
449-
450-
m_totalIterations += solverResult.iterationCount;
443+
B2PlaneSolverResult result = b2SolvePlanes(target - m_transform.p, m_planes, m_planeCount);
451444

452-
B2Vec2 moverTranslation = solverResult.position - m_transform.p;
445+
m_totalIterations += result.iterationCount;
453446

454-
float fraction = b2World_CastMover(m_worldId, ref mover, moverTranslation, castFilter);
447+
float fraction = b2World_CastMover(m_worldId, ref mover, result.translation, castFilter);
455448

456-
B2Vec2 delta = fraction * moverTranslation;
449+
B2Vec2 delta = fraction * result.translation;
457450
m_transform.p += delta;
458451

459452
if (b2LengthSquared(delta) < tolerance * tolerance)

src/Box2D.NET.Samples/Samples/Determinisms/FallingHinges.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public FallingHinges(SampleContext context) : base(context)
3838
m_camera.m_center = new B2Vec2(0.0f, 7.5f);
3939
m_camera.m_zoom = 10.0f;
4040
}
41-
m_data = CreateFallingHinges( m_worldId );
41+
42+
m_data = CreateFallingHinges(m_worldId);
4243
m_done = false;
4344
}
4445

@@ -47,9 +48,9 @@ public override void Step()
4748
{
4849
base.Step();
4950

50-
if (m_done == false)
51+
if (m_context.settings.pause == false && m_done == false)
5152
{
52-
m_done = UpdateFallingHinges( m_worldId, ref m_data );
53+
m_done = UpdateFallingHinges(m_worldId, ref m_data);
5354
}
5455
}
5556

@@ -60,7 +61,6 @@ public override void Draw(Settings settings)
6061
if (m_done)
6162
{
6263
DrawTextLine($"sleep step = {m_data.sleepStep}, hash = 0x{m_data.hash:X8}");
63-
6464
}
6565
}
6666
}

src/Box2D.NET.Samples/Samples/Doohickey.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@ namespace Box2D.NET.Samples.Samples;
1313

1414
public class Doohickey
1515
{
16-
B2BodyId m_wheelId1;
17-
B2BodyId m_wheelId2;
18-
B2BodyId m_barId1;
19-
B2BodyId m_barId2;
16+
private B2BodyId m_wheelId1;
17+
private B2BodyId m_wheelId2;
18+
private B2BodyId m_barId1;
19+
private B2BodyId m_barId2;
2020

21-
B2JointId m_axleId1;
22-
B2JointId m_axleId2;
23-
B2JointId m_sliderId;
24-
25-
bool m_isSpawned;
21+
private B2JointId m_axleId1;
22+
private B2JointId m_axleId2;
23+
private B2JointId m_sliderId;
2624

25+
private bool m_isSpawned;
2726

2827
public Doohickey()
2928
{
@@ -37,6 +36,8 @@ public void Spawn(B2WorldId worldId, B2Vec2 position, float scale)
3736
bodyDef.type = B2BodyType.b2_dynamicBody;
3837

3938
B2ShapeDef shapeDef = b2DefaultShapeDef();
39+
shapeDef.material.rollingResistance = 0.1f;
40+
4041
B2Circle circle = new B2Circle(new B2Vec2(0.0f, 0.0f), 1.0f * scale);
4142
B2Capsule capsule = new B2Capsule(new B2Vec2(-3.5f * scale, 0.0f), new B2Vec2(3.5f * scale, 0.0f), 0.15f * scale);
4243

src/Box2D.NET.Samples/Samples/Joints/BallAndChain.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ public BallAndChain(SampleContext context) : base(context)
5151

5252
B2ShapeDef shapeDef = b2DefaultShapeDef();
5353
shapeDef.density = 20.0f;
54-
54+
shapeDef.filter.categoryBits = 0x1;
55+
shapeDef.filter.maskBits = 0x2;
5556
B2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
5657

5758
int jointIndex = 0;
@@ -70,8 +71,10 @@ public BallAndChain(SampleContext context) : base(context)
7071
jointDef.bodyIdB = bodyId;
7172
jointDef.localAnchorA = b2Body_GetLocalPoint(jointDef.bodyIdA, pivot);
7273
jointDef.localAnchorB = b2Body_GetLocalPoint(jointDef.bodyIdB, pivot);
73-
// jointDef.enableMotor = true;
74+
jointDef.enableMotor = true;
7475
jointDef.maxMotorTorque = m_frictionTorque;
76+
jointDef.enableSpring = i > 0;
77+
jointDef.hertz = 4.0f;
7578
m_jointIds[jointIndex++] = b2CreateRevoluteJoint(m_worldId, ref jointDef);
7679

7780
prevBodyId = bodyId;
@@ -83,8 +86,10 @@ public BallAndChain(SampleContext context) : base(context)
8386
B2BodyDef bodyDef = b2DefaultBodyDef();
8487
bodyDef.type = B2BodyType.b2_dynamicBody;
8588
bodyDef.position = new B2Vec2((1.0f + 2.0f * m_count) * hx + circle.radius - hx, m_count * hx);
86-
8789
B2BodyId bodyId = b2CreateBody(m_worldId, ref bodyDef);
90+
91+
shapeDef.filter.categoryBits = 0x2;
92+
shapeDef.filter.maskBits = 0x1;
8893
b2CreateCircleShape(bodyId, ref shapeDef, ref circle);
8994

9095
B2Vec2 pivot = new B2Vec2((2.0f * m_count) * hx, m_count * hx);
@@ -94,6 +99,8 @@ public BallAndChain(SampleContext context) : base(context)
9499
jointDef.localAnchorB = b2Body_GetLocalPoint(jointDef.bodyIdB, pivot);
95100
jointDef.enableMotor = true;
96101
jointDef.maxMotorTorque = m_frictionTorque;
102+
jointDef.enableSpring = true;
103+
jointDef.hertz = 4.0f;
97104
m_jointIds[jointIndex++] = b2CreateRevoluteJoint(m_worldId, ref jointDef);
98105
B2_ASSERT(jointIndex == m_count + 1);
99106
}

src/Box2D.NET.Samples/Samples/Joints/Bridge.cs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ public class Bridge : Sample
2626
private B2BodyId[] m_bodyIds = new B2BodyId[m_count];
2727
private B2JointId[] m_jointIds = new B2JointId[m_count + 1];
2828
private float m_frictionTorque;
29-
private float m_gravityScale;
29+
private float m_constraintHertz;
30+
private float m_constraintDampingRatio;
31+
private float m_springHertz;
32+
private float m_springDampingRatio;
3033

3134
private static Sample Create(SampleContext context)
3235
{
@@ -48,15 +51,25 @@ public Bridge(SampleContext context) : base(context)
4851
}
4952

5053
{
51-
B2Polygon box = b2MakeBox(0.5f, 0.125f);
54+
m_constraintHertz = 60.0f;
55+
m_constraintDampingRatio = 0.0f;
56+
m_springHertz = 2.0f;
57+
m_springDampingRatio = 0.7f;
58+
m_frictionTorque = 200.0f;
59+
60+
B2Polygon box = b2MakeBox( 0.5f, 0.125f );
5261

5362
B2ShapeDef shapeDef = b2DefaultShapeDef();
5463
shapeDef.density = 20.0f;
5564

5665
B2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
66+
jointDef.enableMotor = true;
67+
jointDef.maxMotorTorque = m_frictionTorque;
68+
jointDef.enableSpring = true;
69+
jointDef.hertz = m_springHertz;
70+
jointDef.dampingRatio = m_springDampingRatio;
71+
5772
int jointIndex = 0;
58-
m_frictionTorque = 200.0f;
59-
m_gravityScale = 1.0f;
6073

6174
float xbase = -80.0f;
6275

@@ -76,8 +89,6 @@ public Bridge(SampleContext context) : base(context)
7689
jointDef.bodyIdB = m_bodyIds[i];
7790
jointDef.localAnchorA = b2Body_GetLocalPoint(jointDef.bodyIdA, pivot);
7891
jointDef.localAnchorB = b2Body_GetLocalPoint(jointDef.bodyIdB, pivot);
79-
jointDef.enableMotor = true;
80-
jointDef.maxMotorTorque = m_frictionTorque;
8192
m_jointIds[jointIndex++] = b2CreateRevoluteJoint(m_worldId, ref jointDef);
8293

8394
prevBodyId = m_bodyIds[i];
@@ -89,8 +100,6 @@ public Bridge(SampleContext context) : base(context)
89100
jointDef.bodyIdB = groundId;
90101
jointDef.localAnchorA = b2Body_GetLocalPoint(jointDef.bodyIdA, pivot);
91102
jointDef.localAnchorB = b2Body_GetLocalPoint(jointDef.bodyIdB, pivot);
92-
jointDef.enableMotor = true;
93-
jointDef.maxMotorTorque = m_frictionTorque;
94103
m_jointIds[jointIndex++] = b2CreateRevoluteJoint(m_worldId, ref jointDef);
95104

96105
B2_ASSERT(jointIndex == m_count + 1);
@@ -133,15 +142,14 @@ public override void UpdateGui()
133142
{
134143
base.UpdateGui();
135144

136-
float height = 80.0f;
145+
float height = 180.0f;
137146
ImGui.SetNextWindowPos(new Vector2(10.0f, m_camera.m_height - height - 50.0f), ImGuiCond.Once);
138-
ImGui.SetNextWindowSize(new Vector2(240.0f, height));
147+
ImGui.SetNextWindowSize(new Vector2(320.0f, height));
139148

140149
ImGui.Begin("Bridge", ImGuiWindowFlags.NoResize);
141150

142-
// Slider takes half the window
143-
ImGui.PushItemWidth(ImGui.GetWindowWidth() * 0.5f);
144-
bool updateFriction = ImGui.SliderFloat("Joint Friction", ref m_frictionTorque, 0.0f, 1000.0f, "%2.f");
151+
ImGui.PushItemWidth(ImGui.GetWindowWidth() * 0.6f);
152+
bool updateFriction = ImGui.SliderFloat("Joint Friction", ref m_frictionTorque, 0.0f, 10000.0f, "%2.f");
145153
if (updateFriction)
146154
{
147155
for (int i = 0; i <= m_count; ++i)
@@ -150,14 +158,40 @@ public override void UpdateGui()
150158
}
151159
}
152160

153-
if (ImGui.SliderFloat("Gravity scale", ref m_gravityScale, -1.0f, 1.0f, "%.1f"))
161+
if ( ImGui.SliderFloat( "Spring hertz", ref m_springHertz, 0.0f, 30.0f, "%.0f" ) )
154162
{
155-
for (int i = 0; i < m_count; ++i)
163+
for ( int i = 0; i <= m_count; ++i )
156164
{
157-
b2Body_SetGravityScale(m_bodyIds[i], m_gravityScale);
165+
b2RevoluteJoint_SetSpringHertz( m_jointIds[i], m_springHertz );
158166
}
159167
}
160168

169+
if ( ImGui.SliderFloat( "Spring damping", ref m_springDampingRatio, 0.0f, 2.0f, "%.1f" ) )
170+
{
171+
for ( int i = 0; i <= m_count; ++i )
172+
{
173+
b2RevoluteJoint_SetSpringDampingRatio( m_jointIds[i], m_springDampingRatio );
174+
}
175+
}
176+
177+
if ( ImGui.SliderFloat( "Constraint hertz", ref m_constraintHertz, 15.0f, 240.0f, "%.0f" ) )
178+
{
179+
for ( int i = 0; i <= m_count; ++i )
180+
{
181+
b2Joint_SetConstraintTuning( m_jointIds[i], m_constraintHertz, m_constraintDampingRatio );
182+
}
183+
}
184+
185+
if ( ImGui.SliderFloat( "Constraint damping", ref m_constraintDampingRatio, 0.0f, 10.0f, "%.1f" ) )
186+
{
187+
for ( int i = 0; i <= m_count; ++i )
188+
{
189+
b2Joint_SetConstraintTuning( m_jointIds[i], m_constraintHertz, m_constraintDampingRatio );
190+
}
191+
}
192+
193+
ImGui.PopItemWidth();
194+
161195
ImGui.End();
162196
}
163197
}

src/Box2D.NET.Samples/Samples/Joints/Door.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public class Door : Sample
2424
private B2JointId m_jointId;
2525
private float m_impulse;
2626
private float m_translationError;
27+
private float m_jointHertz;
28+
private float m_jointDampingRatio;
2729
private bool m_enableLimit;
2830

2931
private static Sample Create(SampleContext context)
@@ -47,6 +49,10 @@ public Door(SampleContext context) : base(context)
4749
}
4850

4951
m_enableLimit = true;
52+
m_impulse = 50000.0f;
53+
m_translationError = 0.0f;
54+
m_jointHertz = 240.0f;
55+
m_jointDampingRatio = 1.0f;
5056

5157
{
5258
B2BodyDef bodyDef = b2DefaultBodyDef();
@@ -62,12 +68,11 @@ public Door(SampleContext context) : base(context)
6268
B2Polygon box = b2MakeBox(0.1f, 1.5f);
6369
b2CreatePolygonShape(m_doorId, ref shapeDef, ref box);
6470

65-
B2Vec2 pivot = new B2Vec2(0.0f, 0.0f);
6671
B2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
6772
jointDef.bodyIdA = groundId;
6873
jointDef.bodyIdB = m_doorId;
69-
jointDef.localAnchorA = b2Body_GetLocalPoint(jointDef.bodyIdA, pivot);
70-
jointDef.localAnchorB = b2Body_GetLocalPoint(jointDef.bodyIdB, pivot);
74+
jointDef.localAnchorA = new B2Vec2(0.0f, 0.0f);
75+
jointDef.localAnchorB = new B2Vec2(0.0f, -1.5f);
7176
jointDef.targetAngle = 0.0f;
7277
jointDef.enableSpring = true;
7378
jointDef.hertz = 1.0f;
@@ -81,10 +86,8 @@ public Door(SampleContext context) : base(context)
8186
jointDef.enableLimit = m_enableLimit;
8287

8388
m_jointId = b2CreateRevoluteJoint(m_worldId, ref jointDef);
89+
b2Joint_SetConstraintTuning(m_jointId, m_jointHertz, m_jointDampingRatio);
8490
}
85-
86-
m_impulse = 50000.0f;
87-
m_translationError = 0.0f;
8891
}
8992

9093
public override void UpdateGui()
@@ -109,6 +112,16 @@ public override void UpdateGui()
109112
b2RevoluteJoint_EnableLimit(m_jointId, m_enableLimit);
110113
}
111114

115+
if ( ImGui.SliderFloat( "hertz", ref m_jointHertz, 15.0f, 480.0f, "%.0f" ) )
116+
{
117+
b2Joint_SetConstraintTuning( m_jointId, m_jointHertz, m_jointDampingRatio );
118+
}
119+
120+
if ( ImGui.SliderFloat( "damping", ref m_jointDampingRatio, 0.0f, 10.0f, "%.1f" ) )
121+
{
122+
b2Joint_SetConstraintTuning( m_jointId, m_jointHertz, m_jointDampingRatio );
123+
}
124+
112125
ImGui.End();
113126
}
114127

src/Box2D.NET.Samples/Samples/Joints/GearLift.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public GearLift(SampleContext context) : base(context)
7979
float linkCount = 40;
8080
float doorHalfHeight = 1.5f;
8181

82-
B2Vec2 gearPosition1 = new B2Vec2(-4.25f, 10.25f);
82+
B2Vec2 gearPosition1 = new B2Vec2(-4.25f, 9.75f);
8383
B2Vec2 gearPosition2 = gearPosition1 + new B2Vec2(2.0f, 1.0f);
8484
B2Vec2 linkAttachPosition = gearPosition2 + new B2Vec2(gearRadius + 2.0f * toothHalfWidth + toothRadius, 0.0f);
8585
B2Vec2 doorPosition = linkAttachPosition - new B2Vec2(0.0f, 2.0f * linkCount * linkHalfLength + doorHalfHeight);

0 commit comments

Comments
 (0)