From 92dc2143211aeb2021842cdb177af78d68b883ac Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Thu, 19 Dec 2024 18:33:33 -0800 Subject: [PATCH] Sensor Overlaps (#858) Improved naming and comments. Added b2Body_EnableSensorEvents, b2Body_EnableContactEvents, b2Shape_GetSensorCapacity, b2Shape_GetSensorOverlaps --- CMakeLists.txt | 2 + include/box2d/box2d.h | 131 ++++++++++++++++++++-------------- include/box2d/collision.h | 78 ++++++++++---------- include/box2d/types.h | 34 +++++---- samples/sample_collision.cpp | 56 +++++++-------- samples/sample_continuous.cpp | 73 ++++++++++++++++++- samples/sample_events.cpp | 104 +++++++++++++++++++-------- src/body.c | 79 +++++++++++++------- src/body.h | 17 +++-- src/constants.h | 12 ++-- src/constraint_graph.c | 36 +++++----- src/constraint_graph.h | 4 +- src/contact.c | 40 +++++------ src/contact.h | 2 +- src/contact_solver.c | 14 ++-- src/distance.c | 60 ++++++++-------- src/distance_joint.c | 10 +-- src/dynamic_tree.c | 2 +- src/geometry.c | 45 ++++++------ src/hull.c | 10 +-- src/island.h | 4 +- src/joint.c | 20 +++--- src/manifold.c | 36 +++++----- src/shape.c | 112 +++++++++++++++++++++++------ src/shape.h | 2 +- src/solver.c | 61 ++++++++-------- src/solver_set.c | 10 +-- src/types.c | 4 +- src/world.c | 83 ++++++++++----------- src/world.h | 9 +-- test/test_determinism.c | 5 +- test/test_distance.c | 6 +- 32 files changed, 701 insertions(+), 460 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 27b435bc3..6b6fc0ca8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,9 +144,11 @@ if(PROJECT_IS_TOP_LEVEL) set_property(TARGET samples PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") endif() + if(APPLE) set_target_properties(samples PROPERTIES XCODE_GENERATE_SCHEME TRUE XCODE_SCHEME_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") + endif() endif() if(BOX2D_BENCHMARKS) diff --git a/include/box2d/box2d.h b/include/box2d/box2d.h index b9c811f78..b20fb98ec 100644 --- a/include/box2d/box2d.h +++ b/include/box2d/box2d.h @@ -34,8 +34,8 @@ B2_API bool b2World_IsValid( b2WorldId id ); /// Simulate a world for one time step. This performs collision detection, integration, and constraint solution. /// @param worldId The world to simulate -/// @param timeStep The amount of time to simulate, this should be a fixed number. Typically 1/60. -/// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Typically 4. +/// @param timeStep The amount of time to simulate, this should be a fixed number. Usually 1/60. +/// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Usually 4. B2_API void b2World_Step( b2WorldId worldId, float timeStep, int subStepCount ); /// Call this to draw shapes and other debug draw data @@ -121,19 +121,19 @@ B2_API void b2World_EnableContinuous( b2WorldId worldId, bool flag ); B2_API bool b2World_IsContinuousEnabled( b2WorldId worldId ); /// Adjust the restitution threshold. It is recommended not to make this value very small -/// because it will prevent bodies from sleeping. Typically in meters per second. +/// because it will prevent bodies from sleeping. Usually in meters per second. /// @see b2WorldDef B2_API void b2World_SetRestitutionThreshold( b2WorldId worldId, float value ); -/// Get the the restitution speed threshold. Typically in meters per second. +/// Get the the restitution speed threshold. Usually in meters per second. B2_API float b2World_GetRestitutionThreshold( b2WorldId worldId ); -/// Adjust the hit event threshold. This controls the collision velocity needed to generate a b2ContactHitEvent. -/// Typically in meters per second. +/// Adjust the hit event threshold. This controls the collision speed needed to generate a b2ContactHitEvent. +/// Usually in meters per second. /// @see b2WorldDef::hitEventThreshold B2_API void b2World_SetHitEventThreshold( b2WorldId worldId, float value ); -/// Get the the hit event speed threshold. Typically in meters per second. +/// Get the the hit event speed threshold. Usually in meters per second. B2_API float b2World_GetHitEventThreshold( b2WorldId worldId ); /// Register the custom filter callback. This is optional. @@ -143,7 +143,7 @@ B2_API void b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFc B2_API void b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context ); /// Set the gravity vector for the entire world. Box2D has no concept of an up direction and this -/// is left as a decision for the application. Typically in m/s^2. +/// is left as a decision for the application. Usually in m/s^2. /// @see b2WorldDef B2_API void b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity ); @@ -159,9 +159,9 @@ B2_API void b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionD /// @param worldId The world id /// @param hertz The contact stiffness (cycles per second) /// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional) -/// @param pushVelocity The maximum contact constraint push out velocity (meters per second) +/// @param pushSpeed The maximum contact constraint push out speed (meters per second) /// @note Advanced feature -B2_API void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushVelocity ); +B2_API void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushSpeed ); /// Adjust joint tuning parameters /// @param worldId The world id @@ -170,11 +170,11 @@ B2_API void b2World_SetContactTuning( b2WorldId worldId, float hertz, float damp /// @note Advanced feature B2_API void b2World_SetJointTuning( b2WorldId worldId, float hertz, float dampingRatio ); -/// Set the maximum linear velocity. Typically in m/s. -B2_API void b2World_SetMaximumLinearVelocity( b2WorldId worldId, float maximumLinearVelocity ); +/// Set the maximum linear speed. Usually in m/s. +B2_API void b2World_SetMaximumLinearSpeed( b2WorldId worldId, float maximumLinearSpeed ); -/// Get the maximum linear velocity. Typically in m/s. -B2_API float b2World_GetMaximumLinearVelocity( b2WorldId worldId ); +/// Get the maximum linear speed. Usually in m/s. +B2_API float b2World_GetMaximumLinearSpeed( b2WorldId worldId ); /// Enable/disable constraint warm starting. Advanced feature for testing. Disabling /// sleeping greatly reduces stability and provides no performance gain. @@ -270,13 +270,13 @@ B2_API b2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector ); /// Get a world vector on a body given a local vector B2_API b2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector ); -/// Get the linear velocity of a body's center of mass. Typically in meters per second. +/// Get the linear velocity of a body's center of mass. Usually in meters per second. B2_API b2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId ); /// Get the angular velocity of a body in radians per second B2_API float b2Body_GetAngularVelocity( b2BodyId bodyId ); -/// Set the linear velocity of a body. Typically in meters per second. +/// Set the linear velocity of a body. Usually in meters per second. B2_API void b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity ); /// Set the angular velocity of a body in radians per second @@ -286,7 +286,7 @@ B2_API void b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity ); /// it will generate a torque and affect the angular velocity. This optionally wakes up the body. /// The force is ignored if the body is not awake. /// @param bodyId The body id -/// @param force The world force vector, typically in newtons (N) +/// @param force The world force vector, usually in newtons (N) /// @param point The world position of the point of application /// @param wake Option to wake up the body B2_API void b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake ); @@ -301,7 +301,7 @@ B2_API void b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake /// Apply a torque. This affects the angular velocity without affecting the linear velocity. /// This optionally wakes the body. The torque is ignored if the body is not awake. /// @param bodyId The body id -/// @param torque about the z-axis (out of the screen), typically in N*m. +/// @param torque about the z-axis (out of the screen), usually in N*m. /// @param wake also wake up the body B2_API void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake ); @@ -310,7 +310,7 @@ B2_API void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake ); /// is not at the center of mass. This optionally wakes the body. /// The impulse is ignored if the body is not awake. /// @param bodyId The body id -/// @param impulse the world impulse vector, typically in N*s or kg*m/s. +/// @param impulse the world impulse vector, usually in N*s or kg*m/s. /// @param point the world position of the point of application. /// @param wake also wake up the body /// @warning This should be used for one-shot impulses. If you need a steady force, @@ -320,7 +320,7 @@ B2_API void b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 p /// Apply an impulse to the center of mass. This immediately modifies the velocity. /// The impulse is ignored if the body is not awake. This optionally wakes the body. /// @param bodyId The body id -/// @param impulse the world impulse vector, typically in N*s or kg*m/s. +/// @param impulse the world impulse vector, usually in N*s or kg*m/s. /// @param wake also wake up the body /// @warning This should be used for one-shot impulses. If you need a steady force, /// use a force instead, which will work better with the sub-stepping solver. @@ -329,16 +329,16 @@ B2_API void b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, /// Apply an angular impulse. The impulse is ignored if the body is not awake. /// This optionally wakes the body. /// @param bodyId The body id -/// @param impulse the angular impulse, typically in units of kg*m*m/s +/// @param impulse the angular impulse, usually in units of kg*m*m/s /// @param wake also wake up the body /// @warning This should be used for one-shot impulses. If you need a steady force, /// use a force instead, which will work better with the sub-stepping solver. B2_API void b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake ); -/// Get the mass of the body, typically in kilograms +/// Get the mass of the body, usually in kilograms B2_API float b2Body_GetMass( b2BodyId bodyId ); -/// Get the rotational inertia of the body, typically in kg*m^2 +/// Get the rotational inertia of the body, usually in kg*m^2 B2_API float b2Body_GetRotationalInertia( b2BodyId bodyId ); /// Get the center of mass position of the body in local space @@ -395,10 +395,10 @@ B2_API void b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep ); /// Returns true if sleeping is enabled for this body B2_API bool b2Body_IsSleepEnabled( b2BodyId bodyId ); -/// Set the sleep threshold, typically in meters per second +/// Set the sleep threshold, usually in meters per second B2_API void b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold ); -/// Get the sleep threshold, typically in meters per second. +/// Get the sleep threshold, usually in meters per second. B2_API float b2Body_GetSleepThreshold( b2BodyId bodyId ); /// Returns true if this body is enabled @@ -423,9 +423,19 @@ B2_API void b2Body_SetBullet( b2BodyId bodyId, bool flag ); /// Is this body a bullet? B2_API bool b2Body_IsBullet( b2BodyId bodyId ); +/// Enable/disable sensor events on all shapes. +/// @see b2ShapeDef::enableSensorEvents +/// @warning changing this at runtime may cause mismatched begin/end touch events +B2_API void b2Body_EnableSensorEvents( b2BodyId bodyId, bool flag ); + +/// Enable/disable contact events on all shapes. +/// @see b2ShapeDef::enableContactEvents +/// @warning changing this at runtime may cause mismatched begin/end touch events +B2_API void b2Body_EnableContactEvents( b2BodyId bodyId, bool flag ); + /// Enable/disable hit events on all shapes /// @see b2ShapeDef::enableHitEvents -B2_API void b2Body_EnableHitEvents( b2BodyId bodyId, bool enableHitEvents ); +B2_API void b2Body_EnableHitEvents( b2BodyId bodyId, bool flag ); /// Get the world that owns this body B2_API b2WorldId b2Body_GetWorld( b2BodyId bodyId ); @@ -513,12 +523,12 @@ B2_API void b2Shape_SetUserData( b2ShapeId shapeId, void* userData ); /// from an event or query. B2_API void* b2Shape_GetUserData( b2ShapeId shapeId ); -/// Set the mass density of a shape, typically in kg/m^2. +/// Set the mass density of a shape, usually in kg/m^2. /// This will optionally update the mass properties on the parent body. /// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass ); -/// Get the density of a shape, typically in kg/m^2 +/// Get the density of a shape, usually in kg/m^2 B2_API float b2Shape_GetDensity( b2ShapeId shapeId ); /// Set the friction on a shape @@ -621,8 +631,25 @@ B2_API b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId ); B2_API int b2Shape_GetContactCapacity( b2ShapeId shapeId ); /// Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data. +/// @note Box2D uses speculative collision so some contact points may be separated. +/// @returns the number of elements filled in the provided array +/// @warning do not ignore the return value, it specifies the valid number of elements B2_API int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity ); +/// Get the maximum capacity required for retrieving all the overlapped shapes on a sensor shape. +/// This returns 0 if the provided shape is not a sensor. +/// @param shapeId the id of a sensor shape +/// @returns the required capacity to get all the overlaps in b2Shape_GetSensorOverlaps +B2_API int b2Shape_GetSensorCapacity( b2ShapeId shapeId ); + +/// Get the overlapped shapes for a sensor shape. +/// @param shapeId the id of a sensor shape +/// @param overlappedShapes a user allocated array that is filled with the overlapping shapes +/// @param capacity the capacity of overlappedShapes +/// @returns the number of elements filled in the provided array +/// @warning do not ignore the return value, it specifies the valid number of elements +B2_API int b2Shape_GetSensorOverlaps( b2ShapeId shapeId, b2ShapeId* overlappedShapes, int capacity ); + /// Get the current world AABB B2_API b2AABB b2Shape_GetAABB( b2ShapeId shapeId ); @@ -779,19 +806,19 @@ B2_API void b2DistanceJoint_EnableMotor( b2JointId jointId, bool enableMotor ); /// Is the distance joint motor enabled? B2_API bool b2DistanceJoint_IsMotorEnabled( b2JointId jointId ); -/// Set the distance joint motor speed, typically in meters per second +/// Set the distance joint motor speed, usually in meters per second B2_API void b2DistanceJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); -/// Get the distance joint motor speed, typically in meters per second +/// Get the distance joint motor speed, usually in meters per second B2_API float b2DistanceJoint_GetMotorSpeed( b2JointId jointId ); -/// Set the distance joint maximum motor force, typically in newtons +/// Set the distance joint maximum motor force, usually in newtons B2_API void b2DistanceJoint_SetMaxMotorForce( b2JointId jointId, float force ); -/// Get the distance joint maximum motor force, typically in newtons +/// Get the distance joint maximum motor force, usually in newtons B2_API float b2DistanceJoint_GetMaxMotorForce( b2JointId jointId ); -/// Get the distance joint current motor force, typically in newtons +/// Get the distance joint current motor force, usually in newtons B2_API float b2DistanceJoint_GetMotorForce( b2JointId jointId ); /** @} */ @@ -822,22 +849,22 @@ B2_API void b2MotorJoint_SetAngularOffset( b2JointId jointId, float angularOffse /// Get the motor joint angular offset target in radians B2_API float b2MotorJoint_GetAngularOffset( b2JointId jointId ); -/// Set the motor joint maximum force, typically in newtons +/// Set the motor joint maximum force, usually in newtons B2_API void b2MotorJoint_SetMaxForce( b2JointId jointId, float maxForce ); -/// Get the motor joint maximum force, typically in newtons +/// Get the motor joint maximum force, usually in newtons B2_API float b2MotorJoint_GetMaxForce( b2JointId jointId ); -/// Set the motor joint maximum torque, typically in newton-meters +/// Set the motor joint maximum torque, usually in newton-meters B2_API void b2MotorJoint_SetMaxTorque( b2JointId jointId, float maxTorque ); -/// Get the motor joint maximum torque, typically in newton-meters +/// Get the motor joint maximum torque, usually in newton-meters B2_API float b2MotorJoint_GetMaxTorque( b2JointId jointId ); -/// Set the motor joint correction factor, typically in [0, 1] +/// Set the motor joint correction factor, usually in [0, 1] B2_API void b2MotorJoint_SetCorrectionFactor( b2JointId jointId, float correctionFactor ); -/// Get the motor joint correction factor, typically in [0, 1] +/// Get the motor joint correction factor, usually in [0, 1] B2_API float b2MotorJoint_GetCorrectionFactor( b2JointId jointId ); /**@}*/ @@ -873,10 +900,10 @@ B2_API void b2MouseJoint_SetSpringDampingRatio( b2JointId jointId, float damping /// Get the mouse joint damping ratio, non-dimensional B2_API float b2MouseJoint_GetSpringDampingRatio( b2JointId jointId ); -/// Set the mouse joint maximum force, typically in newtons +/// Set the mouse joint maximum force, usually in newtons B2_API void b2MouseJoint_SetMaxForce( b2JointId jointId, float maxForce ); -/// Get the mouse joint maximum force, typically in newtons +/// Get the mouse joint maximum force, usually in newtons B2_API float b2MouseJoint_GetMaxForce( b2JointId jointId ); /**@}*/ @@ -950,19 +977,19 @@ B2_API void b2PrismaticJoint_EnableMotor( b2JointId jointId, bool enableMotor ); /// Is the prismatic joint motor enabled? B2_API bool b2PrismaticJoint_IsMotorEnabled( b2JointId jointId ); -/// Set the prismatic joint motor speed, typically in meters per second +/// Set the prismatic joint motor speed, usually in meters per second B2_API void b2PrismaticJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); -/// Get the prismatic joint motor speed, typically in meters per second +/// Get the prismatic joint motor speed, usually in meters per second B2_API float b2PrismaticJoint_GetMotorSpeed( b2JointId jointId ); -/// Set the prismatic joint maximum motor force, typically in newtons +/// Set the prismatic joint maximum motor force, usually in newtons B2_API void b2PrismaticJoint_SetMaxMotorForce( b2JointId jointId, float force ); -/// Get the prismatic joint maximum motor force, typically in newtons +/// Get the prismatic joint maximum motor force, usually in newtons B2_API float b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId ); -/// Get the prismatic joint current motor force, typically in newtons +/// Get the prismatic joint current motor force, usually in newtons B2_API float b2PrismaticJoint_GetMotorForce( b2JointId jointId ); /// Get the current joint translation, usually in meters. @@ -1035,13 +1062,13 @@ B2_API void b2RevoluteJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ) /// Get the revolute joint motor speed in radians per second B2_API float b2RevoluteJoint_GetMotorSpeed( b2JointId jointId ); -/// Get the revolute joint current motor torque, typically in newton-meters +/// Get the revolute joint current motor torque, usually in newton-meters B2_API float b2RevoluteJoint_GetMotorTorque( b2JointId jointId ); -/// Set the revolute joint maximum motor torque, typically in newton-meters +/// Set the revolute joint maximum motor torque, usually in newton-meters B2_API void b2RevoluteJoint_SetMaxMotorTorque( b2JointId jointId, float torque ); -/// Get the revolute joint maximum motor torque, typically in newton-meters +/// Get the revolute joint maximum motor torque, usually in newton-meters B2_API float b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId ); /**@}*/ @@ -1152,13 +1179,13 @@ B2_API void b2WheelJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); /// Get the wheel joint motor speed in radians per second B2_API float b2WheelJoint_GetMotorSpeed( b2JointId jointId ); -/// Set the wheel joint maximum motor torque, typically in newton-meters +/// Set the wheel joint maximum motor torque, usually in newton-meters B2_API void b2WheelJoint_SetMaxMotorTorque( b2JointId jointId, float torque ); -/// Get the wheel joint maximum motor torque, typically in newton-meters +/// Get the wheel joint maximum motor torque, usually in newton-meters B2_API float b2WheelJoint_GetMaxMotorTorque( b2JointId jointId ); -/// Get the wheel joint current motor torque, typically in newton-meters +/// Get the wheel joint current motor torque, usually in newton-meters B2_API float b2WheelJoint_GetMotorTorque( b2JointId jointId ); /**@}*/ diff --git a/include/box2d/collision.h b/include/box2d/collision.h index 712605498..d9b262bf2 100644 --- a/include/box2d/collision.h +++ b/include/box2d/collision.h @@ -8,7 +8,7 @@ #include -typedef struct b2DistanceCache b2DistanceCache; +typedef struct b2SimplexCache b2SimplexCache; typedef struct b2Hull b2Hull; /** @@ -181,34 +181,34 @@ B2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot B2_API b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius ); /// Make a square polygon, bypassing the need for a convex hull. -/// @param h the half-width -B2_API b2Polygon b2MakeSquare( float h ); +/// @param halfWidth the half-width +B2_API b2Polygon b2MakeSquare( float halfWidth ); /// Make a box (rectangle) polygon, bypassing the need for a convex hull. -/// @param hx the half-width -/// @param hy the half-height -B2_API b2Polygon b2MakeBox( float hx, float hy ); +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) +B2_API b2Polygon b2MakeBox( float halfWidth, float halfHeight ); /// Make a rounded box, bypassing the need for a convex hull. -/// @param hx the half-width -/// @param hy the half-height +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) /// @param radius the radius of the rounded extension -B2_API b2Polygon b2MakeRoundedBox( float hx, float hy, float radius ); +B2_API b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius ); /// Make an offset box, bypassing the need for a convex hull. -/// @param hx the half-width -/// @param hy the half-height +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) /// @param center the local center of the box /// @param rotation the local rotation of the box -B2_API b2Polygon b2MakeOffsetBox( float hx, float hy, b2Vec2 center, b2Rot rotation ); +B2_API b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation ); /// Make an offset rounded box, bypassing the need for a convex hull. -/// @param hx the half-width -/// @param hy the half-height +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) /// @param center the local center of the box /// @param rotation the local rotation of the box /// @param radius the radius of the rounded extension -B2_API b2Polygon b2MakeOffsetRoundedBox( float hx, float hy, b2Vec2 center, b2Rot rotation, float radius ); +B2_API b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius ); /// Transform a polygon. This is useful for transferring a shape from one body to another. B2_API b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon ); @@ -330,7 +330,7 @@ typedef struct b2SegmentDistanceResult B2_API b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 ); /// A distance proxy is used by the GJK algorithm. It encapsulates any shape. -typedef struct b2DistanceProxy +typedef struct b2ShapeProxy { /// The point cloud b2Vec2 points[B2_MAX_POLYGON_VERTICES]; @@ -340,13 +340,13 @@ typedef struct b2DistanceProxy /// The external radius of the point cloud float radius; -} b2DistanceProxy; +} b2ShapeProxy; /// Used to warm start the GJK simplex. If you call this function multiple times with nearby /// transforms this might improve performance. Otherwise you can zero initialize this. /// The distance cache must be initialized to zero on the first call. /// Users should generally just zero initialize this structure for each call. -typedef struct b2DistanceCache +typedef struct b2SimplexCache { /// The number of stored simplex points uint16_t count; @@ -356,18 +356,18 @@ typedef struct b2DistanceCache /// The cached simplex indices on shape B uint8_t indexB[3]; -} b2DistanceCache; +} b2SimplexCache; -static const b2DistanceCache b2_emptyDistanceCache = B2_ZERO_INIT; +static const b2SimplexCache b2_emptySimplexCache = B2_ZERO_INIT; /// Input for b2ShapeDistance typedef struct b2DistanceInput { /// The proxy for shape A - b2DistanceProxy proxyA; + b2ShapeProxy proxyA; /// The proxy for shape B - b2DistanceProxy proxyB; + b2ShapeProxy proxyB; /// The world transform for shape A b2Transform transformA; @@ -382,8 +382,10 @@ typedef struct b2DistanceInput /// Output for b2ShapeDistance typedef struct b2DistanceOutput { - b2Vec2 pointA; ///< Closest point on shapeA - b2Vec2 pointB; ///< Closest point on shapeB + b2Vec2 pointA; ///< Closest point on shapeA + b2Vec2 pointB; ///< Closest point on shapeB + // todo_erin implement this + // b2Vec2 normal; ///< Normal vector that points from A to B float distance; ///< The final distance, zero if overlapped int32_t iterations; ///< Number of GJK iterations used int32_t simplexCount; ///< The number of simplexes stored in the simplex array @@ -408,16 +410,16 @@ typedef struct b2Simplex } b2Simplex; /// Compute the closest points between two shapes represented as point clouds. -/// b2DistanceCache cache is input/output. On the first call set b2DistanceCache.count to zero. +/// b2SimplexCache cache is input/output. On the first call set b2SimplexCache.count to zero. /// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these. -B2_API b2DistanceOutput b2ShapeDistance( b2DistanceCache* cache, const b2DistanceInput* input, b2Simplex* simplexes, +B2_API b2DistanceOutput b2ShapeDistance( b2SimplexCache* cache, const b2DistanceInput* input, b2Simplex* simplexes, int simplexCapacity ); /// Input parameters for b2ShapeCast typedef struct b2ShapeCastPairInput { - b2DistanceProxy proxyA; ///< The proxy for shape A - b2DistanceProxy proxyB; ///< The proxy for shape B + b2ShapeProxy proxyA; ///< The proxy for shape A + b2ShapeProxy proxyB; ///< The proxy for shape B b2Transform transformA; ///< The world transform for shape A b2Transform transformB; ///< The world transform for shape B b2Vec2 translationB; ///< The translation of shape B @@ -428,7 +430,7 @@ typedef struct b2ShapeCastPairInput B2_API b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input ); /// Make a proxy for use in GJK and related functions. -B2_API b2DistanceProxy b2MakeProxy( const b2Vec2* vertices, int32_t count, float radius ); +B2_API b2ShapeProxy b2MakeProxy( const b2Vec2* vertices, int32_t count, float radius ); /// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin, /// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass @@ -448,11 +450,11 @@ B2_API b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time ); /// Input parameters for b2TimeOfImpact typedef struct b2TOIInput { - b2DistanceProxy proxyA; ///< The proxy for shape A - b2DistanceProxy proxyB; ///< The proxy for shape B - b2Sweep sweepA; ///< The movement of shape A - b2Sweep sweepB; ///< The movement of shape B - float tMax; ///< Defines the sweep interval [0, tMax] + b2ShapeProxy proxyA; ///< The proxy for shape A + b2ShapeProxy proxyB; ///< The proxy for shape B + b2Sweep sweepA; ///< The movement of shape A + b2Sweep sweepB; ///< The movement of shape B + float maxFraction; ///< Defines the sweep interval [0, maxFraction] } b2TOIInput; /// Describes the TOI output @@ -469,7 +471,7 @@ typedef enum b2TOIState typedef struct b2TOIOutput { b2TOIState state; ///< The type of result - float t; ///< The time of the collision + float fraction; ///< The sweep time of the collision } b2TOIOutput; /// Compute the upper bound on time before two shapes penetrate. Time is represented as @@ -582,11 +584,11 @@ B2_API b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA /// Compute the contact manifold between a chain segment and a capsule B2_API b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, - b2Transform xfB, b2DistanceCache* cache ); + b2Transform xfB, b2SimplexCache* cache ); /// Compute the contact manifold between a chain segment and a rounded polygon B2_API b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB, - b2Transform xfB, b2DistanceCache* cache ); + b2Transform xfB, b2SimplexCache* cache ); /**@}*/ @@ -645,7 +647,7 @@ typedef struct b2TreeNode /// The dynamic tree structure. This should be considered private data. /// It is placed here for performance reasons. typedef struct b2DynamicTree -{ +{ /// The tree nodes b2TreeNode* nodes; diff --git a/include/box2d/types.h b/include/box2d/types.h index 0ce563ddd..237bb433f 100644 --- a/include/box2d/types.h +++ b/include/box2d/types.h @@ -81,14 +81,14 @@ typedef struct b2WorldDef /// Gravity vector. Box2D has no up-vector defined. b2Vec2 gravity; - /// Restitution velocity threshold, usually in m/s. Collisions above this + /// Restitution speed threshold, usually in m/s. Collisions above this /// speed have restitution applied (will bounce). float restitutionThreshold; - /// This parameter controls how fast overlap is resolved and has units of meters per second - float contactPushVelocity; + /// This parameter controls how fast overlap is resolved and usually has units of meters per second + float contactPushSpeed; - /// Threshold velocity for hit events. Usually meters per second. + /// Threshold speed for hit events. Usually meters per second. float hitEventThreshold; /// Contact stiffness. Cycles per second. @@ -103,8 +103,8 @@ typedef struct b2WorldDef /// Joint bounciness. Non-dimensional. float jointDampingRatio; - /// Maximum linear velocity. Usually meters per second. - float maximumLinearVelocity; + /// Maximum linear speed. Usually meters per second. + float maximumLinearSpeed; /// Mixing rule for friction. Default is b2_mixGeometricMean. b2MixingRule frictionMixingRule; @@ -179,20 +179,20 @@ typedef struct b2BodyDef /// The initial world rotation of the body. Use b2MakeRot() if you have an angle. b2Rot rotation; - /// The initial linear velocity of the body's origin. Typically in meters per second. + /// The initial linear velocity of the body's origin. Usually in meters per second. b2Vec2 linearVelocity; /// The initial angular velocity of the body. Radians per second. float angularVelocity; - /// Linear damping is use to reduce the linear velocity. The damping parameter + /// Linear damping is used to reduce the linear velocity. The damping parameter /// can be larger than 1 but the damping effect becomes sensitive to the /// time step when the damping parameter is large. /// Generally linear damping is undesirable because it makes objects move slowly /// as if they are floating. float linearDamping; - /// Angular damping is use to reduce the angular velocity. The damping parameter + /// Angular damping is used to reduce the angular velocity. The damping parameter /// can be larger than 1.0f but the damping effect becomes sensitive to the /// time step when the damping parameter is large. /// Angular damping can be use slow down rotating bodies. @@ -201,7 +201,7 @@ typedef struct b2BodyDef /// Scale the gravity applied to this body. Non-dimensional. float gravityScale; - /// Sleep velocity threshold, default is 0.05 meter per second + /// Sleep speed threshold, default is 0.05 meters per second float sleepThreshold; /// Use this to store application specific body data. @@ -335,6 +335,9 @@ typedef struct b2ShapeDef /// The restitution (bounce) usually in the range [0,1]. float restitution; + /// The rolling resistance usually in the range [0,1]. + float rollingResistance; + /// The density, usually in kg/m^2. float density; @@ -349,9 +352,9 @@ typedef struct b2ShapeDef /// Instead, use a ray or shape cast for those scenarios. bool isSensor; -/// Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. -/// This applies for sensors and non-sensors. -bool enableSensorEvents; + /// Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. + /// This applies for sensors and non-sensors. + bool enableSensorEvents; /// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. bool enableContactEvents; @@ -386,7 +389,7 @@ B2_API b2ShapeDef b2DefaultShapeDef( void ); /// - chains have a counter-clockwise winding order /// - chains are either a loop or open /// - a chain must have at least 4 points -/// - the distance between any two points must be greater than b2_linearSlop +/// - the distance between any two points must be greater than B2_LINEAR_SLOP /// - a chain shape should not self intersect (this is not validated) /// - an open chain shape has NO COLLISION on the first and final edge /// - you may overlap two open chains on their first three and/or last three points to get smooth collision @@ -1014,7 +1017,8 @@ typedef struct b2ContactBeginTouchEvent /// Id of the second shape b2ShapeId shapeIdB; - /// The initial contact manifold + /// The initial contact manifold. This is recorded before the solver is called, + /// so all the impulses will be zero. b2Manifold manifold; } b2ContactBeginTouchEvent; diff --git a/samples/sample_collision.cpp b/samples/sample_collision.cpp index ecd1a40c7..f3008fb89 100644 --- a/samples/sample_collision.cpp +++ b/samples/sample_collision.cpp @@ -50,7 +50,7 @@ class ShapeDistance : public Sample m_transform = { { 1.5f, -1.5f }, b2Rot_identity }; m_angle = 0.0f; - m_cache = b2_emptyDistanceCache; + m_cache = b2_emptySimplexCache; m_simplexCount = 0; m_startPoint = { 0.0f, 0.0f }; m_basePosition = { 0.0f, 0.0f }; @@ -71,9 +71,9 @@ class ShapeDistance : public Sample m_proxyB = MakeProxy( m_typeB, m_radiusB ); } - b2DistanceProxy MakeProxy( ShapeType type, float radius ) + b2ShapeProxy MakeProxy( ShapeType type, float radius ) { - b2DistanceProxy proxy = {}; + b2ShapeProxy proxy = {}; proxy.radius = radius; switch ( type ) @@ -410,10 +410,10 @@ class ShapeDistance : public Sample ShapeType m_typeB; float m_radiusA; float m_radiusB; - b2DistanceProxy m_proxyA; - b2DistanceProxy m_proxyB; + b2ShapeProxy m_proxyA; + b2ShapeProxy m_proxyB; - b2DistanceCache m_cache; + b2SimplexCache m_cache; b2Simplex m_simplexes[SIMPLEX_CAPACITY]; int m_simplexCount; int m_simplexIndex; @@ -2246,10 +2246,10 @@ class Manifold : public Sample g_camera.m_zoom = 25.0f * 0.45f; } - m_smgroxCache1 = b2_emptyDistanceCache; - m_smgroxCache2 = b2_emptyDistanceCache; - m_smgcapCache1 = b2_emptyDistanceCache; - m_smgcapCache2 = b2_emptyDistanceCache; + m_smgroxCache1 = b2_emptySimplexCache; + m_smgroxCache2 = b2_emptySimplexCache; + m_smgcapCache1 = b2_emptySimplexCache; + m_smgcapCache2 = b2_emptySimplexCache; m_transform = b2Transform_identity; m_transform.p.x = 1.0f; @@ -2398,10 +2398,10 @@ class Manifold : public Sample if ( m_enableCaching == false ) { - m_smgroxCache1 = b2_emptyDistanceCache; - m_smgroxCache2 = b2_emptyDistanceCache; - m_smgcapCache1 = b2_emptyDistanceCache; - m_smgcapCache2 = b2_emptyDistanceCache; + m_smgroxCache1 = b2_emptySimplexCache; + m_smgroxCache2 = b2_emptySimplexCache; + m_smgcapCache1 = b2_emptySimplexCache; + m_smgcapCache2 = b2_emptySimplexCache; } // circle-circle @@ -2828,10 +2828,10 @@ class Manifold : public Sample return new Manifold( settings ); } - b2DistanceCache m_smgroxCache1; - b2DistanceCache m_smgroxCache2; - b2DistanceCache m_smgcapCache1; - b2DistanceCache m_smgcapCache2; + b2SimplexCache m_smgroxCache1; + b2SimplexCache m_smgroxCache2; + b2SimplexCache m_smgcapCache1; + b2SimplexCache m_smgcapCache2; b2Hull m_wedge; @@ -3111,7 +3111,7 @@ class SmoothManifold : public Sample for ( int i = 0; i < m_count; ++i ) { const b2ChainSegment* segment = m_segments + i; - b2DistanceCache cache = {}; + b2SimplexCache cache = {}; b2Manifold m = b2CollideChainSegmentAndPolygon( segment, transform1, &rox, transform2, &cache ); DrawManifold( &m ); } @@ -3308,7 +3308,7 @@ class ShapeCast : public Sample distanceInput.transformA = m_transformA; distanceInput.transformB = transformB2; distanceInput.useRadii = false; - b2DistanceCache distanceCache; + b2SimplexCache distanceCache; distanceCache.count = 0; b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceCache, &distanceInput, nullptr, 0 ); @@ -3429,7 +3429,7 @@ class TimeOfImpact : public Sample #if 0 - input 0x00000044f14fd550 {proxyA={points=0x00000044f14fd550 {{...}, {...}, {...}, {...}, {...}, {...}, {...}, ...} ...} ...} const b2TOIInput * -- proxyA {points=0x00000044f14fd550 {{x=-123.750000 y=134.750000 }, {x=-123.250000 y=134.750000 }, {x=-123.250000 ...}, ...} ...} b2DistanceProxy +- proxyA {points=0x00000044f14fd550 {{x=-123.750000 y=134.750000 }, {x=-123.250000 y=134.750000 }, {x=-123.250000 ...}, ...} ...} b2ShapeProxy - points 0x00000044f14fd550 {{x=-123.750000 y=134.750000 }, {x=-123.250000 y=134.750000 }, {x=-123.250000 y=135.250000 }, ...} b2Vec2[8] + [0] {x=-123.750000 y=134.750000 } b2Vec2 + [1] {x=-123.250000 y=134.750000 } b2Vec2 @@ -3441,7 +3441,7 @@ class TimeOfImpact : public Sample + [7] {x=8.40272566e-18 y=7.539e-43#DEN } b2Vec2 count 4 int radius 0.00000000 float -- proxyB {points=0x00000044f14fd598 {{x=0.00000000 y=-0.125000000 }, {x=0.00000000 y=0.125000000 }, {x=-123.250000 ...}, ...} ...} b2DistanceProxy +- proxyB {points=0x00000044f14fd598 {{x=0.00000000 y=-0.125000000 }, {x=0.00000000 y=0.125000000 }, {x=-123.250000 ...}, ...} ...} b2ShapeProxy - points 0x00000044f14fd598 {{x=0.00000000 y=-0.125000000 }, {x=0.00000000 y=0.125000000 }, {x=-123.250000 y=...}, ...} b2Vec2[8] + [0] {x=0.00000000 y=-0.125000000 } b2Vec2 + [1] {x=0.00000000 y=0.125000000 } b2Vec2 @@ -3486,11 +3486,11 @@ class TimeOfImpact : public Sample input.proxyB = b2MakeProxy( m_verticesB, m_countB, m_radiusB ); input.sweepA = sweepA; input.sweepB = sweepB; - input.tMax = 1.0f; + input.maxFraction = 1.0f; b2TOIOutput output = b2TimeOfImpact( &input ); - g_draw.DrawString( 5, m_textLine, "toi = %g", output.t ); + g_draw.DrawString( 5, m_textLine, "toi = %g", output.fraction ); m_textLine += m_textIncrement; // g_draw.DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, @@ -3517,7 +3517,7 @@ class TimeOfImpact : public Sample // g_draw.DrawPolygon( vertices, m_countB, b2_colorGreen ); // Draw B at t = hit_time - transformB = b2GetSweepTransform( &sweepB, output.t ); + transformB = b2GetSweepTransform( &sweepB, output.fraction ); for ( int i = 0; i < m_countB; ++i ) { vertices[i] = b2TransformPoint( transformB, m_verticesB[i] ); @@ -3538,10 +3538,10 @@ class TimeOfImpact : public Sample b2DistanceInput dinput; dinput.proxyA = input.proxyA; dinput.proxyB = input.proxyB; - dinput.transformA = b2GetSweepTransform( &sweepA, output.t ); - dinput.transformB = b2GetSweepTransform( &sweepB, output.t ); + dinput.transformA = b2GetSweepTransform( &sweepA, output.fraction ); + dinput.transformB = b2GetSweepTransform( &sweepB, output.fraction ); dinput.useRadii = false; - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; b2DistanceOutput doutput = b2ShapeDistance( &cache, &dinput, nullptr, 0 ); g_draw.DrawString( 5, m_textLine, "distance = %g", doutput.distance ); m_textLine += m_textIncrement; diff --git a/samples/sample_continuous.cpp b/samples/sample_continuous.cpp index 05364c664..8fe66c9d0 100644 --- a/samples/sample_continuous.cpp +++ b/samples/sample_continuous.cpp @@ -785,7 +785,7 @@ class SpeculativeGhost : public Sample static int sampleSpeculativeGhost = RegisterSample( "Continuous", "Speculative Ghost", SpeculativeGhost::Create ); -// This shows that while Box2D uses speculative collision, it does not lead to speculative ghost collisions at small distances +// This shows that Box2D does not have pixel perfect collision. class PixelImperfect : public Sample { public: @@ -853,6 +853,77 @@ class PixelImperfect : public Sample static int samplePixelImperfect = RegisterSample( "Continuous", "Pixel Imperfect", PixelImperfect::Create ); +class RestitutionThreshold : public Sample +{ +public: + explicit RestitutionThreshold( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 7.0f, 5.0f }; + g_camera.m_zoom = 6.0f; + } + + float pixelsPerMeter = 30.f; + + // With the default threshold the ball will not bounce. + b2World_SetRestitutionThreshold( m_worldId, 0.1f ); + + { + b2BodyDef block0BodyDef = b2DefaultBodyDef(); + block0BodyDef.type = b2_staticBody; + block0BodyDef.position = { 205.f / pixelsPerMeter, 120.f / pixelsPerMeter }; + block0BodyDef.rotation = b2MakeRot( 70.f * 3.14f / 180.f ); + b2BodyId block0BodyId = b2CreateBody( m_worldId, &block0BodyDef ); + b2Polygon block0Shape = b2MakeBox( 50.f / pixelsPerMeter, 5.f / pixelsPerMeter ); + b2ShapeDef block0ShapeDef = b2DefaultShapeDef(); + block0ShapeDef.friction = 0.f; + b2CreatePolygonShape( block0BodyId, &block0ShapeDef, &block0Shape ); + } + + { + // Make a ball + b2BodyDef ballBodyDef = b2DefaultBodyDef(); + ballBodyDef.type = b2_dynamicBody; + ballBodyDef.position = { 200.f / pixelsPerMeter, 250.f / pixelsPerMeter }; + m_ballId = b2CreateBody( m_worldId, &ballBodyDef ); + + b2Circle ballShape = {}; + ballShape.radius = 5.f / pixelsPerMeter; + b2ShapeDef ballShapeDef = b2DefaultShapeDef(); + ballShapeDef.friction = 0.f; + ballShapeDef.restitution = 1.f; + b2CreateCircleShape( m_ballId, &ballShapeDef, &ballShape ); + + b2Body_SetLinearVelocity( m_ballId, { 0.f, -2.9f } ); // Initial velocity + b2Body_SetFixedRotation( m_ballId, true ); // Do not rotate a ball + } + } + + void Step( Settings& settings ) override + { + b2ContactData data; + b2Body_GetContactData( m_ballId, &data, 1 ); + + b2Vec2 p = b2Body_GetPosition( m_ballId ); + b2Vec2 v = b2Body_GetLinearVelocity( m_ballId ); + g_draw.DrawString( 5, m_textLine, "p.x = %.9f, v.y = %.9f", p.x, v.y ); + m_textLine += m_textIncrement; + + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new RestitutionThreshold( settings ); + } + + b2BodyId m_ballId; +}; + +static int sampleRestitutionThreshold = RegisterSample( "Continuous", "Restitution Threshold", RestitutionThreshold::Create ); + class Drop : public Sample { public: diff --git a/samples/sample_events.cpp b/samples/sample_events.cpp index 3f85b6290..c43315227 100644 --- a/samples/sample_events.cpp +++ b/samples/sample_events.cpp @@ -202,7 +202,7 @@ class SensorFunnel : public Sample else { Human* human = m_humans + index; - DestroyHuman(human); + DestroyHuman( human ); } m_isSpawned[index] = false; @@ -221,7 +221,7 @@ class SensorFunnel : public Sample else { DestroyHuman( m_humans + i ); - } + } m_isSpawned[i] = false; } @@ -504,7 +504,7 @@ class FootSensor : public Sample b2Vec2 points[20]; float x = 10.0f; - for (int i = 0; i < 20; ++i) + for ( int i = 0; i < 20; ++i ) { points[i] = { x, 0.0f }; x -= 1.0f; @@ -516,7 +516,6 @@ class FootSensor : public Sample chainDef.isLoop = false; b2CreateChain( groundId, &chainDef ); - } { @@ -538,7 +537,6 @@ class FootSensor : public Sample m_overlapCount = 0; } - void Step( Settings& settings ) override { if ( glfwGetKey( g_mainWindow, GLFW_KEY_A ) == GLFW_PRESS ) @@ -580,6 +578,18 @@ class FootSensor : public Sample g_draw.DrawString( 5, m_textLine, "count == %d", m_overlapCount ); m_textLine += m_textIncrement; + + int capacity = b2Shape_GetSensorCapacity( m_sensorId ); + m_overlaps.clear(); + m_overlaps.resize( capacity ); + int count = b2Shape_GetSensorOverlaps( m_sensorId, m_overlaps.data(), capacity ); + for ( int i = 0; i < count; ++i ) + { + b2ShapeId shapeId = m_overlaps[i]; + b2AABB aabb = b2Shape_GetAABB( shapeId ); + b2Vec2 point = b2AABB_Center( aabb ); + g_draw.DrawPoint( point, 10.0f, b2_colorWhite ); + } } static Sample* Create( Settings& settings ) @@ -589,12 +599,12 @@ class FootSensor : public Sample b2BodyId m_playerId; b2ShapeId m_sensorId; + std::vector m_overlaps; int m_overlapCount; }; static int sampleCharacterSensor = RegisterSample( "Events", "Foot Sensor", FootSensor::Create ); - struct BodyUserData { int index; @@ -761,6 +771,7 @@ class ContactEvent : public Sample std::vector contactData; + // Process contact begin touch events. b2ContactEvents contactEvents = b2World_GetContactEvents( m_worldId ); for ( int i = 0; i < contactEvents.beginCount; ++i ) { @@ -768,41 +779,72 @@ class ContactEvent : public Sample b2BodyId bodyIdA = b2Shape_GetBody( event.shapeIdA ); b2BodyId bodyIdB = b2Shape_GetBody( event.shapeIdB ); + // The begin touch events have the contact manifolds, but the impulses are zero. This is because the manifolds + // are gathered before the contact solver is run. + + // We can get the final contact data from the shapes. The manifold is shared by the two shapes, so we just need the + // contact data from one of the shapes. Choose the one with the smallest number of contacts. + int capacityA = b2Shape_GetContactCapacity( event.shapeIdA ); - contactData.resize( capacityA ); - int countA = b2Shape_GetContactData( event.shapeIdA, contactData.data(), capacityA ); - assert( countA >= 1 ); + int capacityB = b2Shape_GetContactCapacity( event.shapeIdB ); - for ( int j = 0; j < countA; ++j ) + if ( capacityA < capacityB ) { - b2Manifold manifold = contactData[j].manifold; - b2Vec2 normal = manifold.normal; - assert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON ); + contactData.resize( capacityA ); - for ( int k = 0; k < manifold.pointCount; ++k ) + // The count may be less than the capacity + int countA = b2Shape_GetContactData( event.shapeIdA, contactData.data(), capacityA ); + assert( countA >= 1 ); + + for ( int j = 0; j < countA; ++j ) { - b2ManifoldPoint point = manifold.points[k]; - g_draw.DrawSegment( point.point, point.point + 4.0f * normal, b2_colorBlueViolet ); - g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite ); - } - } + b2ShapeId idA = contactData[j].shapeIdA; + b2ShapeId idB = contactData[j].shapeIdB; + if ( B2_ID_EQUALS( idA, event.shapeIdB ) || B2_ID_EQUALS( idB, event.shapeIdB ) ) + { + assert( B2_ID_EQUALS( idA, event.shapeIdA ) || B2_ID_EQUALS( idB, event.shapeIdA ) ); - int capacityB = b2Shape_GetContactCapacity( event.shapeIdB ); - contactData.resize( capacityB ); - int countB = b2Shape_GetContactData( event.shapeIdB, contactData.data(), capacityB ); - assert( countB >= 1 ); + b2Manifold manifold = contactData[j].manifold; + b2Vec2 normal = manifold.normal; + assert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON ); - for ( int j = 0; j < countB; ++j ) + for ( int k = 0; k < manifold.pointCount; ++k ) + { + b2ManifoldPoint point = manifold.points[k]; + g_draw.DrawSegment( point.point, point.point + point.maxNormalImpulse * normal, b2_colorBlueViolet ); + g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite ); + } + } + } + } + else { - b2Manifold manifold = contactData[j].manifold; - b2Vec2 normal = manifold.normal; - assert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON ); + contactData.resize( capacityB ); + + // The count may be less than the capacity + int countB = b2Shape_GetContactData( event.shapeIdB, contactData.data(), capacityB ); + assert( countB >= 1 ); - for ( int k = 0; k < manifold.pointCount; ++k ) + for ( int j = 0; j < countB; ++j ) { - b2ManifoldPoint point = manifold.points[k]; - g_draw.DrawSegment( point.point, point.point + 4.0f * normal, b2_colorYellowGreen ); - g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite ); + b2ShapeId idA = contactData[j].shapeIdA; + b2ShapeId idB = contactData[j].shapeIdB; + + if ( B2_ID_EQUALS( idA, event.shapeIdA ) || B2_ID_EQUALS( idB, event.shapeIdA ) ) + { + assert( B2_ID_EQUALS( idA, event.shapeIdB ) || B2_ID_EQUALS( idB, event.shapeIdB ) ); + + b2Manifold manifold = contactData[j].manifold; + b2Vec2 normal = manifold.normal; + assert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON ); + + for ( int k = 0; k < manifold.pointCount; ++k ) + { + b2ManifoldPoint point = manifold.points[k]; + g_draw.DrawSegment( point.point, point.point + point.maxNormalImpulse * normal, b2_colorYellowGreen ); + g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite ); + } + } } } diff --git a/src/body.c b/src/body.c index db125d2a1..cec6fd3d0 100644 --- a/src/body.c +++ b/src/body.c @@ -234,9 +234,7 @@ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def ) bodySim->localCenter = b2Vec2_zero; bodySim->force = b2Vec2_zero; bodySim->torque = 0.0f; - bodySim->mass = 0.0f; bodySim->invMass = 0.0f; - bodySim->inertia = 0.0f; bodySim->invInertia = 0.0f; bodySim->minExtent = B2_HUGE; bodySim->maxExtent = 0.0f; @@ -287,6 +285,8 @@ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def ) body->islandNext = B2_NULL_INDEX; body->bodyMoveIndex = B2_NULL_INDEX; body->id = bodyId; + body->mass = 0.0f; + body->inertia = 0.0f; body->sleepThreshold = def->sleepThreshold; body->sleepTime = 0.0f; body->type = def->type; @@ -512,9 +512,10 @@ void b2UpdateBodyMassData( b2World* world, b2Body* body ) b2BodySim* bodySim = b2GetBodySim( world, body ); // Compute mass data from shapes. Each shape has its own density. - bodySim->mass = 0.0f; + body->mass = 0.0f; + body->inertia = 0.0f; + bodySim->invMass = 0.0f; - bodySim->inertia = 0.0f; bodySim->invInertia = 0.0f; bodySim->localCenter = b2Vec2_zero; bodySim->minExtent = B2_HUGE; @@ -558,28 +559,28 @@ void b2UpdateBodyMassData( b2World* world, b2Body* body ) } b2MassData massData = b2ComputeShapeMass( s ); - bodySim->mass += massData.mass; + body->mass += massData.mass; localCenter = b2MulAdd( localCenter, massData.mass, massData.center ); - bodySim->inertia += massData.rotationalInertia; + body->inertia += massData.rotationalInertia; } // Compute center of mass. - if ( bodySim->mass > 0.0f ) + if ( body->mass > 0.0f ) { - bodySim->invMass = 1.0f / bodySim->mass; + bodySim->invMass = 1.0f / body->mass; localCenter = b2MulSV( bodySim->invMass, localCenter ); } - if ( bodySim->inertia > 0.0f && body->fixedRotation == false ) + if ( body->inertia > 0.0f && body->fixedRotation == false ) { // Center the inertia about the center of mass. - bodySim->inertia -= bodySim->mass * b2Dot( localCenter, localCenter ); - B2_ASSERT( bodySim->inertia > 0.0f ); - bodySim->invInertia = 1.0f / bodySim->inertia; + body->inertia -= body->mass * b2Dot( localCenter, localCenter ); + B2_ASSERT( body->inertia > 0.0f ); + bodySim->invInertia = 1.0f / body->inertia; } else { - bodySim->inertia = 0.0f; + body->inertia = 0.0f; bodySim->invInertia = 0.0f; } @@ -686,8 +687,8 @@ void b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation ) b2BroadPhase* broadPhase = &world->broadPhase; b2Transform transform = bodySim->transform; - const float margin = b2_aabbMargin; - const float speculativeDistance = b2_speculativeDistance; + const float margin = B2_AABB_MARGIN; + const float speculativeDistance = B2_SPECULATIVE_DISTANCE; int shapeId = body->headShapeId; while ( shapeId != B2_NULL_INDEX ) @@ -1094,7 +1095,7 @@ void b2Body_SetType( b2BodyId bodyId, b2BodyType type ) B2_ASSERT( otherBody->setIndex == b2_awakeSet ); // The joint must live in a graph color. - B2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < B2_GRAPH_COLOR_COUNT ); // In this case the joint must be re-inserted into the constraint graph to ensure the correct // graph color. @@ -1193,16 +1194,14 @@ float b2Body_GetMass( b2BodyId bodyId ) { b2World* world = b2GetWorld( bodyId.world0 ); b2Body* body = b2GetBodyFullId( world, bodyId ); - b2BodySim* bodySim = b2GetBodySim( world, body ); - return bodySim->mass; + return body->mass; } float b2Body_GetRotationalInertia( b2BodyId bodyId ) { b2World* world = b2GetWorld( bodyId.world0 ); b2Body* body = b2GetBodyFullId( world, bodyId ); - b2BodySim* bodySim = b2GetBodySim( world, body ); - return bodySim->inertia; + return body->inertia; } b2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId ) @@ -1236,16 +1235,16 @@ void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData ) b2Body* body = b2GetBodyFullId( world, bodyId ); b2BodySim* bodySim = b2GetBodySim( world, body ); - bodySim->mass = massData.mass; - bodySim->inertia = massData.rotationalInertia; + body->mass = massData.mass; + body->inertia = massData.rotationalInertia; bodySim->localCenter = massData.center; b2Vec2 center = b2TransformPoint( bodySim->transform, massData.center ); bodySim->center = center; bodySim->center0 = center; - bodySim->invMass = bodySim->mass > 0.0f ? 1.0f / bodySim->mass : 0.0f; - bodySim->invInertia = bodySim->inertia > 0.0f ? 1.0f / bodySim->inertia : 0.0f; + bodySim->invMass = body->mass > 0.0f ? 1.0f / body->mass : 0.0f; + bodySim->invInertia = body->inertia > 0.0f ? 1.0f / body->inertia : 0.0f; } b2MassData b2Body_GetMassData( b2BodyId bodyId ) @@ -1253,7 +1252,7 @@ b2MassData b2Body_GetMassData( b2BodyId bodyId ) b2World* world = b2GetWorld( bodyId.world0 ); b2Body* body = b2GetBodyFullId( world, bodyId ); b2BodySim* bodySim = b2GetBodySim( world, body ); - b2MassData massData = { bodySim->mass, bodySim->localCenter, bodySim->inertia }; + b2MassData massData = { body->mass, bodySim->localCenter, body->inertia }; return massData; } @@ -1636,7 +1635,33 @@ bool b2Body_IsBullet( b2BodyId bodyId ) return bodySim->isBullet; } -void b2Body_EnableHitEvents( b2BodyId bodyId, bool enableHitEvents ) +void b2Body_EnableSensorEvents(b2BodyId bodyId, bool flag) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shape->enableSensorEvents = flag; + shapeId = shape->nextShapeId; + } +} + +void b2Body_EnableContactEvents(b2BodyId bodyId, bool flag) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shape->enableContactEvents = flag; + shapeId = shape->nextShapeId; + } +} + +void b2Body_EnableHitEvents( b2BodyId bodyId, bool flag ) { b2World* world = b2GetWorld( bodyId.world0 ); b2Body* body = b2GetBodyFullId( world, bodyId ); @@ -1644,7 +1669,7 @@ void b2Body_EnableHitEvents( b2BodyId bodyId, bool enableHitEvents ) while ( shapeId != B2_NULL_INDEX ) { b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); - shape->enableHitEvents = enableHitEvents; + shape->enableHitEvents = flag; shapeId = shape->nextShapeId; } } diff --git a/src/body.h b/src/body.h index 45b91ae65..46992fc74 100644 --- a/src/body.h +++ b/src/body.h @@ -8,12 +8,7 @@ #include "box2d/math_functions.h" #include "box2d/types.h" -typedef struct b2Polygon b2Polygon; typedef struct b2World b2World; -typedef struct b2JointSim b2JointSim; -typedef struct b2ContactSim b2ContactSim; -typedef struct b2Shape b2Shape; -typedef struct b2Body b2Body; // Body organizational details that are not used in the solver. typedef struct b2Body @@ -49,6 +44,11 @@ typedef struct b2Body int islandPrev; int islandNext; + float mass; + + // Rotational inertia about the center of mass. + float inertia; + float sleepThreshold; float sleepTime; @@ -112,10 +112,9 @@ typedef struct b2BodySim b2Vec2 force; float torque; - float mass, invMass; - - // Rotational inertia about the center of mass. - float inertia, invInertia; + // inverse inertia + float invMass; + float invInertia; float minExtent; float maxExtent; diff --git a/src/constants.h b/src/constants.h index 1959db6ca..f875dd0d3 100644 --- a/src/constants.h +++ b/src/constants.h @@ -10,16 +10,16 @@ extern float b2_lengthUnitsPerMeter; #define B2_HUGE ( 100000.0f * b2_lengthUnitsPerMeter ) // Maximum parallel workers. Used to size some static arrays. -#define b2_maxWorkers 64 +#define B2_MAX_WORKERS 64 // Maximum number of colors in the constraint graph. Constraints that cannot // find a color are added to the overflow set which are solved single-threaded. -#define b2_graphColorCount 12 +#define B2_GRAPH_COLOR_COUNT 12 // A small length used as a collision and constraint tolerance. Usually it is // chosen to be numerically significant, but visually insignificant. In meters. // @warning modifying this can have a significant impact on stability -#define b2_linearSlop ( 0.005f * b2_lengthUnitsPerMeter ) +#define B2_LINEAR_SLOP ( 0.005f * b2_lengthUnitsPerMeter ) // Maximum number of simultaneous worlds that can be allocated #define B2_MAX_WORLDS 128 @@ -30,15 +30,15 @@ extern float b2_lengthUnitsPerMeter; #define B2_MAX_ROTATION ( 0.25f * B2_PI ) // @warning modifying this can have a significant impact on performance and stability -#define b2_speculativeDistance ( 4.0f * b2_linearSlop ) +#define B2_SPECULATIVE_DISTANCE ( 4.0f * B2_LINEAR_SLOP ) // This is used to fatten AABBs in the dynamic tree. This allows proxies // to move by a small amount without triggering a tree adjustment. This is in meters. // @warning modifying this can have a significant impact on performance -#define b2_aabbMargin ( 0.05f * b2_lengthUnitsPerMeter ) +#define B2_AABB_MARGIN ( 0.05f * b2_lengthUnitsPerMeter ) // The time that a body must be still before it will go to sleep. In seconds. -#define b2_timeToSleep 0.5f +#define B2_TIME_TO_SLEEP 0.5f enum b2TreeNodeFlags { diff --git a/src/constraint_graph.c b/src/constraint_graph.c index 154cec989..3fc77763d 100644 --- a/src/constraint_graph.c +++ b/src/constraint_graph.c @@ -25,12 +25,12 @@ // This is used for debugging by making all constraints be assigned to overflow. #define B2_FORCE_OVERFLOW 0 -_Static_assert( b2_graphColorCount == 12, "graph color count assumed to be 12" ); +_Static_assert( B2_GRAPH_COLOR_COUNT == 12, "graph color count assumed to be 12" ); void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity ) { - _Static_assert( b2_graphColorCount >= 2, "must have at least two constraint graph colors" ); - _Static_assert( b2_overflowIndex == b2_graphColorCount - 1, "bad over flow index" ); + _Static_assert( B2_GRAPH_COLOR_COUNT >= 2, "must have at least two constraint graph colors" ); + _Static_assert( B2_OVERFLOW_INDEX == B2_GRAPH_COLOR_COUNT - 1, "bad over flow index" ); *graph = ( b2ConstraintGraph ){ 0 }; @@ -38,7 +38,7 @@ void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity ) // Initialize graph color bit set. // No bitset for overflow color. - for ( int i = 0; i < b2_overflowIndex; ++i ) + for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i ) { b2GraphColor* color = graph->colors + i; color->bodySet = b2CreateBitSet( bodyCapacity ); @@ -48,12 +48,12 @@ void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity ) void b2DestroyGraph( b2ConstraintGraph* graph ) { - for ( int i = 0; i < b2_graphColorCount; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i ) { b2GraphColor* color = graph->colors + i; // The bit set should never be used on the overflow color - B2_ASSERT( i != b2_overflowIndex || color->bodySet.bits == NULL ); + B2_ASSERT( i != B2_OVERFLOW_INDEX || color->bodySet.bits == NULL ); b2DestroyBitSet( &color->bodySet ); @@ -72,7 +72,7 @@ void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* c B2_ASSERT( contact->flags & b2_contactTouchingFlag ); b2ConstraintGraph* graph = &world->constraintGraph; - int colorIndex = b2_overflowIndex; + int colorIndex = B2_OVERFLOW_INDEX; int bodyIdA = contact->edges[0].bodyId; int bodyIdB = contact->edges[1].bodyId; @@ -85,7 +85,7 @@ void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* c #if B2_FORCE_OVERFLOW == 0 if ( staticA == false && staticB == false ) { - for ( int i = 0; i < b2_overflowIndex; ++i ) + for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i ) { b2GraphColor* color = graph->colors + i; if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) ) @@ -102,7 +102,7 @@ void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* c else if ( staticA == false ) { // No static contacts in color 0 - for ( int i = 1; i < b2_overflowIndex; ++i ) + for ( int i = 1; i < B2_OVERFLOW_INDEX; ++i ) { b2GraphColor* color = graph->colors + i; if ( b2GetBit( &color->bodySet, bodyIdA ) ) @@ -118,7 +118,7 @@ void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* c else if ( staticB == false ) { // No static contacts in color 0 - for ( int i = 1; i < b2_overflowIndex; ++i ) + for ( int i = 1; i < B2_OVERFLOW_INDEX; ++i ) { b2GraphColor* color = graph->colors + i; if ( b2GetBit( &color->bodySet, bodyIdB ) ) @@ -185,10 +185,10 @@ void b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int col { b2ConstraintGraph* graph = &world->constraintGraph; - B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = graph->colors + colorIndex; - if ( colorIndex != b2_overflowIndex ) + if ( colorIndex != B2_OVERFLOW_INDEX ) { // might clear a bit for a static body, but this has no effect b2ClearBit( &color->bodySet, bodyIdA ); @@ -218,7 +218,7 @@ static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyId #if B2_FORCE_OVERFLOW == 0 if ( staticA == false && staticB == false ) { - for ( int i = 0; i < b2_overflowIndex; ++i ) + for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i ) { b2GraphColor* color = graph->colors + i; if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) ) @@ -233,7 +233,7 @@ static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyId } else if ( staticA == false ) { - for ( int i = 0; i < b2_overflowIndex; ++i ) + for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i ) { b2GraphColor* color = graph->colors + i; if ( b2GetBit( &color->bodySet, bodyIdA ) ) @@ -247,7 +247,7 @@ static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyId } else if ( staticB == false ) { - for ( int i = 0; i < b2_overflowIndex; ++i ) + for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i ) { b2GraphColor* color = graph->colors + i; if ( b2GetBit( &color->bodySet, bodyIdB ) ) @@ -261,7 +261,7 @@ static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyId } #endif - return b2_overflowIndex; + return B2_OVERFLOW_INDEX; } b2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint ) @@ -295,10 +295,10 @@ void b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int color { b2ConstraintGraph* graph = &world->constraintGraph; - B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = graph->colors + colorIndex; - if ( colorIndex != b2_overflowIndex ) + if ( colorIndex != B2_OVERFLOW_INDEX ) { // May clear static bodies, no effect b2ClearBit( &color->bodySet, bodyIdA ); diff --git a/src/constraint_graph.h b/src/constraint_graph.h index 6b96f8215..87b17fe99 100644 --- a/src/constraint_graph.h +++ b/src/constraint_graph.h @@ -19,7 +19,7 @@ typedef struct b2World b2World; // This holds constraints that cannot fit the graph color limit. This happens when a single dynamic body // is touching many other bodies. -#define b2_overflowIndex b2_graphColorCount - 1 +#define B2_OVERFLOW_INDEX (B2_GRAPH_COLOR_COUNT - 1) typedef struct b2GraphColor { @@ -43,7 +43,7 @@ typedef struct b2GraphColor typedef struct b2ConstraintGraph { // including overflow at the end - b2GraphColor colors[b2_graphColorCount]; + b2GraphColor colors[B2_GRAPH_COLOR_COUNT]; } b2ConstraintGraph; void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity ); diff --git a/src/contact.c b/src/contact.c index 3c074c65c..06ff25763 100644 --- a/src/contact.c +++ b/src/contact.c @@ -66,9 +66,9 @@ static inline float b2MixFloats( float value1, float value2, b2MixingRule mixing } // todo make relative for all -// typedef b2Manifold b2ManifoldFcn(const b2Shape* shapeA, const b2Shape* shapeB, b2Transform xfB, b2DistanceCache* cache); +// typedef b2Manifold b2ManifoldFcn(const b2Shape* shapeA, const b2Shape* shapeB, b2Transform xfB, b2SimplexCache* cache); typedef b2Manifold b2ManifoldFcn( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ); + b2SimplexCache* cache ); struct b2ContactRegister { @@ -80,83 +80,83 @@ static struct b2ContactRegister s_registers[b2_shapeTypeCount][b2_shapeTypeCount static bool s_initialized = false; static b2Manifold b2CircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollideCircles( &shapeA->circle, xfA, &shapeB->circle, xfB ); } static b2Manifold b2CapsuleAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollideCapsuleAndCircle( &shapeA->capsule, xfA, &shapeB->circle, xfB ); } static b2Manifold b2CapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollideCapsules( &shapeA->capsule, xfA, &shapeB->capsule, xfB ); } static b2Manifold b2PolygonAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollidePolygonAndCircle( &shapeA->polygon, xfA, &shapeB->circle, xfB ); } static b2Manifold b2PolygonAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollidePolygonAndCapsule( &shapeA->polygon, xfA, &shapeB->capsule, xfB ); } static b2Manifold b2PolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollidePolygons( &shapeA->polygon, xfA, &shapeB->polygon, xfB ); } static b2Manifold b2SegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollideSegmentAndCircle( &shapeA->segment, xfA, &shapeB->circle, xfB ); } static b2Manifold b2SegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollideSegmentAndCapsule( &shapeA->segment, xfA, &shapeB->capsule, xfB ); } static b2Manifold b2SegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollideSegmentAndPolygon( &shapeA->segment, xfA, &shapeB->polygon, xfB ); } static b2Manifold b2ChainSegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { B2_MAYBE_UNUSED( cache ); return b2CollideChainSegmentAndCircle( &shapeA->chainSegment, xfA, &shapeB->circle, xfB ); } static b2Manifold b2ChainSegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, - b2Transform xfB, b2DistanceCache* cache ) + b2Transform xfB, b2SimplexCache* cache ) { return b2CollideChainSegmentAndCapsule( &shapeA->chainSegment, xfA, &shapeB->capsule, xfB, cache ); } static b2Manifold b2ChainSegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, - b2Transform xfB, b2DistanceCache* cache ) + b2Transform xfB, b2SimplexCache* cache ) { return b2CollideChainSegmentAndPolygon( &shapeA->chainSegment, xfA, &shapeB->polygon, xfB, cache ); } @@ -332,7 +332,7 @@ void b2CreateContact( b2World* world, b2Shape* shapeA, b2Shape* shapeB ) contactSim->invIB = 0.0f; contactSim->shapeIdA = shapeIdA; contactSim->shapeIdB = shapeIdB; - contactSim->cache = b2_emptyDistanceCache; + contactSim->cache = b2_emptySimplexCache; contactSim->manifold = ( b2Manifold ){ 0 }; contactSim->friction = b2MixFloats( shapeA->friction, shapeB->friction, world->frictionMixingRule ); contactSim->restitution = b2MixFloats( shapeA->restitution, shapeB->restitution, world->restitutionMixingRule ); @@ -500,7 +500,7 @@ b2ContactSim* b2GetContactSim( b2World* world, b2Contact* contact ) if ( contact->setIndex == b2_awakeSet && contact->colorIndex != B2_NULL_INDEX ) { // contact lives in constraint graph - B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = world->constraintGraph.colors + contact->colorIndex; return b2ContactSimArray_Get( &color->contactSims, contact->localIndex ); } @@ -521,7 +521,7 @@ bool b2ShouldShapesCollide( b2Filter filterA, b2Filter filterB ) } static bool b2TestShapeOverlap( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, - b2DistanceCache* cache ) + b2SimplexCache* cache ) { b2DistanceInput input; input.proxyA = b2MakeShapeDistanceProxy( shapeA ); @@ -578,12 +578,12 @@ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, // This flag is for testing if (world->enableSpeculative == false && pointCount == 2) { - if ( contactSim->manifold.points[0].separation > 1.5f * b2_linearSlop ) + if ( contactSim->manifold.points[0].separation > 1.5f * B2_LINEAR_SLOP ) { contactSim->manifold.points[0] = contactSim->manifold.points[1]; contactSim->manifold.pointCount = 1; } - else if ( contactSim->manifold.points[0].separation > 1.5f * b2_linearSlop ) + else if ( contactSim->manifold.points[0].separation > 1.5f * B2_LINEAR_SLOP ) { contactSim->manifold.pointCount = 1; } @@ -688,6 +688,6 @@ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, b2Manifold b2ComputeManifold(b2Shape* shapeA, b2Transform transformA, b2Shape* shapeB, b2Transform transformB) { b2ManifoldFcn* fcn = s_registers[shapeA->type][shapeB->type].fcn; - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; return fcn( shapeA, transformA, shapeB, transformB, &cache ); } diff --git a/src/contact.h b/src/contact.h index 4c9d6667f..f11a61452 100644 --- a/src/contact.h +++ b/src/contact.h @@ -138,7 +138,7 @@ typedef struct b2ContactSim // b2ContactSimFlags uint32_t simFlags; - b2DistanceCache cache; + b2SimplexCache cache; } b2ContactSim; void b2InitializeContactRegisters( void ); diff --git a/src/contact_solver.c b/src/contact_solver.c index 42ecb4ac6..91a57382e 100644 --- a/src/contact_solver.c +++ b/src/contact_solver.c @@ -18,7 +18,7 @@ void b2PrepareOverflowContacts( b2StepContext* context ) b2World* world = context->world; b2ConstraintGraph* graph = context->graph; - b2GraphColor* color = graph->colors + b2_overflowIndex; + b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX; b2ContactConstraint* constraints = color->overflowConstraints; int contactCount = color->contactSims.count; b2ContactSim* contacts = color->contactSims.data; @@ -145,7 +145,7 @@ void b2WarmStartOverflowContacts( b2StepContext* context ) b2TracyCZoneNC( warmstart_overflow_contact, "WarmStart Overflow Contact", b2_colorDarkOrange, true ); b2ConstraintGraph* graph = context->graph; - b2GraphColor* color = graph->colors + b2_overflowIndex; + b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX; b2ContactConstraint* constraints = color->overflowConstraints; int contactCount = color->contactSims.count; b2World* world = context->world; @@ -209,7 +209,7 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) b2TracyCZoneNC( solve_contact, "Solve Contact", b2_colorAliceBlue, true ); b2ConstraintGraph* graph = context->graph; - b2GraphColor* color = graph->colors + b2_overflowIndex; + b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX; b2ContactConstraint* constraints = color->overflowConstraints; int contactCount = color->contactSims.count; b2World* world = context->world; @@ -217,7 +217,7 @@ void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) b2BodyState* states = awakeSet->bodyStates.data; float inv_h = context->inv_h; - const float pushout = context->world->contactPushoutVelocity; + const float pushout = context->world->contactPushSpeed; // This is a dummy body to represent a static body since static bodies don't have a solver body. b2BodyState dummyState = b2_identityBodyState; @@ -344,7 +344,7 @@ void b2ApplyOverflowRestitution( b2StepContext* context ) b2TracyCZoneNC( overflow_resitution, "Overflow Restitution", b2_colorViolet, true ); b2ConstraintGraph* graph = context->graph; - b2GraphColor* color = graph->colors + b2_overflowIndex; + b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX; b2ContactConstraint* constraints = color->overflowConstraints; int contactCount = color->contactSims.count; b2World* world = context->world; @@ -440,7 +440,7 @@ void b2StoreOverflowImpulses( b2StepContext* context ) b2TracyCZoneNC( store_impulses, "Store", b2_colorFirebrick, true ); b2ConstraintGraph* graph = context->graph; - b2GraphColor* color = graph->colors + b2_overflowIndex; + b2GraphColor* color = graph->colors + B2_OVERFLOW_INDEX; b2ContactConstraint* constraints = color->overflowConstraints; b2ContactSim* contacts = color->contactSims.data; int contactCount = color->contactSims.count; @@ -1582,7 +1582,7 @@ void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, b2BodyState* states = context->states; b2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints; b2FloatW inv_h = b2SplatW( context->inv_h ); - b2FloatW minBiasVel = b2SplatW( -context->world->contactPushoutVelocity ); + b2FloatW minBiasVel = b2SplatW( -context->world->contactPushSpeed ); for ( int i = startIndex; i < endIndex; ++i ) { diff --git a/src/distance.c b/src/distance.c index c9846981e..b379013f4 100644 --- a/src/distance.c +++ b/src/distance.c @@ -107,10 +107,10 @@ b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Ve // GJK using Voronoi regions (Christer Ericson) and Barycentric coordinates. // todo try not copying -b2DistanceProxy b2MakeProxy( const b2Vec2* vertices, int count, float radius ) +b2ShapeProxy b2MakeProxy( const b2Vec2* vertices, int count, float radius ) { count = b2MinInt( count, B2_MAX_POLYGON_VERTICES ); - b2DistanceProxy proxy; + b2ShapeProxy proxy; for ( int i = 0; i < count; ++i ) { proxy.points[i] = vertices[i]; @@ -130,7 +130,7 @@ static b2Vec2 b2Weight3( float a1, b2Vec2 w1, float a2, b2Vec2 w2, float a3, b2V return ( b2Vec2 ){ a1 * w1.x + a2 * w2.x + a3 * w3.x, a1 * w1.y + a2 * w2.y + a3 * w3.y }; } -static int b2FindSupport( const b2DistanceProxy* proxy, b2Vec2 direction ) +static int b2FindSupport( const b2ShapeProxy* proxy, b2Vec2 direction ) { int bestIndex = 0; float bestValue = b2Dot( proxy->points[0], direction ); @@ -147,8 +147,8 @@ static int b2FindSupport( const b2DistanceProxy* proxy, b2Vec2 direction ) return bestIndex; } -static b2Simplex b2MakeSimplexFromCache( const b2DistanceCache* cache, const b2DistanceProxy* proxyA, b2Transform transformA, - const b2DistanceProxy* proxyB, b2Transform transformB ) +static b2Simplex b2MakeSimplexFromCache( const b2SimplexCache* cache, const b2ShapeProxy* proxyA, b2Transform transformA, + const b2ShapeProxy* proxyB, b2Transform transformB ) { B2_ASSERT( cache->count <= 3 ); b2Simplex s; @@ -190,7 +190,7 @@ static b2Simplex b2MakeSimplexFromCache( const b2DistanceCache* cache, const b2D return s; } -static void b2MakeSimplexCache( b2DistanceCache* cache, const b2Simplex* simplex ) +static void b2MakeSimplexCache( b2SimplexCache* cache, const b2Simplex* simplex ) { cache->count = (uint16_t)simplex->count; const b2SimplexVertex* vertices[] = { &simplex->v1, &simplex->v2, &simplex->v3 }; @@ -457,13 +457,13 @@ void b2SolveSimplex3( b2Simplex* s ) s->count = 3; } -b2DistanceOutput b2ShapeDistance( b2DistanceCache* cache, const b2DistanceInput* input, b2Simplex* simplexes, +b2DistanceOutput b2ShapeDistance( b2SimplexCache* cache, const b2DistanceInput* input, b2Simplex* simplexes, int simplexCapacity ) { b2DistanceOutput output = { 0 }; - const b2DistanceProxy* proxyA = &input->proxyA; - const b2DistanceProxy* proxyB = &input->proxyB; + const b2ShapeProxy* proxyA = &input->proxyA; + const b2ShapeProxy* proxyB = &input->proxyB; b2Transform transformA = input->transformA; b2Transform transformB = input->transformB; @@ -628,14 +628,14 @@ b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input ) b2CastOutput output = { 0 }; output.fraction = input->maxFraction; - b2DistanceProxy proxyA = input->proxyA; + b2ShapeProxy proxyA = input->proxyA; b2Transform xfA = input->transformA; b2Transform xfB = input->transformB; b2Transform xf = b2InvMulTransforms( xfA, xfB ); // Put proxyB in proxyA's frame to reduce round-off error - b2DistanceProxy proxyB; + b2ShapeProxy proxyB; proxyB.count = input->proxyB.count; proxyB.radius = input->proxyB.radius; B2_ASSERT( proxyB.count <= B2_MAX_POLYGON_VERTICES ); @@ -666,7 +666,7 @@ b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input ) b2Vec2 v = b2Sub( wA, wB ); // Sigma is the target distance between proxies - const float linearSlop = b2_linearSlop; + const float linearSlop = B2_LINEAR_SLOP; const float sigma = b2MaxFloat( linearSlop, radius - linearSlop ); // Main iteration loop. @@ -798,16 +798,16 @@ typedef enum b2SeparationType typedef struct b2SeparationFunction { - const b2DistanceProxy* proxyA; - const b2DistanceProxy* proxyB; + const b2ShapeProxy* proxyA; + const b2ShapeProxy* proxyB; b2Sweep sweepA, sweepB; b2Vec2 localPoint; b2Vec2 axis; b2SeparationType type; } b2SeparationFunction; -b2SeparationFunction b2MakeSeparationFunction( const b2DistanceCache* cache, const b2DistanceProxy* proxyA, const b2Sweep* sweepA, - const b2DistanceProxy* proxyB, const b2Sweep* sweepB, float t1 ) +b2SeparationFunction b2MakeSeparationFunction( const b2SimplexCache* cache, const b2ShapeProxy* proxyA, const b2Sweep* sweepA, + const b2ShapeProxy* proxyB, const b2Sweep* sweepB, float t1 ) { b2SeparationFunction f; @@ -1010,7 +1010,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) b2TOIOutput output; output.state = b2_toiStateUnknown; - output.t = input->tMax; + output.fraction = input->maxFraction; b2Sweep sweepA = input->sweepA; b2Sweep sweepB = input->sweepB; @@ -1021,16 +1021,16 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) // c1 can be at the origin yet the points are far away // b2Vec2 origin = b2Add(sweepA.c1, input->proxyA.points[0]); - const b2DistanceProxy* proxyA = &input->proxyA; - const b2DistanceProxy* proxyB = &input->proxyB; + const b2ShapeProxy* proxyA = &input->proxyA; + const b2ShapeProxy* proxyB = &input->proxyB; - float tMax = input->tMax; + float tMax = input->maxFraction; float totalRadius = proxyA->radius + proxyB->radius; // todo_erin consider different target - // float target = b2MaxFloat( b2_linearSlop, totalRadius ); - float target = b2MaxFloat( b2_linearSlop, totalRadius - b2_linearSlop ); - float tolerance = 0.25f * b2_linearSlop; + // float target = b2MaxFloat( B2_LINEAR_SLOP, totalRadius ); + float target = b2MaxFloat( B2_LINEAR_SLOP, totalRadius - B2_LINEAR_SLOP ); + float tolerance = 0.25f * B2_LINEAR_SLOP; B2_ASSERT( target > tolerance ); float t1 = 0.0f; @@ -1038,7 +1038,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) int distanceIterations = 0; // Prepare input for distance query. - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; b2DistanceInput distanceInput; distanceInput.proxyA = input->proxyA; distanceInput.proxyB = input->proxyB; @@ -1070,7 +1070,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) #if B2_TOI_DEBUG b2_toiOverlappedCount += 1; #endif - output.t = 0.0f; + output.fraction = 0.0f; break; } @@ -1081,7 +1081,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) #if B2_TOI_DEBUG b2_toiHitCount += 1; #endif - output.t = t1; + output.fraction = t1; break; } @@ -1132,7 +1132,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) #if B2_TOI_DEBUG b2_toiSeparatedCount += 1; #endif - output.t = tMax; + output.fraction = tMax; done = true; break; } @@ -1156,7 +1156,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) #if B2_TOI_DEBUG b2_toiFailedCount += 1; #endif - output.t = t1; + output.fraction = t1; done = true; break; } @@ -1169,7 +1169,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) #if B2_TOI_DEBUG b2_toiHitCount += 1; #endif - output.t = t1; + output.fraction = t1; done = true; break; } @@ -1249,7 +1249,7 @@ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) #if B2_TOI_DEBUG b2_toiFailedCount += 1; #endif - output.t = t1; + output.fraction = t1; break; } } diff --git a/src/distance_joint.c b/src/distance_joint.c index 854c838cc..bdbb44bd7 100644 --- a/src/distance_joint.c +++ b/src/distance_joint.c @@ -22,7 +22,7 @@ void b2DistanceJoint_SetLength( b2JointId jointId, float length ) b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); b2DistanceJoint* joint = &base->distanceJoint; - joint->length = b2ClampFloat( length, b2_linearSlop, B2_HUGE ); + joint->length = b2ClampFloat( length, B2_LINEAR_SLOP, B2_HUGE ); joint->impulse = 0.0f; joint->lowerImpulse = 0.0f; joint->upperImpulse = 0.0f; @@ -53,8 +53,8 @@ void b2DistanceJoint_SetLengthRange( b2JointId jointId, float minLength, float m b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); b2DistanceJoint* joint = &base->distanceJoint; - minLength = b2ClampFloat( minLength, b2_linearSlop, B2_HUGE ); - maxLength = b2ClampFloat( maxLength, b2_linearSlop, B2_HUGE ); + minLength = b2ClampFloat( minLength, B2_LINEAR_SLOP, B2_HUGE ); + maxLength = b2ClampFloat( maxLength, B2_LINEAR_SLOP, B2_HUGE ); joint->minLength = b2MinFloat( minLength, maxLength ); joint->maxLength = b2MaxFloat( minLength, maxLength ); joint->impulse = 0.0f; @@ -526,7 +526,7 @@ void b2DrawDistanceJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform trans b2Vec2 pMax = b2MulAdd( pA, joint->maxLength, axis ); b2Vec2 offset = b2MulSV( 0.05f * b2_lengthUnitsPerMeter, b2RightPerp( axis ) ); - if ( joint->minLength > b2_linearSlop ) + if ( joint->minLength > B2_LINEAR_SLOP ) { // draw->DrawPoint(pMin, 4.0f, c2, draw->context); draw->DrawSegment( b2Sub( pMin, offset ), b2Add( pMin, offset ), b2_colorLightGreen, draw->context ); @@ -538,7 +538,7 @@ void b2DrawDistanceJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform trans draw->DrawSegment( b2Sub( pMax, offset ), b2Add( pMax, offset ), b2_colorRed, draw->context ); } - if ( joint->minLength > b2_linearSlop && joint->maxLength < B2_HUGE ) + if ( joint->minLength > B2_LINEAR_SLOP && joint->maxLength < B2_HUGE ) { draw->DrawSegment( pMin, pMax, b2_colorGray, draw->context ); } diff --git a/src/dynamic_tree.c b/src/dynamic_tree.c index 37f88f596..f196d3a41 100644 --- a/src/dynamic_tree.c +++ b/src/dynamic_tree.c @@ -1808,7 +1808,7 @@ int32_t b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild ) // considered leaves in the tree rebuild. // Free all internal nodes that have grown. // todo use a node growth metric instead of simply enlarged to reduce rebuild size and frequency - // this should be weighed against b2_aabbMargin + // this should be weighed against B2_AABB_MARGIN while ( true ) { if ( node->height == 0 || ( ( node->flags & b2_enlargedNode ) == 0 && fullBuild == false ) ) diff --git a/src/geometry.c b/src/geometry.c index 712656be8..0919d5d41 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -130,22 +130,22 @@ b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot return shape; } -b2Polygon b2MakeSquare( float h ) +b2Polygon b2MakeSquare( float halfWidth ) { - return b2MakeBox( h, h ); + return b2MakeBox( halfWidth, halfWidth ); } -b2Polygon b2MakeBox( float hx, float hy ) +b2Polygon b2MakeBox( float halfWidth, float halfHeight ) { - B2_ASSERT( b2IsValidFloat( hx ) && hx > 0.0f ); - B2_ASSERT( b2IsValidFloat( hy ) && hy > 0.0f ); + B2_ASSERT( b2IsValidFloat( halfWidth ) && halfWidth > 0.0f ); + B2_ASSERT( b2IsValidFloat( halfHeight ) && halfHeight > 0.0f ); b2Polygon shape = { 0 }; shape.count = 4; - shape.vertices[0] = ( b2Vec2 ){ -hx, -hy }; - shape.vertices[1] = ( b2Vec2 ){ hx, -hy }; - shape.vertices[2] = ( b2Vec2 ){ hx, hy }; - shape.vertices[3] = ( b2Vec2 ){ -hx, hy }; + shape.vertices[0] = ( b2Vec2 ){ -halfWidth, -halfHeight }; + shape.vertices[1] = ( b2Vec2 ){ halfWidth, -halfHeight }; + shape.vertices[2] = ( b2Vec2 ){ halfWidth, halfHeight }; + shape.vertices[3] = ( b2Vec2 ){ -halfWidth, halfHeight }; shape.normals[0] = ( b2Vec2 ){ 0.0f, -1.0f }; shape.normals[1] = ( b2Vec2 ){ 1.0f, 0.0f }; shape.normals[2] = ( b2Vec2 ){ 0.0f, 1.0f }; @@ -155,24 +155,24 @@ b2Polygon b2MakeBox( float hx, float hy ) return shape; } -b2Polygon b2MakeRoundedBox( float hx, float hy, float radius ) +b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius ) { B2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f ); - b2Polygon shape = b2MakeBox( hx, hy ); + b2Polygon shape = b2MakeBox( halfWidth, halfHeight ); shape.radius = radius; return shape; } -b2Polygon b2MakeOffsetBox( float hx, float hy, b2Vec2 center, b2Rot rotation ) +b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation ) { b2Transform xf = { center, rotation }; b2Polygon shape = { 0 }; shape.count = 4; - shape.vertices[0] = b2TransformPoint( xf, ( b2Vec2 ){ -hx, -hy } ); - shape.vertices[1] = b2TransformPoint( xf, ( b2Vec2 ){ hx, -hy } ); - shape.vertices[2] = b2TransformPoint( xf, ( b2Vec2 ){ hx, hy } ); - shape.vertices[3] = b2TransformPoint( xf, ( b2Vec2 ){ -hx, hy } ); + shape.vertices[0] = b2TransformPoint( xf, ( b2Vec2 ){ -halfWidth, -halfHeight } ); + shape.vertices[1] = b2TransformPoint( xf, ( b2Vec2 ){ halfWidth, -halfHeight } ); + shape.vertices[2] = b2TransformPoint( xf, ( b2Vec2 ){ halfWidth, halfHeight } ); + shape.vertices[3] = b2TransformPoint( xf, ( b2Vec2 ){ -halfWidth, halfHeight } ); shape.normals[0] = b2RotateVector( xf.q, ( b2Vec2 ){ 0.0f, -1.0f } ); shape.normals[1] = b2RotateVector( xf.q, ( b2Vec2 ){ 1.0f, 0.0f } ); shape.normals[2] = b2RotateVector( xf.q, ( b2Vec2 ){ 0.0f, 1.0f } ); @@ -182,17 +182,17 @@ b2Polygon b2MakeOffsetBox( float hx, float hy, b2Vec2 center, b2Rot rotation ) return shape; } -b2Polygon b2MakeOffsetRoundedBox( float hx, float hy, b2Vec2 center, b2Rot rotation, float radius ) +b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius ) { B2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f ); b2Transform xf = { center, rotation }; b2Polygon shape = { 0 }; shape.count = 4; - shape.vertices[0] = b2TransformPoint( xf, ( b2Vec2 ){ -hx, -hy } ); - shape.vertices[1] = b2TransformPoint( xf, ( b2Vec2 ){ hx, -hy } ); - shape.vertices[2] = b2TransformPoint( xf, ( b2Vec2 ){ hx, hy } ); - shape.vertices[3] = b2TransformPoint( xf, ( b2Vec2 ){ -hx, hy } ); + shape.vertices[0] = b2TransformPoint( xf, ( b2Vec2 ){ -halfWidth, -halfHeight } ); + shape.vertices[1] = b2TransformPoint( xf, ( b2Vec2 ){ halfWidth, -halfHeight } ); + shape.vertices[2] = b2TransformPoint( xf, ( b2Vec2 ){ halfWidth, halfHeight } ); + shape.vertices[3] = b2TransformPoint( xf, ( b2Vec2 ){ -halfWidth, halfHeight } ); shape.normals[0] = b2RotateVector( xf.q, ( b2Vec2 ){ 0.0f, -1.0f } ); shape.normals[1] = b2RotateVector( xf.q, ( b2Vec2 ){ 1.0f, 0.0f } ); shape.normals[2] = b2RotateVector( xf.q, ( b2Vec2 ){ 0.0f, 1.0f } ); @@ -495,7 +495,7 @@ bool b2PointInPolygon( b2Vec2 point, const b2Polygon* shape ) input.transformB = b2Transform_identity; input.useRadii = false; - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); return output.distance <= shape->radius; @@ -550,6 +550,7 @@ b2CastOutput b2RayCastCircle( const b2RayCastInput* input, const b2Circle* shape return output; } + // hit point relative to center b2Vec2 hitPoint = b2MulAdd( s, fraction, d ); output.fraction = fraction / length; diff --git a/src/hull.c b/src/hull.c index 6fe70b0f3..27cc3953a 100644 --- a/src/hull.c +++ b/src/hull.c @@ -49,7 +49,7 @@ static b2Hull b2RecurseHull( b2Vec2 p1, b2Vec2 p2, b2Vec2* ps, int count ) } } - if ( bestDistance < 2.0f * b2_linearSlop ) + if ( bestDistance < 2.0f * B2_LINEAR_SLOP ) { return hull; } @@ -81,8 +81,8 @@ static b2Hull b2RecurseHull( b2Vec2 p1, b2Vec2 p2, b2Vec2* ps, int count ) } // quickhull algorithm -// - merges vertices based on b2_linearSlop -// - removes collinear points using b2_linearSlop +// - merges vertices based on B2_LINEAR_SLOP +// - removes collinear points using B2_LINEAR_SLOP // - returns an empty hull if it fails b2Hull b2ComputeHull( const b2Vec2* points, int count ) { @@ -103,7 +103,7 @@ b2Hull b2ComputeHull( const b2Vec2* points, int count ) // Also compute the bounding box for later. b2Vec2 ps[B2_MAX_POLYGON_VERTICES]; int n = 0; - const float linearSlop = b2_linearSlop; + const float linearSlop = B2_LINEAR_SLOP; const float tolSqr = 16.0f * linearSlop * linearSlop; for ( int i = 0; i < count; ++i ) { @@ -303,7 +303,7 @@ bool b2ValidateHull( const b2Hull* hull ) } // test for collinear points - const float linearSlop = b2_linearSlop; + const float linearSlop = B2_LINEAR_SLOP; for ( int i = 0; i < hull->count; ++i ) { int i1 = i; diff --git a/src/island.h b/src/island.h index c43346413..1bc5a1530 100644 --- a/src/island.h +++ b/src/island.h @@ -8,10 +8,8 @@ #include #include -typedef struct b2Body b2Body; typedef struct b2Contact b2Contact; typedef struct b2Joint b2Joint; -typedef struct b2StepContext b2StepContext; typedef struct b2World b2World; // Deterministic solver @@ -58,10 +56,10 @@ typedef struct b2Island int constraintRemoveCount; } b2Island; +// This is used to move islands across solver sets typedef struct b2IslandSim { int islandId; - } b2IslandSim; b2Island* b2CreateIsland( b2World* world, int setIndex ); diff --git a/src/joint.c b/src/joint.c index 67da93ac3..e16ce8350 100644 --- a/src/joint.c +++ b/src/joint.c @@ -110,7 +110,7 @@ b2JointSim* b2GetJointSim( b2World* world, b2Joint* joint ) { if ( joint->setIndex == b2_awakeSet ) { - B2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = world->constraintGraph.colors + joint->colorIndex; return b2JointSimArray_Get( &color->jointSims, joint->localIndex ); } @@ -370,10 +370,10 @@ b2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* de b2DistanceJoint empty = { 0 }; joint->distanceJoint = empty; - joint->distanceJoint.length = b2MaxFloat( def->length, b2_linearSlop ); + joint->distanceJoint.length = b2MaxFloat( def->length, B2_LINEAR_SLOP ); joint->distanceJoint.hertz = def->hertz; joint->distanceJoint.dampingRatio = def->dampingRatio; - joint->distanceJoint.minLength = b2MaxFloat( def->minLength, b2_linearSlop ); + joint->distanceJoint.minLength = b2MaxFloat( def->minLength, B2_LINEAR_SLOP ); joint->distanceJoint.maxLength = b2MaxFloat( def->minLength, def->maxLength ); joint->distanceJoint.maxMotorForce = def->maxMotorForce; joint->distanceJoint.motorSpeed = def->motorSpeed; @@ -1182,8 +1182,8 @@ void b2PrepareOverflowJoints( b2StepContext* context ) b2TracyCZoneNC( prepare_joints, "PrepJoints", b2_colorOldLace, true ); b2ConstraintGraph* graph = context->graph; - b2JointSim* joints = graph->colors[b2_overflowIndex].jointSims.data; - int jointCount = graph->colors[b2_overflowIndex].jointSims.count; + b2JointSim* joints = graph->colors[B2_OVERFLOW_INDEX].jointSims.data; + int jointCount = graph->colors[B2_OVERFLOW_INDEX].jointSims.count; for ( int i = 0; i < jointCount; ++i ) { @@ -1199,8 +1199,8 @@ void b2WarmStartOverflowJoints( b2StepContext* context ) b2TracyCZoneNC( prepare_joints, "PrepJoints", b2_colorOldLace, true ); b2ConstraintGraph* graph = context->graph; - b2JointSim* joints = graph->colors[b2_overflowIndex].jointSims.data; - int jointCount = graph->colors[b2_overflowIndex].jointSims.count; + b2JointSim* joints = graph->colors[B2_OVERFLOW_INDEX].jointSims.data; + int jointCount = graph->colors[B2_OVERFLOW_INDEX].jointSims.count; for ( int i = 0; i < jointCount; ++i ) { @@ -1216,8 +1216,8 @@ void b2SolveOverflowJoints( b2StepContext* context, bool useBias ) b2TracyCZoneNC( solve_joints, "SolveJoints", b2_colorLemonChiffon, true ); b2ConstraintGraph* graph = context->graph; - b2JointSim* joints = graph->colors[b2_overflowIndex].jointSims.data; - int jointCount = graph->colors[b2_overflowIndex].jointSims.count; + b2JointSim* joints = graph->colors[B2_OVERFLOW_INDEX].jointSims.data; + int jointCount = graph->colors[B2_OVERFLOW_INDEX].jointSims.count; for ( int i = 0; i < jointCount; ++i ) { @@ -1297,7 +1297,7 @@ void b2DrawJoint( b2DebugDraw* draw, b2World* world, b2Joint* joint ) if ( draw->drawGraphColors ) { - b2HexColor colors[b2_graphColorCount] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, + b2HexColor colors[B2_GRAPH_COLOR_COUNT] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, b2_colorChocolate, b2_colorGoldenRod, b2_colorCoral, b2_colorBlack }; diff --git a/src/manifold.c b/src/manifold.c index 9927158de..be98e6f21 100644 --- a/src/manifold.c +++ b/src/manifold.c @@ -52,7 +52,7 @@ b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2C float radiusB = circleB->radius; float separation = distance - radiusA - radiusB; - if ( separation > b2_speculativeDistance ) + if ( separation > B2_SPECULATIVE_DISTANCE ) { return manifold; } @@ -117,7 +117,7 @@ b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA float radiusA = capsuleA->radius; float radiusB = circleB->radius; float separation = distance - radiusA - radiusB; - if ( separation > b2_speculativeDistance ) + if ( separation > B2_SPECULATIVE_DISTANCE ) { return manifold; } @@ -140,7 +140,7 @@ b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB ) { b2Manifold manifold = { 0 }; - const float speculativeDistance = b2_speculativeDistance; + const float speculativeDistance = B2_SPECULATIVE_DISTANCE; b2Transform xf = b2InvMulTransforms( xfA, xfB ); @@ -322,7 +322,7 @@ b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const float radiusA = capsuleA->radius; float radiusB = capsuleB->radius; float radius = radiusA + radiusB; - float maxDistance = radius + b2_speculativeDistance; + float maxDistance = radius + B2_SPECULATIVE_DISTANCE; if ( distanceSquared > maxDistance * maxDistance ) { @@ -423,7 +423,7 @@ b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const float sp = b2Dot( b2Sub( cp, p1 ), normalA ); float sq = b2Dot( b2Sub( cq, p1 ), normalA ); - if ( sp <= distance + b2_linearSlop || sq <= distance + b2_linearSlop ) + if ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP ) { b2ManifoldPoint* mp; mp = manifold.points + 0; @@ -469,7 +469,7 @@ b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const float sp = b2Dot( b2Sub( cp, p2 ), normalB ); float sq = b2Dot( b2Sub( cq, p2 ), normalB ); - if ( sp <= distance + b2_linearSlop || sq <= distance + b2_linearSlop ) + if ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP ) { b2ManifoldPoint* mp; mp = manifold.points + 0; @@ -722,7 +722,7 @@ static float b2FindMaxSeparation( int32_t* edgeIndex, const b2Polygon* poly1, co // if (separation > speculation_distance) // return // find reference and incident edge -// if separation >= 0.1f * b2_linearSlop +// if separation >= 0.1f * B2_LINEAR_SLOP // compute closest points between reference and incident edge // if vertices are closest // single vertex-vertex contact @@ -773,7 +773,7 @@ b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const float radius = localPolyA.radius + localPolyB.radius; - if ( separationA > b2_speculativeDistance + radius || separationB > b2_speculativeDistance + radius ) + if ( separationA > B2_SPECULATIVE_DISTANCE + radius || separationB > B2_SPECULATIVE_DISTANCE + radius ) { return ( b2Manifold ){ 0 }; } @@ -827,7 +827,7 @@ b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const // Using slop here to ensure vertex-vertex normal vectors can be safely normalized // todo this means edge clipping needs to handle slightly non-overlapping edges. - if ( separationA > 0.1f * b2_linearSlop || separationB > 0.1f * b2_linearSlop ) + if ( separationA > 0.1f * B2_LINEAR_SLOP || separationB > 0.1f * B2_LINEAR_SLOP ) { // Polygons are disjoint. Find closest points between reference edge and incident edge // Reference edge on polygon A @@ -849,7 +849,7 @@ b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Vec2 normal = b2Sub( v21, v11 ); B2_ASSERT( result.distanceSquared > 0.0f ); float distance = sqrtf( result.distanceSquared ); - if ( distance > b2_speculativeDistance + radius ) + if ( distance > B2_SPECULATIVE_DISTANCE + radius ) { return manifold; } @@ -872,7 +872,7 @@ b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Vec2 normal = b2Sub( v22, v11 ); B2_ASSERT( result.distanceSquared > 0.0f ); float distance = sqrtf( result.distanceSquared ); - if ( distance > b2_speculativeDistance + radius ) + if ( distance > B2_SPECULATIVE_DISTANCE + radius ) { return manifold; } @@ -895,7 +895,7 @@ b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Vec2 normal = b2Sub( v21, v12 ); B2_ASSERT( result.distanceSquared > 0.0f ); float distance = sqrtf( result.distanceSquared ); - if ( distance > b2_speculativeDistance + radius ) + if ( distance > B2_SPECULATIVE_DISTANCE + radius ) { return manifold; } @@ -918,7 +918,7 @@ b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Vec2 normal = b2Sub( v22, v12 ); B2_ASSERT( result.distanceSquared > 0.0f ); float distance = sqrtf( result.distanceSquared ); - if ( distance > b2_speculativeDistance + radius ) + if ( distance > B2_SPECULATIVE_DISTANCE + radius ) { return manifold; } @@ -1044,7 +1044,7 @@ b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Tra float radius = circleB->radius; float separation = distance - radius; - if ( separation > b2_speculativeDistance ) + if ( separation > B2_SPECULATIVE_DISTANCE ) { return manifold; } @@ -1066,7 +1066,7 @@ b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Tra } b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, - b2Transform xfB, b2DistanceCache* cache ) + b2Transform xfB, b2SimplexCache* cache ) { b2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius ); return b2CollideChainSegmentAndPolygon( segmentA, xfA, &polyB, xfB, cache ); @@ -1208,7 +1208,7 @@ static enum b2NormalType b2ClassifyNormal( struct b2ChainSegmentParams params, b } b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB, - b2Transform xfB, b2DistanceCache* cache ) + b2Transform xfB, b2SimplexCache* cache ) { b2Manifold manifold = { 0 }; @@ -1275,7 +1275,7 @@ b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Tr b2DistanceOutput output = b2ShapeDistance( cache, &input, NULL, 0 ); - if ( output.distance > radiusB + b2_speculativeDistance ) + if ( output.distance > radiusB + B2_SPECULATIVE_DISTANCE ) { return manifold; } @@ -1288,7 +1288,7 @@ b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Tr int32_t incidentIndex = -1; int32_t incidentNormal = -1; - if ( behind1 == false && output.distance > 0.1f * b2_linearSlop ) + if ( behind1 == false && output.distance > 0.1f * B2_LINEAR_SLOP ) { // The closest features may be two vertices or an edge and a vertex even when there should // be face contact diff --git a/src/shape.c b/src/shape.c index 2f4b5c562..0a2d2cf6b 100644 --- a/src/shape.c +++ b/src/shape.c @@ -35,8 +35,8 @@ static b2ChainShape* b2GetChainShape( b2World* world, b2ChainId chainId ) static void b2UpdateShapeAABBs( b2Shape* shape, b2Transform transform, b2BodyType proxyType ) { // Compute a bounding box with a speculative margin - const float speculativeDistance = b2_speculativeDistance; - const float aabbMargin = b2_aabbMargin; + const float speculativeDistance = B2_SPECULATIVE_DISTANCE; + const float aabbMargin = B2_AABB_MARGIN; b2AABB aabb = b2ComputeShapeAABB( shape, transform ); aabb.lowerBound.x -= speculativeDistance; @@ -183,7 +183,7 @@ b2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2C b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule ) { float lengthSqr = b2DistanceSquared( capsule->center1, capsule->center2 ); - if ( lengthSqr <= b2_linearSlop * b2_linearSlop ) + if ( lengthSqr <= B2_LINEAR_SLOP * B2_LINEAR_SLOP ) { b2Circle circle = { b2Lerp( capsule->center1, capsule->center2, 0.5f ), capsule->radius }; return b2CreateShape( bodyId, def, &circle, b2_circleShape ); @@ -201,7 +201,7 @@ b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2 b2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment ) { float lengthSqr = b2DistanceSquared( segment->point1, segment->point2 ); - if ( lengthSqr <= b2_linearSlop * b2_linearSlop ) + if ( lengthSqr <= B2_LINEAR_SLOP * B2_LINEAR_SLOP ) { B2_ASSERT( false ); return b2_nullShapeId; @@ -399,7 +399,7 @@ void b2DestroyChain( b2ChainId chainId ) { b2World* world = b2GetWorldLocked( chainId.world0 ); - b2ChainShape* chain = b2GetChainShape(world, chainId); + b2ChainShape* chain = b2GetChainShape( world, chainId ); bool wakeBodies = true; b2Body* body = b2BodyArray_Get( &world->bodies, chain->bodyId ); @@ -443,25 +443,25 @@ void b2DestroyChain( b2ChainId chainId ) b2ValidateSolverSets( world ); } -b2WorldId b2Chain_GetWorld(b2ChainId chainId) +b2WorldId b2Chain_GetWorld( b2ChainId chainId ) { b2World* world = b2GetWorld( chainId.world0 ); return ( b2WorldId ){ chainId.world0 + 1, world->revision }; } -int b2Chain_GetSegmentCount(b2ChainId chainId) +int b2Chain_GetSegmentCount( b2ChainId chainId ) { b2World* world = b2GetWorldLocked( chainId.world0 ); b2ChainShape* chain = b2GetChainShape( world, chainId ); return chain->count; } -int b2Chain_GetSegments(b2ChainId chainId, b2ShapeId* segmentArray, int capacity) +int b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int capacity ) { b2World* world = b2GetWorldLocked( chainId.world0 ); b2ChainShape* chain = b2GetChainShape( world, chainId ); - int count = b2MinInt(chain->count, capacity); + int count = b2MinInt( chain->count, capacity ); for ( int i = 0; i < count; ++i ) { int shapeId = chain->shapeIndices[i]; @@ -514,7 +514,7 @@ b2Vec2 b2GetShapeCentroid( const b2Shape* shape ) } } -// todo maybe compute this on shape creation +// todo_erin maybe compute this on shape creation float b2GetShapePerimeter( const b2Shape* shape ) { switch ( shape->type ) @@ -549,7 +549,7 @@ float b2GetShapePerimeter( const b2Shape* shape ) } } -// This projects the the shape perimeter onto an infinite line +// This projects the shape perimeter onto an infinite line float b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line ) { switch ( shape->type ) @@ -579,7 +579,7 @@ float b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line ) upper = b2MaxFloat( upper, value ); } - return (upper - lower) + 2.0f * shape->polygon.radius; + return ( upper - lower ) + 2.0f * shape->polygon.radius; } case b2_segmentShape: @@ -612,9 +612,7 @@ b2MassData b2ComputeShapeMass( const b2Shape* shape ) case b2_polygonShape: return b2ComputePolygonMass( &shape->polygon, shape->density ); default: - { return ( b2MassData ){ 0 }; - } } } @@ -780,7 +778,7 @@ void b2DestroyShapeProxy( b2Shape* shape, b2BroadPhase* bp ) } } -b2DistanceProxy b2MakeShapeDistanceProxy( const b2Shape* shape ) +b2ShapeProxy b2MakeShapeDistanceProxy( const b2Shape* shape ) { switch ( shape->type ) { @@ -797,7 +795,7 @@ b2DistanceProxy b2MakeShapeDistanceProxy( const b2Shape* shape ) default: { B2_ASSERT( false ); - b2DistanceProxy empty = { 0 }; + b2ShapeProxy empty = { 0 }; return empty; } } @@ -810,7 +808,7 @@ b2BodyId b2Shape_GetBody( b2ShapeId shapeId ) return b2MakeBodyId( world, shape->bodyId ); } -b2WorldId b2Shape_GetWorld(b2ShapeId shapeId) +b2WorldId b2Shape_GetWorld( b2ShapeId shapeId ) { b2World* world = b2GetWorld( shapeId.world0 ); return ( b2WorldId ){ shapeId.world0 + 1, world->revision }; @@ -932,7 +930,7 @@ void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass ) shape->density = density; - if (updateBodyMass == true) + if ( updateBodyMass == true ) { b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); b2UpdateBodyMassData( world, body ); @@ -1276,7 +1274,7 @@ b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId ) int chainId = shape->chainSegment.chainId; if ( chainId != B2_NULL_INDEX ) { - b2ChainShape* chain = b2ChainShapeArray_Get(&world->chainShapes, chainId); + b2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, chainId ); b2ChainId id = { chainId + 1, shapeId.world0, chain->revision }; return id; } @@ -1308,7 +1306,7 @@ void b2Chain_SetFriction( b2ChainId chainId, float friction ) } } -float b2Chain_GetFriction(b2ChainId chainId) +float b2Chain_GetFriction( b2ChainId chainId ) { b2World* world = b2GetWorld( chainId.world0 ); b2ChainShape* chainShape = b2GetChainShape( world, chainId ); @@ -1338,7 +1336,7 @@ void b2Chain_SetRestitution( b2ChainId chainId, float restitution ) } } -float b2Chain_GetRestitution(b2ChainId chainId) +float b2Chain_GetRestitution( b2ChainId chainId ) { b2World* world = b2GetWorld( chainId.world0 ); b2ChainShape* chainShape = b2GetChainShape( world, chainId ); @@ -1365,7 +1363,6 @@ int b2Shape_GetContactCapacity( b2ShapeId shapeId ) return body->contactCount; } -// todo sample needed int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity ) { b2World* world = b2GetWorldLocked( shapeId.world0 ); @@ -1413,6 +1410,75 @@ int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int c return index; } +int b2Shape_GetSensorCapacity(b2ShapeId shapeId) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return 0; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + if ( shape->isSensor == false ) + { + return 0; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + + // Conservative and fast + return body->contactCount; +} + +int b2Shape_GetSensorOverlaps(b2ShapeId shapeId, b2ShapeId* overlappedShapes, int capacity) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return 0; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + if ( shape->isSensor == false ) + { + return 0; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + int contactKey = body->headContactKey; + int index = 0; + while ( contactKey != B2_NULL_INDEX && index < capacity ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + contactKey = contact->edges[edgeIndex].nextKey; + + if ( (contact->flags & b2_contactSensorTouchingFlag) == 0 ) + { + continue; + } + + if ( contact->shapeIdA == shapeId.index1 - 1 ) + { + b2Shape* otherShape = world->shapes.data + contact->shapeIdB; + overlappedShapes[index] = ( b2ShapeId ){ otherShape->id + 1, shapeId.world0, otherShape->revision }; + index += 1; + } + else if ( contact->shapeIdB == shapeId.index1 - 1 ) + { + b2Shape* otherShape = world->shapes.data + contact->shapeIdA; + overlappedShapes[index] = ( b2ShapeId ){ otherShape->id + 1, shapeId.world0, otherShape->revision }; + index += 1; + } + } + + B2_ASSERT( index <= capacity ); + + return index; +} + b2AABB b2Shape_GetAABB( b2ShapeId shapeId ) { b2World* world = b2GetWorld( shapeId.world0 ); @@ -1444,7 +1510,7 @@ b2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target ) input.transformB = b2Transform_identity; input.useRadii = true; - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); return output.pointA; diff --git a/src/shape.h b/src/shape.h index 0a6983535..8d8d8cce2 100644 --- a/src/shape.h +++ b/src/shape.h @@ -75,7 +75,7 @@ b2Vec2 b2GetShapeCentroid( const b2Shape* shape ); float b2GetShapePerimeter( const b2Shape* shape ); float b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line ); -b2DistanceProxy b2MakeShapeDistanceProxy( const b2Shape* shape ); +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 ); diff --git a/src/solver.c b/src/solver.c index 71da07c0e..3210df4c4 100644 --- a/src/solver.c +++ b/src/solver.c @@ -89,7 +89,12 @@ static void b2IntegrateVelocitiesTask( int startIndex, int endIndex, b2StepConte // v2 = v1 * 1 / (1 + c * dt) float linearDamping = 1.0f / ( 1.0f + h * sim->linearDamping ); float angularDamping = 1.0f / ( 1.0f + h * sim->angularDamping ); - b2Vec2 linearVelocityDelta = b2MulSV( h * sim->invMass, b2MulAdd( sim->force, sim->mass * sim->gravityScale, gravity ) ); + + // Gravity scale will be zero for kinematic bodies + float gravityScale = sim->invMass > 0.0f ? sim->gravityScale : 0.0f; + + // lvd = h * im * f + h * g + b2Vec2 linearVelocityDelta = b2Add( b2MulSV( h * sim->invMass, sim->force ), b2MulSV( h * gravityScale, gravity ) ); float angularVelocityDelta = h * sim->invInertia * sim->torque; v = b2MulAdd( linearVelocityDelta, linearDamping, v ); @@ -292,26 +297,26 @@ static bool b2ContinuousQueryCallback( int proxyId, int shapeId, void* context ) input.proxyB = b2MakeShapeDistanceProxy( fastShape ); input.sweepA = b2MakeSweep( bodySim ); input.sweepB = continuousContext->sweep; - input.tMax = continuousContext->fraction; + input.maxFraction = continuousContext->fraction; float hitFraction = continuousContext->fraction; bool didHit = false; b2TOIOutput output = b2TimeOfImpact( &input ); - if ( 0.0f < output.t && output.t < continuousContext->fraction ) + if ( 0.0f < output.fraction && output.fraction < continuousContext->fraction ) { - hitFraction = output.t; + hitFraction = output.fraction; didHit = true; } - else if ( 0.0f == output.t ) + else if ( 0.0f == output.fraction ) { // fallback to TOI of a small circle around the fast shape centroid b2Vec2 centroid = b2GetShapeCentroid( fastShape ); - input.proxyB = b2MakeProxy( ¢roid, 1, b2_speculativeDistance ); + input.proxyB = b2MakeProxy( ¢roid, 1, B2_SPECULATIVE_DISTANCE ); output = b2TimeOfImpact( &input ); - if ( 0.0f < output.t && output.t < continuousContext->fraction ) + if ( 0.0f < output.fraction && output.fraction < continuousContext->fraction ) { - hitFraction = output.t; + hitFraction = output.fraction; didHit = true; } } @@ -402,8 +407,8 @@ static void b2SolveContinuous( b2World* world, int bodySimIndex ) } } - const float speculativeDistance = b2_speculativeDistance; - const float aabbMargin = b2_aabbMargin; + const float speculativeDistance = B2_SPECULATIVE_DISTANCE; + const float aabbMargin = B2_AABB_MARGIN; if ( context.fraction < 1.0f ) { @@ -513,8 +518,8 @@ static void b2FinalizeBodiesTask( int startIndex, int endIndex, uint32_t threadI bool enableContinuous = world->enableContinuous; - const float speculativeDistance = b2_speculativeDistance; - const float aabbMargin = b2_aabbMargin; + const float speculativeDistance = B2_SPECULATIVE_DISTANCE; + const float aabbMargin = B2_AABB_MARGIN; B2_ASSERT( startIndex <= endIndex ); @@ -606,7 +611,7 @@ static void b2FinalizeBodiesTask( int startIndex, int endIndex, uint32_t threadI // Any single body in an island can keep it awake b2Island* island = b2IslandArray_Get( &world->islands, body->islandId ); - if ( body->sleepTime < b2_timeToSleep ) + if ( body->sleepTime < B2_TIME_TO_SLEEP ) { // keep island awake int islandIndex = island->localIndex; @@ -1182,7 +1187,7 @@ void b2Solve( b2World* world, b2StepContext* stepContext ) int awakeContactCount = 0; int awakeJointCount = 0; int activeColorCount = 0; - for ( int i = 0; i < b2_graphColorCount - 1; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT - 1; ++i ) { int perColorContactCount = colors[i].contactSims.count; int perColorJointCount = colors[i].jointSims.count; @@ -1225,22 +1230,22 @@ void b2Solve( b2World* world, b2StepContext* stepContext ) // Configure blocks for tasks parallel-for each active graph color // The blocks are a mix of SIMD contact blocks and joint blocks - int activeColorIndices[b2_graphColorCount]; + int activeColorIndices[B2_GRAPH_COLOR_COUNT]; - int colorContactCounts[b2_graphColorCount]; - int colorContactBlockSizes[b2_graphColorCount]; - int colorContactBlockCounts[b2_graphColorCount]; + int colorContactCounts[B2_GRAPH_COLOR_COUNT]; + int colorContactBlockSizes[B2_GRAPH_COLOR_COUNT]; + int colorContactBlockCounts[B2_GRAPH_COLOR_COUNT]; - int colorJointCounts[b2_graphColorCount]; - int colorJointBlockSizes[b2_graphColorCount]; - int colorJointBlockCounts[b2_graphColorCount]; + int colorJointCounts[B2_GRAPH_COLOR_COUNT]; + int colorJointBlockSizes[B2_GRAPH_COLOR_COUNT]; + int colorJointBlockCounts[B2_GRAPH_COLOR_COUNT]; int graphBlockCount = 0; // c is the active color index int simdContactCount = 0; int c = 0; - for ( int i = 0; i < b2_graphColorCount - 1; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT - 1; ++i ) { int colorContactCount = colors[i].contactSims.count; int colorJointCount = colors[i].jointSims.count; @@ -1314,11 +1319,11 @@ void b2Solve( b2World* world, b2StepContext* stepContext ) b2ContactConstraintSIMD* simdContactConstraints = b2AllocateStackItem( &world->stackAllocator, simdContactCount * simdConstraintSize, "contact constraint" ); - int overflowContactCount = colors[b2_overflowIndex].contactSims.count; + int overflowContactCount = colors[B2_OVERFLOW_INDEX].contactSims.count; b2ContactConstraint* overflowContactConstraints = b2AllocateStackItem( &world->stackAllocator, overflowContactCount * sizeof( b2ContactConstraint ), "overflow contact constraint" ); - graph->colors[b2_overflowIndex].overflowConstraints = overflowContactConstraints; + graph->colors[B2_OVERFLOW_INDEX].overflowConstraints = overflowContactConstraints; // Distribute transient constraints to each graph color and build flat arrays of contact and joint pointers { @@ -1475,7 +1480,7 @@ void b2Solve( b2World* world, b2StepContext* stepContext ) } // Prepare graph work blocks - b2SolverBlock* graphColorBlocks[b2_graphColorCount]; + b2SolverBlock* graphColorBlocks[B2_GRAPH_COLOR_COUNT]; b2SolverBlock* baseGraphBlock = graphBlocks; for ( int i = 0; i < activeColorCount; ++i ) @@ -1610,8 +1615,8 @@ void b2Solve( b2World* world, b2StepContext* stepContext ) B2_ASSERT( (int)( stage - stages ) == stageCount ); - B2_ASSERT( workerCount <= b2_maxWorkers ); - b2WorkerContext workerContext[b2_maxWorkers]; + B2_ASSERT( workerCount <= B2_MAX_WORKERS ); + b2WorkerContext workerContext[B2_MAX_WORKERS]; stepContext->graph = graph; stepContext->joints = joints; @@ -1702,7 +1707,7 @@ void b2Solve( b2World* world, b2StepContext* stepContext ) float threshold = world->hitEventThreshold; b2GraphColor* colors = world->constraintGraph.colors; - for ( int i = 0; i < b2_graphColorCount; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i ) { b2GraphColor* color = colors + i; int contactCount = color->contactSims.count; diff --git a/src/solver_set.c b/src/solver_set.c index 4f59b08cb..035fe340d 100644 --- a/src/solver_set.c +++ b/src/solver_set.c @@ -310,12 +310,12 @@ void b2TrySleepIsland( b2World* world, int islandId ) B2_ASSERT( contact->setIndex == b2_awakeSet ); B2_ASSERT( contact->islandId == islandId ); int colorIndex = contact->colorIndex; - B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = world->constraintGraph.colors + colorIndex; // Remove bodies from graph coloring associated with this constraint - if ( colorIndex != b2_overflowIndex ) + if ( colorIndex != B2_OVERFLOW_INDEX ) { // might clear a bit for a static body, but this has no effect b2ClearBit( &color->bodySet, contact->edges[0].bodyId ); @@ -359,13 +359,13 @@ void b2TrySleepIsland( b2World* world, int islandId ) int colorIndex = joint->colorIndex; int localIndex = joint->localIndex; - B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = world->constraintGraph.colors + colorIndex; b2JointSim* awakeJointSim = b2JointSimArray_Get( &color->jointSims, localIndex ); - if ( colorIndex != b2_overflowIndex ) + if ( colorIndex != B2_OVERFLOW_INDEX ) { // might clear a bit for a static body, but this has no effect b2ClearBit( &color->bodySet, joint->edges[0].bodyId ); @@ -566,7 +566,7 @@ void b2TransferJoint( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourc b2JointSim* sourceSim; if ( sourceSet->setIndex == b2_awakeSet ) { - B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = world->constraintGraph.colors + colorIndex; sourceSim = b2JointSimArray_Get( &color->jointSims, localIndex ); diff --git a/src/types.c b/src/types.c index d4945a258..5089c5f6e 100644 --- a/src/types.c +++ b/src/types.c @@ -13,13 +13,13 @@ b2WorldDef b2DefaultWorldDef( void ) def.gravity.y = -10.0f; def.hitEventThreshold = 1.0f * b2_lengthUnitsPerMeter; def.restitutionThreshold = 1.0f * b2_lengthUnitsPerMeter; - def.contactPushVelocity = 3.0f * b2_lengthUnitsPerMeter; + def.contactPushSpeed = 3.0f * b2_lengthUnitsPerMeter; def.contactHertz = 30.0; def.contactDampingRatio = 10.0f; def.jointHertz = 60.0; def.jointDampingRatio = 2.0f; // 400 meters per second, faster than the speed of sound - def.maximumLinearVelocity = 400.0f * b2_lengthUnitsPerMeter; + def.maximumLinearSpeed = 400.0f * b2_lengthUnitsPerMeter; def.frictionMixingRule = b2_mixGeometricMean; def.restitutionMixingRule = b2_mixMaximum; def.enableSleep = true; diff --git a/src/world.c b/src/world.c index b471178a2..f983461d8 100644 --- a/src/world.c +++ b/src/world.c @@ -177,8 +177,8 @@ b2WorldId b2CreateWorld( const b2WorldDef* def ) world->gravity = def->gravity; world->hitEventThreshold = def->hitEventThreshold; world->restitutionThreshold = def->restitutionThreshold; - world->maxLinearVelocity = def->maximumLinearVelocity; - world->contactPushoutVelocity = def->contactPushVelocity; + world->maxLinearVelocity = def->maximumLinearSpeed; + world->contactPushSpeed = def->contactPushSpeed; world->contactHertz = def->contactHertz; world->contactDampingRatio = def->contactDampingRatio; world->jointHertz = def->jointHertz; @@ -195,7 +195,7 @@ b2WorldId b2CreateWorld( const b2WorldDef* def ) if ( def->workerCount > 0 && def->enqueueTask != NULL && def->finishTask != NULL ) { - world->workerCount = b2MinInt( def->workerCount, b2_maxWorkers ); + world->workerCount = b2MinInt( def->workerCount, B2_MAX_WORKERS ); world->enqueueTaskFcn = def->enqueueTask; world->finishTaskFcn = def->finishTask; world->userTaskContext = def->userTaskContext; @@ -248,8 +248,8 @@ void b2DestroyWorld( b2WorldId worldId ) b2SensorEndTouchEventArray_Destroy( world->sensorEndEvents + 0 ); b2SensorEndTouchEventArray_Destroy( world->sensorEndEvents + 1 ); b2ContactBeginTouchEventArray_Destroy( &world->contactBeginEvents ); - b2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 0); - b2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 1); + b2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 0 ); + b2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 1 ); b2ContactHitEventArray_Destroy( &world->contactHitEvents ); int chainCapacity = world->chainShapes.count; @@ -366,7 +366,7 @@ static void b2CollideTask( int startIndex, int endIndex, uint32_t threadIndex, v bool touching = b2UpdateContact( world, contactSim, shapeA, transformA, centerOffsetA, shapeB, transformB, centerOffsetB ); - // State changes that affect island connectivity. Also contact and sensor events. + // State changes that affect island connectivity. Also affects contact and sensor events. if ( touching == true && wasTouching == false ) { contactSim->simFlags |= b2_simStartedTouching; @@ -442,7 +442,7 @@ static void b2Collide( b2StepContext* context ) // gather contacts into a single array for easier parallel-for int contactCount = 0; b2GraphColor* graphColors = world->constraintGraph.colors; - for ( int i = 0; i < b2_graphColorCount; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i ) { contactCount += graphColors[i].contactSims.count; } @@ -459,7 +459,7 @@ static void b2Collide( b2StepContext* context ) b2ContactSim** contactSims = b2AllocateStackItem( &world->stackAllocator, contactCount * sizeof( b2ContactSim ), "contacts" ); int contactIndex = 0; - for ( int i = 0; i < b2_graphColorCount; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i ) { b2GraphColor* color = graphColors + i; int count = color->contactSims.count; @@ -541,7 +541,7 @@ static void b2Collide( b2StepContext* context ) if ( colorIndex != B2_NULL_INDEX ) { // contact lives in constraint graph - B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* color = graphColors + colorIndex; contactSim = b2ContactSimArray_Get( &color->contactSims, localIndex ); } @@ -878,7 +878,7 @@ static bool DrawQueryCallback( int proxyId, int shapeId, void* context ) { color = shape->customColor; } - else if ( body->type == b2_dynamicBody && bodySim->mass == 0.0f ) + else if ( body->type == b2_dynamicBody && body->mass == 0.0f ) { // Bad body color = b2_colorRed; @@ -953,9 +953,9 @@ static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw ) b2HexColor impulseColor = b2_colorMagenta; b2HexColor frictionColor = b2_colorYellow; - b2HexColor graphColors[b2_graphColorCount] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, - b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, - b2_colorChocolate, b2_colorGoldenRod, b2_colorCoral, b2_colorBlack }; + b2HexColor graphColors[B2_GRAPH_COLOR_COUNT] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, + b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, + b2_colorChocolate, b2_colorGoldenRod, b2_colorCoral, b2_colorBlack }; int bodyCapacity = b2GetIdCapacity( &world->bodyIdPool ); b2SetBitCountAndClear( &world->debugBodySet, bodyCapacity ); @@ -997,7 +997,7 @@ static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw ) b2Vec2 p = b2TransformPoint( transform, offset ); char buffer[32]; - snprintf( buffer, 32, " %.2f", bodySim->mass ); + snprintf( buffer, 32, " %.2f", body->mass ); draw->DrawString( p, buffer, draw->context ); } @@ -1026,7 +1026,7 @@ static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw ) } } - const float linearSlop = b2_linearSlop; + const float linearSlop = B2_LINEAR_SLOP; if ( draw->drawContacts && body->type == b2_dynamicBody && body->setIndex == b2_awakeSet ) { int contactKey = body->headContactKey; @@ -1045,7 +1045,7 @@ static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw ) // avoid double draw if ( b2GetBit( &world->debugContactSet, contactId ) == false ) { - B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT ); b2GraphColor* gc = world->constraintGraph.colors + contact->colorIndex; b2ContactSim* contactSim = b2ContactSimArray_Get( &gc->contactSims, contact->localIndex ); @@ -1060,7 +1060,7 @@ static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw ) if ( draw->drawGraphColors ) { // graph color - float pointSize = contact->colorIndex == b2_overflowIndex ? 7.5f : 5.0f; + float pointSize = contact->colorIndex == B2_OVERFLOW_INDEX ? 7.5f : 5.0f; draw->DrawPoint( point->point, pointSize, graphColors[contact->colorIndex], draw->context ); // g_draw.DrawString(point->position, "%d", point->color); } @@ -1164,7 +1164,7 @@ void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ) { color = shape->customColor; } - else if ( body->type == b2_dynamicBody && bodySim->mass == 0.0f ) + else if ( body->type == b2_dynamicBody && body->mass == 0.0f ) { // Bad body color = b2_colorRed; @@ -1285,7 +1285,8 @@ void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ) b2Vec2 p = b2TransformPoint( transform, offset ); char buffer[32]; - snprintf( buffer, 32, " %.2f", bodySim->mass ); + float mass = bodySim->invMass > 0.0f ? 1.0f / bodySim->invMass : 0.0f; + snprintf( buffer, 32, " %.2f", mass ); draw->DrawString( p, buffer, draw->context ); } } @@ -1295,7 +1296,7 @@ void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ) { const float k_impulseScale = 1.0f; const float k_axisScale = 0.3f; - const float linearSlop = b2_linearSlop; + const float linearSlop = B2_LINEAR_SLOP; b2HexColor speculativeColor = b2_colorLightGray; b2HexColor addColor = b2_colorGreen; @@ -1304,11 +1305,11 @@ void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ) b2HexColor impulseColor = b2_colorMagenta; b2HexColor frictionColor = b2_colorYellow; - b2HexColor colors[b2_graphColorCount] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, - b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, - b2_colorChocolate, b2_colorGoldenRod, b2_colorCoral, b2_colorBlack }; + b2HexColor colors[B2_GRAPH_COLOR_COUNT] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, + b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, + b2_colorChocolate, b2_colorGoldenRod, b2_colorCoral, b2_colorBlack }; - for ( int colorIndex = 0; colorIndex < b2_graphColorCount; ++colorIndex ) + for ( int colorIndex = 0; colorIndex < B2_GRAPH_COLOR_COUNT; ++colorIndex ) { b2GraphColor* graphColor = world->constraintGraph.colors + colorIndex; @@ -1324,10 +1325,10 @@ void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ) { b2ManifoldPoint* point = contact->manifold.points + j; - if ( draw->drawGraphColors && 0 <= colorIndex && colorIndex <= b2_graphColorCount ) + if ( draw->drawGraphColors && 0 <= colorIndex && colorIndex <= B2_GRAPH_COLOR_COUNT ) { // graph color - float pointSize = colorIndex == b2_overflowIndex ? 7.5f : 5.0f; + float pointSize = colorIndex == B2_OVERFLOW_INDEX ? 7.5f : 5.0f; draw->DrawPoint( point->point, pointSize, colors[colorIndex], draw->context ); // g_draw.DrawString(point->position, "%d", point->color); } @@ -1650,7 +1651,7 @@ bool b2World_IsWarmStartingEnabled( b2WorldId worldId ) return world->enableWarmStarting; } -int b2World_GetAwakeBodyCount(b2WorldId worldId) +int b2World_GetAwakeBodyCount( b2WorldId worldId ) { b2World* world = b2GetWorldFromId( worldId ); b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); @@ -1711,7 +1712,7 @@ float b2World_GetHitEventThreshold( b2WorldId worldId ) return world->hitEventThreshold; } -void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushVelocity ) +void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushSpeed ) { b2World* world = b2GetWorldFromId( worldId ); B2_ASSERT( world->locked == false ); @@ -1722,7 +1723,7 @@ void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRati world->contactHertz = b2ClampFloat( hertz, 0.0f, FLT_MAX ); world->contactDampingRatio = b2ClampFloat( dampingRatio, 0.0f, FLT_MAX ); - world->contactPushoutVelocity = b2ClampFloat( pushVelocity, 0.0f, FLT_MAX ); + world->contactPushSpeed = b2ClampFloat( pushSpeed, 0.0f, FLT_MAX ); } void b2World_SetJointTuning( b2WorldId worldId, float hertz, float dampingRatio ) @@ -1738,7 +1739,7 @@ void b2World_SetJointTuning( b2WorldId worldId, float hertz, float dampingRatio world->jointDampingRatio = b2ClampFloat( dampingRatio, 0.0f, FLT_MAX ); } -void b2World_SetMaximumLinearVelocity( b2WorldId worldId, float maximumLinearVelocity ) +void b2World_SetMaximumLinearSpeed( b2WorldId worldId, float maximumLinearVelocity ) { B2_ASSERT( b2IsValidFloat( maximumLinearVelocity ) && maximumLinearVelocity > 0.0f ); @@ -1785,7 +1786,7 @@ b2Counters b2World_GetCounters( b2WorldId worldId ) s.byteCount = b2GetByteCount(); s.taskCount = world->taskCount; - for ( int i = 0; i < b2_graphColorCount; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i ) { s.colorCounts[i] = world->constraintGraph.colors[i].contactSims.count + world->constraintGraph.colors[i].jointSims.count; } @@ -1882,7 +1883,7 @@ void b2World_DumpMemoryStats( b2WorldId worldId ) int bodyBitSetBytes = 0; contactSimCapacity = 0; jointSimCapacity = 0; - for ( int i = 0; i < b2_graphColorCount; ++i ) + for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i ) { b2GraphColor* c = world->constraintGraph.colors + i; bodyBitSetBytes += b2GetBitSetBytes( &c->bodySet ); @@ -1967,7 +1968,7 @@ typedef struct WorldOverlapContext b2World* world; b2OverlapResultFcn* fcn; b2QueryFilter filter; - b2DistanceProxy proxy; + b2ShapeProxy proxy; b2Transform transform; void* userContext; } WorldOverlapContext; @@ -1999,7 +2000,7 @@ static bool TreeOverlapCallback( int proxyId, int shapeId, void* context ) input.transformB = transform; input.useRadii = true; - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); if ( output.distance > 0.0f ) @@ -2150,7 +2151,7 @@ static float RayCastCallback( const b2RayCastInput* input, int proxyId, int shap float fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext ); // The user may return -1 to skip this shape - if (0.0f <= fraction && fraction <= 1.0f) + if ( 0.0f <= fraction && fraction <= 1.0f ) { worldContext->fraction = fraction; } @@ -2556,7 +2557,7 @@ static bool ExplosionCallback( int proxyId, int shapeId, void* context ) input.transformB = b2Transform_identity; input.useRadii = true; - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); float radius = explosionContext->radius; @@ -2655,7 +2656,7 @@ void b2World_RebuildStaticTree( b2WorldId worldId ) b2DynamicTree_Rebuild( staticTree, true ); } -void b2World_EnableSpeculative(b2WorldId worldId, bool flag) +void b2World_EnableSpeculative( b2WorldId worldId, bool flag ) { b2World* world = b2GetWorldFromId( worldId ); world->enableSpeculative = flag; @@ -2979,7 +2980,7 @@ void b2ValidateSolverSets( b2World* world ) B2_ASSERT( totalIslandCount == islandIdCount ); // Validate constraint graph - for ( int colorIndex = 0; colorIndex < b2_graphColorCount; ++colorIndex ) + for ( int colorIndex = 0; colorIndex < B2_GRAPH_COLOR_COUNT; ++colorIndex ) { b2GraphColor* color = world->constraintGraph.colors + colorIndex; { @@ -2999,7 +3000,7 @@ void b2ValidateSolverSets( b2World* world ) int bodyIdA = contact->edges[0].bodyId; int bodyIdB = contact->edges[1].bodyId; - if ( colorIndex < b2_overflowIndex ) + if ( colorIndex < B2_OVERFLOW_INDEX ) { b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); @@ -3023,7 +3024,7 @@ void b2ValidateSolverSets( b2World* world ) int bodyIdA = joint->edges[0].bodyId; int bodyIdB = joint->edges[1].bodyId; - if ( colorIndex < b2_overflowIndex ) + if ( colorIndex < B2_OVERFLOW_INDEX ) { b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); @@ -3119,7 +3120,7 @@ void b2ValidateContacts( b2World* world ) // If touching and not a sensor if ( touching && isSensor == false ) { - B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < b2_graphColorCount ); + B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT ); } else { diff --git a/src/world.h b/src/world.h index f31238f9e..9ab4daefc 100644 --- a/src/world.h +++ b/src/world.h @@ -12,8 +12,6 @@ #include "box2d/types.h" -typedef struct b2ContactSim b2ContactSim; - enum b2SetType { b2_staticSet = 0, @@ -41,9 +39,8 @@ typedef struct b2TaskContext } b2TaskContext; -/// The world class manages all physics entities, dynamic simulation, -/// and asynchronous queries. The world also contains efficient memory -/// management facilities. +// The world struct manages all physics entities, dynamic simulation, and asynchronous queries. +// The world also contains efficient memory management facilities. typedef struct b2World { b2StackAllocator stackAllocator; @@ -130,7 +127,7 @@ typedef struct b2World float hitEventThreshold; float restitutionThreshold; float maxLinearVelocity; - float contactPushoutVelocity; + float contactPushSpeed; float contactHertz; float contactDampingRatio; float jointHertz; diff --git a/test/test_determinism.c b/test/test_determinism.c index 44bc4a0a6..43102d0e6 100644 --- a/test/test_determinism.c +++ b/test/test_determinism.c @@ -86,6 +86,7 @@ static void FinishTask( void* userTask, void* userContext ) enkiWaitForTaskSet( scheduler, task ); } +// todo_erin move this to shared static void TiltedStacks( int testIndex, int workerCount ) { scheduler = enkiNewTaskScheduler(); @@ -325,8 +326,8 @@ static int CrossPlatformTest(void) } ENSURE( stepCount < maxSteps ); - ENSURE( sleepStep == 282 ); - ENSURE( hash == 0x7efc22e7 ); + ENSURE( sleepStep == 263 ); + ENSURE( hash == 0x7de58fbe ); free( bodies ); diff --git a/test/test_distance.c b/test/test_distance.c index 27cee4dae..6ebab85f9 100644 --- a/test/test_distance.c +++ b/test/test_distance.c @@ -44,7 +44,7 @@ static int ShapeDistanceTest( void ) input.transformB = b2Transform_identity; input.useRadii = false; - b2DistanceCache cache = { 0 }; + b2SimplexCache cache = { 0 }; b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); ENSURE_SMALL( output.distance - 1.0f, FLT_EPSILON ); @@ -91,12 +91,12 @@ static int TimeOfImpactTest( void ) input.proxyB = b2MakeProxy( vbs, ARRAY_COUNT( vbs ), 0.0f ); input.sweepA = ( b2Sweep ){ b2Vec2_zero, b2Vec2_zero, b2Vec2_zero, b2Rot_identity, b2Rot_identity }; input.sweepB = ( b2Sweep ){ b2Vec2_zero, b2Vec2_zero, ( b2Vec2 ){ -2.0f, 0.0f }, b2Rot_identity, b2Rot_identity }; - input.tMax = 1.0f; + input.maxFraction = 1.0f; b2TOIOutput output = b2TimeOfImpact( &input ); ENSURE( output.state == b2_toiStateHit ); - ENSURE_SMALL( output.t - 0.5f, 0.005f ); + ENSURE_SMALL( output.fraction - 0.5f, 0.005f ); return 0; }