From 26aecc499eeb1bb21080ba14f12752ca8c7001af Mon Sep 17 00:00:00 2001 From: Dima Pulkinen Date: Wed, 3 Sep 2025 13:19:37 +0300 Subject: [PATCH 1/2] Update Box2D to 2.4.2 --- src/libraries/box2d/b2_body.h | 22 +- src/libraries/box2d/b2_collision.h | 25 +- src/libraries/box2d/b2_fixture.h | 6 + src/libraries/box2d/b2_gear_joint.h | 1 + src/libraries/box2d/b2_joint.h | 7 +- src/libraries/box2d/b2_math.h | 14 +- src/libraries/box2d/b2_polygon_shape.h | 20 +- src/libraries/box2d/b2_rope.h | 2 + src/libraries/box2d/b2_weld_joint.h | 2 +- src/libraries/box2d/b2_world.h | 3 + .../box2d/collision/b2_collision.cpp | 322 ++++++++++++++++++ src/libraries/box2d/collision/b2_distance.cpp | 32 +- .../box2d/collision/b2_polygon_shape.cpp | 163 ++------- src/libraries/box2d/common/b2_settings.cpp | 2 +- src/libraries/box2d/dynamics/b2_body.cpp | 9 +- .../box2d/dynamics/b2_contact_solver.cpp | 2 +- .../box2d/dynamics/b2_distance_joint.cpp | 2 +- .../box2d/dynamics/b2_gear_joint.cpp | 16 +- src/libraries/box2d/dynamics/b2_island.cpp | 2 +- .../box2d/dynamics/b2_mouse_joint.cpp | 4 +- src/libraries/box2d/dynamics/b2_world.cpp | 2 +- src/libraries/box2d/rope/b2_rope.cpp | 2 +- src/modules/physics/box2d/Body.cpp | 6 +- src/modules/physics/box2d/Joint.cpp | 2 - 24 files changed, 473 insertions(+), 195 deletions(-) diff --git a/src/libraries/box2d/b2_body.h b/src/libraries/box2d/b2_body.h index f1038202b..16b2bb002 100644 --- a/src/libraries/box2d/b2_body.h +++ b/src/libraries/box2d/b2_body.h @@ -243,7 +243,7 @@ class B2_API b2Body /// Get the mass data of the body. /// @return a struct containing the mass, inertia and center of the body. - void GetMassData(b2MassData* data) const; + b2MassData GetMassData() const; /// Set the mass properties to override the mass properties of the fixtures. /// Note that this changes the center of mass position. @@ -377,9 +377,7 @@ class B2_API b2Body /// Get the user data pointer that was provided in the body definition. b2BodyUserData& GetUserData(); - - /// Set the user data. Use this to store your application specific data. - void SetUserData(void* data); + const b2BodyUserData& GetUserData() const; /// Get the parent world of this body. b2World* GetWorld(); @@ -404,7 +402,6 @@ class B2_API b2Body friend class b2PrismaticJoint; friend class b2PulleyJoint; friend class b2RevoluteJoint; - friend class b2RopeJoint; friend class b2WeldJoint; friend class b2WheelJoint; @@ -551,11 +548,13 @@ inline float b2Body::GetInertia() const return m_I + m_mass * b2Dot(m_sweep.localCenter, m_sweep.localCenter); } -inline void b2Body::GetMassData(b2MassData* data) const +inline b2MassData b2Body::GetMassData() const { - data->mass = m_mass; - data->I = m_I + m_mass * b2Dot(m_sweep.localCenter, m_sweep.localCenter); - data->center = m_sweep.localCenter; + b2MassData data; + data.mass = m_mass; + data.I = m_I + m_mass * b2Dot(m_sweep.localCenter, m_sweep.localCenter); + data.center = m_sweep.localCenter; + return data; } inline b2Vec2 b2Body::GetWorldPoint(const b2Vec2& localPoint) const @@ -736,6 +735,11 @@ inline b2BodyUserData& b2Body::GetUserData() return m_userData; } +inline const b2BodyUserData& b2Body::GetUserData() const +{ + return m_userData; +} + inline void b2Body::ApplyForce(const b2Vec2& force, const b2Vec2& point, bool wake) { if (m_type != b2_dynamicBody) diff --git a/src/libraries/box2d/b2_collision.h b/src/libraries/box2d/b2_collision.h index 4c4592061..055704e7c 100644 --- a/src/libraries/box2d/b2_collision.h +++ b/src/libraries/box2d/b2_collision.h @@ -244,7 +244,7 @@ B2_API void b2CollideEdgeAndCircle(b2Manifold* manifold, /// Compute the collision manifold between an edge and a polygon. B2_API void b2CollideEdgeAndPolygon(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, - const b2PolygonShape* circleB, const b2Transform& xfB); + const b2PolygonShape* polygonB, const b2Transform& xfB); /// Clipping for contact manifolds. B2_API int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2], @@ -255,6 +255,29 @@ B2_API bool b2TestOverlap( const b2Shape* shapeA, int32 indexA, const b2Shape* shapeB, int32 indexB, const b2Transform& xfA, const b2Transform& xfB); +/// Convex hull used for polygon collision +struct b2Hull +{ + b2Vec2 points[b2_maxPolygonVertices]; + int32 count; +}; + +/// Compute the convex hull of a set of points. Returns an empty hull if it fails. +/// Some failure cases: +/// - all points very close together +/// - all points on a line +/// - less than 3 points +/// - more than b2_maxPolygonVertices points +/// This welds close points and removes collinear points. +b2Hull b2ComputeHull(const b2Vec2* points, int32 count); + +/// This determines if a hull is valid. Checks for: +/// - convexity +/// - collinear points +/// This is expensive and should not be called at runtime. +bool b2ValidateHull(const b2Hull& hull); + + // ---------------- Inline Functions ------------------------------------------ inline bool b2AABB::IsValid() const diff --git a/src/libraries/box2d/b2_fixture.h b/src/libraries/box2d/b2_fixture.h index f12789b5e..47d321df5 100644 --- a/src/libraries/box2d/b2_fixture.h +++ b/src/libraries/box2d/b2_fixture.h @@ -157,6 +157,7 @@ class B2_API b2Fixture /// Get the user data that was assigned in the fixture definition. Use this to /// store your application specific data. b2FixtureUserData& GetUserData(); + const b2FixtureUserData& GetUserData() const; /// Test a point for containment in this fixture. /// @param p a point in world coordinates. @@ -280,6 +281,11 @@ inline b2FixtureUserData& b2Fixture::GetUserData() return m_userData; } +inline const b2FixtureUserData& b2Fixture::GetUserData() const +{ + return m_userData; +} + inline b2Body* b2Fixture::GetBody() { return m_body; diff --git a/src/libraries/box2d/b2_gear_joint.h b/src/libraries/box2d/b2_gear_joint.h index 1c40687a0..a7e4fa519 100644 --- a/src/libraries/box2d/b2_gear_joint.h +++ b/src/libraries/box2d/b2_gear_joint.h @@ -114,6 +114,7 @@ class B2_API b2GearJoint : public b2Joint float m_constant; float m_ratio; + float m_tolerance; float m_impulse; diff --git a/src/libraries/box2d/b2_joint.h b/src/libraries/box2d/b2_joint.h index 2b4d586cd..586b13ad1 100644 --- a/src/libraries/box2d/b2_joint.h +++ b/src/libraries/box2d/b2_joint.h @@ -44,7 +44,6 @@ enum b2JointType e_wheelJoint, e_weldJoint, e_frictionJoint, - e_ropeJoint, e_motorJoint }; @@ -138,6 +137,7 @@ class B2_API b2Joint /// Get the user data pointer. b2JointUserData& GetUserData(); + const b2JointUserData& GetUserData() const; /// Short-cut function to determine if either body is enabled. bool IsEnabled() const; @@ -220,6 +220,11 @@ inline b2JointUserData& b2Joint::GetUserData() return m_userData; } +inline const b2JointUserData& b2Joint::GetUserData() const +{ + return m_userData; +} + inline bool b2Joint::GetCollideConnected() const { return m_collideConnected; diff --git a/src/libraries/box2d/b2_math.h b/src/libraries/box2d/b2_math.h index cf90ad448..e7d7bb0dd 100644 --- a/src/libraries/box2d/b2_math.h +++ b/src/libraries/box2d/b2_math.h @@ -42,7 +42,7 @@ inline bool b2IsValid(float x) struct B2_API b2Vec2 { /// Default constructor does nothing (for performance). - b2Vec2() {} + b2Vec2() = default; /// Construct using coordinates. b2Vec2(float xIn, float yIn) : x(xIn), y(yIn) {} @@ -133,7 +133,7 @@ struct B2_API b2Vec2 struct B2_API b2Vec3 { /// Default constructor does nothing (for performance). - b2Vec3() {} + b2Vec3() = default; /// Construct using coordinates. b2Vec3(float xIn, float yIn, float zIn) : x(xIn), y(yIn), z(zIn) {} @@ -172,7 +172,7 @@ struct B2_API b2Vec3 struct B2_API b2Mat22 { /// The default constructor does nothing (for performance). - b2Mat22() {} + b2Mat22() = default; /// Construct this matrix using columns. b2Mat22(const b2Vec2& c1, const b2Vec2& c2) @@ -246,7 +246,7 @@ struct B2_API b2Mat22 struct B2_API b2Mat33 { /// The default constructor does nothing (for performance). - b2Mat33() {} + b2Mat33() = default; /// Construct this matrix using columns. b2Mat33(const b2Vec3& c1, const b2Vec3& c2, const b2Vec3& c3) @@ -287,7 +287,7 @@ struct B2_API b2Mat33 /// Rotation struct B2_API b2Rot { - b2Rot() {} + b2Rot() = default; /// Initialize from an angle in radians explicit b2Rot(float angle) @@ -339,7 +339,7 @@ struct B2_API b2Rot struct B2_API b2Transform { /// The default constructor does nothing. - b2Transform() {} + b2Transform() = default; /// Initialize using a position vector and a rotation. b2Transform(const b2Vec2& position, const b2Rot& rotation) : p(position), q(rotation) {} @@ -368,6 +368,8 @@ struct B2_API b2Transform /// we must interpolate the center of mass position. struct B2_API b2Sweep { + b2Sweep() = default; + /// Get the interpolated transform at a specific time. /// @param transform the output transform /// @param beta is a factor in [0,1], where 0 indicates alpha0. diff --git a/src/libraries/box2d/b2_polygon_shape.h b/src/libraries/box2d/b2_polygon_shape.h index 5a4e563f8..8a208b72b 100644 --- a/src/libraries/box2d/b2_polygon_shape.h +++ b/src/libraries/box2d/b2_polygon_shape.h @@ -25,6 +25,8 @@ #include "b2_api.h" #include "b2_shape.h" +struct b2Hull; + /// A solid convex polygon. It is assumed that the interior of the polygon is to /// the left of each edge. /// Polygons have a maximum number of vertices equal to b2_maxPolygonVertices. @@ -43,9 +45,13 @@ class B2_API b2PolygonShape : public b2Shape /// Create a convex hull from the given array of local points. /// The count must be in the range [3, b2_maxPolygonVertices]. /// @warning the points may be re-ordered, even if they form a convex polygon - /// @warning collinear points are handled but not removed. Collinear points - /// may lead to poor stacking behavior. - void Set(const b2Vec2* points, int32 count); + /// @warning if this fails then the polygon is invalid + /// @returns true if valid + bool Set(const b2Vec2* points, int32 count); + + /// Create a polygon from a given convex hull (see b2ComputeHull). + /// @warning the hull must be valid or this will crash or have unexpected behavior + void Set(const b2Hull& hull); /// Build vertices to represent an axis-aligned box centered on the local origin. /// @param hx the half-width. @@ -84,12 +90,4 @@ class B2_API b2PolygonShape : public b2Shape int32 m_count; }; -inline b2PolygonShape::b2PolygonShape() -{ - m_type = e_polygon; - m_radius = b2_polygonRadius; - m_count = 0; - m_centroid.SetZero(); -} - #endif diff --git a/src/libraries/box2d/b2_rope.h b/src/libraries/box2d/b2_rope.h index 47f5fc442..0c4f81959 100644 --- a/src/libraries/box2d/b2_rope.h +++ b/src/libraries/box2d/b2_rope.h @@ -55,6 +55,8 @@ struct B2_API b2RopeTuning bendingModel = b2_pbdAngleBendingModel; damping = 0.0f; stretchStiffness = 1.0f; + stretchHertz = 1.0f; + stretchDamping = 0.0f; bendStiffness = 0.5f; bendHertz = 1.0f; bendDamping = 0.0f; diff --git a/src/libraries/box2d/b2_weld_joint.h b/src/libraries/box2d/b2_weld_joint.h index f226c8a46..38501f20a 100644 --- a/src/libraries/box2d/b2_weld_joint.h +++ b/src/libraries/box2d/b2_weld_joint.h @@ -85,7 +85,7 @@ class B2_API b2WeldJoint : public b2Joint float GetReferenceAngle() const { return m_referenceAngle; } /// Set/get stiffness in N*m - void SetStiffness(float hz) { m_stiffness = hz; } + void SetStiffness(float stiffness) { m_stiffness = stiffness; } float GetStiffness() const { return m_stiffness; } /// Set/get damping in N*m*s diff --git a/src/libraries/box2d/b2_world.h b/src/libraries/box2d/b2_world.h index 57d37096c..afd73bd46 100644 --- a/src/libraries/box2d/b2_world.h +++ b/src/libraries/box2d/b2_world.h @@ -221,6 +221,9 @@ class B2_API b2World friend class b2ContactManager; friend class b2Controller; + b2World(const b2World&) = delete; + void operator=(const b2World&) = delete; + void Solve(const b2TimeStep& step); void SolveTOI(const b2TimeStep& step); diff --git a/src/libraries/box2d/collision/b2_collision.cpp b/src/libraries/box2d/collision/b2_collision.cpp index 8a0501be3..750bff00f 100644 --- a/src/libraries/box2d/collision/b2_collision.cpp +++ b/src/libraries/box2d/collision/b2_collision.cpp @@ -256,3 +256,325 @@ bool b2TestOverlap( const b2Shape* shapeA, int32 indexA, return output.distance < 10.0f * b2_epsilon; } + +// quickhull recursion +static b2Hull b2RecurseHull(b2Vec2 p1, b2Vec2 p2, b2Vec2* ps, int32 count) +{ + b2Hull hull; + hull.count = 0; + + if (count == 0) + { + return hull; + } + + // create an edge vector pointing from p1 to p2 + b2Vec2 e = p2 - p1; + e.Normalize(); + + // discard points left of e and find point furthest to the right of e + b2Vec2 rightPoints[b2_maxPolygonVertices]{}; + int32 rightCount = 0; + + int32 bestIndex = 0; + float bestDistance = b2Cross(ps[bestIndex] - p1, e); + if (bestDistance > 0.0f) + { + rightPoints[rightCount++] = ps[bestIndex]; + } + + for (int32 i = 1; i < count; ++i) + { + float distance = b2Cross(ps[i] - p1, e); + if (distance > bestDistance) + { + bestIndex = i; + bestDistance = distance; + } + + if (distance > 0.0f) + { + rightPoints[rightCount++] = ps[i]; + } + } + + if (bestDistance < 2.0f * b2_linearSlop) + { + return hull; + } + + b2Vec2 bestPoint = ps[bestIndex]; + + // compute hull to the right of p1-bestPoint + b2Hull hull1 = b2RecurseHull(p1, bestPoint, rightPoints, rightCount); + + // compute hull to the right of bestPoint-p2 + b2Hull hull2 = b2RecurseHull(bestPoint, p2, rightPoints, rightCount); + + // stich together hulls + for (int32 i = 0; i < hull1.count; ++i) + { + hull.points[hull.count++] = hull1.points[i]; + } + + hull.points[hull.count++] = bestPoint; + + for (int32 i = 0; i < hull2.count; ++i) + { + hull.points[hull.count++] = hull2.points[i]; + } + + b2Assert(hull.count < b2_maxPolygonVertices); + + return hull; +} + +// quickhull algorithm +// - merges vertices based on b2_linearSlop +// - removes collinear points using b2_linearSlop +// - returns an empty hull if it fails +b2Hull b2ComputeHull(const b2Vec2* points, int32 count) +{ + b2Hull hull; + hull.count = 0; + + if (count < 3 || count > b2_maxPolygonVertices) + { + // check your data + return hull; + } + + count = b2Min(count, b2_maxPolygonVertices); + + b2AABB aabb = { {b2_maxFloat, b2_maxFloat}, {-b2_maxFloat, -b2_maxFloat} }; + + // Perform aggressive point welding. First point always remains. + // Also compute the bounding box for later. + b2Vec2 ps[b2_maxPolygonVertices]; + int32 n = 0; + const float tolSqr = 16.0f * b2_linearSlop * b2_linearSlop; + for (int32 i = 0; i < count; ++i) + { + aabb.lowerBound = b2Min(aabb.lowerBound, points[i]); + aabb.upperBound = b2Max(aabb.upperBound, points[i]); + + b2Vec2 vi = points[i]; + + bool unique = true; + for (int32 j = 0; j < i; ++j) + { + b2Vec2 vj = points[j]; + + float distSqr = b2DistanceSquared(vi, vj); + if (distSqr < tolSqr) + { + unique = false; + break; + } + } + + if (unique) + { + ps[n++] = vi; + } + } + + if (n < 3) + { + // all points very close together, check your data and check your scale + return hull; + } + + // Find an extreme point as the first point on the hull + b2Vec2 c = aabb.GetCenter(); + int32 i1 = 0; + float dsq1 = b2DistanceSquared(c, ps[i1]); + for (int32 i = 1; i < n; ++i) + { + float dsq = b2DistanceSquared(c, ps[i]); + if (dsq > dsq1) + { + i1 = i; + dsq1 = dsq; + } + } + + // remove p1 from working set + b2Vec2 p1 = ps[i1]; + ps[i1] = ps[n - 1]; + n = n - 1; + + int32 i2 = 0; + float dsq2 = b2DistanceSquared(p1, ps[i2]); + for (int32 i = 1; i < n; ++i) + { + float dsq = b2DistanceSquared(p1, ps[i]); + if (dsq > dsq2) + { + i2 = i; + dsq2 = dsq; + } + } + + // remove p2 from working set + b2Vec2 p2 = ps[i2]; + ps[i2] = ps[n - 1]; + n = n - 1; + + // split the points into points that are left and right of the line p1-p2. + b2Vec2 rightPoints[b2_maxPolygonVertices - 2]; + int32 rightCount = 0; + + b2Vec2 leftPoints[b2_maxPolygonVertices - 2]; + int32 leftCount = 0; + + b2Vec2 e = p2 - p1; + e.Normalize(); + + for (int32 i = 0; i < n; ++i) + { + float d = b2Cross(ps[i] - p1, e); + + // slop used here to skip points that are very close to the line p1-p2 + if (d >= 2.0f * b2_linearSlop) + { + rightPoints[rightCount++] = ps[i]; + } + else if (d <= -2.0f * b2_linearSlop) + { + leftPoints[leftCount++] = ps[i]; + } + } + + // compute hulls on right and left + b2Hull hull1 = b2RecurseHull(p1, p2, rightPoints, rightCount); + b2Hull hull2 = b2RecurseHull(p2, p1, leftPoints, leftCount); + + if (hull1.count == 0 && hull2.count == 0) + { + // all points collinear + return hull; + } + + // stitch hulls together, preserving CCW winding order + hull.points[hull.count++] = p1; + + for (int32 i = 0; i < hull1.count; ++i) + { + hull.points[hull.count++] = hull1.points[i]; + } + + hull.points[hull.count++] = p2; + + for (int32 i = 0; i < hull2.count; ++i) + { + hull.points[hull.count++] = hull2.points[i]; + } + + b2Assert(hull.count <= b2_maxPolygonVertices); + + // merge collinear + bool searching = true; + while (searching && hull.count > 2) + { + searching = false; + + for (int32 i = 0; i < hull.count; ++i) + { + int32 i1 = i; + int32 i2 = (i + 1) % hull.count; + int32 i3 = (i + 2) % hull.count; + + b2Vec2 p1 = hull.points[i1]; + b2Vec2 p2 = hull.points[i2]; + b2Vec2 p3 = hull.points[i3]; + + b2Vec2 e = p3 - p1; + e.Normalize(); + + b2Vec2 v = p2 - p1; + float distance = b2Cross(p2 - p1, e); + if (distance <= 2.0f * b2_linearSlop) + { + // remove midpoint from hull + for (int32 j = i2; j < hull.count - 1; ++j) + { + hull.points[j] = hull.points[j + 1]; + } + hull.count -= 1; + + // continue searching for collinear points + searching = true; + + break; + } + } + } + + if (hull.count < 3) + { + // all points collinear, shouldn't be reached since this was validated above + hull.count = 0; + } + + return hull; +} + +bool b2ValidateHull(const b2Hull& hull) +{ + if (hull.count < 3 || b2_maxPolygonVertices < hull.count) + { + return false; + } + + // test that every point is behind every edge + for (int32 i = 0; i < hull.count; ++i) + { + // create an edge vector + int32 i1 = i; + int32 i2 = i < hull.count - 1 ? i1 + 1 : 0; + b2Vec2 p = hull.points[i1]; + b2Vec2 e = hull.points[i2] - p; + e.Normalize(); + + for (int32 j = 0; j < hull.count; ++j) + { + // skip points that subtend the current edge + if (j == i1 || j == i2) + { + continue; + } + + float distance = b2Cross(hull.points[j] - p, e); + if (distance >= 0.0f) + { + return false; + } + } + } + + // test for collinear points + for (int32 i = 0; i < hull.count; ++i) + { + int32 i1 = i; + int32 i2 = (i + 1) % hull.count; + int32 i3 = (i + 2) % hull.count; + + b2Vec2 p1 = hull.points[i1]; + b2Vec2 p2 = hull.points[i2]; + b2Vec2 p3 = hull.points[i3]; + + b2Vec2 e = p3 - p1; + e.Normalize(); + + b2Vec2 v = p2 - p1; + float distance = b2Cross(p2 - p1, e); + if (distance <= b2_linearSlop) + { + // p1-p2-p3 are collinear + return false; + } + } + + return true; +} diff --git a/src/libraries/box2d/collision/b2_distance.cpp b/src/libraries/box2d/collision/b2_distance.cpp index 244132408..16fa3cc3d 100644 --- a/src/libraries/box2d/collision/b2_distance.cpp +++ b/src/libraries/box2d/collision/b2_distance.cpp @@ -569,31 +569,29 @@ void b2Distance(b2DistanceOutput* output, // Cache the simplex. simplex.WriteCache(cache); - // Apply radii if requested. + // Apply radii if requested if (input->useRadii) { - float rA = proxyA->m_radius; - float rB = proxyB->m_radius; - - if (output->distance > rA + rB && output->distance > b2_epsilon) - { - // Shapes are still no overlapped. - // Move the witness points to the outer surface. - output->distance -= rA + rB; - b2Vec2 normal = output->pointB - output->pointA; - normal.Normalize(); - output->pointA += rA * normal; - output->pointB -= rB * normal; - } - else + if (output->distance < b2_epsilon) { - // Shapes are overlapped when radii are considered. - // Move the witness points to the middle. + // Shapes are too close to safely compute normal b2Vec2 p = 0.5f * (output->pointA + output->pointB); output->pointA = p; output->pointB = p; output->distance = 0.0f; } + else + { + // Keep closest points on perimeter even if overlapped, this way + // the points move smoothly. + float rA = proxyA->m_radius; + float rB = proxyB->m_radius; + b2Vec2 normal = output->pointB - output->pointA; + normal.Normalize(); + output->distance = b2Max(0.0f, output->distance - rA - rB); + output->pointA += rA * normal; + output->pointB -= rB * normal; + } } } diff --git a/src/libraries/box2d/collision/b2_polygon_shape.cpp b/src/libraries/box2d/collision/b2_polygon_shape.cpp index 368fb801f..165919b46 100644 --- a/src/libraries/box2d/collision/b2_polygon_shape.cpp +++ b/src/libraries/box2d/collision/b2_polygon_shape.cpp @@ -25,6 +25,14 @@ #include +b2PolygonShape::b2PolygonShape() +{ + m_type = e_polygon; + m_radius = b2_polygonRadius; + m_count = 0; + m_centroid.SetZero(); +} + b2Shape* b2PolygonShape::Clone(b2BlockAllocator* allocator) const { void* mem = allocator->Allocate(sizeof(b2PolygonShape)); @@ -115,128 +123,37 @@ static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count) return c; } -void b2PolygonShape::Set(const b2Vec2* vertices, int32 count) +bool b2PolygonShape::Set(const b2Vec2* vertices, int32 count) { - b2Assert(3 <= count && count <= b2_maxPolygonVertices); - if (count < 3) - { - SetAsBox(1.0f, 1.0f); - return; - } - - int32 n = b2Min(count, b2_maxPolygonVertices); + b2Hull hull = b2ComputeHull(vertices, count); - // Perform welding and copy vertices into local buffer. - b2Vec2 ps[b2_maxPolygonVertices]; - int32 tempCount = 0; - for (int32 i = 0; i < n; ++i) + if (hull.count < 3) { - b2Vec2 v = vertices[i]; - - bool unique = true; - for (int32 j = 0; j < tempCount; ++j) - { - if (b2DistanceSquared(v, ps[j]) < ((0.5f * b2_linearSlop) * (0.5f * b2_linearSlop))) - { - unique = false; - break; - } - } - - if (unique) - { - ps[tempCount++] = v; - } + return false; } - n = tempCount; - if (n < 3) - { - // Polygon is degenerate. - b2Assert(false); - SetAsBox(1.0f, 1.0f); - return; - } - - // Create the convex hull using the Gift wrapping algorithm - // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm - - // Find the right most point on the hull - int32 i0 = 0; - float x0 = ps[0].x; - for (int32 i = 1; i < n; ++i) - { - float x = ps[i].x; - if (x > x0 || (x == x0 && ps[i].y < ps[i0].y)) - { - i0 = i; - x0 = x; - } - } - - int32 hull[b2_maxPolygonVertices]; - int32 m = 0; - int32 ih = i0; - - for (;;) - { - b2Assert(m < b2_maxPolygonVertices); - hull[m] = ih; - - int32 ie = 0; - for (int32 j = 1; j < n; ++j) - { - if (ie == ih) - { - ie = j; - continue; - } - - b2Vec2 r = ps[ie] - ps[hull[m]]; - b2Vec2 v = ps[j] - ps[hull[m]]; - float c = b2Cross(r, v); - if (c < 0.0f) - { - ie = j; - } - - // Collinearity check - if (c == 0.0f && v.LengthSquared() > r.LengthSquared()) - { - ie = j; - } - } + Set(hull); - ++m; - ih = ie; + return true; +} - if (ie == i0) - { - break; - } - } - - if (m < 3) - { - // Polygon is degenerate. - b2Assert(false); - SetAsBox(1.0f, 1.0f); - return; - } +void b2PolygonShape::Set(const b2Hull& hull) +{ + b2Assert(hull.count >= 3); - m_count = m; + m_count = hull.count; - // Copy vertices. - for (int32 i = 0; i < m; ++i) + // Copy vertices + for (int32 i = 0; i < hull.count; ++i) { - m_vertices[i] = ps[hull[i]]; + m_vertices[i] = hull.points[i]; } // Compute normals. Ensure the edges have non-zero length. - for (int32 i = 0; i < m; ++i) + for (int32 i = 0; i < m_count; ++i) { int32 i1 = i; - int32 i2 = i + 1 < m ? i + 1 : 0; + int32 i2 = i + 1 < m_count ? i + 1 : 0; b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; b2Assert(edge.LengthSquared() > b2_epsilon * b2_epsilon); m_normals[i] = b2Cross(edge, 1.0f); @@ -244,7 +161,7 @@ void b2PolygonShape::Set(const b2Vec2* vertices, int32 count) } // Compute the polygon centroid. - m_centroid = ComputeCentroid(m_vertices, m); + m_centroid = ComputeCentroid(m_vertices, m_count); } bool b2PolygonShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const @@ -432,28 +349,18 @@ void b2PolygonShape::ComputeMass(b2MassData* massData, float density) const bool b2PolygonShape::Validate() const { - for (int32 i = 0; i < m_count; ++i) + if (m_count < 3 || b2_maxPolygonVertices < m_count) { - int32 i1 = i; - int32 i2 = i < m_count - 1 ? i1 + 1 : 0; - b2Vec2 p = m_vertices[i1]; - b2Vec2 e = m_vertices[i2] - p; - - for (int32 j = 0; j < m_count; ++j) - { - if (j == i1 || j == i2) - { - continue; - } + return false; + } - b2Vec2 v = m_vertices[j] - p; - float c = b2Cross(e, v); - if (c < 0.0f) - { - return false; - } - } + b2Hull hull; + for (int32 i = 0; i < m_count; ++i) + { + hull.points[i] = m_vertices[i]; } - return true; + hull.count = m_count; + + return b2ValidateHull(hull); } diff --git a/src/libraries/box2d/common/b2_settings.cpp b/src/libraries/box2d/common/b2_settings.cpp index 5ee213ef2..16a21ddc2 100644 --- a/src/libraries/box2d/common/b2_settings.cpp +++ b/src/libraries/box2d/common/b2_settings.cpp @@ -29,7 +29,7 @@ #include "common/Exception.h" -b2Version b2_version = {2, 4, 0}; +b2Version b2_version = {2, 4, 1}; // Memory allocators. Modify these to use your own allocator. void* b2Alloc_Default(int32 size) diff --git a/src/libraries/box2d/dynamics/b2_body.cpp b/src/libraries/box2d/dynamics/b2_body.cpp index 574666442..00f36856d 100644 --- a/src/libraries/box2d/dynamics/b2_body.cpp +++ b/src/libraries/box2d/dynamics/b2_body.cpp @@ -244,6 +244,8 @@ void b2Body::DestroyFixture(b2Fixture* fixture) // You tried to remove a shape that is not attached to this body. b2Assert(found); + const float density = fixture->m_density; + // Destroy any contacts associated with the fixture. b2ContactEdge* edge = m_contactList; while (edge) @@ -278,8 +280,11 @@ void b2Body::DestroyFixture(b2Fixture* fixture) --m_fixtureCount; - // Reset the mass data. - ResetMassData(); + // Reset the mass data + if (density > 0.0f) + { + ResetMassData(); + } } void b2Body::ResetMassData() diff --git a/src/libraries/box2d/dynamics/b2_contact_solver.cpp b/src/libraries/box2d/dynamics/b2_contact_solver.cpp index e6f432a23..d6c08fb17 100644 --- a/src/libraries/box2d/dynamics/b2_contact_solver.cpp +++ b/src/libraries/box2d/dynamics/b2_contact_solver.cpp @@ -775,7 +775,7 @@ bool b2ContactSolver::SolveTOIPositionConstraints(int32 toiIndexA, int32 toiInde } float mB = 0.0f; - float iB = 0.; + float iB = 0.0f; if (indexB == toiIndexA || indexB == toiIndexB) { mB = pc->invMassB; diff --git a/src/libraries/box2d/dynamics/b2_distance_joint.cpp b/src/libraries/box2d/dynamics/b2_distance_joint.cpp index e54dbaf4f..221309ef0 100644 --- a/src/libraries/box2d/dynamics/b2_distance_joint.cpp +++ b/src/libraries/box2d/dynamics/b2_distance_joint.cpp @@ -392,7 +392,7 @@ void b2DistanceJoint::Draw(b2Draw* draw) const b2Vec2 pB = b2Mul(xfB, m_localAnchorB); b2Vec2 axis = pB - pA; - float length = axis.Normalize(); + axis.Normalize(); b2Color c1(0.7f, 0.7f, 0.7f); b2Color c2(0.3f, 0.9f, 0.3f); diff --git a/src/libraries/box2d/dynamics/b2_gear_joint.cpp b/src/libraries/box2d/dynamics/b2_gear_joint.cpp index ff36d3e10..5fb547245 100644 --- a/src/libraries/box2d/dynamics/b2_gear_joint.cpp +++ b/src/libraries/box2d/dynamics/b2_gear_joint.cpp @@ -82,6 +82,9 @@ b2GearJoint::b2GearJoint(const b2GearJointDef* def) m_localAxisC.SetZero(); coordinateA = aA - aC - m_referenceAngleA; + + // position error is measured in radians + m_tolerance = b2_angularSlop; } else { @@ -94,6 +97,9 @@ b2GearJoint::b2GearJoint(const b2GearJointDef* def) b2Vec2 pC = m_localAnchorC; b2Vec2 pA = b2MulT(xfC.q, b2Mul(xfA.q, m_localAnchorA) + (xfA.p - xfC.p)); coordinateA = b2Dot(pA - pC, m_localAxisC); + + // position error is measured in meters + m_tolerance = b2_linearSlop; } m_bodyD = m_joint2->GetBodyA(); @@ -291,8 +297,6 @@ bool b2GearJoint::SolvePositionConstraints(const b2SolverData& data) b2Rot qA(aA), qB(aB), qC(aC), qD(aD); - float linearError = 0.0f; - float coordinateA, coordinateB; b2Vec2 JvAC, JvBD; @@ -373,8 +377,12 @@ bool b2GearJoint::SolvePositionConstraints(const b2SolverData& data) data.positions[m_indexD].c = cD; data.positions[m_indexD].a = aD; - // TODO_ERIN not implemented - return linearError < b2_linearSlop; + if (b2Abs(C) < m_tolerance) + { + return true; + } + + return false; } b2Vec2 b2GearJoint::GetAnchorA() const diff --git a/src/libraries/box2d/dynamics/b2_island.cpp b/src/libraries/box2d/dynamics/b2_island.cpp index bd42b5446..34413056b 100644 --- a/src/libraries/box2d/dynamics/b2_island.cpp +++ b/src/libraries/box2d/dynamics/b2_island.cpp @@ -29,8 +29,8 @@ #include "box2d/b2_timer.h" #include "box2d/b2_world.h" -#include "b2_island.h" #include "b2_contact_solver.h" +#include "b2_island.h" /* Position Correction Notes diff --git a/src/libraries/box2d/dynamics/b2_mouse_joint.cpp b/src/libraries/box2d/dynamics/b2_mouse_joint.cpp index 1ea6dd420..dbc214fae 100644 --- a/src/libraries/box2d/dynamics/b2_mouse_joint.cpp +++ b/src/libraries/box2d/dynamics/b2_mouse_joint.cpp @@ -84,8 +84,6 @@ void b2MouseJoint::InitVelocityConstraints(const b2SolverData& data) b2Rot qB(aB); - float mass = m_bodyB->GetMass(); - float d = m_damping; float k = m_stiffness; @@ -118,7 +116,7 @@ void b2MouseJoint::InitVelocityConstraints(const b2SolverData& data) m_C *= m_beta; // Cheat with some damping - wB *= 0.98f; + wB *= b2Max(0.0f, 1.0f - 0.02f * (60.0f * data.step.dt)); if (data.step.warmStarting) { diff --git a/src/libraries/box2d/dynamics/b2_world.cpp b/src/libraries/box2d/dynamics/b2_world.cpp index 1a0f79131..78ec084c8 100644 --- a/src/libraries/box2d/dynamics/b2_world.cpp +++ b/src/libraries/box2d/dynamics/b2_world.cpp @@ -515,7 +515,7 @@ void b2World::Solve(const b2TimeStep& step) b2Body* other = je->other; - // Don't simulate joints connected to diabled bodies. + // Don't simulate joints connected to disabled bodies. if (other->IsEnabled() == false) { continue; diff --git a/src/libraries/box2d/rope/b2_rope.cpp b/src/libraries/box2d/rope/b2_rope.cpp index d2425a2af..71fbe79ee 100644 --- a/src/libraries/box2d/rope/b2_rope.cpp +++ b/src/libraries/box2d/rope/b2_rope.cpp @@ -243,7 +243,7 @@ void b2Rope::SetTuning(const b2RopeTuning& tuning) void b2Rope::Step(float dt, int32 iterations, const b2Vec2& position) { - if (dt == 0.0) + if (dt == 0.0f) { return; } diff --git a/src/modules/physics/box2d/Body.cpp b/src/modules/physics/box2d/Body.cpp index 10256fc3a..28ee0ded9 100644 --- a/src/modules/physics/box2d/Body.cpp +++ b/src/modules/physics/box2d/Body.cpp @@ -124,8 +124,7 @@ float Body::getInertia() const int Body::getMassData(lua_State *L) { - b2MassData data; - body->GetMassData(&data); + b2MassData data = body->GetMassData(); b2Vec2 center = Physics::scaleUp(data.center); lua_pushnumber(L, center.x); lua_pushnumber(L, center.y); @@ -265,8 +264,7 @@ void Body::setMassData(float x, float y, float m, float i) void Body::setMass(float m) { - b2MassData data; - body->GetMassData(&data); + b2MassData data = body->GetMassData(); data.mass = m; body->SetMassData(&data); hasCustomMass = true; diff --git a/src/modules/physics/box2d/Joint.cpp b/src/modules/physics/box2d/Joint.cpp index 700a29656..d1b2a405c 100644 --- a/src/modules/physics/box2d/Joint.cpp +++ b/src/modules/physics/box2d/Joint.cpp @@ -78,8 +78,6 @@ Joint::Type Joint::getType() const return JOINT_WELD; case e_wheelJoint: return JOINT_WHEEL; - case e_ropeJoint: - return JOINT_ROPE; case e_motorJoint: return JOINT_MOTOR; default: From 2b36acbfdfbba9feae676a4d5e17f0c5e7960bf8 Mon Sep 17 00:00:00 2001 From: Dima Pulkinen Date: Wed, 3 Sep 2025 14:52:04 +0300 Subject: [PATCH 2/2] Remove "rope" JointType --- src/modules/physics/Joint.cpp | 1 - src/modules/physics/Joint.h | 1 - src/modules/physics/box2d/wrap_Joint.cpp | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/modules/physics/Joint.cpp b/src/modules/physics/Joint.cpp index c727642d1..5e67deddc 100644 --- a/src/modules/physics/Joint.cpp +++ b/src/modules/physics/Joint.cpp @@ -52,7 +52,6 @@ StringMap::Entry Joint::typeEntries[] = {"friction", Joint::JOINT_FRICTION}, {"weld", Joint::JOINT_WELD}, {"wheel", Joint::JOINT_WHEEL}, - {"rope", Joint::JOINT_ROPE}, {"motor", Joint::JOINT_MOTOR}, }; diff --git a/src/modules/physics/Joint.h b/src/modules/physics/Joint.h index 2b4f75ce4..e3041a7d6 100644 --- a/src/modules/physics/Joint.h +++ b/src/modules/physics/Joint.h @@ -48,7 +48,6 @@ class Joint : public Object JOINT_FRICTION, JOINT_WELD, JOINT_WHEEL, - JOINT_ROPE, JOINT_MOTOR, JOINT_MAX_ENUM }; diff --git a/src/modules/physics/box2d/wrap_Joint.cpp b/src/modules/physics/box2d/wrap_Joint.cpp index 77093466d..9bf557ee4 100644 --- a/src/modules/physics/box2d/wrap_Joint.cpp +++ b/src/modules/physics/box2d/wrap_Joint.cpp @@ -66,8 +66,6 @@ void luax_pushjoint(lua_State *L, Joint *j) return luax_pushtype(L, WeldJoint::type, j); case Joint::JOINT_WHEEL: return luax_pushtype(L, WheelJoint::type, j); - case Joint::JOINT_ROPE: - return luax_pushtype(L, RopeJoint::type, j); case Joint::JOINT_MOTOR: return luax_pushtype(L, MotorJoint::type, j); default: