Skip to content

Commit 6a5c81b

Browse files
committed
[upstream] Draw forces (erincatto/box2d#988)
Added b2Body_ClearForces. Joint and force scale for debug draw.
1 parent 674b744 commit 6a5c81b

17 files changed

Lines changed: 256 additions & 135 deletions

src/Box2D.NET.Samples/Draw.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ public Draw()
6767
m_debugDraw.drawContacts = false;
6868
m_debugDraw.drawGraphColors = false;
6969
m_debugDraw.drawContactNormals = false;
70-
m_debugDraw.drawContactImpulses = false;
70+
m_debugDraw.drawContactForces = false;
7171
m_debugDraw.drawContactFeatures = false;
72-
m_debugDraw.drawFrictionImpulses = false;
72+
m_debugDraw.drawFrictionForces = false;
7373
m_debugDraw.drawIslands = false;
7474

7575
m_debugDraw.context = this;

src/Box2D.NET.Samples/SampleApp.cs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -789,21 +789,26 @@ private void UpdateUI()
789789

790790
ImGui.Separator();
791791

792-
ImGui.Checkbox("Shapes", ref _context.settings.drawShapes);
793-
ImGui.Checkbox("Joints", ref _context.settings.drawJoints);
794-
ImGui.Checkbox("Joint Extras", ref _context.settings.drawJointExtras);
795-
ImGui.Checkbox("Bounds", ref _context.settings.drawBounds);
796-
ImGui.Checkbox("Contact Points", ref _context.settings.drawContactPoints);
797-
ImGui.Checkbox("Contact Normals", ref _context.settings.drawContactNormals);
798-
ImGui.Checkbox("Contact Impulses", ref _context.settings.drawContactImpulses);
799-
ImGui.Checkbox("Contact Features", ref _context.settings.drawContactFeatures);
800-
ImGui.Checkbox("Friction Impulses", ref _context.settings.drawFrictionImpulses);
801-
ImGui.Checkbox("Mass", ref _context.settings.drawMass);
802-
ImGui.Checkbox("Body Names", ref _context.settings.drawBodyNames);
803-
ImGui.Checkbox("Graph Colors", ref _context.settings.drawGraphColors);
804-
ImGui.Checkbox("Islands", ref _context.settings.drawIslands);
805-
ImGui.Checkbox("Counters", ref _context.settings.drawCounters);
806-
ImGui.Checkbox("Profile", ref _context.settings.drawProfile);
792+
ImGui.Checkbox( "Shapes", ref _context.settings.drawShapes );
793+
ImGui.Checkbox( "Joints", ref _context.settings.drawJoints );
794+
ImGui.Checkbox( "Joint Extras", ref _context.settings.drawJointExtras );
795+
ImGui.Checkbox( "Bounds", ref _context.settings.drawBounds );
796+
ImGui.Checkbox( "Contact Points", ref _context.settings.drawContactPoints );
797+
ImGui.Checkbox( "Contact Normals", ref _context.settings.drawContactNormals );
798+
ImGui.Checkbox( "Contact Features", ref _context.settings.drawContactFeatures );
799+
ImGui.Checkbox( "Contact Forces", ref _context.settings.drawContactForces );
800+
ImGui.Checkbox( "Friction Forces", ref _context.settings.drawFrictionForces );
801+
ImGui.Checkbox( "Mass", ref _context.settings.drawMass );
802+
ImGui.Checkbox( "Body Names", ref _context.settings.drawBodyNames );
803+
ImGui.Checkbox( "Graph Colors", ref _context.settings.drawGraphColors );
804+
ImGui.Checkbox( "Islands", ref _context.settings.drawIslands );
805+
ImGui.Checkbox( "Counters", ref _context.settings.drawCounters );
806+
ImGui.Checkbox( "Profile", ref _context.settings.drawProfile );
807+
808+
ImGui.PushItemWidth( 80.0f );
809+
ImGui.InputFloat( "Joint Scale", ref _context.settings.jointScale );
810+
ImGui.InputFloat( "Force Scale", ref _context.settings.forceScale );
811+
ImGui.PopItemWidth();
807812

808813
Vector2 button_sz = new Vector2(-1, 0);
809814
if (ImGui.Button("Pause (P)", button_sz))
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// SPDX-FileCopyrightText: 2025 Erin Catto
2+
// SPDX-FileCopyrightText: 2025 Ikpil Choi(ikpil@naver.com)
3+
// SPDX-License-Identifier: MIT
4+
5+
using static Box2D.NET.B2Types;
6+
using static Box2D.NET.B2Bodies;
7+
using static Box2D.NET.B2Shapes;
8+
using static Box2D.NET.B2Joints;
9+
10+
namespace Box2D.NET.Samples.Samples.Issues;
11+
12+
public class UnstableJoints : Sample
13+
{
14+
private static readonly int SamplePrismaticJointCrash = SampleFactory.Shared.RegisterSample("Issues", "Unstable Joints", Create);
15+
16+
private static Sample Create(SampleContext context)
17+
{
18+
return new UnstableJoints(context);
19+
}
20+
21+
public UnstableJoints(SampleContext context) : base(context)
22+
{
23+
if (m_context.settings.restart == false)
24+
{
25+
m_context.camera.m_center = new B2Vec2(0.0f, 1.75f);
26+
m_context.camera.m_zoom = 32.0f;
27+
}
28+
29+
{
30+
B2BodyDef bodyDef = b2DefaultBodyDef();
31+
B2BodyId groundId = b2CreateBody(m_worldId, ref bodyDef);
32+
33+
B2ShapeDef shapeDef = b2DefaultShapeDef();
34+
B2Segment segment = new B2Segment(new B2Vec2(-100.0f, 0.0f), new B2Vec2(100.0f, 0.0f));
35+
b2CreateSegmentShape(groundId, ref shapeDef, ref segment);
36+
}
37+
38+
B2BodyId centerId;
39+
{
40+
B2BodyDef bd = b2DefaultBodyDef();
41+
bd.type = B2BodyType.b2_dynamicBody;
42+
bd.position = new B2Vec2(0, 3);
43+
centerId = b2CreateBody(m_worldId, ref bd);
44+
45+
B2ShapeDef sd = b2DefaultShapeDef();
46+
47+
B2Circle circle;
48+
circle.center = new B2Vec2(0, 0);
49+
50+
// Note: this will crash due to divergence (inf/nan) with a radius of 0.1
51+
//circle.radius = 0.1f;
52+
circle.radius = 0.5f;
53+
54+
b2CreateCircleShape(centerId, ref sd, ref circle);
55+
}
56+
57+
B2PrismaticJointDef jd = b2DefaultPrismaticJointDef();
58+
jd.enableSpring = true;
59+
jd.hertz = 10.0f;
60+
jd.dampingRatio = 2.0f;
61+
62+
{
63+
B2BodyDef bd = b2DefaultBodyDef();
64+
bd.type = B2BodyType.b2_dynamicBody;
65+
bd.position = new B2Vec2(-3.5f, 3);
66+
67+
B2BodyId leftId = b2CreateBody(m_worldId, ref bd);
68+
69+
B2ShapeDef sd = b2DefaultShapeDef();
70+
71+
B2Circle circle;
72+
circle.center = new B2Vec2(0, 0);
73+
circle.radius = 2.0f;
74+
b2CreateCircleShape(leftId, ref sd, ref circle);
75+
76+
jd.@base.bodyIdA = centerId;
77+
jd.@base.bodyIdB = leftId;
78+
jd.targetTranslation = -3.0f;
79+
b2CreatePrismaticJoint(m_worldId, ref jd);
80+
}
81+
82+
{
83+
B2BodyDef bd = b2DefaultBodyDef();
84+
bd.type = B2BodyType.b2_dynamicBody;
85+
bd.position = new B2Vec2(3.5f, 3);
86+
B2BodyId rightId = b2CreateBody(m_worldId, ref bd);
87+
88+
B2ShapeDef sd = b2DefaultShapeDef();
89+
90+
B2Circle circle;
91+
circle.center = new B2Vec2(0, 0);
92+
circle.radius = 2.0f;
93+
94+
b2CreateCircleShape(rightId, ref sd, ref circle);
95+
96+
jd.@base.bodyIdA = centerId;
97+
jd.@base.bodyIdB = rightId;
98+
jd.targetTranslation = 3.0f;
99+
b2CreatePrismaticJoint(m_worldId, ref jd);
100+
}
101+
}
102+
}

src/Box2D.NET.Samples/Samples/Sample.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,8 @@ public virtual void Draw(Settings settings)
486486
}
487487

