Skip to content

Commit 46cedd0

Browse files
committed
[upstream] Contact recycling (erincatto/box2d#1038)
Contact recycling - re-use contact points across time steps when the relative movement is small. This feature improves stability and performance. The performance lift is around 25% on benchmarks. I've tested this a lot and I haven't seen any artifacts. Contact points are not re-computed until shapes move more than 5cm relative to each other. This distance can be accessed with these functions: ```c b2World_GetContactRecycleDistance() b2World_SetContactRecycleDistance() ``` The default value is `B2_CONTACT_RECYCLE_DISTANCE`. You can set this distance to zero to disable contact recycling. Contact recycling skips some updates (i.e. friction, presolve) until the shapes move relative to each other by ~5cm. I identified some contacts between slow moving shapes can be delayed. I addressed this by adjusting the recycle distance for non-touching shapes to use the speculative contact distance. Other stuff: - Build timeouts - `B2_VALIDATE` that asserts only in debug build - Update trees task moved earlier in the step - Moved `constants.h` to the include folder - Contact solver now uses body indices of 0 to indicate null (static body) - AABB margin now considers the shape size to help reduce the number of pairs
1 parent 103ba3c commit 46cedd0

32 files changed

+219
-97
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,9 @@ FodyWeavers.xsd
384384
!.vscode/extensions.json
385385
*.code-workspace
386386

387+
# ai
388+
.claude/
389+
387390
# Local History for Visual Studio Code
388391
.history/
389392

src/Box2D.NET.Samples/Box2D.NET.Samples.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</PropertyGroup>
1919

2020
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
21-
<DefineConstants>$(DefineConstants);B2_SNOOP_TOI_COUNTERS;ENABLED;B2_SNOOP_TABLE_COUNTERS;B2_SNOOP_PAIR_COUNTERS</DefineConstants>
21+
<DefineConstants>$(DefineConstants);B2_SNOOP_TOI_COUNTERS;ENABLED;B2_SNOOP_TABLE_COUNTERS;B2_SNOOP_PAIR_COUNTERS;B2_ENABLE_VALIDATION</DefineConstants>
2222
</PropertyGroup>
2323

2424
<ItemGroup>

src/Box2D.NET.Samples/SampleApp.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -790,24 +790,42 @@ private void UpdateUI()
790790
ImGui.Checkbox("Sleep", ref _context.enableSleep);
791791
ImGui.Checkbox("Warm Starting", ref _context.enableWarmStarting);
792792
ImGui.Checkbox("Continuous", ref _context.enableContinuous);
793+
ImGui.Checkbox("Contact Recycling", ref _context.enableRecycling);
793794

794795
ImGui.Separator();
795796

796797
ImGui.Checkbox("Shapes", ref _context.debugDraw.drawShapes);
797798
ImGui.Checkbox("Joints", ref _context.debugDraw.drawJoints);
798799
ImGui.Checkbox("Joint Extras", ref _context.debugDraw.drawJointExtras);
799800
ImGui.Checkbox("Bounds", ref _context.debugDraw.drawBounds);
800-
ImGui.Checkbox("Contact Points", ref _context.debugDraw.drawContactPoints);
801-
ImGui.Checkbox("Contact Normals", ref _context.debugDraw.drawContactNormals);
802-
ImGui.Checkbox("Contact Features", ref _context.debugDraw.drawContactFeatures);
803-
ImGui.Checkbox("Contact Forces", ref _context.debugDraw.drawContactForces);
804-
ImGui.Checkbox("Friction Forces", ref _context.debugDraw.drawFrictionForces);
805801
ImGui.Checkbox("Mass", ref _context.debugDraw.drawMass);
806802
ImGui.Checkbox("Body Names", ref _context.debugDraw.drawBodyNames);
807803
ImGui.Checkbox("Graph Colors", ref _context.debugDraw.drawGraphColors);
808804
ImGui.Checkbox("Islands", ref _context.debugDraw.drawIslands);
809805
ImGui.Checkbox("Counters", ref _context.drawCounters);
810806
ImGui.Checkbox("Profile", ref _context.drawProfile);
807+
ImGui.Separator();
808+
809+
ImGui.Separator();
810+
811+
{
812+
bool changed = false;
813+
string[] drawTypes =
814+
[
815+
"None", "Clip", "AnchorA", "AnchorB", "Average"
816+
];
817+
int drawType = (int)_context.debugDraw.contactDrawType;
818+
changed = changed || ImGui.Combo("Contact", ref drawType, drawTypes, drawTypes.Length);
819+
_context.debugDraw.contactDrawType = (B2ContactDrawType)drawType;
820+
}
821+
822+
ImGui.Checkbox("Contact Normals", ref _context.debugDraw.drawContactNormals);
823+
ImGui.Checkbox("Contact Features", ref _context.debugDraw.drawContactFeatures);
824+
ImGui.Checkbox("Contact Forces", ref _context.debugDraw.drawContactForces);
825+
ImGui.Checkbox("Friction Forces", ref _context.debugDraw.drawFrictionForces);
826+
827+
ImGui.Separator();
828+
811829

812830
ImGui.PushItemWidth(80.0f);
813831
ImGui.InputFloat("Joint Scale", ref _context.debugDraw.jointScale);

src/Box2D.NET.Samples/SampleContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class SampleContext
3434
public bool drawProfile = false;
3535
public bool enableWarmStarting = true;
3636
public bool enableContinuous = true;
37+
public bool enableRecycling = true;
3738
public bool enableSleep = true;
3839
public bool showUI = true;
3940

src/Box2D.NET.Samples/Samples/Benchmarks/BenchmarkBarrel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public BenchmarkBarrel(SampleContext context) : base(context)
6969
float x = -40.0f * gridSize;
7070
for (int i = 0; i < 81; ++i)
7171
{
72-
B2Polygon box = b2MakeOffsetBox(0.5f * gridSize, 0.5f * gridSize, new B2Vec2(x, y), b2Rot_identity);
72+
B2Polygon box = b2MakeOffsetBox(0.55f * gridSize, 0.5f * gridSize, new B2Vec2(x, y), b2Rot_identity);
7373
b2CreatePolygonShape(groundId, shapeDef, box);
7474
x += gridSize;
7575
}
@@ -78,7 +78,7 @@ public BenchmarkBarrel(SampleContext context) : base(context)
7878
x = -40.0f * gridSize;
7979
for (int i = 0; i < 100; ++i)
8080
{
81-
B2Polygon box = b2MakeOffsetBox(0.5f * gridSize, 0.5f * gridSize, new B2Vec2(x, y), b2Rot_identity);
81+
B2Polygon box = b2MakeOffsetBox(0.5f * gridSize, 0.55f * gridSize, new B2Vec2(x, y), b2Rot_identity);
8282
b2CreatePolygonShape(groundId, shapeDef, box);
8383
y += gridSize;
8484
}
@@ -87,7 +87,7 @@ public BenchmarkBarrel(SampleContext context) : base(context)
8787
x = 40.0f * gridSize;
8888
for (int i = 0; i < 100; ++i)
8989
{
90-
B2Polygon box = b2MakeOffsetBox(0.5f * gridSize, 0.5f * gridSize, new B2Vec2(x, y), b2Rot_identity);
90+
B2Polygon box = b2MakeOffsetBox(0.5f * gridSize, 0.55f * gridSize, new B2Vec2(x, y), b2Rot_identity);
9191
b2CreatePolygonShape(groundId, shapeDef, box);
9292
y += gridSize;
9393
}

src/Box2D.NET.Samples/Samples/Collisions/Manifold.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ void DrawManifold(in B2Manifold manifold, B2Vec2 origin1, B2Vec2 origin2)
189189
{
190190
ref B2ManifoldPoint mp = ref manifold.points[i];
191191

192-
B2Vec2 p1 = mp.point;
192+
B2Vec2 p1 = mp.clipPoint;
193193
B2Vec2 p2 = b2MulAdd(p1, 0.5f, manifold.normal);
194194
DrawLine(m_draw, p1, p2, B2HexColor.b2_colorViolet);
195195

src/Box2D.NET.Samples/Samples/Collisions/ShapeCast.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ public ShapeCast(SampleContext context) : base(context)
105105
}
106106
#endif
107107

108-
m_box = b2MakeOffsetBox(0.5f, 0.5f, new B2Vec2(0.0f, 0.0f), b2Rot_identity);
108+
//m_box = b2MakeOffsetBox(0.5f, 0.5f, new B2Vec2(0.0f, 0.0f), b2Rot_identity);
109+
m_box = b2MakeBox( 8.984375f, 0.5f );
109110

110111
#if ZERO
111112
{

src/Box2D.NET.Samples/Samples/Collisions/SmoothManifold.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ void DrawManifold(in B2Manifold manifold)
229229
{
230230
ref B2ManifoldPoint mp = ref manifold.points[i];
231231

232-
B2Vec2 p1 = mp.point;
232+
B2Vec2 p1 = mp.clipPoint;
233233
B2Vec2 p2 = b2MulAdd(p1, 0.5f, manifold.normal);
234234
DrawLine(m_draw, p1, p2, B2HexColor.b2_colorWhite);
235235

src/Box2D.NET.Samples/Samples/Events/ContactEvent.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ public override void Step()
234234
for (int k = 0; k < manifold.pointCount; ++k)
235235
{
236236
B2ManifoldPoint point = manifold.points[k];
237-
DrawLine(m_draw, point.point, point.point + point.totalNormalImpulse * normal, B2HexColor.b2_colorBlueViolet);
238-
DrawPoint(m_draw, point.point, 10.0f, B2HexColor.b2_colorWhite);
237+
DrawLine(m_draw, point.clipPoint, point.clipPoint + point.totalNormalImpulse * normal, B2HexColor.b2_colorBlueViolet);
238+
DrawPoint(m_draw, point.clipPoint, 10.0f, B2HexColor.b2_colorWhite);
239239
}
240240
}
241241
}
@@ -264,8 +264,8 @@ public override void Step()
264264
for (int k = 0; k < manifold.pointCount; ++k)
265265
{
266266
B2ManifoldPoint point = manifold.points[k];
267-
DrawLine(m_draw, point.point, point.point + point.totalNormalImpulse * normal, B2HexColor.b2_colorYellowGreen);
268-
DrawPoint(m_draw, point.point, 10.0f, B2HexColor.b2_colorWhite);
267+
DrawLine(m_draw, point.clipPoint, point.clipPoint + point.totalNormalImpulse * normal, B2HexColor.b2_colorYellowGreen);
268+
DrawPoint(m_draw, point.clipPoint, 10.0f, B2HexColor.b2_colorWhite);
269269
}
270270
}
271271
}

src/Box2D.NET.Samples/Samples/Events/PersistentContact.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public override void Step()
9898
for (int i = 0; i < data.manifold.pointCount; ++i)
9999
{
100100
ref readonly B2ManifoldPoint manifoldPoint = ref data.manifold.points[i];
101-
B2Vec2 p1 = manifoldPoint.point;
101+
B2Vec2 p1 = manifoldPoint.clipPoint;
102102
B2Vec2 p2 = p1 + manifoldPoint.totalNormalImpulse * data.manifold.normal;
103103
DrawLine(m_draw, p1, p2, B2HexColor.b2_colorCrimson);
104104
DrawPoint(m_draw, p1, 6.0f, B2HexColor.b2_colorCrimson);

0 commit comments

Comments
 (0)