Skip to content

Commit 116a25a

Browse files
committed
[upstream] Bug fixes (box2d #956)
- Total contact impulse now includes restitution impulse - Improved tunneling prevention with similar performance - Graph coloring now puts dynamic-static constraints at higher priority more effectively - Fixed crash disabling a dynamic body connected to a static body by a joint - Fixed joint graph color bug when changing body type - Improved graph color validation using population count - Added mutex wrappers for future development - Made spinner benchmark more stressful on solver - All debug draw now uses the provided camera bounds box2d erincatto/box2d#956
1 parent dd5366b commit 116a25a

35 files changed

+823
-461
lines changed

src/Box2D.NET.Samples/Draw.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ public Draw()
5959
m_debugDraw.DrawStringFcn = DrawStringFcn;
6060
m_debugDraw.drawingBounds = bounds;
6161

62-
m_debugDraw.useDrawingBounds = false;
6362
m_debugDraw.drawShapes = true;
6463
m_debugDraw.drawJoints = true;
6564
m_debugDraw.drawJointExtras = false;

src/Box2D.NET.Samples/SampleApp.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ private void OnWindowUpdate(double dt)
294294
// #todo restore all drawing settings that may have been overridden by a sample
295295
_context.settings.subStepCount = 4;
296296
_context.settings.drawJoints = true;
297-
_context.settings.useCameraBounds = true;
298297

299298
s_sample?.Dispose();
300299
s_sample = null;

src/Box2D.NET.Samples/Samples/Bodies/BodyType.cs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public BodyType(SampleContext context) : base(context)
4747
B2BodyId groundId = b2_nullBodyId;
4848
{
4949
B2BodyDef bodyDef = b2DefaultBodyDef();
50+
bodyDef.name = "ground";
5051
groundId = b2CreateBody(m_worldId, ref bodyDef);
5152

5253
B2Segment segment = new B2Segment(new B2Vec2(-20.0f, 0.0f), new B2Vec2(20.0f, 0.0f));
@@ -59,6 +60,7 @@ public BodyType(SampleContext context) : base(context)
5960
B2BodyDef bodyDef = b2DefaultBodyDef();
6061
bodyDef.type = B2BodyType.b2_dynamicBody;
6162
bodyDef.position = new B2Vec2(-2.0f, 3.0f);
63+
bodyDef.name = "attach1";
6264
m_attachmentId = b2CreateBody(m_worldId, ref bodyDef);
6365

6466
B2Polygon box = b2MakeBox(0.5f, 2.0f);
@@ -73,6 +75,7 @@ public BodyType(SampleContext context) : base(context)
7375
bodyDef.type = m_type;
7476
bodyDef.isEnabled = m_isEnabled;
7577
bodyDef.position = new B2Vec2(3.0f, 3.0f);
78+
bodyDef.name = "attach2";
7679
m_secondAttachmentId = b2CreateBody(m_worldId, ref bodyDef);
7780

7881
B2Polygon box = b2MakeBox(0.5f, 2.0f);
@@ -87,6 +90,7 @@ public BodyType(SampleContext context) : base(context)
8790
bodyDef.type = m_type;
8891
bodyDef.isEnabled = m_isEnabled;
8992
bodyDef.position = new B2Vec2(-4.0f, 5.0f);
93+
bodyDef.name = "platform";
9094
m_platformId = b2CreateBody(m_worldId, ref bodyDef);
9195

9296
B2Polygon box = b2MakeOffsetBox(0.5f, 4.0f, new B2Vec2(4.0f, 0.0f), b2MakeRot(0.5f * B2_PI));
@@ -137,6 +141,7 @@ public BodyType(SampleContext context) : base(context)
137141
B2BodyDef bodyDef = b2DefaultBodyDef();
138142
bodyDef.type = B2BodyType.b2_dynamicBody;
139143
bodyDef.position = new B2Vec2(-3.0f, 8.0f);
144+
bodyDef.name = "crate1";
140145
B2BodyId bodyId = b2CreateBody(m_worldId, ref bodyDef);
141146

142147
B2Polygon box = b2MakeBox(0.75f, 0.75f);
@@ -153,6 +158,7 @@ public BodyType(SampleContext context) : base(context)
153158
bodyDef.type = m_type;
154159
bodyDef.isEnabled = m_isEnabled;
155160
bodyDef.position = new B2Vec2(2.0f, 8.0f);
161+
bodyDef.name = "crate2";
156162
m_secondPayloadId = b2CreateBody(m_worldId, ref bodyDef);
157163

158164
B2Polygon box = b2MakeBox(0.75f, 0.75f);
@@ -169,6 +175,7 @@ public BodyType(SampleContext context) : base(context)
169175
bodyDef.type = m_type;
170176
bodyDef.isEnabled = m_isEnabled;
171177
bodyDef.position = new B2Vec2(8.0f, 0.2f);
178+
bodyDef.name = "debris";
172179
m_touchingBodyId = b2CreateBody(m_worldId, ref bodyDef);
173180

174181
B2Capsule capsule = new B2Capsule(new B2Vec2(0.0f, 0.0f), new B2Vec2(1.0f, 0.0f), 0.25f);
@@ -186,6 +193,7 @@ public BodyType(SampleContext context) : base(context)
186193
bodyDef.isEnabled = m_isEnabled;
187194
bodyDef.position = new B2Vec2(-8.0f, 12.0f);
188195
bodyDef.gravityScale = 0.0f;
196+
bodyDef.name = "floater";
189197
m_floatingBodyId = b2CreateBody(m_worldId, ref bodyDef);
190198

191199
B2Circle circle = new B2Circle(new B2Vec2(0.0f, 0.5f), 0.25f);
@@ -221,6 +229,9 @@ public override void UpdateGui()
221229
{
222230
m_type = B2BodyType.b2_kinematicBody;
223231
b2Body_SetType(m_platformId, B2BodyType.b2_kinematicBody);
232+
b2Body_SetLinearVelocity(m_secondAttachmentId, b2Vec2_zero);
233+
b2Body_SetAngularVelocity(m_secondAttachmentId, 0.0f);
234+
224235
b2Body_SetLinearVelocity(m_platformId, new B2Vec2(-m_speed, 0.0f));
225236
b2Body_SetAngularVelocity(m_platformId, 0.0f);
226237
b2Body_SetType(m_secondAttachmentId, B2BodyType.b2_kinematicBody);
@@ -243,24 +254,14 @@ public override void UpdateGui()
243254
{
244255
if (m_isEnabled)
245256
{
246-
b2Body_Enable(m_platformId);
247-
b2Body_Enable(m_secondAttachmentId);
257+
b2Body_Enable(m_attachmentId);
248258
b2Body_Enable(m_secondPayloadId);
249-
b2Body_Enable(m_touchingBodyId);
250259
b2Body_Enable(m_floatingBodyId);
251-
252-
if (m_type == B2BodyType.b2_kinematicBody)
253-
{
254-
b2Body_SetLinearVelocity(m_platformId, new B2Vec2(-m_speed, 0.0f));
255-
b2Body_SetAngularVelocity(m_platformId, 0.0f);
256-
}
257260
}
258261
else
259262
{
260-
b2Body_Disable(m_platformId);
261-
b2Body_Disable(m_secondAttachmentId);
263+
b2Body_Disable(m_attachmentId);
262264
b2Body_Disable(m_secondPayloadId);
263-
b2Body_Disable(m_touchingBodyId);
264265
b2Body_Disable(m_floatingBodyId);
265266
}
266267
}

src/Box2D.NET.Samples/Samples/Continuous/BounceHouse.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public BounceHouse(SampleContext context) : base(context)
6868
b2CreateSegmentShape(groundId, ref shapeDef, ref segment);
6969
}
7070

71-
m_shapeType = ShapeType.e_boxShape;
71+
m_shapeType = ShapeType.e_circleShape;
7272
m_bodyId = b2_nullBodyId;
7373
m_enableHitEvents = true;
7474

@@ -93,6 +93,7 @@ void Launch()
9393
bodyDef.linearVelocity = new B2Vec2(10.0f, 20.0f);
9494
bodyDef.position = new B2Vec2(0.0f, 0.0f);
9595
bodyDef.gravityScale = 0.0f;
96+
bodyDef.isBullet = true;
9697

9798
// Circle shapes centered on the body can spin fast without risk of tunnelling.
9899
bodyDef.allowFastRotation = m_shapeType == ShapeType.e_circleShape;
@@ -101,8 +102,8 @@ void Launch()
101102

102103
B2ShapeDef shapeDef = b2DefaultShapeDef();
103104
shapeDef.density = 1.0f;
104-
shapeDef.material.restitution = 1.2f;
105-
shapeDef.material.friction = 0.3f;
105+
shapeDef.material.restitution = 1.0f;
106+
shapeDef.material.friction = 0.0f;
106107
shapeDef.enableHitEvents = m_enableHitEvents;
107108

108109
if (m_shapeType == ShapeType.e_circleShape)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// SPDX-FileCopyrightText: 2025 Erin Catto
2+
// SPDX-FileCopyrightText: 2025 Ikpil Choi([email protected])
3+
// SPDX-License-Identifier: MIT
4+
5+
using System.Numerics;
6+
using ImGuiNET;
7+
using static Box2D.NET.B2Joints;
8+
using static Box2D.NET.B2Geometries;
9+
using static Box2D.NET.B2Types;
10+
using static Box2D.NET.B2MathFunction;
11+
using static Box2D.NET.B2Bodies;
12+
using static Box2D.NET.B2Shapes;
13+
using static Box2D.NET.B2Ids;
14+
15+
namespace Box2D.NET.Samples.Samples.Issues;
16+
17+
public class Crash01 : Sample
18+
{
19+
private static readonly int SampleBodyType = SampleFactory.Shared.RegisterSample("Issues", "Crash01", Create);
20+
21+
private B2BodyId m_attachmentId;
22+
private B2BodyId m_platformId;
23+
private B2BodyType m_type;
24+
private bool m_isEnabled;
25+
26+
private static Sample Create(SampleContext context)
27+
{
28+
return new Crash01(context);
29+
}
30+
31+
public Crash01(SampleContext context) : base(context)
32+
{
33+
if (m_context.settings.restart == false)
34+
{
35+
m_context.camera.m_center = new B2Vec2(0.8f, 6.4f);
36+
m_context.camera.m_zoom = 25.0f * 0.4f;
37+
}
38+
39+
m_type = B2BodyType.b2_dynamicBody;
40+
m_isEnabled = true;
41+
42+
B2BodyId groundId = b2_nullBodyId;
43+
{
44+
B2BodyDef bodyDef = b2DefaultBodyDef();
45+
bodyDef.name = "ground";
46+
groundId = b2CreateBody(m_worldId, ref bodyDef);
47+
48+
B2Segment segment = new B2Segment(new B2Vec2(-20.0f, 0.0f), new B2Vec2(20.0f, 0.0f));
49+
B2ShapeDef shapeDef = b2DefaultShapeDef();
50+
b2CreateSegmentShape(groundId, ref shapeDef, ref segment);
51+
}
52+
53+
// Define attachment
54+
{
55+
B2BodyDef bodyDef = b2DefaultBodyDef();
56+
bodyDef.type = B2BodyType.b2_dynamicBody;
57+
bodyDef.position = new B2Vec2(-2.0f, 3.0f);
58+
bodyDef.name = "attach1";
59+
m_attachmentId = b2CreateBody(m_worldId, ref bodyDef);
60+
61+
B2Polygon box = b2MakeBox(0.5f, 2.0f);
62+
B2ShapeDef shapeDef = b2DefaultShapeDef();
63+
shapeDef.density = 1.0f;
64+
b2CreatePolygonShape(m_attachmentId, ref shapeDef, ref box);
65+
}
66+
67+
// Define platform
68+
{
69+
B2BodyDef bodyDef = b2DefaultBodyDef();
70+
bodyDef.type = m_type;
71+
bodyDef.isEnabled = m_isEnabled;
72+
bodyDef.position = new B2Vec2(-4.0f, 5.0f);
73+
bodyDef.name = "platform";
74+
m_platformId = b2CreateBody(m_worldId, ref bodyDef);
75+
76+
B2Polygon box = b2MakeOffsetBox(0.5f, 4.0f, new B2Vec2(4.0f, 0.0f), b2MakeRot(0.5f * B2_PI));
77+
78+
B2ShapeDef shapeDef = b2DefaultShapeDef();
79+
shapeDef.density = 2.0f;
80+
b2CreatePolygonShape(m_platformId, ref shapeDef, ref box);
81+
82+
B2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();
83+
B2Vec2 pivot = new B2Vec2(-2.0f, 5.0f);
84+
revoluteDef.@base.bodyIdA = m_attachmentId;
85+
revoluteDef.@base.bodyIdB = m_platformId;
86+
revoluteDef.@base.localFrameA.p = b2Body_GetLocalPoint(m_attachmentId, pivot);
87+
revoluteDef.@base.localFrameB.p = b2Body_GetLocalPoint(m_platformId, pivot);
88+
revoluteDef.maxMotorTorque = 50.0f;
89+
revoluteDef.enableMotor = true;
90+
b2CreateRevoluteJoint(m_worldId, ref revoluteDef);
91+
92+
B2PrismaticJointDef prismaticDef = b2DefaultPrismaticJointDef();
93+
B2Vec2 anchor = new B2Vec2(0.0f, 5.0f);
94+
prismaticDef.@base.bodyIdA = groundId;
95+
prismaticDef.@base.bodyIdB = m_platformId;
96+
prismaticDef.@base.localFrameA.p = b2Body_GetLocalPoint(groundId, anchor);
97+
prismaticDef.@base.localFrameB.p = b2Body_GetLocalPoint(m_platformId, anchor);
98+
prismaticDef.maxMotorForce = 1000.0f;
99+
prismaticDef.motorSpeed = 0.0f;
100+
prismaticDef.enableMotor = true;
101+
prismaticDef.lowerTranslation = -10.0f;
102+
prismaticDef.upperTranslation = 10.0f;
103+
prismaticDef.enableLimit = true;
104+
105+
b2CreatePrismaticJoint(m_worldId, ref prismaticDef);
106+
}
107+
}
108+
109+
public override void UpdateGui()
110+
{
111+
float fontSize = ImGui.GetFontSize();
112+
float height = 11.0f * fontSize;
113+
ImGui.SetNextWindowPos(new Vector2(0.5f * fontSize, m_camera.m_height - height - 2.0f * fontSize), ImGuiCond.Once);
114+
ImGui.SetNextWindowSize(new Vector2(9.0f * fontSize, height));
115+
ImGui.Begin("Crash 01", ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize);
116+
117+
if (ImGui.RadioButton("Static", m_type == B2BodyType.b2_staticBody))
118+
{
119+
m_type = B2BodyType.b2_staticBody;
120+
b2Body_SetType(m_platformId, B2BodyType.b2_staticBody);
121+
}
122+
123+
if (ImGui.RadioButton("Kinematic", m_type == B2BodyType.b2_kinematicBody))
124+
{
125+
m_type = B2BodyType.b2_kinematicBody;
126+
b2Body_SetType(m_platformId, B2BodyType.b2_kinematicBody);
127+
b2Body_SetLinearVelocity(m_platformId, new B2Vec2(-0.1f, 0.0f));
128+
}
129+
130+
if (ImGui.RadioButton("Dynamic", m_type == B2BodyType.b2_dynamicBody))
131+
{
132+
m_type = B2BodyType.b2_dynamicBody;
133+
b2Body_SetType(m_platformId, B2BodyType.b2_dynamicBody);
134+
}
135+
136+
if (ImGui.Checkbox("Enable", ref m_isEnabled))
137+
{
138+
if (m_isEnabled)
139+
{
140+
b2Body_Enable(m_attachmentId);
141+
}
142+
else
143+
{
144+
b2Body_Disable(m_attachmentId);
145+
}
146+
}
147+
148+
ImGui.End();
149+
}
150+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// SPDX-FileCopyrightText: 2025 Erin Catto
2+
// SPDX-FileCopyrightText: 2025 Ikpil Choi([email protected])
3+
// SPDX-License-Identifier: MIT
4+
5+
using System.Numerics;
6+
using ImGuiNET;
7+
using static Box2D.NET.B2Joints;
8+
using static Box2D.NET.B2Geometries;
9+
using static Box2D.NET.B2Types;
10+
using static Box2D.NET.B2MathFunction;
11+
using static Box2D.NET.B2Bodies;
12+
using static Box2D.NET.B2Shapes;
13+
14+
namespace Box2D.NET.Samples.Samples.Issues;
15+
16+
public class DisableCrash : Sample
17+
{
18+
private static readonly int SampleDisableCrash = SampleFactory.Shared.RegisterSample("Issues", "Disable", Create);
19+
20+
private B2BodyId m_attachmentId;
21+
private B2BodyId m_platformId;
22+
bool m_isEnabled;
23+
24+
private static Sample Create(SampleContext context)
25+
{
26+
return new DisableCrash(context);
27+
}
28+
29+
public DisableCrash(SampleContext context)
30+
: base(context)
31+
{
32+
if (m_context.settings.restart == false)
33+
{
34+
m_context.camera.m_center = new B2Vec2(0.8f, 6.4f);
35+
m_context.camera.m_zoom = 25.0f * 0.4f;
36+
}
37+
38+
m_isEnabled = true;
39+
40+
// Define attachment
41+
{
42+
B2BodyDef bodyDef = b2DefaultBodyDef();
43+
bodyDef.type = B2BodyType.b2_dynamicBody;
44+
bodyDef.position = new B2Vec2(-2.0f, 3.0f);
45+
bodyDef.isEnabled = m_isEnabled;
46+
m_attachmentId = b2CreateBody(m_worldId, ref bodyDef);
47+
48+
B2Polygon box = b2MakeBox(0.5f, 2.0f);
49+
B2ShapeDef shapeDef = b2DefaultShapeDef();
50+
b2CreatePolygonShape(m_attachmentId, ref shapeDef, ref box);
51+
}
52+
53+
// Define platform
54+
{
55+
B2BodyDef bodyDef = b2DefaultBodyDef();
56+
bodyDef.position = new B2Vec2(-4.0f, 5.0f);
57+
m_platformId = b2CreateBody(m_worldId, ref bodyDef);
58+
59+
B2Polygon box = b2MakeOffsetBox(0.5f, 4.0f, new B2Vec2(4.0f, 0.0f), b2MakeRot(0.5f * B2_PI));
60+
61+
B2ShapeDef shapeDef = b2DefaultShapeDef();
62+
b2CreatePolygonShape(m_platformId, ref shapeDef, ref box);
63+
64+
B2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();
65+
B2Vec2 pivot = new B2Vec2(-2.0f, 5.0f);
66+
revoluteDef.@base.bodyIdA = m_attachmentId;
67+
revoluteDef.@base.bodyIdB = m_platformId;
68+
revoluteDef.@base.localFrameA.p = b2Body_GetLocalPoint(m_attachmentId, pivot);
69+
revoluteDef.@base.localFrameB.p = b2Body_GetLocalPoint(m_platformId, pivot);
70+
revoluteDef.maxMotorTorque = 50.0f;
71+
revoluteDef.enableMotor = true;
72+
b2CreateRevoluteJoint(m_worldId, ref revoluteDef);
73+
}
74+
}
75+
76+
public override void UpdateGui()
77+
{
78+
float fontSize = ImGui.GetFontSize();
79+
float height = 11.0f * fontSize;
80+
float winX = 0.5f * fontSize;
81+
float winY = m_camera.m_height - height - 2.0f * fontSize;
82+
ImGui.SetNextWindowPos(new Vector2(winX, winY), ImGuiCond.Once);
83+
ImGui.SetNextWindowSize(new Vector2(9.0f * fontSize, height));
84+
ImGui.Begin("Disable Crash", ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize);
85+
86+
if (ImGui.Checkbox("Enable", ref m_isEnabled))
87+
{
88+
if (m_isEnabled)
89+
{
90+
b2Body_Enable(m_attachmentId);
91+
}
92+
else
93+
{
94+
b2Body_Disable(m_attachmentId);
95+
}
96+
}
97+
98+
ImGui.End();
99+
}
100+
}

0 commit comments

Comments
 (0)