488488
m_context.draw.m_debugDraw.drawingBounds = m_camera.GetViewBounds();
489+
m_context.draw.m_debugDraw.jointScale = settings.jointScale;
490+
m_context.draw.m_debugDraw.forceScale = settings.forceScale;
489491
m_context.draw.m_debugDraw.drawShapes = settings.drawShapes;
490492
m_context.draw.m_debugDraw.drawJoints = settings.drawJoints;
491493
m_context.draw.m_debugDraw.drawJointExtras = settings.drawJointExtras;
@@ -495,9 +497,9 @@ public virtual void Draw(Settings settings)
495497
m_context.draw.m_debugDraw.drawContacts = settings.drawContactPoints;
496498
m_context.draw.m_debugDraw.drawGraphColors = settings.drawGraphColors;
497499
m_context.draw.m_debugDraw.drawContactNormals = settings.drawContactNormals;
498-
m_context.draw.m_debugDraw.drawContactImpulses = settings.drawContactImpulses;
500+
m_context.draw.m_debugDraw.drawContactForces = settings.drawContactForces;
499501
m_context.draw.m_debugDraw.drawContactFeatures = settings.drawContactFeatures;
500-
m_context.draw.m_debugDraw.drawFrictionImpulses = settings.drawFrictionImpulses;
502+
m_context.draw.m_debugDraw.drawFrictionForces = settings.drawFrictionForces;
501503
m_context.draw.m_debugDraw.drawIslands = settings.drawIslands;
502504

