From b6984de2be5fdd68ce4e33ba07e7e24c0f1fb9c7 Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sat, 25 Jan 2025 12:08:20 -0800 Subject: [PATCH 1/9] wip --- include/box2d/collision.h | 10 ++-- include/box2d/types.h | 4 +- samples/main.cpp | 13 +----- samples/sample.cpp | 29 +++++++++--- samples/sample.h | 7 +-- samples/sample_benchmark.cpp | 29 +++++++++--- samples/sample_shapes.cpp | 91 +++++++++++++++++++++++++++++++++++- samples/sample_stacking.cpp | 17 +++++-- src/constraint_graph.c | 4 +- src/contact.c | 20 ++++++++ src/contact.h | 3 +- src/contact_solver.c | 33 ++++++++++++- src/contact_solver.h | 4 ++ src/core.h | 3 +- src/shape.c | 2 + src/shape.h | 17 +++++++ src/solver.c | 1 + 17 files changed, 244 insertions(+), 43 deletions(-) diff --git a/include/box2d/collision.h b/include/box2d/collision.h index b71eced8c..c00946cf2 100644 --- a/include/box2d/collision.h +++ b/include/box2d/collision.h @@ -535,14 +535,18 @@ typedef struct b2ManifoldPoint /// @note Box2D uses speculative collision so some contact points may be separated. typedef struct b2Manifold { - /// The manifold points, up to two are possible in 2D - b2ManifoldPoint points[2]; - /// The unit normal vector in world space, points from shape A to bodyB b2Vec2 normal; + /// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s + float rollingImpulse; + + /// The manifold points, up to two are possible in 2D + b2ManifoldPoint points[2]; + /// The number of contacts points, will be 0, 1, or 2 int pointCount; + } b2Manifold; /// Compute the contact manifold between two circles diff --git a/include/box2d/types.h b/include/box2d/types.h index dab2cee19..a608a8530 100644 --- a/include/box2d/types.h +++ b/include/box2d/types.h @@ -346,9 +346,11 @@ typedef struct b2ShapeDef float restitution; /// The rolling resistance usually in the range [0,1]. - /// todo float rollingResistance; + /// The tangent speed for conveyor belts + float tangentSpeed; + /// User material identifier. This is passed with query results and to friction and restitution /// combining functions. It is not used internally. int material; diff --git a/samples/main.cpp b/samples/main.cpp index 50e47df4b..d80c821be 100644 --- a/samples/main.cpp +++ b/samples/main.cpp @@ -640,8 +640,6 @@ int main( int, char** ) float frameTime = 0.0; - // int32_t frame = 0; - while ( !glfwWindowShouldClose( g_mainWindow ) ) { double time1 = glfwGetTime(); @@ -754,21 +752,14 @@ int main( int, char** ) // Limit frame rate to 60Hz double time2 = glfwGetTime(); - double targetTime = time1 + 1.0f / 60.0f; - // int loopCount = 0; + double targetTime = time1 + 1.0 / 60.0; while ( time2 < targetTime ) { b2Yield(); time2 = glfwGetTime(); - //++loopCount; } - frameTime = (float)( time2 - time1 ); - // if (frame % 17 == 0) - //{ - // printf("loop count = %d, frame time = %.1f\n", loopCount, 1000.0f * frameTime); - // } - //++frame; + frameTime = float( time2 - time1 ); } delete s_sample; diff --git a/samples/sample.cpp b/samples/sample.cpp index 2c77b6ae0..2c3e3fcd1 100644 --- a/samples/sample.cpp +++ b/samples/sample.cpp @@ -101,14 +101,8 @@ Sample::Sample( Settings& settings ) m_threadCount = 1 + settings.workerCount; - b2WorldDef worldDef = b2DefaultWorldDef(); - worldDef.workerCount = settings.workerCount; - worldDef.enqueueTask = EnqueueTask; - worldDef.finishTask = FinishTask; - worldDef.userTaskContext = this; - worldDef.enableSleep = settings.enableSleep; + m_worldId = b2_nullWorldId; - m_worldId = b2CreateWorld( &worldDef ); m_textLine = 30; m_textIncrement = 22; m_mouseJointId = b2_nullJointId; @@ -122,6 +116,9 @@ Sample::Sample( Settings& settings ) g_seed = RAND_SEED; + m_settings = &settings; + + CreateWorld( ); TestMathCpp(); } @@ -134,6 +131,24 @@ Sample::~Sample() delete[] m_tasks; } +void Sample::CreateWorld( ) +{ + if ( B2_IS_NON_NULL( m_worldId ) ) + { + b2DestroyWorld( m_worldId ); + m_worldId = b2_nullWorldId; + } + + b2WorldDef worldDef = b2DefaultWorldDef(); + worldDef.workerCount = m_settings->workerCount; + worldDef.enqueueTask = EnqueueTask; + worldDef.finishTask = FinishTask; + worldDef.userTaskContext = this; + worldDef.enableSleep = m_settings->enableSleep; + + m_worldId = b2CreateWorld( &worldDef ); +} + void Sample::DrawTitle( const char* string ) { g_draw.DrawString( 5, 5, string ); diff --git a/samples/sample.h b/samples/sample.h index e1236d8d0..356b7ec22 100644 --- a/samples/sample.h +++ b/samples/sample.h @@ -5,11 +5,10 @@ #include "box2d/id.h" #include "box2d/types.h" +#include "settings.h" #define ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) ) -struct Settings; - namespace enki { class TaskScheduler; @@ -43,6 +42,8 @@ class Sample explicit Sample( Settings& settings ); virtual ~Sample(); + void CreateWorld( ); + void DrawTitle( const char* string ); virtual void Step( Settings& settings ); virtual void UpdateUI() @@ -66,9 +67,9 @@ class Sample static constexpr int m_maxTasks = 64; static constexpr int m_maxThreads = 64; + const Settings* m_settings; enki::TaskScheduler* m_scheduler; class SampleTask* m_tasks; - int m_taskCount; int m_threadCount; diff --git a/samples/sample_benchmark.cpp b/samples/sample_benchmark.cpp index 71c3338a6..62dac89a4 100644 --- a/samples/sample_benchmark.cpp +++ b/samples/sample_benchmark.cpp @@ -557,7 +557,7 @@ class BenchmarkLargePyramid : public Sample settings.enableSleep = false; } - CreateLargePyramid(m_worldId); + CreateLargePyramid( m_worldId ); } static Sample* Create( Settings& settings ) @@ -624,6 +624,9 @@ class BenchmarkCreateDestroy : public Sample m_bodies[i] = b2_nullBodyId; } + m_createTime = 0.0f; + m_destroyTime = 0.0f; + m_baseCount = g_sampleDebug ? 40 : 100; m_iterations = g_sampleDebug ? 1 : 10; m_bodyCount = 0; @@ -631,6 +634,8 @@ class BenchmarkCreateDestroy : public Sample void CreateScene() { + uint64_t ticks = b2GetTicks(); + for ( int i = 0; i < e_maxBodyCount; ++i ) { if ( B2_IS_NON_NULL( m_bodies[i] ) ) @@ -640,6 +645,8 @@ class BenchmarkCreateDestroy : public Sample } } + m_destroyTime += b2GetMillisecondsAndReset( &ticks ); + int count = m_baseCount; float rad = 0.5f; float shift = rad * 2.0f; @@ -675,22 +682,28 @@ class BenchmarkCreateDestroy : public Sample } } + m_createTime += b2GetMilliseconds( ticks ); + m_bodyCount = index; + + b2World_Step( m_worldId, 1.0f / 60.0f, 4 ); } void Step( Settings& settings ) override { - uint64_t ticks = b2GetTicks(); + m_createTime = 0.0f; + m_destroyTime = 0.0f; for ( int i = 0; i < m_iterations; ++i ) { CreateScene(); } - float ms = b2GetMilliseconds( ticks ); + DrawTextLine( "total: create = %g ms, destroy = %g ms", m_createTime, m_destroyTime ); - g_draw.DrawString( 5, m_textLine, "milliseconds = %g", ms ); - m_textLine += m_textIncrement; + float createPerBody = 1000.0f * m_createTime / m_iterations / m_bodyCount; + float destroyPerBody = 1000.0f * m_destroyTime / m_iterations / m_bodyCount; + DrawTextLine( "body: create = %g us, destroy = %g us", createPerBody, destroyPerBody ); Sample::Step( settings ); } @@ -700,6 +713,8 @@ class BenchmarkCreateDestroy : public Sample return new BenchmarkCreateDestroy( settings ); } + float m_createTime; + float m_destroyTime; b2BodyId m_bodies[e_maxBodyCount]; int m_bodyCount; int m_baseCount; @@ -1529,14 +1544,14 @@ class BenchmarkRain : public Sample void Step( Settings& settings ) override { - if (settings.pause == false || settings.singleStep == true) + if ( settings.pause == false || settings.singleStep == true ) { StepRain( m_worldId, m_stepCount ); } Sample::Step( settings ); - if (m_stepCount % 1000 == 0) + if ( m_stepCount % 1000 == 0 ) { m_stepCount += 0; } diff --git a/samples/sample_shapes.cpp b/samples/sample_shapes.cpp index a09d1e5d7..aaa831a2b 100644 --- a/samples/sample_shapes.cpp +++ b/samples/sample_shapes.cpp @@ -952,7 +952,96 @@ class Friction : public Sample } }; -static int sampleIndex3 = RegisterSample( "Shapes", "Friction", Friction::Create ); +static int sampleFriction = RegisterSample( "Shapes", "Friction", Friction::Create ); + +class RollingResistance : public Sample +{ +public: + explicit RollingResistance( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 5.0f, 20.0f }; + g_camera.m_zoom = 27.5f; + } + + m_lift = 0.0f; + CreateScene(); + } + + void CreateScene() + { + b2Circle circle = { b2Vec2_zero, 0.5f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + for ( int i = 0; i < 20; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Segment segment = { { -40.0f, 2.0f * i }, { 40.0f, 2.0f * i + m_lift } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -39.5f, 2.0f * i + 0.75f }; + bodyDef.angularVelocity = -10.0f; + bodyDef.linearVelocity = { 5.0f, 0.0f }; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + shapeDef.rollingResistance = 0.1f * i; + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + } + + void Keyboard( int key ) override + { + switch ( key ) + { + case GLFW_KEY_1: + m_lift = 0.0f; + CreateWorld(); + CreateScene(); + break; + + case GLFW_KEY_2: + m_lift = 5.0f; + CreateWorld(); + CreateScene(); + break; + + case GLFW_KEY_3: + m_lift = -5.0f; + CreateWorld(); + CreateScene(); + break; + + default: + Sample::Keyboard( key ); + break; + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + for ( int i = 0; i < 20; ++i ) + { + g_draw.DrawString( { -41.5f, 2.0f * i + 1.0f }, "%.2f", 0.1f * i ); + } + } + + static Sample* Create( Settings& settings ) + { + return new RollingResistance( settings ); + } + + float m_lift; +}; + +static int sampleRollingResistance = RegisterSample( "Shapes", "Rolling Resistance", RollingResistance::Create ); // This sample shows how to modify the geometry on an existing shape. This is only supported on // dynamic and kinematic shapes because static shapes don't look for new collisions. diff --git a/samples/sample_stacking.cpp b/samples/sample_stacking.cpp index 79728327d..235372bb7 100644 --- a/samples/sample_stacking.cpp +++ b/samples/sample_stacking.cpp @@ -32,16 +32,17 @@ class SingleBox : public Sample float groundWidth = 66.0f * extent; b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.friction = 0.5f; + //shapeDef.friction = 0.5f; b2Segment segment = { { -0.5f * 2.0f * groundWidth, 0.0f }, { 0.5f * 2.0f * groundWidth, 0.0f } }; b2CreateSegmentShape( groundId, &shapeDef, &segment ); bodyDef.type = b2_dynamicBody; b2Polygon box = b2MakeBox( extent, extent ); - bodyDef.position = { 0.0f, 4.0f }; - b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - b2CreatePolygonShape( bodyId, &shapeDef, &box ); + bodyDef.position = { 0.0f, 1.0f }; + bodyDef.linearVelocity = { 5.0f, 0.0f }; + m_bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodyId, &shapeDef, &box ); } void Step( Settings& settings ) override @@ -49,12 +50,17 @@ class SingleBox : public Sample Sample::Step( settings ); // g_draw.DrawCircle({0.0f, 2.0f}, 1.0f, b2_colorWhite); + + b2Vec2 position = b2Body_GetPosition( m_bodyId ); + DrawTextLine( "(x, y) = (%.2g, %.2g)", position.x, position.y ); } static Sample* Create( Settings& settings ) { return new SingleBox( settings ); } + + b2BodyId m_bodyId; }; static int sampleSingleBox = RegisterSample( "Stacking", "Single Box", SingleBox::Create ); @@ -425,13 +431,14 @@ class CircleStack : public Sample b2ShapeDef shapeDef = b2DefaultShapeDef(); shapeDef.enableHitEvents = true; + shapeDef.rollingResistance = 0.2f; b2BodyDef bodyDef = b2DefaultBodyDef(); bodyDef.type = b2_dynamicBody; float y = 0.5f; - for ( int i = 0; i < 8; ++i ) + for ( int i = 0; i < 1; ++i ) { bodyDef.position.y = y; diff --git a/src/constraint_graph.c b/src/constraint_graph.c index 3fc77763d..fbd843f76 100644 --- a/src/constraint_graph.c +++ b/src/constraint_graph.c @@ -23,7 +23,7 @@ // cause horrible cache stalls. To make this feasible I would need a way to block these writes. // This is used for debugging by making all constraints be assigned to overflow. -#define B2_FORCE_OVERFLOW 0 +#define B2_FORCE_OVERFLOW 1 _Static_assert( B2_GRAPH_COLOR_COUNT == 12, "graph color count assumed to be 12" ); @@ -259,6 +259,8 @@ static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyId return i; } } +#else + B2_MAYBE_UNUSED( graph, bodyIdA, bodyIdB ); #endif return B2_OVERFLOW_INDEX; diff --git a/src/contact.c b/src/contact.c index 7911b8fc6..1e812923d 100644 --- a/src/contact.c +++ b/src/contact.c @@ -493,6 +493,21 @@ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, contactSim->friction = world->frictionCallback( shapeA->friction, shapeA->material, shapeB->friction, shapeB->material ); contactSim->restitution = world->restitutionCallback( shapeA->restitution, shapeA->material, shapeB->restitution, shapeB->material ); + // todo branch improves perf? + if (shapeA->rollingResistance > 0.0f || shapeB->rollingResistance > 0.0f) + { + float radiusA = b2GetShapeRadius( shapeA ); + float radiusB = b2GetShapeRadius( shapeB ); + float maxRadius = b2MaxFloat( radiusA, radiusB ); + contactSim->rollingResistance = b2MaxFloat( shapeA->rollingResistance, shapeB->rollingResistance ) * maxRadius; + } + else + { + contactSim->rollingResistance = 0.0f; + } + + contactSim->tangentSpeed = shapeB->tangentSpeed - shapeA->tangentSpeed; + int pointCount = contactSim->manifold.pointCount; bool touching = pointCount > 0; @@ -536,6 +551,11 @@ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, contactSim->simFlags &= ~b2_simEnableHitEvent; } + if (pointCount > 0) + { + contactSim->manifold.rollingImpulse = oldManifold.rollingImpulse; + } + // Match old contact ids to new contact ids and copy the // stored impulses to warm start the solver. int unmatchedCount = 0; diff --git a/src/contact.h b/src/contact.h index b6176a025..76d23dfb5 100644 --- a/src/contact.h +++ b/src/contact.h @@ -121,8 +121,7 @@ typedef struct b2ContactSim // Mixed friction and restitution float friction; float restitution; - - // todo for conveyor belts + float rollingResistance; float tangentSpeed; // b2ContactSimFlags diff --git a/src/contact_solver.c b/src/contact_solver.c index 6c25653b8..4731ddd64 100644 --- a/src/contact_solver.c +++ b/src/contact_solver.c @@ -62,6 +62,9 @@ void b2PrepareOverflowContacts( b2StepContext* context ) constraint->normal = manifold->normal; constraint->friction = contactSim->friction; constraint->restitution = contactSim->restitution; + constraint->rollingResistance = contactSim->rollingResistance; + constraint->rollingImpulse = warmStartScale * manifold->rollingImpulse; + constraint->tangentSpeed = contactSim->tangentSpeed; constraint->pointCount = pointCount; b2Vec2 vA = b2Vec2_zero; @@ -101,6 +104,11 @@ void b2PrepareOverflowContacts( b2StepContext* context ) constraint->invMassB = mB; constraint->invIB = iB; + { + float k = iA + iB; + constraint->rollingMass = k > 0.0f ? 1.0f / k : 0.0f; + } + b2Vec2 normal = constraint->normal; b2Vec2 tangent = b2RightPerp( constraint->normal ); @@ -195,6 +203,9 @@ void b2WarmStartOverflowContacts( b2StepContext* context ) vB = b2MulAdd( vB, mB, P ); } + wA -= constraint->rollingImpulse; + wB += constraint->rollingImpulse; + stateA->linearVelocity = vA; stateA->angularVelocity = wA; stateB->linearVelocity = vB; @@ -248,7 +259,9 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) b2Softness softness = constraint->softness; int pointCount = constraint->pointCount; + float totalNormalImpulse = 0.0f; + // Non-penetration for ( int j = 0; j < pointCount; ++j ) { b2ContactConstraintPoint* cp = constraint->points + j; @@ -290,6 +303,7 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) impulse = newImpulse - cp->normalImpulse; cp->normalImpulse = newImpulse; cp->maxNormalImpulse = b2MaxFloat( cp->maxNormalImpulse, impulse ); + totalNormalImpulse += newImpulse; // apply normal impulse b2Vec2 P = b2MulSV( impulse, normal ); @@ -300,6 +314,7 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) wB += iB * b2Cross( rB, P ); } + // Friction for ( int j = 0; j < pointCount; ++j ) { b2ContactConstraintPoint* cp = constraint->points + j; @@ -311,7 +326,7 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) // relative tangent velocity at contact b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); - float vt = b2Dot( b2Sub( vrB, vrA ), tangent ); + float vt = b2Dot( b2Sub( vrB, vrA ), tangent ) + constraint->tangentSpeed; // incremental tangent impulse float impulse = cp->tangentMass * ( -vt ); @@ -330,6 +345,20 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) wB += iB * b2Cross( rB, P ); } + // Rolling resistance + { + float deltaLambda = -constraint->rollingMass * ( wB - wA ); + float lambda = constraint->rollingImpulse; + constraint->rollingImpulse = lambda + deltaLambda; + + float maxLambda = constraint->rollingResistance * totalNormalImpulse; + constraint->rollingImpulse = b2ClampFloat( lambda + deltaLambda, -maxLambda, maxLambda ); + deltaLambda = constraint->rollingImpulse - lambda; + + wA -= iA * deltaLambda; + wB += iB * deltaLambda; + } + stateA->linearVelocity = vA; stateA->angularVelocity = wA; stateB->linearVelocity = vB; @@ -461,6 +490,8 @@ void b2StoreOverflowImpulses( b2StepContext* context ) manifold->points[j].maxNormalImpulse = constraint->points[j].maxNormalImpulse; manifold->points[j].normalVelocity = constraint->points[j].relativeVelocity; } + + manifold->rollingImpulse = constraint->rollingImpulse; } b2TracyCZoneEnd( store_impulses ); diff --git a/src/contact_solver.h b/src/contact_solver.h index 911e6a9d8..6faaeb9ff 100644 --- a/src/contact_solver.h +++ b/src/contact_solver.h @@ -29,6 +29,10 @@ typedef struct b2ContactConstraint float invIA, invIB; float friction; float restitution; + float tangentSpeed; + float rollingResistance; + float rollingMass; + float rollingImpulse; b2Softness softness; int pointCount; } b2ContactConstraint; diff --git a/src/core.h b/src/core.h index 5f540ce18..362f6832e 100644 --- a/src/core.h +++ b/src/core.h @@ -110,7 +110,8 @@ #define B2_ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) ) // Used to prevent the compiler from warning about unused variables -#define B2_MAYBE_UNUSED( x ) ( (void)( x ) ) +//#define B2_MAYBE_UNUSED( x ) ( (void)( x ) ) +#define B2_MAYBE_UNUSED( ... ) (void)( __VA_ARGS__ ) // Use to validate definitions. Do not take my cookie. #define B2_SECRET_COOKIE 1152023 diff --git a/src/shape.c b/src/shape.c index 42dcd4793..fb07822f2 100644 --- a/src/shape.c +++ b/src/shape.c @@ -109,6 +109,8 @@ static b2Shape* b2CreateShapeInternal( b2World* world, b2Body* body, b2Transform shape->density = def->density; shape->friction = def->friction; shape->restitution = def->restitution; + shape->rollingResistance = def->rollingResistance; + shape->tangentSpeed = def->tangentSpeed; shape->material = def->material; shape->filter = def->filter; shape->userData = def->userData; diff --git a/src/shape.h b/src/shape.h index 71b0278a1..6437a2b26 100644 --- a/src/shape.h +++ b/src/shape.h @@ -21,6 +21,8 @@ typedef struct b2Shape float density; float friction; float restitution; + float rollingResistance; + float tangentSpeed; int material; b2AABB aabb; @@ -94,5 +96,20 @@ b2ShapeProxy b2MakeShapeDistanceProxy( const b2Shape* shape ); b2CastOutput b2RayCastShape( const b2RayCastInput* input, const b2Shape* shape, b2Transform transform ); b2CastOutput b2ShapeCastShape( const b2ShapeCastInput* input, const b2Shape* shape, b2Transform transform ); +static inline float b2GetShapeRadius(const b2Shape* shape) +{ + switch ( shape->type ) + { + case b2_capsuleShape: + return shape->capsule.radius; + case b2_circleShape: + return shape->circle.radius; + case b2_polygonShape: + return shape->polygon.radius; + default: + return 0.0f; + } +} + B2_ARRAY_INLINE( b2ChainShape, b2ChainShape ); B2_ARRAY_INLINE( b2Shape, b2Shape ); diff --git a/src/solver.c b/src/solver.c index bc37d6174..b12851249 100644 --- a/src/solver.c +++ b/src/solver.c @@ -22,6 +22,7 @@ #include #include +// Compare to SDL_CPUPauseInstruction #if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) static inline void b2Pause( void ) { From bdea14cdf51919c655f5826d0289bd4e464cc5a5 Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sat, 25 Jan 2025 16:28:44 -0800 Subject: [PATCH 2/9] SIMD rolling resistance --- samples/car.cpp | 1 + samples/sample_benchmark.cpp | 2 + samples/sample_joints.cpp | 8 +- samples/sample_shapes.cpp | 6 +- src/constraint_graph.c | 2 +- src/contact_solver.c | 302 +++++++++++++---------------------- 6 files changed, 122 insertions(+), 199 deletions(-) diff --git a/samples/car.cpp b/samples/car.cpp index cb7109b07..d9e7f34e0 100644 --- a/samples/car.cpp +++ b/samples/car.cpp @@ -53,6 +53,7 @@ void Car::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, f shapeDef.density = 2.0f / scale; shapeDef.friction = 1.5f; + shapeDef.rollingResistance = 0.1f; bodyDef.position = b2Add( { -1.0f * scale, 0.35f * scale }, position ); bodyDef.allowFastRotation = true; diff --git a/samples/sample_benchmark.cpp b/samples/sample_benchmark.cpp index 62dac89a4..c02dbf64a 100644 --- a/samples/sample_benchmark.cpp +++ b/samples/sample_benchmark.cpp @@ -221,6 +221,7 @@ class BenchmarkBarrel : public Sample { m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); circle.radius = RandomFloatRange( 0.25f, 0.75f ); + shapeDef.rollingResistance = 0.2f; b2CreateCircleShape( m_bodies[index], &shapeDef, &circle ); } else if ( m_shapeType == e_capsuleShape ) @@ -230,6 +231,7 @@ class BenchmarkBarrel : public Sample float length = RandomFloatRange( 0.25f, 1.0f ); capsule.center1 = { 0.0f, -0.5f * length }; capsule.center2 = { 0.0f, 0.5f * length }; + shapeDef.rollingResistance = 0.2f; b2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule ); } else if ( m_shapeType == e_mixShape ) diff --git a/samples/sample_joints.cpp b/samples/sample_joints.cpp index 43b88c72e..3dd06d951 100644 --- a/samples/sample_joints.cpp +++ b/samples/sample_joints.cpp @@ -2072,11 +2072,11 @@ class Driving : public Sample m_throttle = 0.0f; m_speed = 35.0f; - m_torque = 2.5f; + m_torque = 5.0f; m_hertz = 5.0f; m_dampingRatio = 0.7f; - m_car.Spawn( m_worldId, { 0.0f, 0.0f }, 1.0f, m_hertz, m_dampingRatio, m_torque, NULL ); + m_car.Spawn( m_worldId, { 0.0f, 0.0f }, 1.0f, m_hertz, m_dampingRatio, m_torque, nullptr ); } void UpdateUI() override @@ -2103,7 +2103,7 @@ class Driving : public Sample m_car.SetSpeed( m_throttle * m_speed ); } - if ( ImGui::SliderFloat( "Torque", &m_torque, 0.0f, 5.0f, "%.1f" ) ) + if ( ImGui::SliderFloat( "Torque", &m_torque, 0.0f, 10.0f, "%.1f" ) ) { m_car.SetTorque( m_torque ); } @@ -2491,7 +2491,7 @@ class ScissorLift : public Sample m_liftJointId = b2CreateDistanceJoint( m_worldId, &distanceDef ); Car car; - car.Spawn( m_worldId, { 0.0f, y + 2.0f }, 1.0f, 3.0f, 0.7f, 0.0f, NULL ); + car.Spawn( m_worldId, { 0.0f, y + 2.0f }, 1.0f, 3.0f, 0.7f, 0.0f, nullptr ); } void UpdateUI() override diff --git a/samples/sample_shapes.cpp b/samples/sample_shapes.cpp index aaa831a2b..26d8ff3af 100644 --- a/samples/sample_shapes.cpp +++ b/samples/sample_shapes.cpp @@ -967,6 +967,7 @@ class RollingResistance : public Sample } m_lift = 0.0f; + m_resistScale = 0.02f; CreateScene(); } @@ -990,7 +991,7 @@ class RollingResistance : public Sample bodyDef.linearVelocity = { 5.0f, 0.0f }; b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - shapeDef.rollingResistance = 0.1f * i; + shapeDef.rollingResistance = m_resistScale * i; b2CreateCircleShape( bodyId, &shapeDef, &circle ); } } @@ -1029,7 +1030,7 @@ class RollingResistance : public Sample for ( int i = 0; i < 20; ++i ) { - g_draw.DrawString( { -41.5f, 2.0f * i + 1.0f }, "%.2f", 0.1f * i ); + g_draw.DrawString( { -41.5f, 2.0f * i + 1.0f }, "%.2f", m_resistScale * i ); } } @@ -1038,6 +1039,7 @@ class RollingResistance : public Sample return new RollingResistance( settings ); } + float m_resistScale; float m_lift; }; diff --git a/src/constraint_graph.c b/src/constraint_graph.c index fbd843f76..950ee6542 100644 --- a/src/constraint_graph.c +++ b/src/constraint_graph.c @@ -23,7 +23,7 @@ // cause horrible cache stalls. To make this feasible I would need a way to block these writes. // This is used for debugging by making all constraints be assigned to overflow. -#define B2_FORCE_OVERFLOW 1 +#define B2_FORCE_OVERFLOW 0 _Static_assert( B2_GRAPH_COLOR_COUNT == 12, "graph color count assumed to be 12" ); diff --git a/src/contact_solver.c b/src/contact_solver.c index 4731ddd64..f8b8cf6bf 100644 --- a/src/contact_solver.c +++ b/src/contact_solver.c @@ -203,8 +203,8 @@ void b2WarmStartOverflowContacts( b2StepContext* context ) vB = b2MulAdd( vB, mB, P ); } - wA -= constraint->rollingImpulse; - wB += constraint->rollingImpulse; + wA -= iA * constraint->rollingImpulse; + wB += iB * constraint->rollingImpulse; stateA->linearVelocity = vA; stateA->angularVelocity = wA; @@ -349,8 +349,6 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) { float deltaLambda = -constraint->rollingMass * ( wB - wA ); float lambda = constraint->rollingImpulse; - constraint->rollingImpulse = lambda + deltaLambda; - float maxLambda = constraint->rollingResistance * totalNormalImpulse; constraint->rollingImpulse = b2ClampFloat( lambda + deltaLambda, -maxLambda, maxLambda ); deltaLambda = constraint->rollingImpulse - lambda; @@ -590,6 +588,13 @@ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) return _mm256_max_ps( a, b ); } +// a = clamp(a, -b, b) +static inline b2FloatW b2ClampSymW( b2FloatW a, b2FloatW b ) +{ + b2FloatW nb = _mm256_sub_ps( _mm256_setzero_ps(), b ); + return _mm256_max_ps(nb, _mm256_min_ps( a, b )); +} + static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) { return _mm256_or_ps( a, b ); @@ -664,6 +669,13 @@ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) return vmaxq_f32( a, b ); } +// a = clamp(a, -b, b) +static inline b2FloatW b2ClampSymW( b2FloatW a, b2FloatW b ) +{ + b2FloatW nb = vnegq_f32( b ); + return vmaxq_f32( nb, vminq_f32( a, b ) ); +} + static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) { return vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32( a ), vreinterpretq_u32_f32( b ) ) ); @@ -772,6 +784,18 @@ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) return _mm_max_ps( a, b ); } +// a = clamp(a, -b, b) +static inline b2FloatW b2ClampSymW( b2FloatW a, b2FloatW b ) +{ + // Create a mask with the sign bit set for each element + __m128 mask = _mm_set1_ps( -0.0f ); + + // XOR the input with the mask to negate each element + __m128 nb = _mm_xor_ps( b, mask ); + + return _mm_max_ps( nb, _mm_min_ps( a, b ) ); +} + static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) { return _mm_or_ps( a, b ); @@ -870,6 +894,17 @@ static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) return r; } +// a = clamp(a, -b, b) +static inline b2FloatW b2ClampSymW( b2FloatW a, b2FloatW b ) +{ + b2FloatW r; + r.x = b2ClampFloat(a.x, -b.x, b.x); + r.y = b2ClampFloat(a.y, -b.y, b.y); + r.z = b2ClampFloat(a.z, -b.z, b.z); + r.w = b2ClampFloat(a.w, -b.w, b.w); + return r; +} + static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) { b2FloatW r; @@ -942,6 +977,9 @@ typedef struct b2ContactConstraintSIMD b2FloatW invIA, invIB; b2Vec2W normal; b2FloatW friction; + b2FloatW rollingResistance; + b2FloatW rollingMass; + b2FloatW rollingImpulse; b2FloatW biasRate; b2FloatW massScale; b2FloatW impulseScale; @@ -967,20 +1005,20 @@ int b2GetContactConstraintSIMDByteCount( void ) } // wide version of b2BodyState -typedef struct b2SimdBody +typedef struct b2BodyStateW { b2Vec2W v; b2FloatW w; b2FloatW flags; b2Vec2W dp; b2RotW dq; -} b2SimdBody; +} b2BodyStateW; // Custom gather/scatter for each SIMD type #if defined( B2_SIMD_AVX2 ) // This is a load and 8x8 transpose -static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) { _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); @@ -1012,7 +1050,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 b2FloatW tt6 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 1, 0, 1, 0 ) ); b2FloatW tt7 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 3, 2, 3, 2 ) ); - b2SimdBody simdBody; + b2BodyStateW simdBody; simdBody.v.X = _mm256_permute2f128_ps( tt0, tt4, 0x20 ); simdBody.v.Y = _mm256_permute2f128_ps( tt1, tt5, 0x20 ); simdBody.w = _mm256_permute2f128_ps( tt2, tt6, 0x20 ); @@ -1025,7 +1063,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 } // This writes everything back to the solver bodies but only the velocities change -static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody ) { _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); @@ -1069,7 +1107,7 @@ static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT i #elif defined( B2_SIMD_NEON ) // This is a load and transpose -static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) { _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); @@ -1102,7 +1140,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 // [w2 w4 f2 f4] b2FloatW t4a = b2UnpackHiW( b2a, b4a ); - b2SimdBody simdBody; + b2BodyStateW simdBody; simdBody.v.X = b2UnpackLoW( t1a, t2a ); simdBody.v.Y = b2UnpackHiW( t1a, t2a ); simdBody.w = b2UnpackLoW( t3a, t4a ); @@ -1123,7 +1161,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 // This writes only the velocities back to the solver bodies // https://developer.arm.com/documentation/102107a/0100/Floating-point-4x4-matrix-transposition -static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody ) { _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); @@ -1175,7 +1213,7 @@ static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT i #elif defined( B2_SIMD_SSE2 ) // This is a load and transpose -static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) { _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); @@ -1207,7 +1245,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 // [w2 w4 f2 f4] b2FloatW t4a = b2UnpackHiW( b2a, b4a ); - b2SimdBody simdBody; + b2BodyStateW simdBody; simdBody.v.X = b2UnpackLoW( t1a, t2a ); simdBody.v.Y = b2UnpackHiW( t1a, t2a ); simdBody.w = b2UnpackLoW( t3a, t4a ); @@ -1227,7 +1265,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 } // This writes only the velocities back to the solver bodies -static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody ) { _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); @@ -1271,7 +1309,7 @@ static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT i #else // This is a load and transpose -static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +static b2BodyStateW b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) { b2BodyState identity = b2_identityBodyState; @@ -1280,7 +1318,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 b2BodyState s3 = indices[2] == B2_NULL_INDEX ? identity : states[indices[2]]; b2BodyState s4 = indices[3] == B2_NULL_INDEX ? identity : states[indices[3]]; - b2SimdBody simdBody; + b2BodyStateW simdBody; simdBody.v.X = ( b2FloatW ){ s1.linearVelocity.x, s2.linearVelocity.x, s3.linearVelocity.x, s4.linearVelocity.x }; simdBody.v.Y = ( b2FloatW ){ s1.linearVelocity.y, s2.linearVelocity.y, s3.linearVelocity.y, s4.linearVelocity.y }; simdBody.w = ( b2FloatW ){ s1.angularVelocity, s2.angularVelocity, s3.angularVelocity, s4.angularVelocity }; @@ -1294,7 +1332,7 @@ static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2 } // This writes only the velocities back to the solver bodies -static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2BodyStateW* B2_RESTRICT simdBody ) { if ( indices[0] != B2_NULL_INDEX ) { @@ -1402,6 +1440,11 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context ( (float*)&constraint->invIA )[j] = iA; ( (float*)&constraint->invIB )[j] = iB; + { + float k = iA + iB; + ( (float*)&constraint->rollingMass )[j] = k > 0.0f ? 1.0f / k : 0.0f; + } + b2Softness soft = ( indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX ) ? staticSoftness : contactSoftness; b2Vec2 normal = manifold->normal; @@ -1410,6 +1453,9 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context ( (float*)&constraint->friction )[j] = contactSim->friction; ( (float*)&constraint->restitution )[j] = contactSim->restitution; + ( (float*)&constraint->rollingResistance )[j] = contactSim->rollingResistance; + ( (float*)&constraint->rollingImpulse )[j] = warmStartScale * manifold->rollingImpulse; + ( (float*)&constraint->biasRate )[j] = soft.biasRate; ( (float*)&constraint->massScale )[j] = soft.massScale; ( (float*)&constraint->impulseScale )[j] = soft.impulseScale; @@ -1561,8 +1607,8 @@ void b2WarmStartContactsTask( int startIndex, int endIndex, b2StepContext* conte for ( int i = startIndex; i < endIndex; ++i ) { b2ContactConstraintSIMD* c = constraints + i; - b2SimdBody bA = b2GatherBodies( states, c->indexA ); - b2SimdBody bB = b2GatherBodies( states, c->indexB ); + b2BodyStateW bA = b2GatherBodies( states, c->indexA ); + b2BodyStateW bB = b2GatherBodies( states, c->indexB ); b2FloatW tangentX = c->normal.Y; b2FloatW tangentY = b2SubW( b2ZeroW(), c->normal.X ); @@ -1599,6 +1645,9 @@ void b2WarmStartContactsTask( int startIndex, int endIndex, b2StepContext* conte bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, P.Y ); } + bA.w = b2MulSubW(bA.w, c->invIA, c->rollingImpulse); + bB.w = b2MulAddW(bB.w, c->invIB, c->rollingImpulse); + b2ScatterBodies( states, c->indexA, &bA ); b2ScatterBodies( states, c->indexB, &bB ); } @@ -1619,8 +1668,8 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, { b2ContactConstraintSIMD* c = constraints + i; - b2SimdBody bA = b2GatherBodies( states, c->indexA ); - b2SimdBody bB = b2GatherBodies( states, c->indexB ); + b2BodyStateW bA = b2GatherBodies( states, c->indexA ); + b2BodyStateW bB = b2GatherBodies( states, c->indexB ); b2FloatW biasRate, massScale, impulseScale; if ( useBias ) @@ -1636,6 +1685,8 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, impulseScale = b2ZeroW(); } + b2FloatW totalNormalImpulse = b2ZeroW(); + b2Vec2W dp = { b2SubW( bB.dp.X, bA.dp.X ), b2SubW( bB.dp.Y, bA.dp.Y ) }; // point1 non-penetration constraint @@ -1675,6 +1726,8 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, c->normalImpulse1 = newImpulse; c->maxNormalImpulse1 = b2MaxW( c->maxNormalImpulse1, newImpulse ); + totalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse ); + // Apply contact impulse b2FloatW Px = b2MulW( impulse, c->normal.X ); b2FloatW Py = b2MulW( impulse, c->normal.Y ); @@ -1722,6 +1775,8 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, c->normalImpulse2 = newImpulse; c->maxNormalImpulse2 = b2MaxW( c->maxNormalImpulse2, newImpulse ); + totalNormalImpulse = b2AddW( totalNormalImpulse, newImpulse ); + // Apply contact impulse b2FloatW Px = b2MulW( impulse, c->normal.X ); b2FloatW Py = b2MulW( impulse, c->normal.Y ); @@ -1806,6 +1861,18 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) ); } + // Rolling resistance + { + b2FloatW deltaLambda = b2MulW( c->rollingMass, b2SubW( bA.w, bB.w )); + b2FloatW lambda = c->rollingImpulse; + b2FloatW maxLambda = b2MulW( c->rollingResistance, totalNormalImpulse ); + c->rollingImpulse = b2ClampSymW( b2AddW(lambda, deltaLambda), maxLambda ); + deltaLambda = b2SubW(c->rollingImpulse, lambda); + + bA.w = b2MulSubW( bA.w, c->invIA, deltaLambda ); + bB.w = b2MulAddW( bB.w, c->invIB, deltaLambda ); + } + b2ScatterBodies( states, c->indexA, &bA ); b2ScatterBodies( states, c->indexB, &bB ); } @@ -1826,8 +1893,8 @@ void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* contex { b2ContactConstraintSIMD* c = constraints + i; - b2SimdBody bA = b2GatherBodies( states, c->indexA ); - b2SimdBody bB = b2GatherBodies( states, c->indexB ); + b2BodyStateW bA = b2GatherBodies( states, c->indexA ); + b2BodyStateW bB = b2GatherBodies( states, c->indexB ); // first point non-penetration constraint { @@ -1912,9 +1979,6 @@ void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* contex b2TracyCZoneEnd( restitution ); } -#if B2_SIMD_WIDTH == 8 - -// todo try making an inner loop on B2_SIMD_WIDTH to have a single implementation of this function void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ) { b2TracyCZoneNC( store_impulses, "Store", b2_colorFireBrick, true ); @@ -1924,9 +1988,10 @@ void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ) b2Manifold dummy = { 0 }; - for ( int i = startIndex; i < endIndex; ++i ) + for ( int constraintIndex = startIndex; constraintIndex < endIndex; ++constraintIndex ) { - const b2ContactConstraintSIMD* c = constraints + i; + const b2ContactConstraintSIMD* c = constraints + constraintIndex; + const float* rollingImpulse = (float*)&c->rollingImpulse; const float* normalImpulse1 = (float*)&c->normalImpulse1; const float* normalImpulse2 = (float*)&c->normalImpulse2; const float* tangentImpulse1 = (float*)&c->tangentImpulse1; @@ -1936,171 +2001,24 @@ void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ) const float* normalVelocity1 = (float*)&c->relativeVelocity1; const float* normalVelocity2 = (float*)&c->relativeVelocity2; - int base = 8 * i; - b2Manifold* m0 = contacts[base + 0] == NULL ? &dummy : &contacts[base + 0]->manifold; - b2Manifold* m1 = contacts[base + 1] == NULL ? &dummy : &contacts[base + 1]->manifold; - b2Manifold* m2 = contacts[base + 2] == NULL ? &dummy : &contacts[base + 2]->manifold; - b2Manifold* m3 = contacts[base + 3] == NULL ? &dummy : &contacts[base + 3]->manifold; - b2Manifold* m4 = contacts[base + 4] == NULL ? &dummy : &contacts[base + 4]->manifold; - b2Manifold* m5 = contacts[base + 5] == NULL ? &dummy : &contacts[base + 5]->manifold; - b2Manifold* m6 = contacts[base + 6] == NULL ? &dummy : &contacts[base + 6]->manifold; - b2Manifold* m7 = contacts[base + 7] == NULL ? &dummy : &contacts[base + 7]->manifold; - - m0->points[0].normalImpulse = normalImpulse1[0]; - m0->points[0].tangentImpulse = tangentImpulse1[0]; - m0->points[0].maxNormalImpulse = maxNormalImpulse1[0]; - m0->points[0].normalVelocity = normalVelocity1[0]; - - m0->points[1].normalImpulse = normalImpulse2[0]; - m0->points[1].tangentImpulse = tangentImpulse2[0]; - m0->points[1].maxNormalImpulse = maxNormalImpulse2[0]; - m0->points[1].normalVelocity = normalVelocity2[0]; - - m1->points[0].normalImpulse = normalImpulse1[1]; - m1->points[0].tangentImpulse = tangentImpulse1[1]; - m1->points[0].maxNormalImpulse = maxNormalImpulse1[1]; - m1->points[0].normalVelocity = normalVelocity1[1]; - - m1->points[1].normalImpulse = normalImpulse2[1]; - m1->points[1].tangentImpulse = tangentImpulse2[1]; - m1->points[1].maxNormalImpulse = maxNormalImpulse2[1]; - m1->points[1].normalVelocity = normalVelocity2[1]; - - m2->points[0].normalImpulse = normalImpulse1[2]; - m2->points[0].tangentImpulse = tangentImpulse1[2]; - m2->points[0].maxNormalImpulse = maxNormalImpulse1[2]; - m2->points[0].normalVelocity = normalVelocity1[2]; - - m2->points[1].normalImpulse = normalImpulse2[2]; - m2->points[1].tangentImpulse = tangentImpulse2[2]; - m2->points[1].maxNormalImpulse = maxNormalImpulse2[2]; - m2->points[1].normalVelocity = normalVelocity2[2]; - - m3->points[0].normalImpulse = normalImpulse1[3]; - m3->points[0].tangentImpulse = tangentImpulse1[3]; - m3->points[0].maxNormalImpulse = maxNormalImpulse1[3]; - m3->points[0].normalVelocity = normalVelocity1[3]; - - m3->points[1].normalImpulse = normalImpulse2[3]; - m3->points[1].tangentImpulse = tangentImpulse2[3]; - m3->points[1].maxNormalImpulse = maxNormalImpulse2[3]; - m3->points[1].normalVelocity = normalVelocity2[3]; - - m4->points[0].normalImpulse = normalImpulse1[4]; - m4->points[0].tangentImpulse = tangentImpulse1[4]; - m4->points[0].maxNormalImpulse = maxNormalImpulse1[4]; - m4->points[0].normalVelocity = normalVelocity1[4]; - - m4->points[1].normalImpulse = normalImpulse2[4]; - m4->points[1].tangentImpulse = tangentImpulse2[4]; - m4->points[1].maxNormalImpulse = maxNormalImpulse2[4]; - m4->points[1].normalVelocity = normalVelocity2[4]; - - m5->points[0].normalImpulse = normalImpulse1[5]; - m5->points[0].tangentImpulse = tangentImpulse1[5]; - m5->points[0].maxNormalImpulse = maxNormalImpulse1[5]; - m5->points[0].normalVelocity = normalVelocity1[5]; - - m5->points[1].normalImpulse = normalImpulse2[5]; - m5->points[1].tangentImpulse = tangentImpulse2[5]; - m5->points[1].maxNormalImpulse = maxNormalImpulse2[5]; - m5->points[1].normalVelocity = normalVelocity2[5]; - - m6->points[0].normalImpulse = normalImpulse1[6]; - m6->points[0].tangentImpulse = tangentImpulse1[6]; - m6->points[0].maxNormalImpulse = maxNormalImpulse1[6]; - m6->points[0].normalVelocity = normalVelocity1[6]; - - m6->points[1].normalImpulse = normalImpulse2[6]; - m6->points[1].tangentImpulse = tangentImpulse2[6]; - m6->points[1].maxNormalImpulse = maxNormalImpulse2[6]; - m6->points[1].normalVelocity = normalVelocity2[6]; - - m7->points[0].normalImpulse = normalImpulse1[7]; - m7->points[0].tangentImpulse = tangentImpulse1[7]; - m7->points[0].maxNormalImpulse = maxNormalImpulse1[7]; - m7->points[0].normalVelocity = normalVelocity1[7]; - - m7->points[1].normalImpulse = normalImpulse2[7]; - m7->points[1].tangentImpulse = tangentImpulse2[7]; - m7->points[1].maxNormalImpulse = maxNormalImpulse2[7]; - m7->points[1].normalVelocity = normalVelocity2[7]; - } - - b2TracyCZoneEnd( store_impulses ); -} - -#else - -void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ) -{ - b2TracyCZoneNC( store_impulses, "Store", b2_colorFirebrick, true ); - - b2ContactSim** contacts = context->contacts; - const b2ContactConstraintSIMD* constraints = context->simdContactConstraints; - - b2Manifold dummy = { 0 }; - - for ( int i = startIndex; i < endIndex; ++i ) - { - const b2ContactConstraintSIMD* c = constraints + i; - const float* normalImpulse1 = (float*)&c->normalImpulse1; - const float* normalImpulse2 = (float*)&c->normalImpulse2; - const float* tangentImpulse1 = (float*)&c->tangentImpulse1; - const float* tangentImpulse2 = (float*)&c->tangentImpulse2; - const float* maxNormalImpulse1 = (float*)&c->maxNormalImpulse1; - const float* maxNormalImpulse2 = (float*)&c->maxNormalImpulse2; - const float* normalVelocity1 = (float*)&c->relativeVelocity1; - const float* normalVelocity2 = (float*)&c->relativeVelocity2; + int baseIndex = B2_SIMD_WIDTH * constraintIndex; - int base = 4 * i; - b2Manifold* m0 = contacts[base + 0] == NULL ? &dummy : &contacts[base + 0]->manifold; - b2Manifold* m1 = contacts[base + 1] == NULL ? &dummy : &contacts[base + 1]->manifold; - b2Manifold* m2 = contacts[base + 2] == NULL ? &dummy : &contacts[base + 2]->manifold; - b2Manifold* m3 = contacts[base + 3] == NULL ? &dummy : &contacts[base + 3]->manifold; - - m0->points[0].normalImpulse = normalImpulse1[0]; - m0->points[0].tangentImpulse = tangentImpulse1[0]; - m0->points[0].maxNormalImpulse = maxNormalImpulse1[0]; - m0->points[0].normalVelocity = normalVelocity1[0]; - - m0->points[1].normalImpulse = normalImpulse2[0]; - m0->points[1].tangentImpulse = tangentImpulse2[0]; - m0->points[1].maxNormalImpulse = maxNormalImpulse2[0]; - m0->points[1].normalVelocity = normalVelocity2[0]; - - m1->points[0].normalImpulse = normalImpulse1[1]; - m1->points[0].tangentImpulse = tangentImpulse1[1]; - m1->points[0].maxNormalImpulse = maxNormalImpulse1[1]; - m1->points[0].normalVelocity = normalVelocity1[1]; - - m1->points[1].normalImpulse = normalImpulse2[1]; - m1->points[1].tangentImpulse = tangentImpulse2[1]; - m1->points[1].maxNormalImpulse = maxNormalImpulse2[1]; - m1->points[1].normalVelocity = normalVelocity2[1]; - - m2->points[0].normalImpulse = normalImpulse1[2]; - m2->points[0].tangentImpulse = tangentImpulse1[2]; - m2->points[0].maxNormalImpulse = maxNormalImpulse1[2]; - m2->points[0].normalVelocity = normalVelocity1[2]; - - m2->points[1].normalImpulse = normalImpulse2[2]; - m2->points[1].tangentImpulse = tangentImpulse2[2]; - m2->points[1].maxNormalImpulse = maxNormalImpulse2[2]; - m2->points[1].normalVelocity = normalVelocity2[2]; - - m3->points[0].normalImpulse = normalImpulse1[3]; - m3->points[0].tangentImpulse = tangentImpulse1[3]; - m3->points[0].maxNormalImpulse = maxNormalImpulse1[3]; - m3->points[0].normalVelocity = normalVelocity1[3]; - - m3->points[1].normalImpulse = normalImpulse2[3]; - m3->points[1].tangentImpulse = tangentImpulse2[3]; - m3->points[1].maxNormalImpulse = maxNormalImpulse2[3]; - m3->points[1].normalVelocity = normalVelocity2[3]; + for ( int laneIndex = 0; laneIndex < B2_SIMD_WIDTH; ++laneIndex ) + { + b2Manifold* m = contacts[baseIndex + laneIndex] == NULL ? &dummy : &contacts[baseIndex + laneIndex]->manifold; + m->rollingImpulse = rollingImpulse[laneIndex]; + + m->points[0].normalImpulse = normalImpulse1[laneIndex]; + m->points[0].tangentImpulse = tangentImpulse1[laneIndex]; + m->points[0].maxNormalImpulse = maxNormalImpulse1[laneIndex]; + m->points[0].normalVelocity = normalVelocity1[laneIndex]; + + m->points[1].normalImpulse = normalImpulse2[laneIndex]; + m->points[1].tangentImpulse = tangentImpulse2[laneIndex]; + m->points[1].maxNormalImpulse = maxNormalImpulse2[laneIndex]; + m->points[1].normalVelocity = normalVelocity2[laneIndex]; + } } b2TracyCZoneEnd( store_impulses ); } - -#endif From e8c9130b5376d5fbaca79e8e5a315d03543aacfd Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sat, 25 Jan 2025 22:51:37 -0800 Subject: [PATCH 3/9] tangent speed --- samples/sample_shapes.cpp | 59 +++++++++++++++++++++++++++++++++++++++ src/constraint_graph.c | 2 +- src/contact.c | 2 +- src/contact_solver.c | 6 +++- 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/samples/sample_shapes.cpp b/samples/sample_shapes.cpp index 26d8ff3af..f9e7a65b6 100644 --- a/samples/sample_shapes.cpp +++ b/samples/sample_shapes.cpp @@ -1045,6 +1045,65 @@ class RollingResistance : public Sample static int sampleRollingResistance = RegisterSample( "Shapes", "Rolling Resistance", RollingResistance::Create ); +class ConveyorBelt : public Sample +{ +public: + explicit ConveyorBelt( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 2.0f, 7.5f }; + g_camera.m_zoom = 12.0f; + } + + // Ground + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Platform + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { -5.0f, 5.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeRoundedBox( 10.0f, 0.25f, 0.25f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.8f; + shapeDef.tangentSpeed = 2.0f; + + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + // Boxes + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon cube = b2MakeSquare( 0.5f ); + for ( int i = 0; i < 5; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -10.0f + 2.0f * i, 7.0f }; + b2BodyId bodyId = b2CreateBody(m_worldId, &bodyDef ); + + b2CreatePolygonShape( bodyId, &shapeDef, &cube ); + } + } + + static Sample* Create( Settings& settings ) + { + return new ConveyorBelt( settings ); + } +}; + +static int sampleConveyorBelt = RegisterSample( "Shapes", "Conveyor Belt", ConveyorBelt::Create ); + // This sample shows how to modify the geometry on an existing shape. This is only supported on // dynamic and kinematic shapes because static shapes don't look for new collisions. class ModifyGeometry : public Sample diff --git a/src/constraint_graph.c b/src/constraint_graph.c index 950ee6542..fbd843f76 100644 --- a/src/constraint_graph.c +++ b/src/constraint_graph.c @@ -23,7 +23,7 @@ // cause horrible cache stalls. To make this feasible I would need a way to block these writes. // This is used for debugging by making all constraints be assigned to overflow. -#define B2_FORCE_OVERFLOW 0 +#define B2_FORCE_OVERFLOW 1 _Static_assert( B2_GRAPH_COLOR_COUNT == 12, "graph color count assumed to be 12" ); diff --git a/src/contact.c b/src/contact.c index 1e812923d..f596a8d66 100644 --- a/src/contact.c +++ b/src/contact.c @@ -506,7 +506,7 @@ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, contactSim->rollingResistance = 0.0f; } - contactSim->tangentSpeed = shapeB->tangentSpeed - shapeA->tangentSpeed; + contactSim->tangentSpeed = shapeA->tangentSpeed + shapeB->tangentSpeed; int pointCount = contactSim->manifold.pointCount; bool touching = pointCount > 0; diff --git a/src/contact_solver.c b/src/contact_solver.c index f8b8cf6bf..25a0ee98c 100644 --- a/src/contact_solver.c +++ b/src/contact_solver.c @@ -326,7 +326,11 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) // relative tangent velocity at contact b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); - float vt = b2Dot( b2Sub( vrB, vrA ), tangent ) + constraint->tangentSpeed; + + // vt = dot(vrB - sB * tangent - (vrA + sA * tangent), tangent) + // = dot(vrB - vrA, tangent) - (sA + sB) + + float vt = b2Dot( b2Sub( vrB, vrA ), tangent ) - constraint->tangentSpeed; // incremental tangent impulse float impulse = cp->tangentMass * ( -vt ); From e441b773be35c06e3786297125b6c5bbaeacc9f1 Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sat, 25 Jan 2025 22:56:56 -0800 Subject: [PATCH 4/9] tangent speed with simd --- src/constraint_graph.c | 2 +- src/contact_solver.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/constraint_graph.c b/src/constraint_graph.c index fbd843f76..950ee6542 100644 --- a/src/constraint_graph.c +++ b/src/constraint_graph.c @@ -23,7 +23,7 @@ // cause horrible cache stalls. To make this feasible I would need a way to block these writes. // This is used for debugging by making all constraints be assigned to overflow. -#define B2_FORCE_OVERFLOW 1 +#define B2_FORCE_OVERFLOW 0 _Static_assert( B2_GRAPH_COLOR_COUNT == 12, "graph color count assumed to be 12" ); diff --git a/src/contact_solver.c b/src/contact_solver.c index 25a0ee98c..e6f131b14 100644 --- a/src/contact_solver.c +++ b/src/contact_solver.c @@ -981,6 +981,7 @@ typedef struct b2ContactConstraintSIMD b2FloatW invIA, invIB; b2Vec2W normal; b2FloatW friction; + b2FloatW tangentSpeed; b2FloatW rollingResistance; b2FloatW rollingMass; b2FloatW rollingImpulse; @@ -1456,6 +1457,7 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context ( (float*)&constraint->normal.Y )[j] = normal.y; ( (float*)&constraint->friction )[j] = contactSim->friction; + ( (float*)&constraint->tangentSpeed )[j] = contactSim->tangentSpeed; ( (float*)&constraint->restitution )[j] = contactSim->restitution; ( (float*)&constraint->rollingResistance )[j] = contactSim->rollingResistance; ( (float*)&constraint->rollingImpulse )[j] = warmStartScale * manifold->rollingImpulse; @@ -1565,6 +1567,10 @@ void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context ( (float*)&constraint->normal.X )[j] = 0.0f; ( (float*)&constraint->normal.Y )[j] = 0.0f; ( (float*)&constraint->friction )[j] = 0.0f; + ( (float*)&constraint->tangentSpeed )[j] = 0.0f; + ( (float*)&constraint->rollingResistance )[j] = 0.0f; + ( (float*)&constraint->rollingMass )[j] = 0.0f; + ( (float*)&constraint->rollingImpulse )[j] = 0.0f; ( (float*)&constraint->biasRate )[j] = 0.0f; ( (float*)&constraint->massScale )[j] = 0.0f; ( (float*)&constraint->impulseScale )[j] = 0.0f; @@ -1808,6 +1814,9 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); b2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) ); + // Tangent speed (conveyor belt) + vt = b2SubW( vt, c->tangentSpeed ); + // Compute tangent force b2FloatW negImpulse = b2MulW( c->tangentMass1, vt ); @@ -1842,6 +1851,9 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); b2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) ); + // Tangent speed (conveyor belt) + vt = b2SubW( vt, c->tangentSpeed ); + // Compute tangent force b2FloatW negImpulse = b2MulW( c->tangentMass2, vt ); From eb4c15e511c33c85f42b2e0561cd81d389339b1c Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sun, 26 Jan 2025 17:00:26 -0800 Subject: [PATCH 5/9] b2SurfaceMaterial for chain shapes --- include/box2d/types.h | 48 +++++++---- samples/data/ramp.svg | 66 ++++++++++++++++ samples/sample.cpp | 145 +++++++++++++++++++++++++++++++++- samples/sample.h | 2 + samples/sample_continuous.cpp | 6 +- samples/sample_events.cpp | 6 +- samples/sample_shapes.cpp | 112 +++++++++++++++++++++++++- samples/settings.cpp | 2 +- samples/shader.cpp | 2 +- shared/benchmarks.c | 6 +- src/body.c | 4 +- src/broad_phase.c | 2 +- src/contact.c | 22 +++--- src/core.h | 3 +- src/island.c | 4 +- src/joint.c | 2 +- src/motor_joint.c | 2 +- src/sensor.c | 2 +- src/shape.c | 108 +++++++++++++++++++------ src/shape.h | 5 +- src/solver.c | 10 +-- src/table.c | 2 +- src/types.c | 62 +++++---------- src/world.c | 46 +++++------ 24 files changed, 523 insertions(+), 146 deletions(-) create mode 100644 samples/data/ramp.svg diff --git a/include/box2d/types.h b/include/box2d/types.h index a608a8530..c7b2d5c1e 100644 --- a/include/box2d/types.h +++ b/include/box2d/types.h @@ -396,6 +396,35 @@ typedef struct b2ShapeDef /// @ingroup shape B2_API b2ShapeDef b2DefaultShapeDef( void ); +/// Surface materials allow chain shapes to have per segment surface properties. +/// @ingroup shape +typedef struct b2SurfaceMaterial +{ + /// The Coulomb (dry) friction coefficient, usually in the range [0,1]. + float friction; + + /// The coefficient of restitution (bounce) usually in the range [0,1]. + /// https://en.wikipedia.org/wiki/Coefficient_of_restitution + float restitution; + + /// The rolling resistance usually in the range [0,1]. + float rollingResistance; + + /// The tangent speed for conveyor belts + float tangentSpeed; + + /// User material identifier. This is passed with query results and to friction and restitution + /// combining functions. It is not used internally. + int material; + + /// Custom debug draw color. + uint32_t customColor; +} b2SurfaceMaterial; + +/// Use this to initialize your surface material +/// @ingroup shape +B2_API b2SurfaceMaterial b2DefaultSurfaceMaterial( void ); + /// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations. /// - chains are one-sided /// - chains have no mass and should be used on static bodies @@ -422,28 +451,19 @@ typedef struct b2ChainDef /// The point count, must be 4 or more. int count; - /// The friction coefficient, usually in the range [0,1]. - float friction; + /// Surface materials for each segment. These are cloned. + const b2SurfaceMaterial* materials; - /// The restitution (elasticity) usually in the range [0,1]. - float restitution; - - /// User material identifier. This is passed with query results and to friction and restitution - /// combining functions. It is not used internally. - int material; + /// The material count. Must be 1 or count. This allows you to provide one + /// material for all segments or a unique material per segment. + int materialCount; /// Contact filtering data. b2Filter filter; - /// Custom debug draw color. - uint32_t customColor; - /// Indicates a closed chain formed by connecting the first and last points bool isLoop; - /// Generate events when a sensor overlaps this chain - bool enableSensorEvents; - /// Used internally to detect a valid definition. DO NOT SET. int internalValue; } b2ChainDef; diff --git a/samples/data/ramp.svg b/samples/data/ramp.svg new file mode 100644 index 000000000..5529d242c --- /dev/null +++ b/samples/data/ramp.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + diff --git a/samples/sample.cpp b/samples/sample.cpp index 2c3e3fcd1..45f8c2042 100644 --- a/samples/sample.cpp +++ b/samples/sample.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2023 Erin Catto // SPDX-License-Identifier: MIT +#define _CRT_SECURE_NO_WARNINGS + #include "sample.h" #include "TaskScheduler.h" @@ -118,7 +120,7 @@ Sample::Sample( Settings& settings ) m_settings = &settings; - CreateWorld( ); + CreateWorld(); TestMathCpp(); } @@ -131,7 +133,7 @@ Sample::~Sample() delete[] m_tasks; } -void Sample::CreateWorld( ) +void Sample::CreateWorld() { if ( B2_IS_NON_NULL( m_worldId ) ) { @@ -503,6 +505,145 @@ void Sample::ShiftOrigin( b2Vec2 newOrigin ) // m_world->ShiftOrigin(newOrigin); } +// const char* path = +// "M 47.625004,185.20833 H 161.39585 l 29.10417,-2.64583 26.45834,-7.9375 26.45833,-13.22917 23.81251,-21.16666 h " +// "13.22916 v 44.97916 H 592.66669 V 0 h 21.16671 v 206.375 l -566.208398,-1e-5"; + +int Sample::ParsePath( const char* svgPath, b2Vec2 offset, b2Vec2* points, int capacity, float scale, bool reverseOrder ) +{ + int pointCount = 0; + b2Vec2 currentPoint = {}; + const char* ptr = svgPath; + char command = *ptr; + + while ( *ptr != '\0' ) + { + if ( isdigit( *ptr ) == 0 && *ptr != '-' ) + { + // note: command can be implicitly repeated + command = *ptr; + + if ( command == 'M' || command == 'L' || command == 'H' || command == 'V' || command == 'm' || command == 'l' || + command == 'h' || command == 'v' ) + { + ptr += 2; // Skip the command character and space + } + + if ( command == 'z' ) + { + break; + } + } + + assert( isdigit( *ptr ) != 0 || *ptr == '-' ); + + float x = 0.0f, y = 0.0f; + switch ( command ) + { + case 'M': + case 'L': + if ( sscanf( ptr, "%f,%f", &x, &y ) == 2 ) + { + currentPoint.x = x; + currentPoint.y = y; + } + else + { + assert( false ); + } + break; + case 'H': + if ( sscanf( ptr, "%f", &x ) == 1 ) + { + currentPoint.x = x; + } + else + { + assert( false ); + } + break; + case 'V': + if ( sscanf( ptr, "%f", &y ) == 1 ) + { + currentPoint.y = y; + } + else + { + assert( false ); + } + break; + case 'm': + case 'l': + if ( sscanf( ptr, "%f,%f", &x, &y ) == 2 ) + { + currentPoint.x += x; + currentPoint.y += y; + } + else + { + assert( false ); + } + break; + case 'h': + if ( sscanf( ptr, "%f", &x ) == 1 ) + { + currentPoint.x += x; + } + else + { + assert( false ); + } + break; + case 'v': + if ( sscanf( ptr, "%f", &y ) == 1 ) + { + currentPoint.y += y; + } + else + { + assert( false ); + } + break; + + default: + assert( false ); + break; + } + + points[pointCount] = { scale * ( currentPoint.x + offset.x ), -scale * ( currentPoint.y + offset.y ) }; + pointCount += 1; + if ( pointCount == capacity ) + { + break; + } + + // Move to the next space or end of string + while ( *ptr != '\0' && isspace( *ptr ) == 0 ) + { + ptr++; + } + + // Skip contiguous spaces + while ( isspace( *ptr ) ) + { + ptr++; + } + + ptr += 0; + } + + if ( pointCount == 0 ) + { + return 0; + } + + if ( reverseOrder ) + { + + } + return pointCount; +} + SampleEntry g_sampleEntries[MAX_SAMPLES] = {}; int g_sampleCount = 0; diff --git a/samples/sample.h b/samples/sample.h index 356b7ec22..259b88a04 100644 --- a/samples/sample.h +++ b/samples/sample.h @@ -60,6 +60,8 @@ class Sample void ResetProfile(); void ShiftOrigin( b2Vec2 newOrigin ); + static int ParsePath( const char* svgPath, b2Vec2 offset, b2Vec2* points, int capacity, float scale, bool reverseOrder ); + friend class DestructionListener; friend class BoundaryListener; friend class ContactListener; diff --git a/samples/sample_continuous.cpp b/samples/sample_continuous.cpp index 86387043a..3d1749e81 100644 --- a/samples/sample_continuous.cpp +++ b/samples/sample_continuous.cpp @@ -667,11 +667,15 @@ class GhostBumps : public Sample points[18] = b2Add( points[17], { -2.0f * hx, 0.0f } ); points[19] = b2Add( points[18], { -2.0f * hx, 0.0f } ); + b2SurfaceMaterial material = {}; + material.friction = m_friction; + b2ChainDef chainDef = b2DefaultChainDef(); chainDef.points = points; chainDef.count = 20; chainDef.isLoop = true; - chainDef.friction = m_friction; + chainDef.materials = &material; + chainDef.materialCount = 1; b2CreateChain( m_groundId, &chainDef ); } diff --git a/samples/sample_events.cpp b/samples/sample_events.cpp index 27bae39eb..789cdccde 100644 --- a/samples/sample_events.cpp +++ b/samples/sample_events.cpp @@ -92,11 +92,15 @@ class SensorFunnel : public Sample // } // printf("};\n"); + b2SurfaceMaterial material = {}; + material.friction = 0.2f; + b2ChainDef chainDef = b2DefaultChainDef(); chainDef.points = points; chainDef.count = count; chainDef.isLoop = true; - chainDef.friction = 0.2f; + chainDef.materials = &material; + chainDef.materialCount = 1; b2CreateChain( groundId, &chainDef ); float sign = 1.0f; diff --git a/samples/sample_shapes.cpp b/samples/sample_shapes.cpp index f9e7a65b6..6d4ffc6b8 100644 --- a/samples/sample_shapes.cpp +++ b/samples/sample_shapes.cpp @@ -112,13 +112,17 @@ class ChainShape : public Sample // } // printf("};\n"); + b2SurfaceMaterial material = {}; + material.friction = 0.2f; + material.customColor = b2_colorSteelBlue; + material.material = 42; + b2ChainDef chainDef = b2DefaultChainDef(); chainDef.points = points; chainDef.count = count; - chainDef.customColor = b2_colorSteelBlue; + chainDef.materials = &material; + chainDef.materialCount = 1; chainDef.isLoop = true; - chainDef.friction = 0.2f; - chainDef.material = 42; b2BodyDef bodyDef = b2DefaultBodyDef(); m_groundId = b2CreateBody( m_worldId, &bodyDef ); @@ -1090,7 +1094,7 @@ class ConveyorBelt : public Sample b2BodyDef bodyDef = b2DefaultBodyDef(); bodyDef.type = b2_dynamicBody; bodyDef.position = { -10.0f + 2.0f * i, 7.0f }; - b2BodyId bodyId = b2CreateBody(m_worldId, &bodyDef ); + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); b2CreatePolygonShape( bodyId, &shapeDef, &cube ); } @@ -1104,6 +1108,106 @@ class ConveyorBelt : public Sample static int sampleConveyorBelt = RegisterSample( "Shapes", "Conveyor Belt", ConveyorBelt::Create ); +class TangentSpeed : public Sample +{ +public: + explicit TangentSpeed( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 60.0f, -15.0f }; + g_camera.m_zoom = 38.0f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + //const char* path = "M 613.8334,185.20833 H 500.06255 L 470.95838,182.5625 444.50004,174.625 418.04171,161.39583 " + // "394.2292,140.22917 h " + // "-13.22916 v 44.97916 H 68.791712 V 0 h -21.16671 v 206.375 l 566.208398,-1e-5 z"; + + const char* path = "m 613.8334,185.20833 -42.33338,0 h -37.04166 l -34.39581,0 -29.10417,-2.64583 -26.45834,-7.9375 " + "-26.45833,-13.22917 -23.81251,-21.16666 h -13.22916 v 44.97916 H 68.791712 V 0 h -21.16671 v " + "206.375 l 566.208398,-1e-5 z"; + + b2Vec2 offset = { -47.375002f, 0.25f }; + + float scale = 0.2f; + b2Vec2 points[20] = {}; + int count = ParsePath( path, offset, points, 20, scale, true ); + + b2SurfaceMaterial materials[20] = {}; + for ( int i = 0; i < 20; ++i ) + { + materials[i].friction = 0.6f; + } + + materials[0].tangentSpeed = -10.0; + materials[0].customColor = b2_colorDarkBlue; + materials[1].tangentSpeed = -20.0; + materials[1].customColor = b2_colorDarkCyan; + materials[2].tangentSpeed = -30.0; + materials[2].customColor = b2_colorDarkGoldenRod; + materials[3].tangentSpeed = -40.0; + materials[3].customColor = b2_colorDarkGray; + materials[4].tangentSpeed = -50.0; + materials[4].customColor = b2_colorDarkGreen; + materials[5].tangentSpeed = -60.0; + materials[5].customColor = b2_colorDarkKhaki; + materials[6].tangentSpeed = -70.0; + materials[6].customColor = b2_colorDarkMagenta; + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = count; + chainDef.isLoop = true; + chainDef.materials = materials; + chainDef.materialCount = count; + + b2CreateChain( groundId, &chainDef ); + } + } + + b2BodyId DropBall() + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 110.0f, -30.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.rollingResistance = 0.3f; + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + return bodyId; + } + + void Step( Settings& settings ) override + { + if ( m_stepCount % 25 == 0 && m_count < m_totalCount && settings.pause == false) + { + DropBall(); + m_count += 1; + } + + Sample::Step( settings ); + + } + + static Sample* Create( Settings& settings ) + { + return new TangentSpeed( settings ); + } + + static constexpr int m_totalCount = 200; + int m_count = 0; +}; + +static int sampleTangentSpeed = RegisterSample( "Shapes", "Tangent Speed", TangentSpeed::Create ); + // This sample shows how to modify the geometry on an existing shape. This is only supported on // dynamic and kinematic shapes because static shapes don't look for new collisions. class ModifyGeometry : public Sample diff --git a/samples/settings.cpp b/samples/settings.cpp index 7eadcebd1..18b5ecc18 100644 --- a/samples/settings.cpp +++ b/samples/settings.cpp @@ -35,7 +35,7 @@ static bool ReadFile( char*& data, int& size, const char* filename ) data = (char*)malloc( size + 1 ); size_t count = fread( data, size, 1, file ); - B2_MAYBE_UNUSED( count ); + B2_UNUSED( count ); fclose( file ); data[size] = 0; diff --git a/samples/shader.cpp b/samples/shader.cpp index b17218a31..61fdcba17 100644 --- a/samples/shader.cpp +++ b/samples/shader.cpp @@ -153,7 +153,7 @@ static GLuint sCreateShaderFromFile( const char* filename, GLenum type ) char* source = static_cast( malloc( size + 1 ) ); size_t count = fread( source, size, 1, file ); - B2_MAYBE_UNUSED( count ); + B2_UNUSED( count ); fclose( file ); source[size] = 0; diff --git a/shared/benchmarks.c b/shared/benchmarks.c index 123afcb49..908e5b50e 100644 --- a/shared/benchmarks.c +++ b/shared/benchmarks.c @@ -356,11 +356,15 @@ void CreateSpinner( b2WorldId worldId ) p = b2RotateVector( q, p ); } + b2SurfaceMaterial material = {0}; + material.friction = 0.1f; + b2ChainDef chainDef = b2DefaultChainDef(); chainDef.points = points; chainDef.count = SPINNER_POINT_COUNT; chainDef.isLoop = true; - chainDef.friction = 0.1f; + chainDef.materials = &material; + chainDef.materialCount = 1; b2CreateChain( groundId, &chainDef ); } diff --git a/src/body.c b/src/body.c index 6d7cd297c..e53f87c56 100644 --- a/src/body.c +++ b/src/body.c @@ -325,7 +325,7 @@ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def ) bool b2IsBodyAwake( b2World* world, b2Body* body ) { - B2_MAYBE_UNUSED( world ); + B2_UNUSED( world ); return body->setIndex == b2_awakeSet; } @@ -425,7 +425,7 @@ void b2DestroyBody( b2BodyId bodyId ) if ( body->setIndex == b2_awakeSet ) { int result = b2BodyStateArray_RemoveSwap( &set->bodyStates, body->localIndex ); - B2_MAYBE_UNUSED( result ); + B2_UNUSED( result ); B2_ASSERT( result == movedIndex ); } else if ( set->setIndex >= b2_firstSleepingSet && set->bodySims.count == 0 ) diff --git a/src/broad_phase.c b/src/broad_phase.c index 5801ad10b..95e7a97fc 100644 --- a/src/broad_phase.c +++ b/src/broad_phase.c @@ -313,7 +313,7 @@ static void b2FindPairsTask( int startIndex, int endIndex, uint32_t threadIndex, { b2TracyCZoneNC( pair_task, "Pair", b2_colorMediumSlateBlue, true ); - B2_MAYBE_UNUSED( threadIndex ); + B2_UNUSED( threadIndex ); b2World* world = context; b2BroadPhase* bp = &world->broadPhase; diff --git a/src/contact.c b/src/contact.c index f596a8d66..01566ec30 100644 --- a/src/contact.c +++ b/src/contact.c @@ -69,70 +69,70 @@ static bool s_initialized = false; static b2Manifold b2CircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollideCircles( &shapeA->circle, xfA, &shapeB->circle, xfB ); } static b2Manifold b2CapsuleAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollideCapsuleAndCircle( &shapeA->capsule, xfA, &shapeB->circle, xfB ); } static b2Manifold b2CapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollideCapsules( &shapeA->capsule, xfA, &shapeB->capsule, xfB ); } static b2Manifold b2PolygonAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollidePolygonAndCircle( &shapeA->polygon, xfA, &shapeB->circle, xfB ); } static b2Manifold b2PolygonAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollidePolygonAndCapsule( &shapeA->polygon, xfA, &shapeB->capsule, xfB ); } static b2Manifold b2PolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollidePolygons( &shapeA->polygon, xfA, &shapeB->polygon, xfB ); } static b2Manifold b2SegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollideSegmentAndCircle( &shapeA->segment, xfA, &shapeB->circle, xfB ); } static b2Manifold b2SegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollideSegmentAndCapsule( &shapeA->segment, xfA, &shapeB->capsule, xfB ); } static b2Manifold b2SegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollideSegmentAndPolygon( &shapeA->segment, xfA, &shapeB->polygon, xfB ); } static b2Manifold b2ChainSegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache ) { - B2_MAYBE_UNUSED( cache ); + B2_UNUSED( cache ); return b2CollideChainSegmentAndCircle( &shapeA->chainSegment, xfA, &shapeB->circle, xfB ); } @@ -595,7 +595,7 @@ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, unmatchedCount += mp2->persisted ? 0 : 1; } - B2_MAYBE_UNUSED( unmatchedCount ); + B2_UNUSED( unmatchedCount ); #if 0 // todo I haven't found an improvement from this yet diff --git a/src/core.h b/src/core.h index 362f6832e..2b7bcc474 100644 --- a/src/core.h +++ b/src/core.h @@ -110,8 +110,7 @@ #define B2_ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) ) // Used to prevent the compiler from warning about unused variables -//#define B2_MAYBE_UNUSED( x ) ( (void)( x ) ) -#define B2_MAYBE_UNUSED( ... ) (void)( __VA_ARGS__ ) +#define B2_UNUSED( ... ) (void)( __VA_ARGS__ ) // Use to validate definitions. Do not take my cookie. #define B2_SECRET_COOKIE 1152023 diff --git a/src/island.c b/src/island.c index 13b299bd4..d39528ffb 100644 --- a/src/island.c +++ b/src/island.c @@ -843,9 +843,7 @@ void b2SplitIslandTask( int startIndex, int endIndex, uint32_t threadIndex, void { b2TracyCZoneNC( split, "Split Island", b2_colorOlive, true ); - B2_MAYBE_UNUSED( startIndex ); - B2_MAYBE_UNUSED( endIndex ); - B2_MAYBE_UNUSED( threadIndex ); + B2_UNUSED( startIndex, endIndex, threadIndex ); uint64_t ticks = b2GetTicks(); b2World* world = context; diff --git a/src/joint.c b/src/joint.c index 41cb00cb1..c7b321624 100644 --- a/src/joint.c +++ b/src/joint.c @@ -121,7 +121,7 @@ b2JointSim* b2GetJointSim( b2World* world, b2Joint* joint ) b2JointSim* b2GetJointSimCheckType( b2JointId jointId, b2JointType type ) { - B2_MAYBE_UNUSED( type ); + B2_UNUSED( type ); b2World* world = b2GetWorld( jointId.world0 ); B2_ASSERT( world->locked == false ); diff --git a/src/motor_joint.c b/src/motor_joint.c index 4dd5defab..d6610d62e 100644 --- a/src/motor_joint.c +++ b/src/motor_joint.c @@ -186,7 +186,7 @@ void b2WarmStartMotorJoint( b2JointSim* base, b2StepContext* context ) void b2SolveMotorJoint( b2JointSim* base, b2StepContext* context, bool useBias ) { - B2_MAYBE_UNUSED( useBias ); + B2_UNUSED( useBias ); B2_ASSERT( base->type == b2_motorJoint ); float mA = base->invMassA; diff --git a/src/sensor.c b/src/sensor.c index 98d7ebc43..8b66b6f6e 100644 --- a/src/sensor.c +++ b/src/sensor.c @@ -46,7 +46,7 @@ struct b2SensorQueryContext static bool b2SensorQueryCallback( int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); struct b2SensorQueryContext* queryContext = context; b2Shape* sensorShape = queryContext->sensorShape; diff --git a/src/shape.c b/src/shape.c index fb07822f2..302fd4300 100644 --- a/src/shape.c +++ b/src/shape.c @@ -143,7 +143,7 @@ static b2Shape* b2CreateShapeInternal( b2World* world, b2Body* body, b2Transform body->headShapeId = shapeId; body->shapeCount += 1; - if (def->isSensor) + if ( def->isSensor ) { shape->sensorIndex = world->sensors.count; b2Sensor sensor = { @@ -271,7 +271,7 @@ static void b2DestroyShapeInternal( b2World* world, b2Shape* shape, b2Body* body } } - if (shape->sensorIndex != B2_NULL_INDEX) + if ( shape->sensorIndex != B2_NULL_INDEX ) { b2Sensor* sensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex ); for ( int i = 0; i < sensor->overlaps2.count; ++i ) @@ -341,9 +341,8 @@ void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass ) b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) { B2_CHECK_DEF( def ); - B2_ASSERT( b2IsValidFloat( def->friction ) && def->friction >= 0.0f ); - B2_ASSERT( b2IsValidFloat( def->restitution ) && def->restitution >= 0.0f ); B2_ASSERT( def->count >= 4 ); + B2_ASSERT( def->materialCount == 1 || def->materialCount == def->count ); b2World* world = b2GetWorldLocked( bodyId.world0 ); if ( world == NULL ) @@ -371,29 +370,37 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) chainShape->bodyId = body->id; chainShape->nextChainId = body->headChainId; chainShape->generation += 1; - chainShape->friction = def->friction; - chainShape->restitution = def->restitution; - chainShape->material = def->material; + + int materialCount = def->materialCount; + chainShape->materialCount = materialCount; + chainShape->materials = b2Alloc( materialCount * sizeof( b2SurfaceMaterial ) ); + + for ( int i = 0; i < materialCount; ++i ) + { + const b2SurfaceMaterial* material = def->materials + i; + B2_ASSERT( b2IsValidFloat( material->friction ) && material->friction >= 0.0f ); + B2_ASSERT( b2IsValidFloat( material->restitution ) && material->restitution >= 0.0f ); + B2_ASSERT( b2IsValidFloat( material->rollingResistance ) && material->rollingResistance >= 0.0f ); + B2_ASSERT( b2IsValidFloat( material->tangentSpeed ) ); + + chainShape->materials[i] = *material; + } body->headChainId = chainId; b2ShapeDef shapeDef = b2DefaultShapeDef(); shapeDef.userData = def->userData; - shapeDef.friction = def->friction; - shapeDef.restitution = def->restitution; - shapeDef.material = def->material; shapeDef.filter = def->filter; - shapeDef.customColor = def->customColor; shapeDef.enableContactEvents = false; shapeDef.enableHitEvents = false; - int n = def->count; const b2Vec2* points = def->points; + int n = def->count; if ( def->isLoop ) { chainShape->count = n; - chainShape->shapeIndices = b2Alloc( n * sizeof( int ) ); + chainShape->shapeIndices = b2Alloc( chainShape->count * sizeof( int ) ); b2ChainSegment chainSegment; @@ -407,6 +414,15 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) chainSegment.chainId = chainId; prevIndex = i; + int materialIndex = materialCount == 1 ? 0 : i; + const b2SurfaceMaterial* material = def->materials + materialIndex; + shapeDef.friction = material->friction; + shapeDef.restitution = material->restitution; + shapeDef.rollingResistance = material->rollingResistance; + shapeDef.tangentSpeed = material->tangentSpeed; + shapeDef.customColor = material->customColor; + shapeDef.material = material->material; + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); chainShape->shapeIndices[i] = shape->id; } @@ -417,6 +433,16 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) chainSegment.segment.point2 = points[n - 1]; chainSegment.ghost2 = points[0]; chainSegment.chainId = chainId; + + int materialIndex = materialCount == 1 ? 0 : n - 2; + const b2SurfaceMaterial* material = def->materials + materialIndex; + shapeDef.friction = material->friction; + shapeDef.restitution = material->restitution; + shapeDef.rollingResistance = material->rollingResistance; + shapeDef.tangentSpeed = material->tangentSpeed; + shapeDef.customColor = material->customColor; + shapeDef.material = material->material; + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); chainShape->shapeIndices[n - 2] = shape->id; } @@ -427,6 +453,16 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) chainSegment.segment.point2 = points[0]; chainSegment.ghost2 = points[1]; chainSegment.chainId = chainId; + + int materialIndex = materialCount == 1 ? 0 : n - 1; + const b2SurfaceMaterial* material = def->materials + materialIndex; + shapeDef.friction = material->friction; + shapeDef.restitution = material->restitution; + shapeDef.rollingResistance = material->rollingResistance; + shapeDef.tangentSpeed = material->tangentSpeed; + shapeDef.customColor = material->customColor; + shapeDef.material = material->material; + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); chainShape->shapeIndices[n - 1] = shape->id; } @@ -434,7 +470,7 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) else { chainShape->count = n - 3; - chainShape->shapeIndices = b2Alloc( n * sizeof( int ) ); + chainShape->shapeIndices = b2Alloc( chainShape->count * sizeof( int ) ); b2ChainSegment chainSegment; @@ -446,6 +482,16 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) chainSegment.ghost2 = points[i + 3]; chainSegment.chainId = chainId; + // Material is associated with leading point of solid segment + int materialIndex = materialCount == 1 ? 0 : i + 1; + const b2SurfaceMaterial* material = def->materials + materialIndex; + shapeDef.friction = material->friction; + shapeDef.restitution = material->restitution; + shapeDef.rollingResistance = material->rollingResistance; + shapeDef.tangentSpeed = material->tangentSpeed; + shapeDef.customColor = material->customColor; + shapeDef.material = material->material; + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); chainShape->shapeIndices[i] = shape->id; } @@ -500,6 +546,8 @@ void b2DestroyChain( b2ChainId chainId ) b2Free( chain->shapeIndices, chain->count * sizeof( int ) ); chain->shapeIndices = NULL; + b2Free( chain->materials, chain->materialCount * sizeof( b2SurfaceMaterial ) ); + // Return chain to free list. b2FreeId( &world->chainIdPool, chain->id ); chain->id = B2_NULL_INDEX; @@ -1363,7 +1411,7 @@ b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId ) void b2Chain_SetFriction( b2ChainId chainId, float friction ) { - B2_ASSERT( b2IsValidFloat( friction ) ); + B2_ASSERT( b2IsValidFloat( friction ) && friction >= 0.0f ); b2World* world = b2GetWorldLocked( chainId.world0 ); if ( world == NULL ) @@ -1372,7 +1420,12 @@ void b2Chain_SetFriction( b2ChainId chainId, float friction ) } b2ChainShape* chainShape = b2GetChainShape( world, chainId ); - chainShape->friction = friction; + + int materialCount = chainShape->materialCount; + for ( int i = 0; i < materialCount; ++i ) + { + chainShape->materials[i].friction = friction; + } int count = chainShape->count; @@ -1388,7 +1441,7 @@ float b2Chain_GetFriction( b2ChainId chainId ) { b2World* world = b2GetWorld( chainId.world0 ); b2ChainShape* chainShape = b2GetChainShape( world, chainId ); - return chainShape->friction; + return chainShape->materials[0].friction; } void b2Chain_SetRestitution( b2ChainId chainId, float restitution ) @@ -1402,7 +1455,12 @@ void b2Chain_SetRestitution( b2ChainId chainId, float restitution ) } b2ChainShape* chainShape = b2GetChainShape( world, chainId ); - chainShape->restitution = restitution; + + int materialCount = chainShape->materialCount; + for ( int i = 0; i < materialCount; ++i ) + { + chainShape->materials[i].restitution = restitution; + } int count = chainShape->count; @@ -1418,7 +1476,7 @@ float b2Chain_GetRestitution( b2ChainId chainId ) { b2World* world = b2GetWorld( chainId.world0 ); b2ChainShape* chainShape = b2GetChainShape( world, chainId ); - return chainShape->restitution; + return chainShape->materials[0].restitution; } void b2Chain_SetMaterial( b2ChainId chainId, int material ) @@ -1430,7 +1488,11 @@ void b2Chain_SetMaterial( b2ChainId chainId, int material ) } b2ChainShape* chainShape = b2GetChainShape( world, chainId ); - chainShape->material = material; + int materialCount = chainShape->materialCount; + for ( int i = 0; i < materialCount; ++i ) + { + chainShape->materials[i].material = material; + } int count = chainShape->count; @@ -1446,7 +1508,7 @@ int b2Chain_GetMaterial( b2ChainId chainId ) { b2World* world = b2GetWorld( chainId.world0 ); b2ChainShape* chainShape = b2GetChainShape( world, chainId ); - return chainShape->material; + return chainShape->materials[0].material; } int b2Shape_GetContactCapacity( b2ShapeId shapeId ) @@ -1516,7 +1578,7 @@ int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int c return index; } -int b2Shape_GetSensorCapacity(b2ShapeId shapeId) +int b2Shape_GetSensorCapacity( b2ShapeId shapeId ) { b2World* world = b2GetWorldLocked( shapeId.world0 ); if ( world == NULL ) @@ -1576,7 +1638,7 @@ b2AABB b2Shape_GetAABB( b2ShapeId shapeId ) return shape->aabb; } -b2MassData b2Shape_GetMassData(b2ShapeId shapeId) +b2MassData b2Shape_GetMassData( b2ShapeId shapeId ) { b2World* world = b2GetWorld( shapeId.world0 ); if ( world == NULL ) diff --git a/src/shape.h b/src/shape.h index 6437a2b26..1afefb0f3 100644 --- a/src/shape.h +++ b/src/shape.h @@ -56,10 +56,9 @@ typedef struct b2ChainShape int bodyId; int nextChainId; int count; + int materialCount; int* shapeIndices; - float friction; - float restitution; - int material; + b2SurfaceMaterial* materials; uint16_t generation; } b2ChainShape; diff --git a/src/solver.c b/src/solver.c index b12851249..587f1cc98 100644 --- a/src/solver.c +++ b/src/solver.c @@ -207,7 +207,7 @@ struct b2ContinuousContext // This is called from b2DynamicTree_Query for continuous collision static bool b2ContinuousQueryCallback( int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); struct b2ContinuousContext* continuousContext = context; b2Shape* fastShape = continuousContext->fastShape; @@ -916,11 +916,9 @@ static void b2ExecuteMainStage( b2SolverStage* stage, b2StepContext* context, ui } // This should not use the thread index because thread 0 can be called twice by enkiTS. -static void b2SolverTask( int startIndex, int endIndex, uint32_t threadIndexDontUse, void* taskContext ) +static void b2SolverTask( int startIndex, int endIndex, uint32_t threadIndexIgnore, void* taskContext ) { - B2_MAYBE_UNUSED( startIndex ); - B2_MAYBE_UNUSED( endIndex ); - B2_MAYBE_UNUSED( threadIndexDontUse ); + B2_UNUSED( startIndex, endIndex, threadIndexIgnore ); b2WorkerContext* workerContext = taskContext; int workerIndex = workerContext->workerIndex; @@ -1146,7 +1144,7 @@ static void b2SolverTask( int startIndex, int endIndex, uint32_t threadIndexDont static void b2BulletBodyTask( int startIndex, int endIndex, uint32_t threadIndex, void* taskContext ) { - B2_MAYBE_UNUSED( threadIndex ); + B2_UNUSED( threadIndex ); b2TracyCZoneNC( bullet_body_task, "Bullet", b2_colorLightSkyBlue, true ); diff --git a/src/table.c b/src/table.c index 00e238066..918b2db8e 100644 --- a/src/table.c +++ b/src/table.c @@ -107,7 +107,7 @@ static void b2AddKeyHaveCapacity( b2HashSet* set, uint64_t key, uint32_t hash ) static void b2GrowTable( b2HashSet* set ) { uint32_t oldCount = set->count; - B2_MAYBE_UNUSED( oldCount ); + B2_UNUSED( oldCount ); uint32_t oldCapacity = set->capacity; b2SetItem* oldItems = set->items; diff --git a/src/types.c b/src/types.c index 9b92defcf..8be9402ed 100644 --- a/src/types.c +++ b/src/types.c @@ -63,10 +63,18 @@ b2ShapeDef b2DefaultShapeDef( void ) return def; } +b2SurfaceMaterial b2DefaultSurfaceMaterial( void ) +{ + b2SurfaceMaterial material = { + .friction = 0.6f, + }; + + return material; +} + b2ChainDef b2DefaultChainDef( void ) { b2ChainDef def = { 0 }; - def.friction = 0.6f; def.filter = b2DefaultFilter(); def.internalValue = B2_SECRET_COOKIE; return def; @@ -74,79 +82,51 @@ b2ChainDef b2DefaultChainDef( void ) static void b2EmptyDrawPolygon( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( vertices ); - B2_MAYBE_UNUSED( vertexCount ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( vertices, vertexCount, color, context ); } -static void b2EmptyDrawSolidPolygon( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, b2HexColor color, - void* context ) +static void b2EmptyDrawSolidPolygon( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, + b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( transform ); - B2_MAYBE_UNUSED( vertices ); - B2_MAYBE_UNUSED( vertexCount ); - B2_MAYBE_UNUSED( radius ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( transform, vertices, vertexCount, radius, color, context ); } static void b2EmptyDrawCircle( b2Vec2 center, float radius, b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( center ); - B2_MAYBE_UNUSED( radius ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( center, radius, color, context ); } static void b2EmptyDrawSolidCircle( b2Transform transform, float radius, b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( transform ); - B2_MAYBE_UNUSED( radius ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( transform, radius, color, context ); } static void b2EmptyDrawSolidCapsule( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( p1 ); - B2_MAYBE_UNUSED( p2 ); - B2_MAYBE_UNUSED( radius ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( p1, p2, radius, color, context ); } static void b2EmptyDrawSegment( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( p1 ); - B2_MAYBE_UNUSED( p2 ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( p1, p2, color, context ); } static void b2EmptyDrawTransform( b2Transform transform, void* context ) { - B2_MAYBE_UNUSED( transform ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( transform, context ); } static void b2EmptyDrawPoint( b2Vec2 p, float size, b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( p ); - B2_MAYBE_UNUSED( size ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( p, size, color, context ); } static void b2EmptyDrawString( b2Vec2 p, const char* s, b2HexColor color, void* context ) { - B2_MAYBE_UNUSED( p ); - B2_MAYBE_UNUSED( s ); - B2_MAYBE_UNUSED( color ); - B2_MAYBE_UNUSED( context ); + B2_UNUSED( p, s, color, context ); } -b2DebugDraw b2DefaultDebugDraw(void) +b2DebugDraw b2DefaultDebugDraw( void ) { b2DebugDraw draw = { 0 }; diff --git a/src/world.c b/src/world.c index 3e7eeb36c..fcc81bbb2 100644 --- a/src/world.c +++ b/src/world.c @@ -75,29 +75,25 @@ b2World* b2GetWorldLocked( int index ) static void* b2DefaultAddTaskFcn( b2TaskCallback* task, int count, int minRange, void* taskContext, void* userContext ) { - B2_MAYBE_UNUSED( minRange ); - B2_MAYBE_UNUSED( userContext ); + B2_UNUSED( minRange, userContext ); task( 0, count, 0, taskContext ); return NULL; } static void b2DefaultFinishTaskFcn( void* userTask, void* userContext ) { - B2_MAYBE_UNUSED( userTask ); - B2_MAYBE_UNUSED( userContext ); + B2_UNUSED( userTask, userContext ); } static float b2DefaultFrictionCallback( float frictionA, int materialA, float frictionB, int materialB ) { - B2_MAYBE_UNUSED( materialA ); - B2_MAYBE_UNUSED( materialB ); + B2_UNUSED( materialA, materialB ); return sqrtf( frictionA * frictionB ); } static float b2DefaultRestitutionCallback( float restitutionA, int materialA, float restitutionB, int materialB ) { - B2_MAYBE_UNUSED( materialA ); - B2_MAYBE_UNUSED( materialB ); + B2_UNUSED( materialA, materialB ); return b2MaxFloat( restitutionA, restitutionB ); } @@ -201,7 +197,7 @@ b2WorldId b2CreateWorld( const b2WorldDef* def ) world->jointHertz = def->jointHertz; world->jointDampingRatio = def->jointDampingRatio; - if (def->frictionCallback == NULL) + if ( def->frictionCallback == NULL ) { world->frictionCallback = b2DefaultFrictionCallback; } @@ -436,9 +432,9 @@ static void b2CollideTask( int startIndex, int endIndex, uint32_t threadIndex, v static void b2UpdateTreesTask( int startIndex, int endIndex, uint32_t threadIndex, void* context ) { - B2_MAYBE_UNUSED( startIndex ); - B2_MAYBE_UNUSED( endIndex ); - B2_MAYBE_UNUSED( threadIndex ); + B2_UNUSED( startIndex ); + B2_UNUSED( endIndex ); + B2_UNUSED( threadIndex ); b2TracyCZoneNC( tree_task, "Rebuild BVH", b2_colorFireBrick, true ); @@ -863,7 +859,7 @@ struct DrawContext static bool DrawQueryCallback( int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); struct DrawContext* drawContext = context; b2World* world = drawContext->world; @@ -993,7 +989,7 @@ static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw ) b2Body* body = b2BodyArray_Get( &world->bodies, bodyId ); - if (draw->drawBodyNames && body->name[0] != 0) + if ( draw->drawBodyNames && body->name[0] != 0 ) { b2Vec2 offset = { 0.1f, 0.1f }; b2BodySim* bodySim = b2GetBodySim( world, body ); @@ -1299,7 +1295,7 @@ void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ) continue; } - if ( body->name[0] == 0) + if ( body->name[0] == 0 ) { continue; } @@ -1849,15 +1845,15 @@ void* b2World_GetUserData( b2WorldId worldId ) return world->userData; } -void b2World_SetFrictionCallback(b2WorldId worldId, b2FrictionCallback* callback) +void b2World_SetFrictionCallback( b2WorldId worldId, b2FrictionCallback* callback ) { b2World* world = b2GetWorldFromId( worldId ); - if (world->locked) + if ( world->locked ) { return; } - if (callback != NULL) + if ( callback != NULL ) { world->frictionCallback = callback; } @@ -1870,12 +1866,12 @@ void b2World_SetFrictionCallback(b2WorldId worldId, b2FrictionCallback* callback void b2World_SetRestitutionCallback( b2WorldId worldId, b2RestitutionCallback* callback ) { b2World* world = b2GetWorldFromId( worldId ); - if (world->locked) + if ( world->locked ) { return; } - if (callback != NULL) + if ( callback != NULL ) { world->restitutionCallback = callback; } @@ -1996,7 +1992,7 @@ typedef struct WorldQueryContext static bool TreeQueryCallback( int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); WorldQueryContext* worldContext = context; b2World* world = worldContext->world; @@ -2055,7 +2051,7 @@ typedef struct WorldOverlapContext static bool TreeOverlapCallback( int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); WorldOverlapContext* worldContext = context; b2World* world = worldContext->world; @@ -2207,7 +2203,7 @@ typedef struct WorldRayCastContext static float RayCastCallback( const b2RayCastInput* input, int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); WorldRayCastContext* worldContext = context; b2World* world = worldContext->world; @@ -2328,7 +2324,7 @@ b2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 tra static float ShapeCastCallback( const b2ShapeCastInput* input, int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); WorldRayCastContext* worldContext = context; b2World* world = worldContext->world; @@ -2618,7 +2614,7 @@ struct ExplosionContext static bool ExplosionCallback( int proxyId, int shapeId, void* context ) { - B2_MAYBE_UNUSED( proxyId ); + B2_UNUSED( proxyId ); struct ExplosionContext* explosionContext = context; b2World* world = explosionContext->world; From ef2847ee693e6ff47a9d1889928d3be67cf1e588 Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sun, 26 Jan 2025 17:24:08 -0800 Subject: [PATCH 6/9] clang fixes and fix memory leak --- src/core.h | 2 +- src/shape.c | 14 ++++++++++---- src/shape.h | 2 ++ src/world.c | 3 ++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core.h b/src/core.h index 2b7bcc474..cb7a5b4d8 100644 --- a/src/core.h +++ b/src/core.h @@ -110,7 +110,7 @@ #define B2_ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) ) // Used to prevent the compiler from warning about unused variables -#define B2_UNUSED( ... ) (void)( __VA_ARGS__ ) +#define B2_UNUSED( ... ) (void)sizeof( ( __VA_ARGS__, 0 ) ) // Use to validate definitions. Do not take my cookie. #define B2_SECRET_COOKIE 1152023 diff --git a/src/shape.c b/src/shape.c index 302fd4300..a3bad3f8a 100644 --- a/src/shape.c +++ b/src/shape.c @@ -501,6 +501,15 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) return id; } +void b2FreeChainData(b2ChainShape* chain) +{ + b2Free( chain->shapeIndices, chain->count * sizeof( int ) ); + chain->shapeIndices = NULL; + + b2Free( chain->materials, chain->materialCount * sizeof( b2SurfaceMaterial ) ); + chain->materials = NULL; +} + void b2DestroyChain( b2ChainId chainId ) { b2World* world = b2GetWorldLocked( chainId.world0 ); @@ -543,10 +552,7 @@ void b2DestroyChain( b2ChainId chainId ) b2DestroyShapeInternal( world, shape, body, wakeBodies ); } - b2Free( chain->shapeIndices, chain->count * sizeof( int ) ); - chain->shapeIndices = NULL; - - b2Free( chain->materials, chain->materialCount * sizeof( b2SurfaceMaterial ) ); + b2FreeChainData( chain ); // Return chain to free list. b2FreeId( &world->chainIdPool, chain->id ); diff --git a/src/shape.h b/src/shape.h index 1afefb0f3..6d986f1dc 100644 --- a/src/shape.h +++ b/src/shape.h @@ -83,6 +83,8 @@ typedef struct void b2CreateShapeProxy( b2Shape* shape, b2BroadPhase* bp, b2BodyType type, b2Transform transform, bool forcePairCreation ); void b2DestroyShapeProxy( b2Shape* shape, b2BroadPhase* bp ); +void b2FreeChainData( b2ChainShape* chain ); + b2MassData b2ComputeShapeMass( const b2Shape* shape ); b2ShapeExtent b2ComputeShapeExtent( const b2Shape* shape, b2Vec2 localCenter ); b2AABB b2ComputeShapeAABB( const b2Shape* shape, b2Transform transform ); diff --git a/src/world.c b/src/world.c index fcc81bbb2..9bfd72801 100644 --- a/src/world.c +++ b/src/world.c @@ -296,11 +296,12 @@ void b2DestroyWorld( b2WorldId worldId ) b2ChainShape* chain = world->chainShapes.data + i; if ( chain->id != B2_NULL_INDEX ) { - b2Free( chain->shapeIndices, chain->count * sizeof( int ) ); + b2FreeChainData( chain ); } else { B2_ASSERT( chain->shapeIndices == NULL ); + B2_ASSERT( chain->materials == NULL ); } } From 32285cfadebaac2caa1be5b64d74067455ffd87c Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sun, 26 Jan 2025 17:37:38 -0800 Subject: [PATCH 7/9] CI fix --- src/broad_phase.c | 4 ++-- src/constraint_graph.c | 2 +- src/dynamic_tree.c | 4 ++-- src/id_pool.c | 4 ++-- src/island.c | 4 ++-- src/world.c | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/broad_phase.c b/src/broad_phase.c index 95e7a97fc..45529ccf9 100644 --- a/src/broad_phase.c +++ b/src/broad_phase.c @@ -513,10 +513,10 @@ void b2ValidateNoEnlarged( const b2BroadPhase* bp ) #if B2_VALIDATE == 1 for ( int j = 0; j < b2_bodyTypeCount; ++j ) { - const b2DynamicTree* tree = bp->trees + j; + const b2DynamicTree* tree = bp->trees + j;f b2DynamicTree_ValidateNoEnlarged( tree ); } #else - B2_MAYBE_UNUSED( bp ); + B2_UNUSED( bp ); #endif } diff --git a/src/constraint_graph.c b/src/constraint_graph.c index 950ee6542..c29066b85 100644 --- a/src/constraint_graph.c +++ b/src/constraint_graph.c @@ -260,7 +260,7 @@ static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyId } } #else - B2_MAYBE_UNUSED( graph, bodyIdA, bodyIdB ); + B2_UNUSED( graph, bodyIdA, bodyIdB ); #endif return B2_OVERFLOW_INDEX; diff --git a/src/dynamic_tree.c b/src/dynamic_tree.c index 7bfa2f066..22e884153 100644 --- a/src/dynamic_tree.c +++ b/src/dynamic_tree.c @@ -1025,7 +1025,7 @@ void b2DynamicTree_Validate( const b2DynamicTree* tree ) B2_ASSERT( tree->nodeCount + freeCount == tree->nodeCapacity ); #else - B2_MAYBE_UNUSED( tree ); + B2_UNUSED( tree ); #endif } @@ -1043,7 +1043,7 @@ void b2DynamicTree_ValidateNoEnlarged(const b2DynamicTree* tree) } } #else - B2_MAYBE_UNUSED( tree ); + B2_UNUSED( tree ); #endif } diff --git a/src/id_pool.c b/src/id_pool.c index 11b542d7b..bca24faff 100644 --- a/src/id_pool.c +++ b/src/id_pool.c @@ -64,8 +64,8 @@ void b2ValidateFreeId( b2IdPool* pool, int id ) void b2ValidateFreeId( b2IdPool* pool, int id ) { - B2_MAYBE_UNUSED( pool ); - B2_MAYBE_UNUSED( id ); + B2_UNUSED( pool ); + B2_UNUSED( id ); } #endif diff --git a/src/island.c b/src/island.c index d39528ffb..3a3a87610 100644 --- a/src/island.c +++ b/src/island.c @@ -964,7 +964,7 @@ void b2ValidateIsland( b2World* world, int islandId ) void b2ValidateIsland( b2World* world, int islandId ) { - B2_MAYBE_UNUSED( world ); - B2_MAYBE_UNUSED( islandId ); + B2_UNUSED( world ); + B2_UNUSED( islandId ); } #endif diff --git a/src/world.c b/src/world.c index 9bfd72801..7d8ad1e93 100644 --- a/src/world.c +++ b/src/world.c @@ -3230,17 +3230,17 @@ void b2ValidateContacts( b2World* world ) void b2ValidateConnectivity( b2World* world ) { - B2_MAYBE_UNUSED( world ); + B2_UNUSED( world ); } void b2ValidateSolverSets( b2World* world ) { - B2_MAYBE_UNUSED( world ); + B2_UNUSED( world ); } void b2ValidateContacts( b2World* world ) { - B2_MAYBE_UNUSED( world ); + B2_UNUSED( world ); } #endif From 313b95e0e2163d6823389d6bc16791f54d36818b Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sun, 26 Jan 2025 17:44:49 -0800 Subject: [PATCH 8/9] CI fix --- samples/sample.cpp | 7 +++---- src/broad_phase.c | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/samples/sample.cpp b/samples/sample.cpp index 45f8c2042..fe3c14108 100644 --- a/samples/sample.cpp +++ b/samples/sample.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include class SampleTask : public enki::ITaskSet { @@ -505,10 +505,9 @@ void Sample::ShiftOrigin( b2Vec2 newOrigin ) // m_world->ShiftOrigin(newOrigin); } -// const char* path = +// Parse an SVG path element with only straight lines. Example: // "M 47.625004,185.20833 H 161.39585 l 29.10417,-2.64583 26.45834,-7.9375 26.45833,-13.22917 23.81251,-21.16666 h " -// "13.22916 v 44.97916 H 592.66669 V 0 h 21.16671 v 206.375 l -566.208398,-1e-5"; - +// "13.22916 v 44.97916 H 592.66669 V 0 h 21.16671 v 206.375 l -566.208398,-1e-5 z" int Sample::ParsePath( const char* svgPath, b2Vec2 offset, b2Vec2* points, int capacity, float scale, bool reverseOrder ) { int pointCount = 0; diff --git a/src/broad_phase.c b/src/broad_phase.c index 45529ccf9..ebfff392e 100644 --- a/src/broad_phase.c +++ b/src/broad_phase.c @@ -513,7 +513,7 @@ void b2ValidateNoEnlarged( const b2BroadPhase* bp ) #if B2_VALIDATE == 1 for ( int j = 0; j < b2_bodyTypeCount; ++j ) { - const b2DynamicTree* tree = bp->trees + j;f + const b2DynamicTree* tree = bp->trees + j; b2DynamicTree_ValidateNoEnlarged( tree ); } #else From 9a0b730829f89c2b678e7b1f7462b5a06bd3d8d3 Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sun, 26 Jan 2025 18:07:41 -0800 Subject: [PATCH 9/9] benchmark run --- benchmark/amd7950x_avx2/joint_grid.csv | 16 ++++++++-------- benchmark/amd7950x_avx2/large_pyramid.csv | 16 ++++++++-------- benchmark/amd7950x_avx2/many_pyramids.csv | 16 ++++++++-------- benchmark/amd7950x_avx2/rain.csv | 16 ++++++++-------- benchmark/amd7950x_avx2/smash.csv | 16 ++++++++-------- benchmark/amd7950x_avx2/spinner.csv | 16 ++++++++-------- benchmark/amd7950x_avx2/tumbler.csv | 16 ++++++++-------- 7 files changed, 56 insertions(+), 56 deletions(-) diff --git a/benchmark/amd7950x_avx2/joint_grid.csv b/benchmark/amd7950x_avx2/joint_grid.csv index 113289ac2..a052e4730 100644 --- a/benchmark/amd7950x_avx2/joint_grid.csv +++ b/benchmark/amd7950x_avx2/joint_grid.csv @@ -1,9 +1,9 @@ threads,ms -1,2565.3 -2,1533.85 -3,1053.01 -4,865.702 -5,748.989 -6,658.623 -7,613.633 -8,578.973 +1,2528.51 +2,1460.58 +3,1004.38 +4,822.484 +5,712.691 +6,638.201 +7,583.016 +8,540.158 diff --git a/benchmark/amd7950x_avx2/large_pyramid.csv b/benchmark/amd7950x_avx2/large_pyramid.csv index 516f542c5..23f526fe1 100644 --- a/benchmark/amd7950x_avx2/large_pyramid.csv +++ b/benchmark/amd7950x_avx2/large_pyramid.csv @@ -1,9 +1,9 @@ threads,ms -1,1584.03 -2,841.862 -3,598.747 -4,471.697 -5,396.306 -6,347.983 -7,309.086 -8,308.832 +1,1579.54 +2,849.264 +3,591.984 +4,466.064 +5,393.207 +6,347.593 +7,305.863 +8,305.205 diff --git a/benchmark/amd7950x_avx2/many_pyramids.csv b/benchmark/amd7950x_avx2/many_pyramids.csv index 6a0f4358b..82f73dfbf 100644 --- a/benchmark/amd7950x_avx2/many_pyramids.csv +++ b/benchmark/amd7950x_avx2/many_pyramids.csv @@ -1,9 +1,9 @@ threads,ms -1,2663.34 -2,1407.85 -3,934.506 -4,725.271 -5,590.288 -6,502.513 -7,422.693 -8,395.369 +1,2592.03 +2,1412.01 +3,953.71 +4,731.394 +5,601.527 +6,511.144 +7,441.201 +8,393.566 diff --git a/benchmark/amd7950x_avx2/rain.csv b/benchmark/amd7950x_avx2/rain.csv index 849ad421d..a44f60e83 100644 --- a/benchmark/amd7950x_avx2/rain.csv +++ b/benchmark/amd7950x_avx2/rain.csv @@ -1,9 +1,9 @@ threads,ms -1,6526.9 -2,4017.74 -3,3052.28 -4,2493.5 -5,2149.12 -6,1911.45 -7,1735.97 -8,1625.71 +1,6462.19 +2,3806.63 +3,2907 +4,2413.23 +5,2069.53 +6,1852.14 +7,1671.47 +8,1547.16 diff --git a/benchmark/amd7950x_avx2/smash.csv b/benchmark/amd7950x_avx2/smash.csv index 3dd380467..a91426f99 100644 --- a/benchmark/amd7950x_avx2/smash.csv +++ b/benchmark/amd7950x_avx2/smash.csv @@ -1,9 +1,9 @@ threads,ms -1,1562.06 -2,1020.55 -3,781.193 -4,661.318 -5,580.115 -6,530.502 -7,489.672 -8,467.998 +1,1533.42 +2,981.749 +3,763.27 +4,642.928 +5,574.053 +6,527.394 +7,492.883 +8,468.971 diff --git a/benchmark/amd7950x_avx2/spinner.csv b/benchmark/amd7950x_avx2/spinner.csv index 091eb9040..921025140 100644 --- a/benchmark/amd7950x_avx2/spinner.csv +++ b/benchmark/amd7950x_avx2/spinner.csv @@ -1,9 +1,9 @@ threads,ms -1,4106.89 -2,2635.21 -3,1986.32 -4,1614.21 -5,1420.63 -6,1272.06 -7,1157.16 -8,1091.25 +1,4126.1 +2,2491.09 +3,1918.78 +4,1543.11 +5,1360.84 +6,1216.53 +7,1110.95 +8,1043.96 diff --git a/benchmark/amd7950x_avx2/tumbler.csv b/benchmark/amd7950x_avx2/tumbler.csv index 1f68a4ca1..f0b75fb86 100644 --- a/benchmark/amd7950x_avx2/tumbler.csv +++ b/benchmark/amd7950x_avx2/tumbler.csv @@ -1,9 +1,9 @@ threads,ms -1,1613.69 -2,1061.88 -3,810.738 -4,670.874 -5,578.959 -6,519.473 -7,479.842 -8,437.851 +1,1624.23 +2,1028.59 +3,796.538 +4,651.347 +5,563.763 +6,510.42 +7,468.899 +8,445.968