503505
b2World_Draw(m_worldId, m_context.draw.m_debugDraw);

src/Box2D.NET.Samples/Settings.cs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,40 @@ public class Settings
1414
public const int MAX_TOKENS = 32;
1515
public const string fileName = "settings.ini";
1616

17-
public int sampleIndex = 0;
1817
public int windowWidth = 1920;
1918
public int windowHeight = 1080;
2019

2120
public float uiScale = 1.0f;
2221
public float hertz = 60.0f;
22+
public float jointScale = 1.0f;
23+
public float forceScale = 1.0f;
2324
public int subStepCount = 4;
2425
public int workerCount = 1;
25-
26-
public bool drawShapes = true;
27-
public bool drawJoints = true;
26+
27+
public bool restart = false;
28+
public bool pause = false;
29+
public bool singleStep = false;
2830
public bool drawJointExtras = false;
2931
public bool drawBounds = false;
3032
public bool drawMass = false;
3133
public bool drawBodyNames = false;
3234
public bool drawContactPoints = false;
3335
public bool drawContactNormals = false;
34-
public bool drawContactImpulses = false;
3536
public bool drawContactFeatures = false;
36-
public bool drawFrictionImpulses = false;
37+
public bool drawContactForces = false;
38+
public bool drawFrictionForces = false;
3739
public bool drawIslands = false;
3840
public bool drawGraphColors = false;
3941
public bool drawCounters = false;
4042
public bool drawProfile = false;
4143
public bool enableWarmStarting = true;
4244
public bool enableContinuous = true;
4345
public bool enableSleep = true;
44-
public bool pause = false;
45-
public bool singleStep = false;
46-
public bool restart = false;
46+
47+
// These are persisted
48+
public int sampleIndex = 0;
49+
public bool drawShapes = true;
50+
public bool drawJoints = true;
4751

4852
public void Save()
4953
{
@@ -80,9 +84,9 @@ public void CopyFrom(Settings other)
8084
drawBodyNames = other.drawBodyNames;
8185
drawContactPoints = other.drawContactPoints;
8286
drawContactNormals = other.drawContactNormals;
83-
drawContactImpulses = other.drawContactImpulses;
87+
drawContactForces = other.drawContactForces;
8488
drawContactFeatures = other.drawContactFeatures;
85-
drawFrictionImpulses = other.drawFrictionImpulses;
89+
drawFrictionForces = other.drawFrictionForces;
8690
drawIslands = other.drawIslands;
8791
drawGraphColors = other.drawGraphColors;
8892
drawCounters = other.drawCounters;

src/Box2D.NET/B2Bodies.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ namespace Box2D.NET
2424
{
2525
public static class B2Bodies
2626
{
27+
// Length of body debug name
28+
public const int B2_NAME_LENGTH = 32;
29+
2730
// Identity body state, notice the deltaRotation is {1, 0}
2831
public static readonly B2BodyState b2_identityBodyState = new B2BodyState()
2932
{
@@ -921,6 +924,7 @@ public static void b2Body_SetTargetTransform(B2BodyId bodyId, B2Transform target
921924
state.angularVelocity = angularVelocity;
922925
}
923926

927+
/// Get the linear velocity of a local point attached to a body. Usually in meters per second.
924928
public static B2Vec2 b2Body_GetLocalPointVelocity(B2BodyId bodyId, B2Vec2 localPoint)
925929
{
926930
B2World world = b2GetWorld(bodyId.world0);
@@ -939,6 +943,7 @@ public static B2Vec2 b2Body_GetLocalPointVelocity(B2BodyId bodyId, B2Vec2 localP
939943
return v;
940944
}
941945

946+
/// Get the linear velocity of a world point attached to a body. Usually in meters per second.
942947
public static B2Vec2 b2Body_GetWorldPointVelocity(B2BodyId bodyId, B2Vec2 worldPoint)
943948
{
944949
B2World world = b2GetWorld(bodyId.world0);
@@ -957,6 +962,13 @@ public static B2Vec2 b2Body_GetWorldPointVelocity(B2BodyId bodyId, B2Vec2 worldP
957962
return v;
958963
}
959964

965+
/// Apply a force at a world point. If the force is not applied at the center of mass,
966+
/// it will generate a torque and affect the angular velocity. This optionally wakes up the body.
967+
/// The force is ignored if the body is not awake.
968+
/// @param bodyId The body id
969+
/// @param force The world force vector, usually in newtons (N)
970+
/// @param point The world position of the point of application
971+
/// @param wake Option to wake up the body
960972
public static void b2Body_ApplyForce(B2BodyId bodyId, B2Vec2 force, B2Vec2 point, bool wake)
961973
{
962974
B2World world = b2GetWorld(bodyId.world0);
@@ -980,6 +992,11 @@ public static void b2Body_ApplyForce(B2BodyId bodyId, B2Vec2 force, B2Vec2 point
980992
}
981993
}
982994

995+
/// Apply a force to the center of mass. This optionally wakes up the body.
996+
/// The force is ignored if the body is not awake.
997+
/// @param bodyId The body id
998+
/// @param force the world force vector, usually in newtons (N).
999+
/// @param wake also wake up the body
9831000
public static void b2Body_ApplyForceToCenter(B2BodyId bodyId, B2Vec2 force, bool wake)
9841001
{
9851002
B2World world = b2GetWorld(bodyId.world0);
@@ -1002,6 +1019,11 @@ public static void b2Body_ApplyForceToCenter(B2BodyId bodyId, B2Vec2 force, bool
10021019
}
10031020
}
10041021

1022+
/// Apply a torque. This affects the angular velocity without affecting the linear velocity.
1023+
/// This optionally wakes the body. The torque is ignored if the body is not awake.
1024+
/// @param bodyId The body id
1025+
/// @param torque about the z-axis (out of the screen), usually in N*m.
1026+
/// @param wake also wake up the body
10051027
public static void b2Body_ApplyTorque(B2BodyId bodyId, float torque, bool wake)
10061028
{
10071029
B2World world = b2GetWorld(bodyId.world0);
@@ -1024,6 +1046,20 @@ public static void b2Body_ApplyTorque(B2BodyId bodyId, float torque, bool wake)
10241046
}
10251047
}
10261048

1049+
/// Clear the force and torque on this body. Forces and torques are automatically cleared after each world
1050+
/// step. So this only needs to be called if the application wants to remove the effect of previous
1051+
/// calls to apply forces and torques before the world step is called.
1052+
/// @param bodyId The body id
1053+
public static void b2Body_ClearForces(B2BodyId bodyId)
1054+
{
1055+
B2World world = b2GetWorld(bodyId.world0);
1056+
B2Body body = b2GetBodyFullId(world, bodyId);
1057+
B2BodySim bodySim = b2GetBodySim(world, body);
1058+
bodySim.force = b2Vec2_zero;
1059+
bodySim.torque = 0.0f;
1060+
}
1061+
1062+
10271063
/// Apply an impulse at a point. This immediately modifies the velocity.
10281064
/// It also modifies the angular velocity if the point of application
10291065
/// is not at the center of mass. This optionally wakes the body.
@@ -1365,7 +1401,7 @@ public static void b2Body_SetName(B2BodyId bodyId, string name)
13651401
}
13661402
else
13671403
{
1368-
body.name = "";
1404+
body.name = string.Empty;
13691405
}
13701406
}
13711407

src/Box2D.NET/B2ConstraintGraphs.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,5 +365,13 @@ public static void b2RemoveJointFromGraph(B2World world, int bodyIdA, int bodyId
365365
movedJoint.localIndex = localIndex;
366366
}
367367
}
368+
369+
public static readonly B2HexColor[] b2_graphColors = new B2HexColor[]
370+
{
371+
B2HexColor.b2_colorRed, B2HexColor.b2_colorOrange, B2HexColor.b2_colorYellow, B2HexColor.b2_colorGreen, B2HexColor.b2_colorCyan, B2HexColor.b2_colorBlue,
372+
B2HexColor.b2_colorViolet, B2HexColor.b2_colorPink, B2HexColor.b2_colorChocolate, B2HexColor.b2_colorGoldenRod, B2HexColor.b2_colorCoral, B2HexColor.b2_colorRosyBrown,
373+
B2HexColor.b2_colorAqua, B2HexColor.b2_colorPeru, B2HexColor.b2_colorLime, B2HexColor.b2_colorGold, B2HexColor.b2_colorPlum, B2HexColor.b2_colorSnow,
374+
B2HexColor.b2_colorTeal, B2HexColor.b2_colorKhaki, B2HexColor.b2_colorSalmon, B2HexColor.b2_colorPeachPuff, B2HexColor.b2_colorHoneyDew, B2HexColor.b2_colorBlack,
375+
};
368376
}
369377
}

src/Box2D.NET/B2DebugDraw.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@ public class B2DebugDraw
1919
public DrawPointFcn DrawPointFcn;
2020
public DrawStringFcn DrawStringFcn;
2121

22-
/// Bounds to use if restricting drawing to a rectangular region
22+
/// World bounds to use for debug draw
2323
public B2AABB drawingBounds;
24+
25+
/// Scale to use when drawing forces
26+
public float forceScale;
27+
28+
/// Global scaling for joint drawing
29+
public float jointScale;
2430

2531
/// Option to draw shapes
2632
public bool drawShapes;
@@ -46,17 +52,17 @@ public class B2DebugDraw
4652
/// Option to visualize the graph coloring used for contacts and joints
4753
public bool drawGraphColors;
4854

55+
/// Option to draw contact feature ids
56+
public bool drawContactFeatures;
57+
4958
/// Option to draw contact normals
5059
public bool drawContactNormals;
5160

52-
/// Option to draw contact normal impulses
53-
public bool drawContactImpulses;
54-
55-
/// Option to draw contact feature ids
56-
public bool drawContactFeatures;
61+
/// Option to draw contact normal forces
62+
public bool drawContactForces;
5763

58-
/// Option to draw contact friction impulses
59-
public bool drawFrictionImpulses;
64+
/// Option to draw contact friction forces
65+
public bool drawFrictionForces;
6066

6167
/// Option to draw islands as bounding boxes
6268
public bool drawIslands;

0 commit comments

Comments
 (0)