diff --git a/Makefile b/Makefile index a109aa78c..84578c547 100644 --- a/Makefile +++ b/Makefile @@ -728,88 +728,93 @@ EXT_OGG_SRCS = \ ext/ogg/framing.c EXT_REACTPHYSICS3D_SRCS = \ - ext/reactphysics3d/src/body/CollisionBody.cpp \ + ext/reactphysics3d/src/body/Body.cpp \ ext/reactphysics3d/src/body/RigidBody.cpp \ ext/reactphysics3d/src/collision/broadphase/DynamicAABBTree.cpp \ + ext/reactphysics3d/src/collision/Collider.cpp \ + ext/reactphysics3d/src/collision/CollisionCallback.cpp \ + ext/reactphysics3d/src/collision/ContactManifold.cpp \ + ext/reactphysics3d/src/collision/ConvexMesh.cpp \ + ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp \ + ext/reactphysics3d/src/collision/HeightField.cpp \ + ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp \ + ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/CollisionDispatch.cpp \ - ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp \ + ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/GJK/GJKAlgorithm.cpp \ + ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp \ + ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp \ + ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp \ ext/reactphysics3d/src/collision/narrowphase/SAT/SATAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp \ - ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp \ + ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp \ + ext/reactphysics3d/src/collision/OverlapCallback.cpp \ + ext/reactphysics3d/src/collision/PolygonVertexArray.cpp \ + ext/reactphysics3d/src/collision/RaycastInfo.cpp \ ext/reactphysics3d/src/collision/shapes/AABB.cpp \ - ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp \ - ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp \ - ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp \ ext/reactphysics3d/src/collision/shapes/BoxShape.cpp \ ext/reactphysics3d/src/collision/shapes/CapsuleShape.cpp \ ext/reactphysics3d/src/collision/shapes/CollisionShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp \ ext/reactphysics3d/src/collision/shapes/ConvexMeshShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp \ + ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp \ ext/reactphysics3d/src/collision/shapes/SphereShape.cpp \ ext/reactphysics3d/src/collision/shapes/TriangleShape.cpp \ - ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp \ - ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp \ - ext/reactphysics3d/src/collision/RaycastInfo.cpp \ - ext/reactphysics3d/src/collision/Collider.cpp \ - ext/reactphysics3d/src/collision/TriangleVertexArray.cpp \ - ext/reactphysics3d/src/collision/PolygonVertexArray.cpp \ ext/reactphysics3d/src/collision/TriangleMesh.cpp \ - ext/reactphysics3d/src/collision/PolyhedronMesh.cpp \ - ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp \ - ext/reactphysics3d/src/collision/ContactManifold.cpp \ + ext/reactphysics3d/src/collision/TriangleVertexArray.cpp \ + ext/reactphysics3d/src/collision/VertexArray.cpp \ + ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp \ + ext/reactphysics3d/src/components/BodyComponents.cpp \ + ext/reactphysics3d/src/components/ColliderComponents.cpp \ + ext/reactphysics3d/src/components/Components.cpp \ + ext/reactphysics3d/src/components/FixedJointComponents.cpp \ + ext/reactphysics3d/src/components/HingeJointComponents.cpp \ + ext/reactphysics3d/src/components/JointComponents.cpp \ + ext/reactphysics3d/src/components/RigidBodyComponents.cpp \ + ext/reactphysics3d/src/components/SliderJointComponents.cpp \ + ext/reactphysics3d/src/components/TransformComponents.cpp \ ext/reactphysics3d/src/constraint/BallAndSocketJoint.cpp \ ext/reactphysics3d/src/constraint/ContactPoint.cpp \ ext/reactphysics3d/src/constraint/FixedJoint.cpp \ ext/reactphysics3d/src/constraint/HingeJoint.cpp \ ext/reactphysics3d/src/constraint/Joint.cpp \ ext/reactphysics3d/src/constraint/SliderJoint.cpp \ - ext/reactphysics3d/src/engine/PhysicsCommon.cpp \ - ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp \ - ext/reactphysics3d/src/systems/ContactSolverSystem.cpp \ - ext/reactphysics3d/src/systems/DynamicsSystem.cpp \ - ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp \ - ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp \ - ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp \ - ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp \ - ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp \ - ext/reactphysics3d/src/engine/PhysicsWorld.cpp \ + ext/reactphysics3d/src/engine/Entity.cpp \ + ext/reactphysics3d/src/engine/EntityManager.cpp \ ext/reactphysics3d/src/engine/Island.cpp \ ext/reactphysics3d/src/engine/Material.cpp \ ext/reactphysics3d/src/engine/OverlappingPairs.cpp \ - ext/reactphysics3d/src/engine/Entity.cpp \ - ext/reactphysics3d/src/engine/EntityManager.cpp \ - ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp \ - ext/reactphysics3d/src/components/Components.cpp \ - ext/reactphysics3d/src/components/CollisionBodyComponents.cpp \ - ext/reactphysics3d/src/components/RigidBodyComponents.cpp \ - ext/reactphysics3d/src/components/TransformComponents.cpp \ - ext/reactphysics3d/src/components/ColliderComponents.cpp \ - ext/reactphysics3d/src/components/JointComponents.cpp \ - ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp \ - ext/reactphysics3d/src/components/FixedJointComponents.cpp \ - ext/reactphysics3d/src/components/HingeJointComponents.cpp \ - ext/reactphysics3d/src/components/SliderJointComponents.cpp \ - ext/reactphysics3d/src/collision/CollisionCallback.cpp \ - ext/reactphysics3d/src/collision/OverlapCallback.cpp \ + ext/reactphysics3d/src/engine/PhysicsCommon.cpp \ + ext/reactphysics3d/src/engine/PhysicsWorld.cpp \ ext/reactphysics3d/src/mathematics/Matrix2x2.cpp \ ext/reactphysics3d/src/mathematics/Matrix3x3.cpp \ ext/reactphysics3d/src/mathematics/Quaternion.cpp \ ext/reactphysics3d/src/mathematics/Transform.cpp \ ext/reactphysics3d/src/mathematics/Vector2.cpp \ ext/reactphysics3d/src/mathematics/Vector3.cpp \ - ext/reactphysics3d/src/memory/PoolAllocator.cpp \ - ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp \ ext/reactphysics3d/src/memory/HeapAllocator.cpp \ + ext/reactphysics3d/src/memory/MemoryAllocator.cpp \ ext/reactphysics3d/src/memory/MemoryManager.cpp \ - ext/reactphysics3d/src/utils/Profiler.cpp \ + ext/reactphysics3d/src/memory/PoolAllocator.cpp \ + ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp \ + ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp \ + ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp \ + ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp \ + ext/reactphysics3d/src/systems/ContactSolverSystem.cpp \ + ext/reactphysics3d/src/systems/DynamicsSystem.cpp \ + ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp \ + ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp \ + ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp \ + ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp \ + ext/reactphysics3d/src/utils/DebugRenderer.cpp \ ext/reactphysics3d/src/utils/DefaultLogger.cpp \ - ext/reactphysics3d/src/utils/DebugRenderer.cpp + ext/reactphysics3d/src/utils/Profiler.cpp \ + ext/reactphysics3d/src/utils/quickhull/QHHalfEdgeStructure.cpp \ + ext/reactphysics3d/src/utils/quickhull/QuickHull.cpp EXT_CPPSPLINE_SRCS = \ ext/cpp-spline/src/Bezier.cpp \ diff --git a/Makefile.nmake b/Makefile.nmake index 639ac0681..0d65c12ab 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -596,88 +596,93 @@ EXT_OGG_SRCS = \ ext/ogg/framing.c EXT_REACTPHYSICS3D_SRCS = \ - ext/reactphysics3d/src/body/CollisionBody.cpp \ + ext/reactphysics3d/src/body/Body.cpp \ ext/reactphysics3d/src/body/RigidBody.cpp \ ext/reactphysics3d/src/collision/broadphase/DynamicAABBTree.cpp \ + ext/reactphysics3d/src/collision/Collider.cpp \ + ext/reactphysics3d/src/collision/CollisionCallback.cpp \ + ext/reactphysics3d/src/collision/ContactManifold.cpp \ + ext/reactphysics3d/src/collision/ConvexMesh.cpp \ + ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp \ + ext/reactphysics3d/src/collision/HeightField.cpp \ + ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp \ + ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/CollisionDispatch.cpp \ - ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp \ + ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/GJK/GJKAlgorithm.cpp \ + ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp \ + ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp \ + ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp \ ext/reactphysics3d/src/collision/narrowphase/SAT/SATAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp \ ext/reactphysics3d/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp \ - ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp \ - ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp \ + ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp \ + ext/reactphysics3d/src/collision/OverlapCallback.cpp \ + ext/reactphysics3d/src/collision/PolygonVertexArray.cpp \ + ext/reactphysics3d/src/collision/RaycastInfo.cpp \ ext/reactphysics3d/src/collision/shapes/AABB.cpp \ - ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp \ - ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp \ - ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp \ ext/reactphysics3d/src/collision/shapes/BoxShape.cpp \ ext/reactphysics3d/src/collision/shapes/CapsuleShape.cpp \ ext/reactphysics3d/src/collision/shapes/CollisionShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp \ ext/reactphysics3d/src/collision/shapes/ConvexMeshShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp \ + ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp \ + ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp \ ext/reactphysics3d/src/collision/shapes/SphereShape.cpp \ ext/reactphysics3d/src/collision/shapes/TriangleShape.cpp \ - ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp \ - ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp \ - ext/reactphysics3d/src/collision/RaycastInfo.cpp \ - ext/reactphysics3d/src/collision/Collider.cpp \ - ext/reactphysics3d/src/collision/TriangleVertexArray.cpp \ - ext/reactphysics3d/src/collision/PolygonVertexArray.cpp \ ext/reactphysics3d/src/collision/TriangleMesh.cpp \ - ext/reactphysics3d/src/collision/PolyhedronMesh.cpp \ - ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp \ - ext/reactphysics3d/src/collision/ContactManifold.cpp \ + ext/reactphysics3d/src/collision/TriangleVertexArray.cpp \ + ext/reactphysics3d/src/collision/VertexArray.cpp \ + ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp \ + ext/reactphysics3d/src/components/BodyComponents.cpp \ + ext/reactphysics3d/src/components/ColliderComponents.cpp \ + ext/reactphysics3d/src/components/Components.cpp \ + ext/reactphysics3d/src/components/FixedJointComponents.cpp \ + ext/reactphysics3d/src/components/HingeJointComponents.cpp \ + ext/reactphysics3d/src/components/JointComponents.cpp \ + ext/reactphysics3d/src/components/RigidBodyComponents.cpp \ + ext/reactphysics3d/src/components/SliderJointComponents.cpp \ + ext/reactphysics3d/src/components/TransformComponents.cpp \ ext/reactphysics3d/src/constraint/BallAndSocketJoint.cpp \ ext/reactphysics3d/src/constraint/ContactPoint.cpp \ ext/reactphysics3d/src/constraint/FixedJoint.cpp \ ext/reactphysics3d/src/constraint/HingeJoint.cpp \ ext/reactphysics3d/src/constraint/Joint.cpp \ ext/reactphysics3d/src/constraint/SliderJoint.cpp \ - ext/reactphysics3d/src/engine/PhysicsCommon.cpp \ - ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp \ - ext/reactphysics3d/src/systems/ContactSolverSystem.cpp \ - ext/reactphysics3d/src/systems/DynamicsSystem.cpp \ - ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp \ - ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp \ - ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp \ - ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp \ - ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp \ - ext/reactphysics3d/src/engine/PhysicsWorld.cpp \ + ext/reactphysics3d/src/engine/Entity.cpp \ + ext/reactphysics3d/src/engine/EntityManager.cpp \ ext/reactphysics3d/src/engine/Island.cpp \ ext/reactphysics3d/src/engine/Material.cpp \ ext/reactphysics3d/src/engine/OverlappingPairs.cpp \ - ext/reactphysics3d/src/engine/Entity.cpp \ - ext/reactphysics3d/src/engine/EntityManager.cpp \ - ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp \ - ext/reactphysics3d/src/components/Components.cpp \ - ext/reactphysics3d/src/components/CollisionBodyComponents.cpp \ - ext/reactphysics3d/src/components/RigidBodyComponents.cpp \ - ext/reactphysics3d/src/components/TransformComponents.cpp \ - ext/reactphysics3d/src/components/ColliderComponents.cpp \ - ext/reactphysics3d/src/components/JointComponents.cpp \ - ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp \ - ext/reactphysics3d/src/components/FixedJointComponents.cpp \ - ext/reactphysics3d/src/components/HingeJointComponents.cpp \ - ext/reactphysics3d/src/components/SliderJointComponents.cpp \ - ext/reactphysics3d/src/collision/CollisionCallback.cpp \ - ext/reactphysics3d/src/collision/OverlapCallback.cpp \ + ext/reactphysics3d/src/engine/PhysicsCommon.cpp \ + ext/reactphysics3d/src/engine/PhysicsWorld.cpp \ ext/reactphysics3d/src/mathematics/Matrix2x2.cpp \ ext/reactphysics3d/src/mathematics/Matrix3x3.cpp \ ext/reactphysics3d/src/mathematics/Quaternion.cpp \ ext/reactphysics3d/src/mathematics/Transform.cpp \ ext/reactphysics3d/src/mathematics/Vector2.cpp \ ext/reactphysics3d/src/mathematics/Vector3.cpp \ - ext/reactphysics3d/src/memory/PoolAllocator.cpp \ - ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp \ ext/reactphysics3d/src/memory/HeapAllocator.cpp \ + ext/reactphysics3d/src/memory/MemoryAllocator.cpp \ ext/reactphysics3d/src/memory/MemoryManager.cpp \ - ext/reactphysics3d/src/utils/Profiler.cpp \ + ext/reactphysics3d/src/memory/PoolAllocator.cpp \ + ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp \ + ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp \ + ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp \ + ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp \ + ext/reactphysics3d/src/systems/ContactSolverSystem.cpp \ + ext/reactphysics3d/src/systems/DynamicsSystem.cpp \ + ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp \ + ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp \ + ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp \ + ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp \ + ext/reactphysics3d/src/utils/DebugRenderer.cpp \ ext/reactphysics3d/src/utils/DefaultLogger.cpp \ - ext/reactphysics3d/src/utils/DebugRenderer.cpp + ext/reactphysics3d/src/utils/Profiler.cpp \ + ext/reactphysics3d/src/utils/quickhull/QHHalfEdgeStructure.cpp \ + ext/reactphysics3d/src/utils/quickhull/QuickHull.cpp EXT_CPPSPLINE_SRCS = \ ext/cpp-spline/src/Bezier.cpp \ diff --git a/ext/reactphysics3d/CHANGELOG.md b/ext/reactphysics3d/CHANGELOG.md index 2c915cfc2..3a955c843 100644 --- a/ext/reactphysics3d/CHANGELOG.md +++ b/ext/reactphysics3d/CHANGELOG.md @@ -1,49 +1,132 @@ # Changelog -## Version 0.9.0 (January 4, 2022) +## [0.10.1] - 2024-06-25 + +### Fixed + +- Issue [#377](https://github.com/DanielChappuis/reactphysics3d/issues/377) Assert when destroying an empty PhysicsWorld +- Issue [#378](https://github.com/DanielChappuis/reactphysics3d/issues/378) Wrong raycasting result against the HeightFieldShape +- Issue [#381](https://github.com/DanielChappuis/reactphysics3d/issues/381) Fix robustness issue with CapsuleShape vs CapsuleShape collision +- Issue [#387](https://github.com/DanielChappuis/reactphysics3d/issues/387) Fix memory allocation issue when destroying a ConvexMesh +- Issue [#388](https://github.com/DanielChappuis/reactphysics3d/issues/388) Fix bodies without simulation collider not taken into account during islands creation +- Fix crash within testbed application in raycasting scene + +## [0.10.0] - 2024-03-10 + +### Changed + +- The library must now be compiled with a C++ 17 compiler +- The internal allocators now allocates memory that is 16-bytes aligned +- If the user sets its own custom allocator, the return allocated memory must now be 16 bytes aligned +- The PolyhedronMesh class has been renamed to ConvexMesh +- The PhysicsCommon::createPolyhedronMesh() method has been renamed to PhysicsCommon::createConvexMesh() +- The PhysicsCommon::destroyPolyhedronMesh() method has been renamed to PhysicsCommon::destroyConvexMesh() +- The PhysicsCommon::createConvexMesh() nows outputs a list of errors that might have happened during the mesh creation +- The PhysicsCommon::createConvexMesh() method now takes a reference to PolygonVertexArray +- When creating a ConvexMesh with PhysicsCommon::createConvexMesh(), the user data (vertices, faces) is now copied into the ConvexMesh and not shared anymore +- The PhysicsCommon::createTriangleMesh() method now directly takes a TriangleVertexArray +- The PhysicsCommon::createTriangleMesh() nows outputs a list of errors that might have happened during the mesh creation +- When creating a TriangleMesh with PhysicsCommon::createTriangleMesh(), the user data (vertices, faces) is now copied into the TriangleMesh and not shared anymore +- The PhysicsCommon::createHeightField() must be used to create a HeightField object +- The PhysicsCommon::createHeightFieldShape() method now takes a HeightField object +- It is not necessary anymore to specify the min/max height when creating a HeightFieldShape +- It is not possible anymore to specify the up axis when creating a HeightFieldShape +- When creating a HeightField with PhysicsCommon::createHeightField(), the user data (heights values) is now copied into the HeightField and not shared anymore +- The signature of the TriangleVertexArray::getTriangleVerticesIndices() method has changed +- The signature of the TriangleVertexArray::getNormal() method has changed +- The getLocalBounds() methods of the collision shapes now returns an AABB +- It is now necessary to enable debug rendering for each body that you want to debug using the Body::setIsDebugEnabled() method ### Added - - The performance of the collision detection and rigid bodies simulation (PhysicsWorld::update() method) has been improved significantly (1.7x speedup on average measured in [PEEL](https://github.com/Pierre-Terdiman/PEEL) scenes) - - Method RigidBody::resetForce() to reset the accumulated external force on a rigid body has been added - - Method RigidBody::resetTorque() to reset the accumulated external torque on a rigid body has been added - - Constructors with local-space anchor/axis have been added to BallAndSocketJointInfo, HingeJointInfo, FixedJointInfo and SliderJointInfo classes - - Method HingeJoint::getAngle() to get the current angle of the hinge joint has been added - - Method Joint::getReactionForce() has been added to retrieve the current reaction force of a joint - - Method Joint::getReactionTorque() has been added to retrieve the current reaction torque of a joint - - Method RigidBody::setLinearLockAxisFactor() to lock the translational movement of a body along the world-space x, y and z axes - - Method RigidBody::setAngularLockAxisFactor() to lock the rotational movement of a body around the world-space x, y and z axes - - Method RigidBody::applyLocalForceAtWorldPosition() to manually apply a force to a rigid body - - Method RigidBody::applyLocalForceAtLocalPosition() to manually apply a force to a rigid body - - Method RigidBody::applyLocalForceToCenterOfMass() to manually apply a force to a rigid body - - Method RigidBody::applyLocalTorque() to apply a local-space torque to a rigid body - - Method RigidBody::getForce() to get the total manually applied force on a rigid body - - Method RigidBody::getTorque() to get the total manually applied torque on a rigid body - - Method RigidBody::setIsSleeping() is now public in order to wake up or put to sleep a rigid body - - A cone limit can now be set to the ball-and-socket joint (this is useful for ragdolls) - - New scenes have been added to the testbed application (Box Tower, Ragdoll, Rope, Ball And Socket Joint, Bridge, Hinge Joint, Hinge Joint chain, Ball and - Socket Joint chain, Ball and Socket Joint net, ...) - - It is now possible to move bodies using the mouse (CTRL + click and drag) in the testbed application +- The library will now return errors found in input data during the creation of ConvexMesh, TriangularMesh and HeighField +- It is now possible to create a ConvexMeshShape by specifying only a list of vertices (automatic computation of convex hull using internal +QuickHull algorithm) +- The performance of static bodies has been improved +- The reporting of contact state is now correct even if the body goes to sleep +- The DebugRenderer can now display the normals of the collider faces for debugging purpose +- It is now possible to select for which bodies the debug information from the DebugRenderer is displayed + +### Removed + +- The TriangleMesh does not support adding multiple parts of a mesh anymore. +- The TriangleMesh::addSubpart() method has been removed. The PhysicsCommon::createTriangleMesh() method should be used instead +- The TriangleMesh::getSubpart() method has been removed. +- The TriangleMesh::getNbSubparts() method has been removed. +- When creating a HeightField, it is not possible to specify the up axis anymore (changing the Transform of the Collider must be used instead) +- No need to specify the min/max height when creating a HeightField anymore (this is now automatically computed) +- The HeightFiedShape::getNbColumns() method has been removed (HeightFieldShape::getHeightField()->getNbColumns() must be used instead) +- The HeightFiedShape::getNbRows() method has been removed (HeightFieldShape::getHeightField()->getNbRows() must be used instead) +- The HeightFiedShape::getHeightAt() method has been removed (HeightFieldShape::getHeightField()->getHeightAt() must be used instead) +- The CollisionBody class has been removed (RigidBody class must be used instead with a Collider where isSimulationCollider is disabled) +- The PhysicsWorld::createCollisionBody() method has been removed +- The PhysicsWorld::destroyCollisionBody() method has been removed +- The PhysicsWorld::getCollisionBody() method has been removed +- The PhysicsWorld::getNbCollisionBodies() method has been removed + +### Fixed + +- Issue [#206](https://github.com/DanielChappuis/reactphysics3d/issues/206) Collision issue and scaling of collider normals +- Issue [#235](https://github.com/DanielChappuis/reactphysics3d/issues/235) Removing a body should wake up its neighbors +- Issue [#237](https://github.com/DanielChappuis/reactphysics3d/issues/237) Wrong assert has been removed +- Issue [#239](https://github.com/DanielChappuis/reactphysics3d/issues/239) Memory allocation alignment +- Issue [#240](https://github.com/DanielChappuis/reactphysics3d/issues/240) Uninitialized variable +- Issue [#347](https://github.com/DanielChappuis/reactphysics3d/issues/347) Missing collision between capsule and triangle edge in some case +- Issue [#362](https://github.com/DanielChappuis/reactphysics3d/issues/362) Bug in Collider::setLocalToBodyTransform() +- Issue [#275](https://github.com/DanielChappuis/reactphysics3d/issues/275) Compilation warning +- Issue [#286](https://github.com/DanielChappuis/reactphysics3d/issues/286) Compilation error on Android +- Issue [#323](https://github.com/DanielChappuis/reactphysics3d/issues/323) Avoid conflict with X11 library +- Issue [#362](https://github.com/DanielChappuis/reactphysics3d/issues/362) Crash +- Issue [#364](https://github.com/DanielChappuis/reactphysics3d/issues/364) Assert in createContacts() method +- Issue [#366](https://github.com/DanielChappuis/reactphysics3d/issues/366) Crash when creating islands +- Issue [#370](https://github.com/DanielChappuis/reactphysics3d/issues/370) Compilation error on recent compiler +- Issue with edge vs edge collision detection for BoxShape, ConvexMeshShape, ConcaveMeshShape and HeightFieldShape (SAT algorithm) +- Compilation error on Clang 19 + +## [0.9.0] - 2022-01-04 ### Changed - - The PhysicsWorld::setGravity() method now takes a const parameter - - Rolling resistance constraint is not solved anymore in the solver. Angular damping needs to be used instead to simulate it. - - The List class has been renamed to Array - - The default number of iterations for the velocity solver is now 6 instead of 10 - - The default number of iterations for the position solver is now 3 instead of 5 - - Rename method RigidBody::applyForceAtWorldPosition() into RigidBody::applyWorldForceAtWorldPosition() - - Rename method RigidBody::applyForceAtLocalPosition() into RigidBody::applyWorldForceAtLocalPosition() - - Rename method RigidBody::applyForceToCenterOfMass() into RigidBody::applyWorldForceAtCenterOfMass() - - Rename method RigidBody::applyTorque() into RigidBody::applyWorldTorque() - - The raycasting broad-phase performance has been improved - - The raycasting performance against HeighFieldShape has been improved (better middle-phase algorithm) - - Robustness of polyhedron vs polyhedron collision detection has been improved in SAT algorithm (face contacts are favored over edge-edge contacts for better stability) +- The PhysicsWorld::setGravity() method now takes a const parameter +- Rolling resistance constraint is not solved anymore in the solver. Angular damping needs to be used instead to simulate it. +- The List class has been renamed to Array +- The default number of iterations for the velocity solver is now 6 instead of 10 +- The default number of iterations for the position solver is now 3 instead of 5 +- Rename method RigidBody::applyForceAtWorldPosition() into RigidBody::applyWorldForceAtWorldPosition() +- Rename method RigidBody::applyForceAtLocalPosition() into RigidBody::applyWorldForceAtLocalPosition() +- Rename method RigidBody::applyForceToCenterOfMass() into RigidBody::applyWorldForceAtCenterOfMass() +- Rename method RigidBody::applyTorque() into RigidBody::applyWorldTorque() +- The raycasting broad-phase performance has been improved +- The raycasting performance against HeighFieldShape has been improved (better middle-phase algorithm) +- Robustness of polyhedron vs polyhedron collision detection has been improved in SAT algorithm (face contacts are favored over edge-edge contacts for better stability) + +### Added + +- The performance of the collision detection and rigid bodies simulation (PhysicsWorld::update() method) has been improved significantly (1.7x speedup on average measured in [PEEL](https://github.com/Pierre-Terdiman/PEEL) scenes) +- Method RigidBody::resetForce() to reset the accumulated external force on a rigid body has been added +- Method RigidBody::resetTorque() to reset the accumulated external torque on a rigid body has been added +- Constructors with local-space anchor/axis have been added to BallAndSocketJointInfo, HingeJointInfo, FixedJointInfo and SliderJointInfo classes +- Method HingeJoint::getAngle() to get the current angle of the hinge joint has been added +- Method Joint::getReactionForce() has been added to retrieve the current reaction force of a joint +- Method Joint::getReactionTorque() has been added to retrieve the current reaction torque of a joint +- Method RigidBody::setLinearLockAxisFactor() to lock the translational movement of a body along the world-space x, y and z axes +- Method RigidBody::setAngularLockAxisFactor() to lock the rotational movement of a body around the world-space x, y and z axes +- Method RigidBody::applyLocalForceAtWorldPosition() to manually apply a force to a rigid body +- Method RigidBody::applyLocalForceAtLocalPosition() to manually apply a force to a rigid body +- Method RigidBody::applyLocalForceToCenterOfMass() to manually apply a force to a rigid body +- Method RigidBody::applyLocalTorque() to apply a local-space torque to a rigid body +- Method RigidBody::getForce() to get the total manually applied force on a rigid body +- Method RigidBody::getTorque() to get the total manually applied torque on a rigid body +- Method RigidBody::setIsSleeping() is now public in order to wake up or put to sleep a rigid body +- A cone limit can now be set to the ball-and-socket joint (this is useful for ragdolls) +- New scenes have been added to the testbed application (Box Tower, Ragdoll, Rope, Ball And Socket Joint, Bridge, Hinge Joint, Hinge Joint chain, Ball and +Socket Joint chain, Ball and Socket Joint net, ...) +- It is now possible to move bodies using the mouse (CTRL + click and drag) in the testbed application ### Removed - - Method Material::getRollingResistance() has been removed (angular damping has to be used instead of rolling resistance) - - Method Material::setRollingResistance() has been removed (angular damping has to be used instead of rolling resistance) +- Method Material::getRollingResistance() has been removed (angular damping has to be used instead of rolling resistance) +- Method Material::setRollingResistance() has been removed (angular damping has to be used instead of rolling resistance) ### Fixed @@ -59,7 +142,7 @@ - Issue [#157](https://github.com/DanielChappuis/reactphysics3d/issues/157) with matrix to quaternion conversion has been fixed - Issue [#184](https://github.com/DanielChappuis/reactphysics3d/issues/184) with update of mass/inertia properties of static bodies - Issue with the computation of the two friction vectors in the contact solver -- Issue with the rendering of the capsule collision shape in the Debug Renderer (missing triangle faces) +- Issue with the rendering of the capsule collision shape in the Debug Renderer (missing triangle faces) - Issue with wrong linear velocity update computed in RigidBody::setLocalCenterOfMass() method - Issue with wrong linear velocity update computed in RigidBody::updateLocalCenterOfMassFromColliders() method - Issue with wrong linear velocity update computed in RigidBody::updateMassPropertiesFromColliders() method @@ -68,284 +151,293 @@ - The default warning level is not set anymore in CMakeLists.txt file (Issue [#220](https://github.com/DanielChappuis/reactphysics3d/issues/220)) - Issue [#225](https://github.com/DanielChappuis/reactphysics3d/issues/225) with collision not working when setting a body to be static before calling updateMassPropertiesFromColliders() -## Version 0.8.0 (May 31, 2020) +## [0.8.0] - 2020-05-31 -Note that this release contains some public API changes. Please read carefully the following changes before upgrading to this new version and +Note that this release contains some public API changes. Please read carefully the following changes before upgrading to this new version and do not hesitate to take a look at the user manual. -### Added +### Changed - - It is now possible to change the size of a BoxShape using the BoxShape::setHalfExtents() method - - It is now possible to change the radius of a SphereShape using the SphereShape::setRadius() method - - It is now possible to change the height and radius of a CapsuleShape using the CapsuleShape::setHeight() and CapsuleShape::setRadius() methods - - It is now possible to change the scale of a ConvexMeshShape using the ConvexMeshShape::setScale() method - - It is now possible to change the scale of a ConcaveMeshShape using the ConcaveMeshShape::setScale() method - - It is now possible to change the scale of a HeightFieldShape using the HeightFieldShape::setScale() method - - A method PhysicsWorld::getCollisionBody(uint index) has been added on a physics world to retrieve a given CollisionBody - - A method PhysicsWorld::getRigidBody(uint index) has been added on a physics world to retrieve a given RigidBody - - A RigidBody::getLocalCenterOfMass() method has been added to retrieve the current local-space center of mass of a rigid body - - Add PhysicsCommon class that needs to be instanciated at the beginning and is used as a factory for other objects of the library (see the user manual) - - The RigidBody::updateLocalCenterOfMassFromColliders() method has been added to compute and set the center of mass of a body using its colliders - - The RigidBody::updateLocalInertiaTensorFromColliders() method has been added to compute and set the local inertia tensor of a body using its colliders - - The RigidBody::getLocalInertiaTensor() method has been added to retrieve the local-space inertia tensor of a rigid body - - The RigidBody::updateMassFromColliders() method has been added to compute and set the mass of a body using its colliders - - A Material nows has a mass density parameter that can be set using the Material::setMassDensity() method. The mass density is used to compute the mass of a collider when computing the mass of a rigid body - - A Collider can now be a trigger. This collider will be used to only report collisions with another collider but no collision response will be applied. You can use the Collider::setIsTrigger() method for this. - - The EventListener class now has a onTrigger() method that is called when a trigger collider is colling with another collider - - In the EventListener, the onContact() and onTrigger() method now reports the type of event (start, stay, exit) for each contact. This way the user can know whether it's a new contact or not or when two colliders are not in contact anymore - - A DebugRenderer class has been added in order to display debug info (colliders, AABBs, contacts, ...) in your simulation using graphics primitives (lines, triangles). - - A RigidBody::applyForceAtLocalPosition() method has been added to apply a force at a given position of the rigid body in local-space - - A default logger can be instanciated using the PhysicsCommon::createDefaultLogger() method - - The CMakeLists.txt file of the library has been refactored to use modern CMake. The targets are now exported when you install the library so that you can import the library with the find_package(ReactPhysics3D) function in your own CMakeLists.txt file - - A Hello World project has been added to show a very simple project that shows how to compile and use the ReactPhysics3D library +- The CollisionWorld::testCollision() methods do not have the 'categoryMaskBits' parameter anymore +- The CollisionWorld::testOverlap() methods do not have the 'categoryMaskBits' parameter anymore +- Many methods in the EventListener class have changed. Check the user manual for more information +- The way to retrieve contacts has changed. Check the user manual for more information +- DynamicsWorld and CollisionWorld classes have been merged into a single class called PhysicsWorld +- The ProxyShape class has been renamed into Collider +- The Material is now part of the Collider instead of the RigidBody. Therefore, it is now possible to have a RigidBody with multiple +colliders and a different material for each Collider +- The Logger has to be set using the PhysicsCommon::setLogger() method +- The Box::getExtent() method has been renamed to Box::getHalfExtents() +- An instance of the BoxShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createBoxShape() method +- An instance of the SphereShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createSphereShape() method +- An instance of the CapsuleShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createCapsuleShape() method +- An instance of the ConvexMeshShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createConvexMeshShape() method +- An instance of the HeightFieldShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createHeightFieldShape() method +- An instance of the ConcaveMeshShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createConcaveMeshShape() method +- An instance of the PolyhedronMesh class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createPolyhedronMesh() method +- An instance of the TriangleMesh class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createTriangleMesh() method +- The ProxyShape class has been renamed to Collider. The CollisionBody::addCollider(), RigidBody::addCollider() methods have to be used to create and add a collider to a body. Then methods CollisionBody::removeCollider(), RigidBody::removeCollider() need to be used to remove a collider from a body. +- The RigidBody::addCollider() method (previously addProxyShape() method) does not take a "mass" parameter anymore +- The RigidBody::setCenterOfMassLocal() method has been renamed to RigidBody::setLocalCenterOfMass() +- The RigidBody::setInertiaTensorLocal() method has been renamed to RigidBody::setLocalInertiaTensor() +- Now, the local inertia tensor of a rigid body has to be set using a Vector3 instead of a Matrix3x3. You only need to provide the three diagonal values of the matrix +- The RigidBody::recomputeMassInformation() method has been renamed to RigidBody::updateMassPropertiesFromColliders. +- Now, you need to manually call the RigidBody::updateMassPropertiesFromColliders() method after adding colliders to a rigid body to recompute its inertia tensor, center of mass and mass. There are other methods that you can use form that (see the user manual) +- The RigidBody::applyForce() method has been renamed to RigidBody::applyForceAtWorldPosition() +- The linear and angular damping function of the rigid bodies has been changed +- The rendering in the testbed application has been improved +- Many of the data inside the library have been refactored for better caching and easier parallelization in the future +- The old Logger class has been renamed to DefaultLogger +- The Logger class is now an abstract class that you can inherit from in order to receive log events from the library +- User manual and API documentation have been updated -### Fixed +### Added - - Issues [#125](https://github.com/DanielChappuis/reactphysics3d/issues/125) and [#106](https://github.com/DanielChappuis/reactphysics3d/issues/106) with CMake install of the library have been fixed - - Issue [#141](https://github.com/DanielChappuis/reactphysics3d/issues/141) with limits of hinge and slider joints has been fixed - - Issue [#117](https://github.com/DanielChappuis/reactphysics3d/issues/117) in documentation has been fixed - - Issue [#131](https://github.com/DanielChappuis/reactphysics3d/issues/131) in documentation has been fixed - - Issue [#139](https://github.com/DanielChappuis/reactphysics3d/issues/139) in API documentation has been fixed - - Issue [#122](https://github.com/DanielChappuis/reactphysics3d/issues/122) in logger has been fixed +- It is now possible to change the size of a BoxShape using the BoxShape::setHalfExtents() method +- It is now possible to change the radius of a SphereShape using the SphereShape::setRadius() method +- It is now possible to change the height and radius of a CapsuleShape using the CapsuleShape::setHeight() and CapsuleShape::setRadius() methods +- It is now possible to change the scale of a ConvexMeshShape using the ConvexMeshShape::setScale() method +- It is now possible to change the scale of a ConcaveMeshShape using the ConcaveMeshShape::setScale() method +- It is now possible to change the scale of a HeightFieldShape using the HeightFieldShape::setScale() method +- A method PhysicsWorld::getCollisionBody(uint index) has been added on a physics world to retrieve a given CollisionBody +- A method PhysicsWorld::getRigidBody(uint index) has been added on a physics world to retrieve a given RigidBody +- A RigidBody::getLocalCenterOfMass() method has been added to retrieve the current local-space center of mass of a rigid body +- Add PhysicsCommon class that needs to be instanciated at the beginning and is used as a factory for other objects of the library (see the user manual) +- The RigidBody::updateLocalCenterOfMassFromColliders() method has been added to compute and set the center of mass of a body using its colliders +- The RigidBody::updateLocalInertiaTensorFromColliders() method has been added to compute and set the local inertia tensor of a body using its colliders +- The RigidBody::getLocalInertiaTensor() method has been added to retrieve the local-space inertia tensor of a rigid body +- The RigidBody::updateMassFromColliders() method has been added to compute and set the mass of a body using its colliders +- A Material nows has a mass density parameter that can be set using the Material::setMassDensity() method. The mass density is used to compute the mass of a collider when computing the mass of a rigid body +- A Collider can now be a trigger. This collider will be used to only report collisions with another collider but no collision response will be applied. You can use the Collider::setIsTrigger() method for this. +- The EventListener class now has a onTrigger() method that is called when a trigger collider is colling with another collider +- In the EventListener, the onContact() and onTrigger() method now reports the type of event (start, stay, exit) for each contact. This way the user can know whether it's a new contact or not or when two colliders are not in contact anymore +- A DebugRenderer class has been added in order to display debug info (colliders, AABBs, contacts, ...) in your simulation using graphics primitives (lines, triangles). +- A RigidBody::applyForceAtLocalPosition() method has been added to apply a force at a given position of the rigid body in local-space +- A default logger can be instanciated using the PhysicsCommon::createDefaultLogger() method +- The CMakeLists.txt file of the library has been refactored to use modern CMake. The targets are now exported when you install the library so that you can import the library with the find_package(ReactPhysics3D) function in your own CMakeLists.txt file +- A Hello World project has been added to show a very simple project that shows how to compile and use the ReactPhysics3D library -### Changed +### Removed - - The CollisionWorld::testCollision() methods do not have the 'categoryMaskBits' parameter anymore - - The CollisionWorld::testOverlap() methods do not have the 'categoryMaskBits' parameter anymore - - Many methods in the EventListener class have changed. Check the user manual for more information - - The way to retrieve contacts has changed. Check the user manual for more information - - DynamicsWorld and CollisionWorld classes have been merged into a single class called PhysicsWorld - - The ProxyShape class has been renamed into Collider - - The Material is now part of the Collider instead of the RigidBody. Therefore, it is now possible to have a RigidBody with multiple - colliders and a different material for each Collider - - The Logger has to be set using the PhysicsCommon::setLogger() method - - The Box::getExtent() method has been renamed to Box::getHalfExtents() - - An instance of the BoxShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createBoxShape() method - - An instance of the SphereShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createSphereShape() method - - An instance of the CapsuleShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createCapsuleShape() method - - An instance of the ConvexMeshShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createConvexMeshShape() method - - An instance of the HeightFieldShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createHeightFieldShape() method - - An instance of the ConcaveMeshShape class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createConcaveMeshShape() method - - An instance of the PolyhedronMesh class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createPolyhedronMesh() method - - An instance of the TriangleMesh class cannot be instanciated directly anymore. You need to use the PhysicsCommon::createTriangleMesh() method - - The ProxyShape class has been renamed to Collider. The CollisionBody::addCollider(), RigidBody::addCollider() methods have to be used to create and add a collider to a body. Then methods CollisionBody::removeCollider(), RigidBody::removeCollider() need to be used to remove a collider from a body. - - The RigidBody::addCollider() method (previously addProxyShape() method) does not take a "mass" parameter anymore - - The RigidBody::setCenterOfMassLocal() method has been renamed to RigidBody::setLocalCenterOfMass() - - The RigidBody::setInertiaTensorLocal() method has been renamed to RigidBody::setLocalInertiaTensor() - - Now, the local inertia tensor of a rigid body has to be set using a Vector3 instead of a Matrix3x3. You only need to provide the three diagonal values of the matrix - - The RigidBody::recomputeMassInformation() method has been renamed to RigidBody::updateMassPropertiesFromColliders. - - Now, you need to manually call the RigidBody::updateMassPropertiesFromColliders() method after adding colliders to a rigid body to recompute its inertia tensor, center of mass and mass. There are other methods that you can use form that (see the user manual) - - The RigidBody::applyForce() method has been renamed to RigidBody::applyForceAtWorldPosition() - - The linear and angular damping function of the rigid bodies has been changed - - The rendering in the testbed application has been improved - - Many of the data inside the library have been refactored for better caching and easier parallelization in the future - - The old Logger class has been renamed to DefaultLogger - - The Logger class is now an abstract class that you can inherit from in order to receive log events from the library - - User manual and API documentation have been updated +- The method DynamicsWorld::getContactsList() has been removed. You need to use the EventListener class to retrieve contacts now (see the user manual). +- The DynamicsWorld::getNbJoints() method has been removed. +- The EventListener::beginInternalTick() method has been removed (because internal ticks do not exist anymore). +- The EventListener::endInternalTick() method has been removed (because internal ticks do not exist anymore). +- The RigidBody::getJointsList() method has been removed. +- It is not possible anymore to set custom pool and stack frame allocators. Only the base allocator can be customized when creating a PhysicsCommon instance. +- The RigidBody::setInverseInertiaTensorLocal() method has been removed. The RigidBody::setInertiaTensorLocal() has to be used instead. +- The RigidBody::getInverseInertiaTensorWorld() method has been removed. +- The Collider::getMass() method has been removed. -### Removed +### Fixed - - The method DynamicsWorld::getContactsList() has been removed. You need to use the EventListener class to retrieve contacts now (see the user manual). - - The DynamicsWorld::getNbJoints() method has been removed. - - The EventListener::beginInternalTick() method has been removed (because internal ticks do not exist anymore). - - The EventListener::endInternalTick() method has been removed (because internal ticks do not exist anymore). - - The RigidBody::getJointsList() method has been removed. - - It is not possible anymore to set custom pool and stack frame allocators. Only the base allocator can be customized when creating a PhysicsCommon instance. - - The RigidBody::setInverseInertiaTensorLocal() method has been removed. The RigidBody::setInertiaTensorLocal() has to be used instead. - - The RigidBody::getInverseInertiaTensorWorld() method has been removed. - - The Collider::getMass() method has been removed. +- Issues [#125](https://github.com/DanielChappuis/reactphysics3d/issues/125) and [#106](https://github.com/DanielChappuis/reactphysics3d/issues/106) with CMake install of the library have been fixed +- Issue [#141](https://github.com/DanielChappuis/reactphysics3d/issues/141) with limits of hinge and slider joints has been fixed +- Issue [#117](https://github.com/DanielChappuis/reactphysics3d/issues/117) in documentation has been fixed +- Issue [#131](https://github.com/DanielChappuis/reactphysics3d/issues/131) in documentation has been fixed +- Issue [#139](https://github.com/DanielChappuis/reactphysics3d/issues/139) in API documentation has been fixed +- Issue [#122](https://github.com/DanielChappuis/reactphysics3d/issues/122) in logger has been fixed -## Version 0.7.1 (July 01, 2019) +## [0.7.1] - 2019-07-01 ### Added - - Make possible for the user to get vertices, normals and triangle indices of a ConcaveMeshShape - - Make possible for the user to get vertices and height values of the HeightFieldShape +- Make possible for the user to get vertices, normals and triangle indices of a ConcaveMeshShape +- Make possible for the user to get vertices and height values of the HeightFieldShape ### Fixed - - Bug [#45](https://github.com/DanielChappuis/reactphysics3d/issues/45) has been fixed. - - Bug [#50](https://github.com/DanielChappuis/reactphysics3d/issues/50) has been fixed. - - Bug [#52](https://github.com/DanielChappuis/reactphysics3d/issues/52) has been fixed. - - Bug [#53](https://github.com/DanielChappuis/reactphysics3d/issues/53) has been fixed. - - Bug [#54](https://github.com/DanielChappuis/reactphysics3d/issues/54) has been fixed. - - Bug [#55](https://github.com/DanielChappuis/reactphysics3d/issues/55) has been fixed. - - Bug [#51](https://github.com/DanielChappuis/reactphysics3d/issues/51) has been fixed. - - Bug [#60](https://github.com/DanielChappuis/reactphysics3d/issues/60) has been fixed. - - Bug [#57](https://github.com/DanielChappuis/reactphysics3d/issues/57) has been fixed. - - Bug [#37](https://github.com/DanielChappuis/reactphysics3d/issues/37) has been fixed. - - Bug [#62](https://github.com/DanielChappuis/reactphysics3d/issues/62) has been fixed. - - Bug [#63](https://github.com/DanielChappuis/reactphysics3d/issues/63) has been fixed. - - Bug [#82](https://github.com/DanielChappuis/reactphysics3d/issues/82) has been fixed. - - Bug [#85](https://github.com/DanielChappuis/reactphysics3d/issues/85) has been fixed. - - Bug [#79](https://github.com/DanielChappuis/reactphysics3d/issues/79) has been fixed. - - Bug: the free() method was called in PoolAllocator instead of release() method of base allocator. +- Bug [#45](https://github.com/DanielChappuis/reactphysics3d/issues/45) has been fixed. +- Bug [#50](https://github.com/DanielChappuis/reactphysics3d/issues/50) has been fixed. +- Bug [#52](https://github.com/DanielChappuis/reactphysics3d/issues/52) has been fixed. +- Bug [#53](https://github.com/DanielChappuis/reactphysics3d/issues/53) has been fixed. +- Bug [#54](https://github.com/DanielChappuis/reactphysics3d/issues/54) has been fixed. +- Bug [#55](https://github.com/DanielChappuis/reactphysics3d/issues/55) has been fixed. +- Bug [#51](https://github.com/DanielChappuis/reactphysics3d/issues/51) has been fixed. +- Bug [#60](https://github.com/DanielChappuis/reactphysics3d/issues/60) has been fixed. +- Bug [#57](https://github.com/DanielChappuis/reactphysics3d/issues/57) has been fixed. +- Bug [#37](https://github.com/DanielChappuis/reactphysics3d/issues/37) has been fixed. +- Bug [#62](https://github.com/DanielChappuis/reactphysics3d/issues/62) has been fixed. +- Bug [#63](https://github.com/DanielChappuis/reactphysics3d/issues/63) has been fixed. +- Bug [#82](https://github.com/DanielChappuis/reactphysics3d/issues/82) has been fixed. +- Bug [#85](https://github.com/DanielChappuis/reactphysics3d/issues/85) has been fixed. +- Bug [#79](https://github.com/DanielChappuis/reactphysics3d/issues/79) has been fixed. +- Bug: the free() method was called in PoolAllocator instead of release() method of base allocator. ### Removed - - The CollisionWorld::setCollisionDispatch() method has been removed. In order to use a custom collision - algorithm, you must not get the collision dispatch object with the - CollisionWorld::getCollisionDispatch() method and set a collision algorithm to this object. - - The methods CollisionBody::getProxyShapesList() has been remove. You can now use the - CollisionBody::getNbProxyShapes() method to know the number of proxy-shapes of a body and the - CollisionBody::getProxyShape(uint proxyShapeIndex) method to get a given proxy-shape of the body. - - The CollisionWorld::testAABBOverlap() methods have been removed. +- The CollisionWorld::setCollisionDispatch() method has been removed. In order to use a custom collision +algorithm, you must not get the collision dispatch object with the +CollisionWorld::getCollisionDispatch() method and set a collision algorithm to this object. +- The methods CollisionBody::getProxyShapesList() has been remove. You can now use the +CollisionBody::getNbProxyShapes() method to know the number of proxy-shapes of a body and the +CollisionBody::getProxyShape(uint proxyShapeIndex) method to get a given proxy-shape of the body. +- The CollisionWorld::testAABBOverlap() methods have been removed. -## Version 0.7.0 (May 1, 2018) +## [0.7.0] - 2018-05-01 ### Added - - - Dedicated Sphere vs Capsule collision detection algorithm. - - Dedicated Capsule vs Capsule collision detection algorithm. - - Allow a Rigid Body to have zero mass (for static bodies for instance). - - Add single frame memory allocator for faster memory allocation in a frame. - - Make possible to have a profiler per DynamicsWorld instead of a unique one. - - Make possible to use your own allocation/deallocation methods instead of default malloc/free - - Make possible to display the AABB of the bodies in the testbed application. - - Add RigidBody::setInverseLocalInertiaTensor() method to directly set the inverse inertia tensor of a rigid body. - - More unit tests have been added + +- Dedicated Sphere vs Capsule collision detection algorithm. +- Dedicated Capsule vs Capsule collision detection algorithm. +- Allow a Rigid Body to have zero mass (for static bodies for instance). +- Add single frame memory allocator for faster memory allocation in a frame. +- Make possible to have a profiler per DynamicsWorld instead of a unique one. +- Make possible to use your own allocation/deallocation methods instead of default malloc/free +- Make possible to display the AABB of the bodies in the testbed application. +- Add RigidBody::setInverseLocalInertiaTensor() method to directly set the inverse inertia tensor of a rigid body. +- More unit tests have been added ### Changed - - Use single-shot contact manifold computation instead of incremental across several frames. - - Replace the EPA narrow-phase collision detection with SAT algorithm. - - The collision detection is now faster and more robust. - - Code has been refactored such that we do not use STL containers anymore. - - The way to create a ConvexMeshShape has changed (see the user manual) - - The vertex indices stride has changed in the TriangleVertexArray for ConvexMeshShape (see the API documentation) - - The raycasting of a ConvexMeshShape does not use GJK algorithm anymore. - - The test do detect if a point is inside of a ConvexMeshShape does not use GJK algorithm anymore. - - A lot of optimizations have been performed and the library is now faster. - - Release code is now compiled with -O2 compiler optimization level (instead of none) - - Documentation has been updated +- Use single-shot contact manifold computation instead of incremental across several frames. +- Replace the EPA narrow-phase collision detection with SAT algorithm. +- The collision detection is now faster and more robust. +- Code has been refactored such that we do not use STL containers anymore. +- The way to create a ConvexMeshShape has changed (see the user manual) +- The vertex indices stride has changed in the TriangleVertexArray for ConvexMeshShape (see the API documentation) +- The raycasting of a ConvexMeshShape does not use GJK algorithm anymore. +- The test do detect if a point is inside of a ConvexMeshShape does not use GJK algorithm anymore. +- A lot of optimizations have been performed and the library is now faster. +- Release code is now compiled with -O2 compiler optimization level (instead of none) +- Documentation has been updated ### Removed - - - Quaternion constructor with Euler angles has been removed. The Quaternion::fromEulerAngles() method should be used instead. - - Cylinder and Cone collision shapes have been removed. The ConvexMeshShape collision shape should be used instead. - - The ProxyShape::setLocalScaling() method has been removed. The ConvexMeshShape, ConcaveMeshShape and HeightFieldShape - collision shapes can be scaled directly. + +- Quaternion constructor with Euler angles has been removed. The Quaternion::fromEulerAngles() method should be used instead. +- Cylinder and Cone collision shapes have been removed. The ConvexMeshShape collision shape should be used instead. +- The ProxyShape::setLocalScaling() method has been removed. The ConvexMeshShape, ConcaveMeshShape and HeightFieldShape +collision shapes can be scaled directly. ### Fixed - - Issues with checkboxes in testbed application were fixed. - - Fix issue with wrong AABB computation that can cause missing collision in broad-phase - - Bug [#26](https://github.com/DanielChappuis/reactphysics3d/issues/26) has been fixed. - - Bug [#30](https://github.com/DanielChappuis/reactphysics3d/issues/30) has been fixed. - - Bug [#32](https://github.com/DanielChappuis/reactphysics3d/issues/32) has been fixed. - - Issue [#31](https://github.com/DanielChappuis/reactphysics3d/issues/31) has been fixed. - - Issue [#34](https://github.com/DanielChappuis/reactphysics3d/issues/34) has been fixed. - - Issue [#37](https://github.com/DanielChappuis/reactphysics3d/issues/37) has been fixed. +- Issues with checkboxes in testbed application were fixed. +- Fix issue with wrong AABB computation that can cause missing collision in broad-phase +- Bug [#26](https://github.com/DanielChappuis/reactphysics3d/issues/26) has been fixed. +- Bug [#30](https://github.com/DanielChappuis/reactphysics3d/issues/30) has been fixed. +- Bug [#32](https://github.com/DanielChappuis/reactphysics3d/issues/32) has been fixed. +- Issue [#31](https://github.com/DanielChappuis/reactphysics3d/issues/31) has been fixed. +- Issue [#34](https://github.com/DanielChappuis/reactphysics3d/issues/34) has been fixed. +- Issue [#37](https://github.com/DanielChappuis/reactphysics3d/issues/37) has been fixed. -## Version 0.6.0 (April 15, 2016) +## [0.6.0] - 2016-04-15 ### Added - - Support for static concave triangular mesh collision shape. - - Support for height field collision shape. - - Support for rolling resistance. - - It is now possible to change the local scaling of a collision shape. - - Add new constructor of ConvexMeshShape that accepts a triangular mesh. - - It is now easier to use your own narrow-phase collision detection algorithm. - - The CollisionWorld and DynamicsWorld now automatically destroys bodies and joints that have not been destroyed at the end. - - New testbed application with demo scenes. +- Support for static concave triangular mesh collision shape. +- Support for height field collision shape. +- Support for rolling resistance. +- It is now possible to change the local scaling of a collision shape. +- Add new constructor of ConvexMeshShape that accepts a triangular mesh. +- It is now easier to use your own narrow-phase collision detection algorithm. +- The CollisionWorld and DynamicsWorld now automatically destroys bodies and joints that have not been destroyed at the end. +- New testbed application with demo scenes. ### Changed - - The DynamicsWorld::update() method now takes the time step for the next simulation step in parameter. +- The DynamicsWorld::update() method now takes the time step for the next simulation step in parameter. -## Version 0.5.0 (March 4, 2015) +## [0.5.0] - 2015-03-04 ### Added - - It is now possible to use multiple collision shapes per body. - - Ray casting support. - - Add methods to check if a point is inside a body or a proxy shape. - - Add collision filtering using collision categories (with bit masks). - - It is possible to attach user data to a body or a proxy shape. - - It is now possible to create a quaternion using Euler angles. - - It now possible to activate of deactivate a body. - - Differentiation between dynamic, kinematic and static bodies. - - Gravity can now be changed after the creation of the world. - - The center of mass and inertia tensor of a body can be set manually (center of mass can be different from body origin). - - Add a simulation step callback in the EventListener class that is called at each internal physics tick. - - Add methods to transform points/vectors from/into local-space/world-space coordinates of a body. - - Add CollisionWorld::testAABBOverlap() method to test overlap between two bodies or two proxy shapes. - - Add a ray casting example. - - Add unit tests for ray casting and collision detection. +- It is now possible to use multiple collision shapes per body. +- Ray casting support. +- Add methods to check if a point is inside a body or a proxy shape. +- Add collision filtering using collision categories (with bit masks). +- It is possible to attach user data to a body or a proxy shape. +- It is now possible to create a quaternion using Euler angles. +- It now possible to activate of deactivate a body. +- Differentiation between dynamic, kinematic and static bodies. +- Gravity can now be changed after the creation of the world. +- The center of mass and inertia tensor of a body can be set manually (center of mass can be different from body origin). +- Add a simulation step callback in the EventListener class that is called at each internal physics tick. +- Add methods to transform points/vectors from/into local-space/world-space coordinates of a body. +- Add CollisionWorld::testAABBOverlap() method to test overlap between two bodies or two proxy shapes. +- Add a ray casting example. +- Add unit tests for ray casting and collision detection. ### Changed - - Replace the Sweep-And-Prune algorithm by a Dynamic AABB Tree for the broad-phase collision detection. - - The center of mass of a body is now automatically computed from its collision shapes. - - Use GLFW instead of GLUT/Freeglut for the examples. +- Replace the Sweep-And-Prune algorithm by a Dynamic AABB Tree for the broad-phase collision detection. +- The center of mass of a body is now automatically computed from its collision shapes. +- Use GLFW instead of GLUT/Freeglut for the examples. ### Fixed - - Fix two issues in the EPA algorithm. +- Fix two issues in the EPA algorithm. -## Version 0.4.0 (October 7, 2013) +## [0.4.0] - 2013-10-07 ### Added - - Add collision shapes (Capsule, Convex Mesh). - - Add joints (Ball and Socket, Hinge, Slider, Fixed). - - Add sleeping technique for inactive bodies. - - Add velocity damping. - - It is now easier to apply force and torque to a rigid body. - - Add the EventListener class to allow the user to be notified when some events occur (contacts, …). - - Add examples for the joints and collision shapes. - - Make possible to modify the collision margin of some collision shapes. - - Add a Material class to keep track of the properties of a rigid body (friction coefficient, bounciness, …). - - Add a hierarchical real-time profiler. +- Add collision shapes (Capsule, Convex Mesh). +- Add joints (Ball and Socket, Hinge, Slider, Fixed). +- Add sleeping technique for inactive bodies. +- Add velocity damping. +- It is now easier to apply force and torque to a rigid body. +- Add the EventListener class to allow the user to be notified when some events occur (contacts, …). +- Add examples for the joints and collision shapes. +- Make possible to modify the collision margin of some collision shapes. +- Add a Material class to keep track of the properties of a rigid body (friction coefficient, bounciness, …). +- Add a hierarchical real-time profiler. ### Changed - - Collision shapes now use the internal memory allocator. - - New internal memory allocator. +- Collision shapes now use the internal memory allocator. +- New internal memory allocator. ### Removed - - Remove the world gravity force from the external force of rigid bodies and allow the user to disable the gravity on a given body. - - Reduce the allocated memory of the broad-phase when several bodies are removed from the world. +- Remove the world gravity force from the external force of rigid bodies and allow the user to disable the gravity on a given body. +- Reduce the allocated memory of the broad-phase when several bodies are removed from the world. ### Fixed - - Fix issue in the Sweep-And-Prune broad-phase collision detection (thanks to Aleksi Sapon). - - Fix issue in the contact solver resulting in less jittering. - +- Fix issue in the Sweep-And-Prune broad-phase collision detection (thanks to Aleksi Sapon). +- Fix issue in the contact solver resulting in less jittering. -## Version 0.3.0 +## [0.3.0] - 2013-03-20 ### Added - - Implementation of a dedicated collision detection algorithm for spheres against spheres instead of using GJK/EPA algorithm. - - Make possible to use a unique instance of a collision shape for multiple rigid bodies. - - Create the API documentation using Doxygen. - - Add Unit tests +- Implementation of a dedicated collision detection algorithm for spheres against spheres instead of using GJK/EPA algorithm. +- Make possible to use a unique instance of a collision shape for multiple rigid bodies. +- Create the API documentation using Doxygen. +- Add Unit tests ### Changed - - The Sweep-and-Prune broad-phase collision detection algorithm has been rewritten according to the technique described by Pierre Terdiman at http://www.codercorner.com/SAP.pdf to be much more efficient than the previous naive implementation. - - The contact solver has been rewritten to use the Sequential Impulses technique from Erin Catto which is mathematically equivalent to the Projected Gauss Seidel technique that was used before. The Sequential Impulses technique is more intuitive. - - Make GJK/EPA algorithm more robust for spheres. - - Change the structure of the code for a better separation between the collision detection and the dynamics simulation code. +- The Sweep-and-Prune broad-phase collision detection algorithm has been rewritten according to the technique described by Pierre Terdiman at http://www.codercorner.com/SAP.pdf to be much more efficient than the previous naive implementation. +- The contact solver has been rewritten to use the Sequential Impulses technique from Erin Catto which is mathematically equivalent to the Projected Gauss Seidel technique that was used before. The Sequential Impulses technique is more intuitive. +- Make GJK/EPA algorithm more robust for spheres. +- Change the structure of the code for a better separation between the collision detection and the dynamics simulation code. -## Version 0.2.0 +## [0.2.0] - 2013-01-01 ### Added - - Add the GJK/EPA algorithm for convex shapes collision detection - - Persistent contacts storing between frames - - Add Sphere, Cylinder and Cone collision shapes - - Add the Transform class for better handling of position and orientation of rigid bodies - - It is now possible to approximate the inertia tensor of rigid bodies using the collision shape inertia tensor. - - The AABB is now computed automatically using the collision shape - - Add the MemoryPool class to avoid intense dynamic allocation during the simulation +- Add the GJK/EPA algorithm for convex shapes collision detection +- Persistent contacts storing between frames +- Add Sphere, Cylinder and Cone collision shapes +- Add the Transform class for better handling of position and orientation of rigid bodies +- It is now possible to approximate the inertia tensor of rigid bodies using the collision shape inertia tensor. +- The AABB is now computed automatically using the collision shape +- Add the MemoryPool class to avoid intense dynamic allocation during the simulation ### Changed - - Simplification of the mathematics library - - Optimization of the constraint solver - - ReactPhysics3D is now under the zlib license - +- Simplification of the mathematics library +- Optimization of the constraint solver +- ReactPhysics3D is now under the zlib license + +[0.10.1]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.10.0...v0.10.1 +[0.10.0]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.9.0...v0.10.0 +[0.9.0]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.8.0...v0.9.0 +[0.8.0]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.7.1...v0.8.0 +[0.7.1]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.7.0...v0.7.1 +[0.7.0]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.6.0...v0.7.0 +[0.6.0]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.5.0...v0.6.0 +[0.5.0]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.4.0...v0.5.0 +[0.4.0]: https://github.com/DanielChappuis/reactphysics3d/compare/v0.3.0...v0.4.0 +[0.3.0]: https://github.com/DanielChappuis/reactphysics3d/releases/tag/v0.3.0 diff --git a/ext/reactphysics3d/FUNDING.yml b/ext/reactphysics3d/FUNDING.yml new file mode 100644 index 000000000..7ae73106d --- /dev/null +++ b/ext/reactphysics3d/FUNDING.yml @@ -0,0 +1,3 @@ +github: [DanielChappuis] +custom: ["https://reactphysics3d.com/donate.html"] + diff --git a/ext/reactphysics3d/LICENSE b/ext/reactphysics3d/LICENSE index c2d4eafed..7c2f2a9b8 100644 --- a/ext/reactphysics3d/LICENSE +++ b/ext/reactphysics3d/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2022 Daniel Chappuis http://www.reactphysics3d.com +Copyright (c) 2010-2024 Daniel Chappuis http://www.reactphysics3d.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/ext/reactphysics3d/README.md b/ext/reactphysics3d/README.md index 5a1fdba85..f56ee9a6c 100644 --- a/ext/reactphysics3d/README.md +++ b/ext/reactphysics3d/README.md @@ -22,7 +22,7 @@ Drawing

-## :dart: Features +## Features - Rigid body dynamics - Discrete collision detection @@ -44,37 +44,42 @@ - Logs - Unit tests -## :book: Documentation +## Documentation You can find the user manual and the Doxygen API documentation here. -## :information_source: Branches +## Branches The "master" branch always contains the last released version of the library and some possible hot fixes. This is the most stable version. On the other side, the "develop" branch is used for development. This branch is frequently updated and can be quite unstable. Therefore, if you want to use the library in your application, it is recommended to checkout the "master" branch. -## :question: Questions +## Questions If you have any questions about the library and how to use it, you should use Github Discussions to read previous questions and answers or to ask new questions. If you want, you can also share your project there if you are using the ReactPhysics3D library. -## :warning: Issues +## Questions/Discussions + +You can use the Discussions section to ask your questions or answer questions from other users of the library. Please, do not open a new issue to ask a question. + +## Issues If you find any issue with the library, you can report it on the issue tracker here. +Please, do not open a new issue to ask a question. Use the Discussions section for your questions. -## :man: Author +## Author The ReactPhysics3D library has been created and is maintained by Daniel Chappuis. -## :copyright: License +## License The ReactPhysics3D library is released under the open-source ZLib license. -## :+1: Sponsorship +## Sponsorship If you are using this library and want to support its development, you can sponsor it here. -## :clap: Credits +## Credits Thanks a lot to Erin Catto, Dirk Gregorius, Erwin Coumans, Pierre Terdiman and Christer Ericson for their amazing GDC presentations, their physics engines, their books or articles and their contributions on many physics engine forums. diff --git a/ext/reactphysics3d/VERSION b/ext/reactphysics3d/VERSION index ac39a106c..571215736 100644 --- a/ext/reactphysics3d/VERSION +++ b/ext/reactphysics3d/VERSION @@ -1 +1 @@ -0.9.0 +0.10.1 diff --git a/ext/reactphysics3d/include/reactphysics3d/body/CollisionBody.h b/ext/reactphysics3d/include/reactphysics3d/body/Body.h similarity index 88% rename from ext/reactphysics3d/include/reactphysics3d/body/CollisionBody.h rename to ext/reactphysics3d/include/reactphysics3d/body/Body.h index 5dd6a879d..3f5014204 100644 --- a/ext/reactphysics3d/include/reactphysics3d/body/CollisionBody.h +++ b/ext/reactphysics3d/include/reactphysics3d/body/Body.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -45,12 +45,11 @@ class DefaultPoolAllocator; class Profiler; class Logger; -// Class CollisionBody +// Class Body /** - * This class represents a body that is able to collide with others - * bodies. + * This class represents a body */ -class CollisionBody { +class Body { protected : @@ -62,6 +61,9 @@ class CollisionBody { /// Reference to the world the body belongs to PhysicsWorld& mWorld; + /// Determines if debug information is computed for this body + bool mIsDebugEnabled; + #ifdef IS_RP3D_PROFILING_ENABLED /// Pointer to the profiler @@ -81,21 +83,24 @@ class CollisionBody { /// (as if the body has moved). void askForBroadPhaseCollisionCheck() const; + /// Update whether the body has at least one simulation provider + void updateHasSimulationCollider(); + public : // -------------------- Methods -------------------- // /// Constructor - CollisionBody(PhysicsWorld& world, Entity entity); + Body(PhysicsWorld& world, Entity entity); /// Destructor - virtual ~CollisionBody(); + virtual ~Body(); /// Deleted copy-constructor - CollisionBody(const CollisionBody& body) = delete; + Body(const Body& body) = delete; /// Deleted assignment operator - CollisionBody& operator=(const CollisionBody& body) = delete; + Body& operator=(const Body& body) = delete; /// Return the corresponding entity of the body Entity getEntity() const; @@ -157,6 +162,12 @@ class CollisionBody { /// Return the body local-space coordinates of a vector given in the world-space coordinates Vector3 getLocalVector(const Vector3& worldVector) const; + /// Set whether or not debug lines are computed for this body + void setIsDebugEnabled(bool enabled); + + /// Return true if debug lines should be computed for this body + bool isDebugEnabled() const; + #ifdef IS_RP3D_PROFILING_ENABLED /// Set the profiler @@ -178,7 +189,7 @@ class CollisionBody { * @param worldAABB The AABB (in world-space coordinates) that will be used to test overlap * @return True if the given AABB overlaps with the AABB of the collision body */ -RP3D_FORCE_INLINE bool CollisionBody::testAABBOverlap(const AABB& worldAABB) const { +RP3D_FORCE_INLINE bool Body::testAABBOverlap(const AABB& worldAABB) const { return worldAABB.testCollision(getAABB()); } @@ -186,19 +197,10 @@ RP3D_FORCE_INLINE bool CollisionBody::testAABBOverlap(const AABB& worldAABB) con /** * @return The entity of the body */ -RP3D_FORCE_INLINE Entity CollisionBody::getEntity() const { +RP3D_FORCE_INLINE Entity Body::getEntity() const { return mEntity; } -#ifdef IS_RP3D_PROFILING_ENABLED - -// Set the profiler -RP3D_FORCE_INLINE void CollisionBody::setProfiler(Profiler* profiler) { - mProfiler = profiler; -} - -#endif - } #endif diff --git a/ext/reactphysics3d/include/reactphysics3d/body/RigidBody.h b/ext/reactphysics3d/include/reactphysics3d/body/RigidBody.h index 2c3971669..8133af9fa 100644 --- a/ext/reactphysics3d/include/reactphysics3d/body/RigidBody.h +++ b/ext/reactphysics3d/include/reactphysics3d/body/RigidBody.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -28,7 +28,7 @@ // Libraries #include -#include +#include #include /// Namespace reactphysics3d @@ -45,16 +45,22 @@ enum class BodyType; * This class represents a rigid body of the physics * engine. A rigid body is a non-deformable body that * has a constant mass. This class inherits from the - * CollisionBody class. + * Body class. */ -class RigidBody : public CollisionBody { +class RigidBody : public Body { protected : // -------------------- Methods -------------------- // - /// Update whether the current overlapping pairs where this body is involed are active or not - void resetOverlappingPairs(); + /// Awake the disabled neighbor bodies + void awakeNeighborDisabledBodies(); + + /// Remove the disabled overlapping pairs + void enableOverlappingPairs(); + + /// Disable the overlapping pairs if both bodies of the pair are disabled (sleeping or static) + void checkForDisabledOverlappingPairs(); /// Compute and return the local-space center of mass of the body using its colliders Vector3 computeCenterOfMass() const; @@ -219,13 +225,6 @@ class RigidBody : public CollisionBody { /// Remove a collider from the body virtual void removeCollider(Collider* collider) override; -#ifdef IS_RP3D_PROFILING_ENABLED - - /// Set the profiler - void setProfiler(Profiler* profiler) override; - -#endif - // -------------------- Friendship -------------------- // friend class PhysicsWorld; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/Collider.h b/ext/reactphysics3d/include/reactphysics3d/collision/Collider.h index 425afb078..6543e9e5a 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/Collider.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/Collider.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -27,7 +27,7 @@ #define REACTPHYSICS3D_COLLIDER_H // Libraries -#include +#include #include #include #include @@ -39,7 +39,7 @@ class MemoryManager; // Class Collider /** - * A collider has a collision shape (box, sphere, capsule, ...) and is attached to a CollisionBody or + * A collider has a collision shape (box, sphere, capsule, ...) and is attached to a * RigidBody. A body can have multiple colliders. The collider also have a mass value and a Material * with many physics parameters like friction or bounciness. When you create a body, you need to attach * at least one collider to it if you want that body to be able to collide in the physics world. @@ -57,7 +57,7 @@ class Collider { Entity mEntity; /// Pointer to the parent body - CollisionBody* mBody; + Body* mBody; /// Pointer to user data void* mUserData; @@ -80,7 +80,7 @@ class Collider { // -------------------- Methods -------------------- // /// Constructor - Collider(Entity entity, CollisionBody* body, MemoryManager& memoryManager); + Collider(Entity entity, Body* body, MemoryManager& memoryManager); /// Destructor virtual ~Collider(); @@ -101,7 +101,7 @@ class Collider { const CollisionShape* getCollisionShape() const; /// Return the parent body - CollisionBody* getBody() const; + Body* getBody() const; /// Return a pointer to the user data attached to this body void* getUserData() const; @@ -157,6 +157,18 @@ class Collider { /// Set whether the collider is a trigger void setIsTrigger(bool isTrigger) const; + /// Return true if the collider can generate contacts for the simulation of the associated body + bool getIsSimulationCollider() const; + + /// Set whether the collider can generate contacts for the simulation of the associated body + void setIsSimulationCollider(bool isSimulationCollider) const; + + /// Return true if the collider will be part of results of queries on the PhysicsWorld + bool getIsWorldQueryCollider() const; + + /// Set whether the collider will be part of results of queries on the PhysicsWorld + void setIsWorldQueryCollider(bool isWorldQueryCollider) const; + #ifdef IS_RP3D_PROFILING_ENABLED /// Set the profiler @@ -167,7 +179,7 @@ class Collider { // -------------------- Friendship -------------------- // friend class OverlappingPair; - friend class CollisionBody; + friend class Body; friend class RigidBody; friend class BroadPhaseAlgorithm; friend class DynamicAABBTree; @@ -193,7 +205,7 @@ RP3D_FORCE_INLINE Entity Collider::getEntity() const { /** * @return Pointer to the parent body */ -RP3D_FORCE_INLINE CollisionBody* Collider::getBody() const { +RP3D_FORCE_INLINE Body* Collider::getBody() const { return mBody; } diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/CollisionCallback.h b/ext/reactphysics3d/include/reactphysics3d/collision/CollisionCallback.h index 1f9b13c43..14d75cedc 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/CollisionCallback.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/CollisionCallback.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -37,7 +37,7 @@ namespace reactphysics3d { // Declarations class OverlappingPair; class ContactManifold; -class CollisionBody; +class Body; class Collider; class MemoryManager; @@ -185,13 +185,13 @@ class CollisionCallback { /** * @return A pointer to the first colliding body of the pair */ - CollisionBody* getBody1() const; + Body* getBody1() const; /// Return a pointer to the second body in contact /** * @return A pointer to the second colliding body of the pair */ - CollisionBody* getBody2() const; + Body* getBody2() const; /// Return a pointer to the first collider in contact (in body 1) /** diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifold.h b/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifold.h index c55c20763..c679eac7e 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifold.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifold.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -36,7 +36,7 @@ namespace reactphysics3d { class ContactManifold; struct ContactManifoldInfo; struct ContactPointInfo; -class CollisionBody; +class Body; class ContactPoint; class DefaultPoolAllocator; @@ -110,7 +110,7 @@ class ContactManifold { friend class PhysicsWorld; friend class Island; - friend class CollisionBody; + friend class Body; friend class ContactManifoldSet; friend class ContactSolverSystem; friend class CollisionDetectionSystem; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifoldInfo.h b/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifoldInfo.h index 5a15b3eca..7acd23b23 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifoldInfo.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/ContactManifoldInfo.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/ContactPair.h b/ext/reactphysics3d/include/reactphysics3d/collision/ContactPair.h index 81cd991e3..2f618f6dd 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/ContactPair.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/ContactPair.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/ContactPointInfo.h b/ext/reactphysics3d/include/reactphysics3d/collision/ContactPointInfo.h index 779d35fef..23f7a9656 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/ContactPointInfo.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/ContactPointInfo.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -34,7 +34,7 @@ namespace reactphysics3d { // Declarations -class CollisionBody; +class Body; // Structure ContactPointInfo /** diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/PolyhedronMesh.h b/ext/reactphysics3d/include/reactphysics3d/collision/ConvexMesh.h similarity index 51% rename from ext/reactphysics3d/include/reactphysics3d/collision/PolyhedronMesh.h rename to ext/reactphysics3d/include/reactphysics3d/collision/ConvexMesh.h index c06cc2301..7633d4aa8 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/PolyhedronMesh.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/ConvexMesh.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -23,12 +23,13 @@ * * ********************************************************************************/ -#ifndef REACTPHYSICS3D_POLYHEDRON_MESH_H -#define REACTPHYSICS3D_POLYHEDRON_MESH_H +#ifndef REACTPHYSICS3D_CONVEX_MESH_H +#define REACTPHYSICS3D_CONVEX_MESH_H // Libraries #include #include +#include #include "HalfEdgeStructure.h" namespace reactphysics3d { @@ -36,13 +37,14 @@ namespace reactphysics3d { // Declarations class DefaultAllocator; class PolygonVertexArray; +struct Message; -// Class PolyhedronMesh +// Class ConvexMesh /** - * This class describes a polyhedron mesh made of faces and vertices. - * The faces do not have to be triangles. + * This class describes a convex mesh made of faces and vertices. + * The faces are made of polygons (not only triangles). */ -class PolyhedronMesh { +class ConvexMesh { private: @@ -51,67 +53,78 @@ class PolyhedronMesh { /// Reference to the memory allocator MemoryAllocator& mMemoryAllocator; - /// Pointer the the polygon vertex array with vertices and faces - /// of the mesh - PolygonVertexArray* mPolygonVertexArray; - /// Half-edge structure of the mesh HalfEdgeStructure mHalfEdgeStructure; + /// All the vertices of the mesh + Array mVertices; + /// Array with the face normals - Vector3* mFacesNormals; + Array mFacesNormals; - /// Centroid of the polyhedron + /// Centroid of the mesh Vector3 mCentroid; + /// Mesh minimum/maximum bounds in the three local x, y and z directions + AABB mBounds; + + /// Volume of the mesh + decimal mVolume; + // -------------------- Methods -------------------- // /// Constructor - PolyhedronMesh(PolygonVertexArray* polygonVertexArray, MemoryAllocator& allocator); + ConvexMesh(MemoryAllocator& allocator); + + /// Initialize a mesh and returns errors if any + bool init(const PolygonVertexArray& polygonVertexArray, std::vector& errors); + + /// Copy the vertices into the mesh + bool copyVertices(const PolygonVertexArray& polygonVertexArray, std::vector& errors); /// Create the half-edge structure of the mesh - bool createHalfEdgeStructure(); + bool createHalfEdgeStructure(const PolygonVertexArray& polygonVertexArray, std::vector& errors); /// Compute the faces normals - void computeFacesNormals(); + bool computeFacesNormals(std::vector& errors); - /// Compute the centroid of the polyhedron - void computeCentroid() ; + /// Compute and return the face normal (not normalized) + Vector3 computeFaceNormal(uint32 faceIndex) const; - /// Compute and return the area of a face - decimal getFaceArea(uint32 faceIndex) const; - - /// Static factory method to create a polyhedron mesh - static PolyhedronMesh* create(PolygonVertexArray* polygonVertexArray, MemoryAllocator& polyhedronMeshAllocator, MemoryAllocator& dataAllocator); + /// Compute the volume of the mesh + void computeVolume(); public: // -------------------- Methods -------------------- // - /// Destructor - ~PolyhedronMesh(); - /// Return the number of vertices uint32 getNbVertices() const; /// Return a vertex - Vector3 getVertex(uint32 index) const; + const Vector3& getVertex(uint32 index) const; /// Return the number of faces uint32 getNbFaces() const; /// Return a face normal - Vector3 getFaceNormal(uint32 faceIndex) const; + const Vector3& getFaceNormal(uint32 faceIndex) const; /// Return the half-edge structure of the mesh const HalfEdgeStructure& getHalfEdgeStructure() const; - /// Return the centroid of the polyhedron - Vector3 getCentroid() const; + /// Return the centroid of the mesh + const Vector3& getCentroid() const; + + /// Return the bounds of the mesh in the x,y,z direction + const AABB& getBounds() const; - /// Compute and return the volume of the polyhedron + /// Compute and return the volume of the mesh decimal getVolume() const; + /// Return the local inertia tensor of the mesh + Vector3 getLocalInertiaTensor(decimal mass, Vector3 scale) const; + // ---------- Friendship ---------- // friend class PhysicsCommon; @@ -121,15 +134,25 @@ class PolyhedronMesh { /** * @return The number of vertices in the mesh */ -RP3D_FORCE_INLINE uint32 PolyhedronMesh::getNbVertices() const { - return mHalfEdgeStructure.getNbVertices(); +RP3D_FORCE_INLINE uint32 ConvexMesh::getNbVertices() const { + return mVertices.size(); +} + +/// Return a vertex +/** + * @param index Index of a given vertex in the mesh + * @return The coordinates of a given vertex in the mesh + */ +RP3D_FORCE_INLINE const Vector3& ConvexMesh::getVertex(uint32 index) const { + assert(index < mVertices.size()); + return mVertices[index]; } // Return the number of faces /** * @return The number of faces in the mesh */ -RP3D_FORCE_INLINE uint32 PolyhedronMesh::getNbFaces() const { +RP3D_FORCE_INLINE uint32 ConvexMesh::getNbFaces() const { return mHalfEdgeStructure.getNbFaces(); } @@ -138,7 +161,7 @@ RP3D_FORCE_INLINE uint32 PolyhedronMesh::getNbFaces() const { * @param faceIndex The index of a given face of the mesh * @return The normal vector of a given face of the mesh */ -RP3D_FORCE_INLINE Vector3 PolyhedronMesh::getFaceNormal(uint32 faceIndex) const { +RP3D_FORCE_INLINE const Vector3& ConvexMesh::getFaceNormal(uint32 faceIndex) const { assert(faceIndex < mHalfEdgeStructure.getNbFaces()); return mFacesNormals[faceIndex]; } @@ -147,7 +170,7 @@ RP3D_FORCE_INLINE Vector3 PolyhedronMesh::getFaceNormal(uint32 faceIndex) const /** * @return The Half-Edge structure of the mesh */ -RP3D_FORCE_INLINE const HalfEdgeStructure& PolyhedronMesh::getHalfEdgeStructure() const { +RP3D_FORCE_INLINE const HalfEdgeStructure& ConvexMesh::getHalfEdgeStructure() const { return mHalfEdgeStructure; } @@ -155,10 +178,38 @@ RP3D_FORCE_INLINE const HalfEdgeStructure& PolyhedronMesh::getHalfEdgeStructure( /** * @return The centroid of the mesh */ -RP3D_FORCE_INLINE Vector3 PolyhedronMesh::getCentroid() const { +RP3D_FORCE_INLINE const Vector3& ConvexMesh::getCentroid() const { return mCentroid; } +// Return the volume of the convex mesh +/** + * @return The volume of the mesh + */ +RP3D_FORCE_INLINE decimal ConvexMesh::getVolume() const { + return mVolume; +} + +// Return the local inertia tensor of the mesh +/// The local inertia tensor of the convex mesh is approximated using the inertia tensor +/// of its bounding box. +/** +* @param mass Mass to use to compute the inertia tensor of the collision shape +* @param scale Scaling factor for the mesh +*/ +RP3D_FORCE_INLINE Vector3 ConvexMesh::getLocalInertiaTensor(decimal mass, Vector3 scale) const { + + // TODO: We should compute a much better inertia tensor here (not using a box) + + const decimal factor = (decimal(1.0) / decimal(3.0)) * mass; + const Vector3 realExtent = decimal(0.5) * scale * (mBounds.getMax() - mBounds.getMin()); + assert(realExtent.x > 0 && realExtent.y > 0 && realExtent.z > 0); + const decimal xSquare = realExtent.x * realExtent.x; + const decimal ySquare = realExtent.y * realExtent.y; + const decimal zSquare = realExtent.z * realExtent.z; + return Vector3(factor * (ySquare + zSquare), factor * (xSquare + zSquare), factor * (xSquare + ySquare)); +} + } #endif diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/HalfEdgeStructure.h b/ext/reactphysics3d/include/reactphysics3d/collision/HalfEdgeStructure.h index 06f110fe1..9aa8f7d23 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/HalfEdgeStructure.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/HalfEdgeStructure.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -34,8 +34,7 @@ namespace reactphysics3d { // Class HalfEdgeStructure /** * This class describes a polyhedron mesh made of faces and vertices. - * The faces do not have to be triangle. Note that the half-edge structure - * is only valid if the mesh is closed (each edge has two adjacent faces). + * The faces do not have to be triangle. */ class HalfEdgeStructure { @@ -60,7 +59,7 @@ class HalfEdgeStructure { Face(MemoryAllocator& allocator) : edgeIndex(0), faceVertices(allocator) {} /// Constructor - Face(Array vertices) : edgeIndex(0), faceVertices(vertices) {} + Face(const Array& vertices) : edgeIndex(0), faceVertices(vertices) {} }; /// Vertex @@ -96,14 +95,14 @@ class HalfEdgeStructure { /// Destructor ~HalfEdgeStructure() = default; - /// Initialize the structure (when all vertices and faces have been added) - void init(); + /// Compute the half-edges (when all vertices and faces have been added) + void computeHalfEdges(); /// Add a vertex uint32 addVertex(uint32 vertexPointIndex); /// Add a face - void addFace(Array faceVertices); + void addFace(const Array& faceVertices); /// Return the number of faces uint32 getNbFaces() const; @@ -123,11 +122,17 @@ class HalfEdgeStructure { /// Return a given vertex const Vertex& getVertex(uint32 index) const; + /// Reserve some memory for vertices, faces and edges + void reserve(uint32 facesCapacity, uint32 verticesCapacity, uint32 edgesCapacity); + + /// Return a string representation of the half-edge structure + std::string to_string() const; + }; // Add a vertex /** - * @param vertexPointIndex Index of the vertex in the vertex data array + * @param vertexPointIndex Index of the vertex in the external user vertex data array */ RP3D_FORCE_INLINE uint32 HalfEdgeStructure::addVertex(uint32 vertexPointIndex) { Vertex vertex(vertexPointIndex); @@ -138,13 +143,12 @@ RP3D_FORCE_INLINE uint32 HalfEdgeStructure::addVertex(uint32 vertexPointIndex) { // Add a face /** * @param faceVertices Array of the vertices in a face (ordered in CCW order as seen from outside - * the polyhedron + * the polyhedron). The indices are the internal indices of the vertices inside the HalfEdgeStructure. */ -RP3D_FORCE_INLINE void HalfEdgeStructure::addFace(Array faceVertices) { +RP3D_FORCE_INLINE void HalfEdgeStructure::addFace(const Array& faceVertices) { // Create a new face - Face face(faceVertices); - mFaces.add(face); + mFaces.add(Face(faceVertices)); } // Return the number of faces diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/HeightField.h b/ext/reactphysics3d/include/reactphysics3d/collision/HeightField.h new file mode 100644 index 000000000..6ba859f0e --- /dev/null +++ b/ext/reactphysics3d/include/reactphysics3d/collision/HeightField.h @@ -0,0 +1,273 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_HEIGHT_FIELD_H +#define REACTPHYSICS3D_HEIGHT_FIELD_H + +// Libraries +#include +#include +#include +#include +#include +#include + +namespace reactphysics3d { + +// Declarations +struct Message; + +// Class HeightField +/** + * This class represents a static height field that can be used to represent + * a terrain. The height field is made of a grid with rows and columns with a + * height value at each grid point. Note that the height values are copied into the shape. + * The height values can be of type integer, float or double. + * Note that the HeightField will be re-centered based on its AABB. It means + * that for instance, if the minimum height value is -200 and the maximum value is 400, the final + * minimum height of the field in the simulation will be -300 and the maximum height will be 300. + */ +class HeightField { + + public: + + /// Data type for the height data of the height field + enum class HeightDataType {HEIGHT_FLOAT_TYPE, HEIGHT_DOUBLE_TYPE, HEIGHT_INT_TYPE}; + + protected: + + // -------------------- Attributes -------------------- // + + /// Reference to a memory allocator + MemoryAllocator& mAllocator; + + /// Number of columns in the grid of the height field (along the local x direction) + uint32 mNbColumns; + + /// Number of rows in the grid of the height field (along the local z direction) + uint32 mNbRows; + + /// Height field width + decimal mWidth; + + /// Height field length + decimal mLength; + + /// Minimum height value of the height field + decimal mMinHeight; + + /// Maximum height value of the height field + decimal mMaxHeight; + + /// Height origin + decimal mHeightOrigin; + + /// Height values scale for height field with integer height values + decimal mIntegerHeightScale; + + /// Data type of the height values + HeightDataType mHeightDataType; + + /// Array of data with all the height values of the height field + Array mHeightFieldData; + + /// Local bounds of the height field + AABB mBounds; + + /// Reference to the half-edge structure + HalfEdgeStructure& mTriangleHalfEdgeStructure; + +#ifdef IS_RP3D_PROFILING_ENABLED + + /// Pointer to the profiler + Profiler* mProfiler; + +#endif + + // -------------------- Methods -------------------- // + + /// Constructor + HeightField(MemoryAllocator& allocator, HalfEdgeStructure& triangleHalfEdgeStructure); + + bool init(int nbGridColumns, int nbGridRows, const void* heightFieldData, + HeightDataType dataType, std::vector& messages, decimal integerHeightScale = 1.0f); + + /// Copy the data from the user into the height-field array + void copyData(const void* heightFieldData); + + /// Raycast a single triangle of the height-field + bool raycastTriangle(const Ray& ray, const Vector3& p1, const Vector3& p2, const Vector3& p3, uint32 shapeId, + Collider* collider, RaycastInfo& raycastInfo, decimal& smallestHitFraction, + TriangleRaycastSide testSide, MemoryAllocator& allocator) const; + + /// Raycast method with feedback information + bool raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* collider, TriangleRaycastSide testSide, + MemoryAllocator& allocator) const; + + /// Compute the min/max grid coords corresponding to the intersection of the AABB of the height field and the AABB to collide + void computeMinMaxGridCoordinates(uint32* minCoords, uint32* maxCoords, const AABB& aabbToCollide) const; + + /// Compute the shape Id for a given triangle + uint32 computeTriangleShapeId(uint32 iIndex, uint32 jIndex, uint32 secondTriangleIncrement) const; + + /// Compute the first grid cell of the heightfield intersected by a ray + bool computeEnteringRayGridCoordinates(const Ray& ray, int32& i, int32& j, Vector3& outHitPoint) const; + + /// Use a callback method on all triangles of the concave shape inside a given AABB + void computeOverlappingTriangles(const AABB& aabb, Array& triangleVertices, + Array& triangleVerticesNormals, + Array& shapeIds, const Vector3& scale) const; + + public: + + /// Deleted copy-constructor + HeightField(const HeightField& heightField) = delete; + + /// Deleted assignment operator + HeightField& operator=(const HeightField& heightField) = delete; + + /// Return the number of rows in the height-field (along the local x direction) + uint32 getNbRows() const; + + /// Return the number of columns in the height-field (along the local z direction) + uint32 getNbColumns() const; + + /// Return the minimum height value of the height-field + decimal getMinHeight() const; + + /// Return the maximum height value of the height-field + decimal getMaxHeight() const; + + /// Return the integer height scale + decimal getIntegerHeightScale() const; + + /// Return the vertex (local-coordinates) of the height-field at a given (x,y) position + Vector3 getVertexAt(uint32 x, uint32 y) const; + + /// Return the height value of a given (x,y) point in the height-field (in local-space) + decimal getHeightAt(uint32 x, uint32 y) const; + + /// Return the type of height value in the height-field + HeightDataType getHeightDataType() const; + + /// Return the minimum bounds of the height-field in the x,y,z direction + const AABB& getBounds() const; + + /// Return the string representation of the shape + std::string to_string() const; + +#ifdef IS_RP3D_PROFILING_ENABLED + + /// Set the profiler + void setProfiler(Profiler* profiler); + +#endif + + // ---------- Friendship ----------- // + + friend class ConvexTriangleAABBOverlapCallback; + friend class ConcaveMeshRaycastCallback; + friend class HeightFieldShape; + friend class PhysicsCommon; +}; + +// Return the minimum bounds of the height-field in the x,y,z direction +/** + * @return The three mimimum bounds of the height-field in the x,y,z direction + */ +RP3D_FORCE_INLINE const AABB& HeightField::getBounds() const { + return mBounds; +} + +// Return the number of rows in the height-field (along the local x direction) +/** + * @return The number of rows of the grid (along x direction) + */ +RP3D_FORCE_INLINE uint32 HeightField::getNbRows() const { + return mNbRows; +} + +// Return the number of columns in the height-field (along the local z direction) +/** + * @return The number of columns of the grid (along z direction) + */ +RP3D_FORCE_INLINE uint32 HeightField::getNbColumns() const { + return mNbColumns; +} + +// Return the minimum height value of the height-field +/** + * @return The mimimum height value of the height-field + */ +RP3D_FORCE_INLINE decimal HeightField::getMinHeight() const { + return mMinHeight; +} + +// Return the maximum height value of the height-field +/** + * @return The maximum height value of the height-field + */ +RP3D_FORCE_INLINE decimal HeightField::getMaxHeight() const { + return mMaxHeight; +} + +// Return the integer height scale +/** + * @return The integer height scale + */ +RP3D_FORCE_INLINE decimal HeightField::getIntegerHeightScale() const { + return mIntegerHeightScale; +} + +// Return the type of height value in the height-field +RP3D_FORCE_INLINE HeightField::HeightDataType HeightField::getHeightDataType() const { + return mHeightDataType; +} + +// Return the height value of a given (x,y) point in the height-field +RP3D_FORCE_INLINE decimal HeightField::getHeightAt(uint32 x, uint32 y) const { + assert(x < mNbColumns); + assert(y < mNbRows); + return mHeightFieldData[y * mNbColumns + x]; +} + +// Compute the shape Id for a given triangle +RP3D_FORCE_INLINE uint32 HeightField::computeTriangleShapeId(uint32 iIndex, uint32 jIndex, uint32 secondTriangleIncrement) const { + return (jIndex * (mNbColumns - 1) + iIndex) * 2 + secondTriangleIncrement; +} + +#ifdef IS_RP3D_PROFILING_ENABLED + +// Set the profiler +RP3D_FORCE_INLINE void HeightField::setProfiler(Profiler* profiler) { + mProfiler = profiler; +} + +#endif + +} + +#endif + diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/OverlapCallback.h b/ext/reactphysics3d/include/reactphysics3d/collision/OverlapCallback.h index 58e628960..4f12816e8 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/OverlapCallback.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/OverlapCallback.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -34,7 +34,7 @@ namespace reactphysics3d { // Declarations -class CollisionBody; +class Body; class PhysicsWorld; class Collider; struct Entity; @@ -96,7 +96,7 @@ class OverlapCallback { OverlapPair(const OverlapPair& contactPair) = default; /// Assignment operator - OverlapPair& operator=(const OverlapPair& contactPair) = default; + OverlapPair& operator=(const OverlapPair& contactPair) = delete; /// Destructor ~OverlapPair() = default; @@ -108,10 +108,10 @@ class OverlapCallback { Collider* getCollider2() const; /// Return a pointer to the first body in contact - CollisionBody* getBody1() const; + Body* getBody1() const; /// Return a pointer to the second body in contact - CollisionBody* getBody2() const; + Body* getBody2() const; /// Return the corresponding type of event for this overlapping pair EventType getEventType() const; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/PolygonVertexArray.h b/ext/reactphysics3d/include/reactphysics3d/collision/PolygonVertexArray.h index fd3faf902..52b21fb84 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/PolygonVertexArray.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/PolygonVertexArray.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -32,16 +32,15 @@ namespace reactphysics3d { +// Declarations +struct Vector3; + // Class PolygonVertexArray /** - * This class is used to describe the vertices and faces of a polyhedron mesh. + * This class is used to describe the vertices and faces of a mesh. * A PolygonVertexArray represents an array of vertices and polygon faces - * of a polyhedron mesh. When you create a PolygonVertexArray, no data is copied - * into the array. It only stores pointer to the data. The purpose is to allow - * the user to share vertices data between the physics engine and the rendering - * part. Therefore, make sure that the data pointed by a PolygonVertexArray - * remains valid during the PolygonVertexArray life. - */ + * of a mesh. When you create a PolygonVertexArray, no data is copied + * into the array. It only stores pointer to the data. */ class PolygonVertexArray { public: @@ -52,7 +51,7 @@ class PolygonVertexArray { /// Data type for the indices in the array enum class IndexDataType {INDEX_INTEGER_TYPE, INDEX_SHORT_TYPE}; - /// Represent a polygon face of the polyhedron + /// Represent a polygon face of the mesh struct PolygonFace { /// Number of vertices in the polygon face @@ -85,7 +84,7 @@ class PolygonVertexArray { /// Number of polygon faces in the array uint32 mNbFaces; - /// Pointer to the first polygon face in the polyhedron + /// Pointer to the first polygon face in the mesh PolygonFace* mPolygonFacesStart; /// Data type of the vertices in the array @@ -96,14 +95,20 @@ class PolygonVertexArray { public: + /// Constructor + PolygonVertexArray(); + /// Constructor PolygonVertexArray(uint32 nbVertices, const void* verticesStart, uint32 verticesStride, const void* indexesStart, uint32 indexesStride, uint32 nbFaces, PolygonFace* facesStart, VertexDataType vertexDataType, IndexDataType indexDataType); - /// Destructor - ~PolygonVertexArray() = default; + /// Initialize the PolygonVertexArray + void init(uint32 nbVertices, const void* verticesStart, uint32 verticesStride, + const void* indexesStart, uint32 indexesStride, + uint32 nbFaces, PolygonFace* facesStart, + VertexDataType vertexDataType, IndexDataType indexDataType); /// Return the vertex data type VertexDataType getVertexDataType() const; @@ -126,7 +131,10 @@ class PolygonVertexArray { /// Return the vertex index of a given vertex in a face uint32 getVertexIndexInFace(uint32 faceIndex32, uint32 noVertexInFace) const; - /// Return a polygon face of the polyhedron + /// Return the coordinates of a given vertex + Vector3 getVertex(uint32 vertexIndex) const; + + /// Return a polygon face of the mesh PolygonFace* getPolygonFace(uint32 faceIndex) const; /// Return the pointer to the start of the vertices array @@ -134,6 +142,10 @@ class PolygonVertexArray { /// Return the pointer to the start of the indices array const unsigned char* getIndicesStart() const; + + // -------------------- Friendship -------------------- // + + friend class PhysicsCommon; }; // Return the vertex data type @@ -184,7 +196,7 @@ RP3D_FORCE_INLINE uint32 PolygonVertexArray::getIndicesStride() const { return mIndicesStride; } -// Return a polygon face of the polyhedron +// Return a polygon face of the mesh /** * @param faceIndex Index of a given face * @return A polygon face @@ -196,7 +208,7 @@ RP3D_FORCE_INLINE PolygonVertexArray::PolygonFace* PolygonVertexArray::getPolygo // Return the pointer to the start of the vertices array /** - * @return A pointer to the start of the vertex array of the polyhedron + * @return A pointer to the start of the vertex array of the mesh */ RP3D_FORCE_INLINE const unsigned char* PolygonVertexArray::getVerticesStart() const { return mVerticesStart; @@ -204,7 +216,7 @@ RP3D_FORCE_INLINE const unsigned char* PolygonVertexArray::getVerticesStart() co // Return the pointer to the start of the indices array /** - * @return A pointer to the start of the face indices array of the polyhedron + * @return A pointer to the start of the face indices array of the mesh */ RP3D_FORCE_INLINE const unsigned char* PolygonVertexArray::getIndicesStart() const { return mIndicesStart; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/RaycastInfo.h b/ext/reactphysics3d/include/reactphysics3d/collision/RaycastInfo.h index 50c418c06..c637af2a3 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/RaycastInfo.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/RaycastInfo.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -33,7 +33,7 @@ namespace reactphysics3d { // Declarations -class CollisionBody; +class Body; class Collider; class CollisionShape; struct Ray; @@ -60,14 +60,11 @@ struct RaycastInfo { /// The hit point "p" is such that p = point1 + hitFraction * (point2 - point1) decimal hitFraction; - /// Mesh subpart index that has been hit (only used for triangles mesh and -1 otherwise) - int meshSubpart; - /// Hit triangle index (only used for triangles mesh and -1 otherwise) int triangleIndex; /// Pointer to the hit collision body - CollisionBody* body; + Body* body; /// Pointer to the hit collider Collider* collider; @@ -75,7 +72,7 @@ struct RaycastInfo { // -------------------- Methods -------------------- // /// Constructor - RaycastInfo() : meshSubpart(-1), triangleIndex(-1), body(nullptr), collider(nullptr) { + RaycastInfo() : hitFraction(-1), triangleIndex(-1), body(nullptr), collider(nullptr) { } diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/TriangleMesh.h b/ext/reactphysics3d/include/reactphysics3d/collision/TriangleMesh.h index 0728b5678..1d3703633 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/TriangleMesh.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/TriangleMesh.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -30,76 +30,177 @@ #include #include #include +#include +#include namespace reactphysics3d { // Declarations class TriangleVertexArray; +struct Message; // Class TriangleMesh /** - * This class represents a mesh made of triangles. A TriangleMesh contains - * one or several parts. Each part is a set of triangles represented in a - * TriangleVertexArray object describing all the triangles vertices of the part. - * A TriangleMesh object can be used to create a ConcaveMeshShape from a triangle - * mesh for instance. + * This class represents a mesh made of triangles. + * A single TriangleMesh object can be used to create one or many ConcaveMeshShape (with + * different scaling for instance). */ class TriangleMesh { protected: - /// All the triangle arrays of the mesh (one triangle array per part) - Array mTriangleArrays; + /// Reference to the memory allocator + MemoryAllocator& mAllocator; + + /// All the vertices of the mesh + Array mVertices; + + /// The three vertices indices for each triangle face of the mesh + Array mTriangles; + + /// The normal vector at each vertex of the mesh + Array mVerticesNormals; + + /// Dynamic AABB tree to accelerate collision with the triangles + DynamicAABBTree mDynamicAABBTree; + + /// Epsilon value for this mesh + decimal mEpsilon; /// Constructor TriangleMesh(reactphysics3d::MemoryAllocator& allocator); + /// Copy the vertices into the mesh + bool copyVertices(const TriangleVertexArray& triangleVertexArray, std::vector& messages); + + /// Copy or compute the vertices normals + void computeVerticesNormals(); + + /// Compute the epsilon value for this mesh + void computeEpsilon(const TriangleVertexArray& triangleVertexArray); + + /// Copy the triangles into the mesh + bool copyData(const TriangleVertexArray& triangleVertexArray, std::vector& errors); + + /// Insert all the triangles into the dynamic AABB tree + void initBVHTree(); + + /// Initialize the mesh using a TriangleVertexArray + bool init(const TriangleVertexArray& triangleVertexArray, std::vector& messages); + + /// Report all shapes overlapping with the AABB given in parameter. + void reportAllShapesOverlappingWithAABB(const AABB& aabb, Array& overlappingNodes); + + /// Remove the ununsed vertices (because they are not used in any triangles or are part of discarded triangles) + void removeUnusedVertices(Array& areUsedVertices); + + /// Return the integer data of leaf node of the dynamic AABB tree + int32 getDynamicAABBTreeNodeDataInt(int32 nodeID) const; + + /// Ray casting method + void raycast(const Ray& ray, DynamicAABBTreeRaycastCallback& callback) const; + public: - /// Destructor - ~TriangleMesh(); + /// Return the number of vertices in the mesh + uint32 getNbVertices() const; + + /// Return the number of triangles faces of the mesh + uint32 getNbTriangles() const; + + /// Return the bounds of the mesh in the x,y,z direction + const AABB& getBounds() const; - /// Add a subpart of the mesh - void addSubpart(TriangleVertexArray* triangleVertexArray); + /// Return the three vertex indices of a given triangle face + void getTriangleVerticesIndices(uint32 triangleIndex, uint32& outV1Index, uint32& outV2Index, + uint32& outV3Index) const; - /// Return a pointer to a given subpart (triangle vertex array) of the mesh - TriangleVertexArray* getSubpart(uint32 indexSubpart) const; + /// Return the coordinates of the three vertices of a given triangle face + void getTriangleVertices(uint32 triangleIndex, Vector3& outV1, Vector3& outV2, Vector3& outV3) const; - /// Return the number of subparts of the mesh - uint32 getNbSubparts() const; + /// Return the normals of the three vertices of a given triangle face + void getTriangleVerticesNormals(uint32 triangleIndex, Vector3& outN1, + Vector3& outN2, Vector3& outN3) const; + /// Return the coordinates of a given vertex + const Vector3& getVertex(uint32 vertexIndex) const; + + /// Return the normal of a given vertex + const Vector3& getVertexNormal(uint32 vertexIndex) const; + +#ifdef IS_RP3D_PROFILING_ENABLED + + /// Set the profiler + void setProfiler(Profiler* profiler); + +#endif // ---------- Friendship ---------- // friend class PhysicsCommon; + friend class ConcaveMeshShape; }; -// Add a subpart of the mesh -/** - * @param triangleVertexArray Pointer to the TriangleVertexArray to add into the mesh - */ -RP3D_FORCE_INLINE void TriangleMesh::addSubpart(TriangleVertexArray* triangleVertexArray) { - mTriangleArrays.add(triangleVertexArray ); +// Return the number of vertices in the mesh +RP3D_FORCE_INLINE uint32 TriangleMesh::getNbVertices() const { + return mVertices.size(); } -// Return a pointer to a given subpart (triangle vertex array) of the mesh -/** - * @param indexSubpart The index of the sub-part of the mesh - * @return A pointer to the triangle vertex array of a given sub-part of the mesh - */ -RP3D_FORCE_INLINE TriangleVertexArray* TriangleMesh::getSubpart(uint32 indexSubpart) const { - assert(indexSubpart < mTriangleArrays.size()); - return mTriangleArrays[indexSubpart]; +// Return the number of triangles faces of the mesh +RP3D_FORCE_INLINE uint32 TriangleMesh::getNbTriangles() const { + return mTriangles.size() / 3; } -// Return the number of sub-parts of the mesh -/** - * @return The number of sub-parts of the mesh - */ -RP3D_FORCE_INLINE uint32 TriangleMesh::getNbSubparts() const { - return static_cast(mTriangleArrays.size()); +// Return the three vertex indices of a given triangle face +RP3D_FORCE_INLINE void TriangleMesh::getTriangleVerticesIndices(uint32 triangleIndex, uint32& outV1Index, + uint32& outV2Index, uint32& outV3Index) const { + assert(triangleIndex < mTriangles.size() / 3); + + outV1Index = mTriangles[triangleIndex * 3]; + outV2Index = mTriangles[triangleIndex * 3 + 1]; + outV3Index = mTriangles[triangleIndex * 3 + 2]; } +// Return the coordinates of the three vertices of a given triangle face +RP3D_FORCE_INLINE void TriangleMesh::getTriangleVertices(uint32 triangleIndex, Vector3& outV1, Vector3& outV2, + Vector3& outV3) const { + assert(triangleIndex < mTriangles.size() / 3); + + outV1 = mVertices[mTriangles[triangleIndex * 3]]; + outV2 = mVertices[mTriangles[triangleIndex * 3 + 1]]; + outV3 = mVertices[mTriangles[triangleIndex * 3 + 2]]; +} + +// Return the normals of the three vertices of a given triangle face +RP3D_FORCE_INLINE void TriangleMesh::getTriangleVerticesNormals(uint32 triangleIndex, Vector3& outN1, + Vector3& outN2, Vector3& outN3) const { + assert(triangleIndex < mTriangles.size() / 3); + + outN1 = mVerticesNormals[mTriangles[triangleIndex * 3]]; + outN2 = mVerticesNormals[mTriangles[triangleIndex * 3 + 1]]; + outN3 = mVerticesNormals[mTriangles[triangleIndex * 3 + 2]]; +} + +// Return the coordinates of a given vertex +RP3D_FORCE_INLINE const Vector3& TriangleMesh::getVertex(uint32 vertexIndex) const { + assert(vertexIndex < mVertices.size()); + return mVertices[vertexIndex]; +} + +// Return the normal of a given vertex +RP3D_FORCE_INLINE const Vector3& TriangleMesh::getVertexNormal(uint32 vertexIndex) const { + assert(vertexIndex < mVertices.size()); + return mVerticesNormals[vertexIndex]; +} + +#ifdef IS_RP3D_PROFILING_ENABLED + +// Set the profiler +RP3D_FORCE_INLINE void TriangleMesh::setProfiler(Profiler* profiler) { + mDynamicAABBTree.setProfiler(profiler); +} + +#endif } #endif diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/TriangleVertexArray.h b/ext/reactphysics3d/include/reactphysics3d/collision/TriangleVertexArray.h index 046698bb3..e82908876 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/TriangleVertexArray.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/TriangleVertexArray.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -39,8 +39,7 @@ struct Vector3; * This class is used to describe the vertices and faces of a triangular mesh. * A TriangleVertexArray represents a continuous array of vertices and indexes * of a triangular mesh. When you create a TriangleVertexArray, no data is copied - * into the array. It only stores pointer to the data. The purpose is to allow - * the user to share vertices data between the physics engine and the rendering + * into the array. It only stores pointer to the data. * part. Therefore, make sure that the data pointed by a TriangleVertexArray * remains valid during the TriangleVertexArray life. */ @@ -97,13 +96,10 @@ class TriangleVertexArray { IndexDataType mIndexDataType; /// True if the vertices normals are provided by the user - bool mAreVerticesNormalsProvidedByUser; + bool mHasNormals; // -------------------- Methods -------------------- // - /// Compute the vertices normals when they are not provided by the user - void computeVerticesNormals(); - public: // -------------------- Methods -------------------- // @@ -120,9 +116,6 @@ class TriangleVertexArray { VertexDataType vertexDataType, NormalDataType normalDataType, IndexDataType indexDataType); - /// Destructor - ~TriangleVertexArray(); - /// Deleted assignment operator TriangleVertexArray& operator=(const TriangleVertexArray& triangleVertexArray) = delete; @@ -135,6 +128,9 @@ class TriangleVertexArray { /// Return the vertex normal data type NormalDataType getVertexNormalDataType() const; + /// Return true if the vertices normal have been provided by the user + bool getHasNormals() const; + /// Return the index data type IndexDataType getIndexDataType() const; @@ -162,20 +158,15 @@ class TriangleVertexArray { /// Return the pointer to the start of the indices array const void* getIndicesStart() const; - /// Return the vertices coordinates of a triangle - void getTriangleVertices(uint32 triangleIndex, Vector3* outTriangleVertices) const; - - /// Return the three vertices normals of a triangle - void getTriangleVerticesNormals(uint32 triangleIndex, Vector3* outTriangleVerticesNormals) const; - - /// Return the indices of the three vertices of a given triangle in the array - void getTriangleVerticesIndices(uint32 triangleIndex, uint32* outVerticesIndices) const; + /// Return the three vertex indices of a given triangle face + void getTriangleVerticesIndices(uint32 triangleIndex, uint32& outV1Index, uint32& outV2Index, + uint32& outV3Index) const; /// Return a vertex of the array - void getVertex(uint32 vertexIndex, Vector3* outVertex); + Vector3 getVertex(uint32 vertexIndex) const; /// Return a vertex normal of the array - void getNormal(uint32 vertexIndex, Vector3* outNormal); + Vector3 getVertexNormal(uint32 vertexIndex) const; }; // Return the vertex data type @@ -266,6 +257,14 @@ RP3D_FORCE_INLINE const void* TriangleVertexArray::getIndicesStart() const { return mIndicesStart; } +// Return true if the vertices normals have been provided by the user +/** + * @return True if the vertices normals have been provided by the user + */ +RP3D_FORCE_INLINE bool TriangleVertexArray::getHasNormals() const { + return mHasNormals; +} + } #endif diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/VertexArray.h b/ext/reactphysics3d/include/reactphysics3d/collision/VertexArray.h new file mode 100644 index 000000000..75129f0aa --- /dev/null +++ b/ext/reactphysics3d/include/reactphysics3d/collision/VertexArray.h @@ -0,0 +1,125 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_VERTEX_ARRAY_H +#define REACTPHYSICS3D_VERTEX_ARRAY_H + +// Libraries +#include +#include + +namespace reactphysics3d { + +// Declarations +struct Vector3; + +// Class VertexArray +/** + * This class is used to describe an array of vertices. + */ +class VertexArray { + + public: + + /// Data type for the vertices in the array + enum class DataType {VERTEX_FLOAT_TYPE, VERTEX_DOUBLE_TYPE}; + + protected: + + /// Number of vertices in the array + uint32 mNbVertices; + + /// Pointer to the first vertex value in the array + const unsigned char* mStart; + + /// Stride (number of bytes) between the beginning of two vertices values in the array + uint32 mStride; + + /// Data type of the vertices in the array + DataType mDataType; + + public: + + /// Constructor + VertexArray(const void* start, uint32 stride, uint32 nbVertices, DataType dataType); + + // TODO : Add constructor with a reference to a vector of Vector3 + + /// Return the vertex data type + DataType getDataType() const; + + /// Return the number of vertices + uint32 getNbVertices() const; + + /// Return the vertices stride (number of bytes) + uint32 getStride() const; + + /// Return the coordinates of a given vertex + Vector3 getVertex(uint32 index) const; + + /// Return the pointer to the start of the vertices array + const unsigned char* getStart() const; + + // -------------------- Friendship -------------------- // + + friend class PhysicsCommon; +}; + +// Return the vertex data type +/** + * @return The data type of the vertices in the array + */ +RP3D_FORCE_INLINE VertexArray::DataType VertexArray::getDataType() const { + return mDataType; +} + +// Return the number of vertices +/** + * @return The number of vertices in the array + */ +RP3D_FORCE_INLINE uint32 VertexArray::getNbVertices() const { + return mNbVertices; +} + +// Return the vertices stride (number of bytes) +/** + * @return The number of bytes between two vertices + */ +RP3D_FORCE_INLINE uint32 VertexArray::getStride() const { + return mStride; +} + +// Return the pointer to the start of the vertices array +/** + * @return A pointer to the start of the vertex array of the mesh + */ +RP3D_FORCE_INLINE const unsigned char* VertexArray::getStart() const { + return mStart; +} + +} + +#endif + diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/broadphase/DynamicAABBTree.h b/ext/reactphysics3d/include/reactphysics3d/collision/broadphase/DynamicAABBTree.h index ac80cfe96..6ed6df55e 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/broadphase/DynamicAABBTree.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/broadphase/DynamicAABBTree.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -38,7 +38,7 @@ namespace reactphysics3d { class BroadPhaseSystem; class BroadPhaseRaycastTestCallback; class DynamicAABBTreeOverlapCallback; -class CollisionBody; +class Body; struct RaycastTest; class AABB; class Profiler; @@ -77,7 +77,7 @@ struct TreeNode { /// Two pieces of data stored at that node (in case the node is a leaf) union { - int32 dataInt[2]; + uint32 dataInt; void* dataPointer; }; }; @@ -220,7 +220,7 @@ class DynamicAABBTree { ~DynamicAABBTree(); /// Add an object into the tree (where node data are two integers) - int32 addObject(const AABB& aabb, int32 data1, int32 data2); + int32 addObject(const AABB& aabb, uint32 data); /// Add an object into the tree (where node data is a pointer) int32 addObject(const AABB& aabb, void* data); @@ -235,7 +235,7 @@ class DynamicAABBTree { const AABB& getFatAABB(int32 nodeID) const; /// Return the pointer to the data array of a given leaf node of the tree - int32* getNodeDataInt(int32 nodeID) const; + int32 getNodeDataInt(int32 nodeID) const; /// Return the data pointer of a given leaf node of the tree void* getNodeDataPointer(int32 nodeID) const; @@ -254,7 +254,7 @@ class DynamicAABBTree { int computeHeight(); /// Return the root AABB of the tree - AABB getRootAABB() const; + const AABB& getRootAABB() const; /// Clear all the nodes and reset the tree void reset(); @@ -280,7 +280,7 @@ RP3D_FORCE_INLINE const AABB& DynamicAABBTree::getFatAABB(int32 nodeID) const { } // Return the pointer to the data array of a given leaf node of the tree -RP3D_FORCE_INLINE int32* DynamicAABBTree::getNodeDataInt(int32 nodeID) const { +RP3D_FORCE_INLINE int32 DynamicAABBTree::getNodeDataInt(int32 nodeID) const { assert(nodeID >= 0 && nodeID < mNbAllocatedNodes); assert(mNodes[nodeID].isLeaf()); return mNodes[nodeID].dataInt; @@ -294,18 +294,17 @@ RP3D_FORCE_INLINE void* DynamicAABBTree::getNodeDataPointer(int32 nodeID) const } // Return the root AABB of the tree -RP3D_FORCE_INLINE AABB DynamicAABBTree::getRootAABB() const { +RP3D_FORCE_INLINE const AABB& DynamicAABBTree::getRootAABB() const { return getFatAABB(mRootNodeID); } // Add an object into the tree. This method creates a new leaf node in the tree and // returns the ID of the corresponding node. -RP3D_FORCE_INLINE int32 DynamicAABBTree::addObject(const AABB& aabb, int32 data1, int32 data2) { +RP3D_FORCE_INLINE int32 DynamicAABBTree::addObject(const AABB& aabb, uint32 data) { int32 nodeId = addObjectInternal(aabb); - mNodes[nodeId].dataInt[0] = data1; - mNodes[nodeId].dataInt[1] = data2; + mNodes[nodeId].dataInt = data; return nodeId; } diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h index 35a7cca52..a96a59d5e 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h index de3b108a7..b170f50b0 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CollisionDispatch.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CollisionDispatch.h index cf09f961a..375ea015c 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CollisionDispatch.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/CollisionDispatch.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -40,7 +40,7 @@ namespace reactphysics3d { /// Enumeration for the type of narrow-phase /// collision detection algorithm enum class NarrowPhaseAlgorithmType { - None, + NoCollisionTest, SphereVsSphere, SphereVsCapsule, CapsuleVsCapsule, @@ -62,6 +62,14 @@ class CollisionDispatch { /// Memory allocator MemoryAllocator& mAllocator; + /// Allocation sizes + size_t mSphereVsSphereAllocatedSize; + size_t mSphereVsCapsuleAllocatedSize; + size_t mCapsuleVsCapsuleAllocatedSize; + size_t mSphereVsConvexPolyAllocatedSize; + size_t mCapsuleVsConvexPolyAllocatedSize; + size_t mConvexPolyVsConvexPolyAllocatedSize; + /// True if the sphere vs sphere algorithm is the default one bool mIsSphereVsSphereDefault = true; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h index 42daf1592..2c6d5abe4 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h index b98e7914c..23799a8f3 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/VoronoiSimplex.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/VoronoiSimplex.h index a4943687c..b3c21f9e8 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/VoronoiSimplex.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/GJK/VoronoiSimplex.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h index 6b74e55aa..a21ed41cb 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h index 126d92022..505182c03 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -47,6 +47,11 @@ struct ContactPointInfo; */ struct NarrowPhaseInfoBatch { + // Struct NarrowPhaseInfo + /** + * A potential collision between two colliders from the middle-phase algorithm + * that have to be tested during narrow-phase collision detection. + */ struct NarrowPhaseInfo { /// Broadphase overlapping pairs ids @@ -111,6 +116,9 @@ struct NarrowPhaseInfoBatch { /// Cached capacity uint32 mCachedCapacity = 0; + /// TriangleShape allocated size + static const size_t mTriangleShapeAllocatedSize; + public: /// For each collision test, we keep some meta data diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h index 4ac07eab4..097020817 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -152,7 +152,7 @@ RP3D_FORCE_INLINE void NarrowPhaseInput::addNarrowPhaseTest(uint64 pairId, Entit case NarrowPhaseAlgorithmType::ConvexPolyhedronVsConvexPolyhedron: mConvexPolyhedronVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, lastFrameInfo, shapeAllocator); break; - case NarrowPhaseAlgorithmType::None: + case NarrowPhaseAlgorithmType::NoCollisionTest: // Must never happen assert(false); break; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h index 36ffa25b1..74e3bf2a2 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h index 86f58036b..29f259984 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h index d113862cc..ec968bc9b 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h index 78b5f5cfc..41ec1789c 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/AABB.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/AABB.h index 1cfebf129..e18070618 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/AABB.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/AABB.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -28,6 +28,7 @@ // Libraries #include +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -82,6 +83,9 @@ class AABB { /// Inflate each side of the AABB by a given size void inflate(decimal dx, decimal dy, decimal dz); + /// Inflate (if necessary) to make sure that a given point fit inside it + void inflateWithPoint(const Vector3& point); + /// Return true if the current AABB is overlapping with the AABB in argument bool testCollision(const AABB& aabb) const; @@ -98,7 +102,7 @@ class AABB { bool contains(const AABB& aabb) const; /// Return true if a point is inside the AABB - bool contains(const Vector3& point) const; + bool contains(const Vector3& point, decimal epsilon = MACHINE_EPSILON) const; /// Return true if the AABB of a triangle intersects the AABB bool testCollisionTriangleAABB(const Vector3* trianglePoints) const; @@ -156,6 +160,20 @@ RP3D_FORCE_INLINE void AABB::inflate(decimal dx, decimal dy, decimal dz) { mMinCoordinates -= Vector3(dx, dy, dz); } +// Inflate (if necessary) to make sure that a given point fit inside it +RP3D_FORCE_INLINE void AABB::inflateWithPoint(const Vector3& point) { + + // Compute mesh bounds + if (point.x > mMaxCoordinates.x) mMaxCoordinates.x = point.x; + if (point.x < mMinCoordinates.x) mMinCoordinates.x = point.x; + + if (point.y > mMaxCoordinates.y) mMaxCoordinates.y = point.y; + if (point.y < mMinCoordinates.y) mMinCoordinates.y = point.y; + + if (point.z > mMaxCoordinates.z) mMaxCoordinates.z = point.z; + if (point.z < mMinCoordinates.z) mMinCoordinates.z = point.z; +} + // Return true if the current AABB is overlapping with the AABB in argument. /// Two AABBs overlap if they overlap in the three x, y and z axis at the same time RP3D_FORCE_INLINE bool AABB::testCollision(const AABB& aabb) const { @@ -189,11 +207,11 @@ RP3D_FORCE_INLINE bool AABB::testCollisionTriangleAABB(const Vector3* trianglePo } // Return true if a point is inside the AABB -RP3D_FORCE_INLINE bool AABB::contains(const Vector3& point) const { +RP3D_FORCE_INLINE bool AABB::contains(const Vector3& point, decimal epsilon) const { - return (point.x >= mMinCoordinates.x - MACHINE_EPSILON && point.x <= mMaxCoordinates.x + MACHINE_EPSILON && - point.y >= mMinCoordinates.y - MACHINE_EPSILON && point.y <= mMaxCoordinates.y + MACHINE_EPSILON && - point.z >= mMinCoordinates.z - MACHINE_EPSILON && point.z <= mMaxCoordinates.z + MACHINE_EPSILON); + return (point.x >= mMinCoordinates.x - epsilon && point.x <= mMaxCoordinates.x + epsilon && + point.y >= mMinCoordinates.y - epsilon && point.y <= mMaxCoordinates.y + epsilon && + point.z >= mMinCoordinates.z - epsilon && point.z <= mMaxCoordinates.z + epsilon); } // Apply a scale factor to the AABB diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/BoxShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/BoxShape.h index 0c8e503e1..cdc5ee4ce 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/BoxShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/BoxShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -34,9 +34,10 @@ namespace reactphysics3d { // Declarations -class CollisionBody; +class Body; class DefaultAllocator; class PhysicsCommon; +class AABB; // Class BoxShape /** @@ -88,13 +89,13 @@ class BoxShape : public ConvexPolyhedronShape { BoxShape& operator=(const BoxShape& shape) = delete; /// Return the half-extents of the box - Vector3 getHalfExtents() const; + const Vector3& getHalfExtents() const; /// Set the half-extents of the box void setHalfExtents(const Vector3& halfExtents); /// Return the local bounds of the shape in x, y and z directions - virtual void getLocalBounds(Vector3& min, Vector3& max) const override; + virtual AABB getLocalBounds() const override; /// Return the local inertia tensor of the collision shape virtual Vector3 getLocalInertiaTensor(decimal mass) const override; @@ -141,7 +142,7 @@ class BoxShape : public ConvexPolyhedronShape { /** * @return The vector with the three half-extents of the box shape */ -RP3D_FORCE_INLINE Vector3 BoxShape::getHalfExtents() const { +RP3D_FORCE_INLINE const Vector3& BoxShape::getHalfExtents() const { return mHalfExtents; } @@ -157,21 +158,6 @@ RP3D_FORCE_INLINE void BoxShape::setHalfExtents(const Vector3& halfExtents) { notifyColliderAboutChangedSize(); } -// Return the local bounds of the shape in x, y and z directions -/// This method is used to compute the AABB of the box -/** - * @param min The minimum bounds of the shape in local-space coordinates - * @param max The maximum bounds of the shape in local-space coordinates - */ -RP3D_FORCE_INLINE void BoxShape::getLocalBounds(Vector3& min, Vector3& max) const { - - // Maximum bounds - max = mHalfExtents; - - // Minimum bounds - min = -max; -} - // Return the number of bytes used by the collision shape RP3D_FORCE_INLINE size_t BoxShape::getSizeInBytes() const { return sizeof(BoxShape); diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CapsuleShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CapsuleShape.h index b4976d97d..592967748 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CapsuleShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CapsuleShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -34,7 +34,7 @@ namespace reactphysics3d { // Declarations -class CollisionBody; +class Body; // Class CapsuleShape /** @@ -103,7 +103,7 @@ class CapsuleShape : public ConvexShape { void setHeight(decimal height); /// Return the local bounds of the shape in x, y and z directions - virtual void getLocalBounds(Vector3& min, Vector3& max) const override; + virtual AABB getLocalBounds() const override; /// Compute and return the volume of the collision shape virtual decimal getVolume() const override; @@ -171,25 +171,6 @@ RP3D_FORCE_INLINE size_t CapsuleShape::getSizeInBytes() const { return sizeof(CapsuleShape); } -// Return the local bounds of the shape in x, y and z directions -// This method is used to compute the AABB of the box -/** - * @param min The minimum bounds of the shape in local-space coordinates - * @param max The maximum bounds of the shape in local-space coordinates - */ -RP3D_FORCE_INLINE void CapsuleShape::getLocalBounds(Vector3& min, Vector3& max) const { - - // Maximum bounds - max.x = mMargin; - max.y = mHalfHeight + mMargin; - max.z = mMargin; - - // Minimum bounds - min.x = -mMargin; - min.y = -max.y; - min.z = min.x; -} - // Compute and return the volume of the collision shape RP3D_FORCE_INLINE decimal CapsuleShape::getVolume() const { return reactphysics3d::PI_RP3D * mMargin * mMargin * (decimal(4.0) * mMargin / decimal(3.0) + decimal(2.0) * mHalfHeight); diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CollisionShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CollisionShape.h index 3d061c0d6..bc8abec4c 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CollisionShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/CollisionShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -54,7 +54,7 @@ enum class CollisionShapeName { TRIANGLE, SPHERE, CAPSULE, BOX, CONVEX_MESH, TRI // Declarations class Collider; -class CollisionBody; +class Body; // Class CollisionShape /** @@ -135,7 +135,7 @@ class CollisionShape { virtual bool isPolyhedron() const=0; /// Return the local bounds of the shape in x, y and z directions - virtual void getLocalBounds(Vector3& min, Vector3& max) const=0; + virtual AABB getLocalBounds() const=0; /// Return the id of the shape uint32 getId() const; @@ -146,8 +146,8 @@ class CollisionShape { /// Compute and return the volume of the collision shape virtual decimal getVolume() const=0; - /// Compute the world-space AABB of the collision shape given a transform - virtual void computeAABB(AABB& aabb, const Transform& transform) const; + /// Compute the transformed AABB of the collision shape given a transform + virtual AABB computeTransformedAABB(const Transform& transform) const; /// Return the string representation of the shape virtual std::string to_string() const=0; @@ -162,7 +162,7 @@ class CollisionShape { // -------------------- Friendship -------------------- // friend class Collider; - friend class CollisionBody; + friend class Body; friend class RigidBody; friend class PhysicsWorld; friend class BroadPhaseSystem; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h index f794d9bea..2f9ac10e4 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -40,6 +40,9 @@ class TriangleShape; class TriangleMesh; // class ConvexTriangleAABBOverlapCallback +/** + * This class represents a callback when an overlap occurs + */ class ConvexTriangleAABBOverlapCallback : public DynamicAABBTreeOverlapCallback { private: @@ -49,15 +52,11 @@ class ConvexTriangleAABBOverlapCallback : public DynamicAABBTreeOverlapCallback // Reference to the concave mesh shape const ConcaveMeshShape& mConcaveMeshShape; - // Reference to the Dynamic AABB tree - const DynamicAABBTree& mDynamicAABBTree; - public: // Constructor - ConvexTriangleAABBOverlapCallback(TriangleCallback& triangleCallback, const ConcaveMeshShape& concaveShape, - const DynamicAABBTree& dynamicAABBTree) - : mTriangleTestCallback(triangleCallback), mConcaveMeshShape(concaveShape), mDynamicAABBTree(dynamicAABBTree) { + ConvexTriangleAABBOverlapCallback(TriangleCallback& triangleCallback, const ConcaveMeshShape& concaveShape) + : mTriangleTestCallback(triangleCallback), mConcaveMeshShape(concaveShape) { } @@ -73,14 +72,12 @@ class ConcaveMeshRaycastCallback : public DynamicAABBTreeRaycastCallback { private : Array mHitAABBNodes; - const DynamicAABBTree& mDynamicAABBTree; const ConcaveMeshShape& mConcaveMeshShape; Collider* mCollider; RaycastInfo& mRaycastInfo; const Ray& mRay; bool mIsHit; MemoryAllocator& mAllocator; - const Vector3& mMeshScale; #ifdef IS_RP3D_PROFILING_ENABLED @@ -92,10 +89,10 @@ class ConcaveMeshRaycastCallback : public DynamicAABBTreeRaycastCallback { public: // Constructor - ConcaveMeshRaycastCallback(const DynamicAABBTree& dynamicAABBTree, const ConcaveMeshShape& concaveMeshShape, - Collider* collider, RaycastInfo& raycastInfo, const Ray& ray, const Vector3& meshScale, MemoryAllocator& allocator) - : mHitAABBNodes(allocator), mDynamicAABBTree(dynamicAABBTree), mConcaveMeshShape(concaveMeshShape), mCollider(collider), - mRaycastInfo(raycastInfo), mRay(ray), mIsHit(false), mAllocator(allocator), mMeshScale(meshScale) { + ConcaveMeshRaycastCallback(const ConcaveMeshShape& concaveMeshShape, + Collider* collider, RaycastInfo& raycastInfo, const Ray& ray, MemoryAllocator& allocator) + : mHitAABBNodes(allocator), mConcaveMeshShape(concaveMeshShape), mCollider(collider), + mRaycastInfo(raycastInfo), mRay(ray), mIsHit(false), mAllocator(allocator) { } @@ -135,16 +132,12 @@ class ConcaveMeshShape : public ConcaveShape { /// Pointer to the triangle mesh TriangleMesh* mTriangleMesh; - /// Dynamic AABB tree to accelerate collision with the triangles - DynamicAABBTree mDynamicAABBTree; - - /// Array with computed vertices normals for each TriangleVertexArray of the triangle mesh (only - /// if the user did not provide its own vertices normals) - Vector3** mComputedVerticesNormals; - /// Reference to the triangle half-edge structure HalfEdgeStructure& mTriangleHalfEdgeStructure; + /// Array with the scaled face normals + Array mScaledVerticesNormals; + // -------------------- Methods -------------------- // /// Constructor @@ -159,14 +152,8 @@ class ConcaveMeshShape : public ConcaveShape { /// Insert all the triangles into the dynamic AABB tree void initBVHTree(); - /// Return the three vertices coordinates (in the array outTriangleVertices) of a triangle - void getTriangleVertices(uint32 subPart, uint32 triangleIndex, Vector3* outTriangleVertices) const; - - /// Return the three vertex normals (in the array outVerticesNormals) of a triangle - void getTriangleVerticesNormals(uint32 subPart, uint32 triangleIndex, Vector3* outVerticesNormals) const; - /// Compute the shape Id for a given triangle of the mesh - uint32 computeTriangleShapeId(uint32 subPart, uint32 triangleIndex) const; + uint32 computeTriangleShapeId(uint32 triangleIndex) const; /// Compute all the triangles of the mesh that are overlapping with the AABB in parameter virtual void computeOverlappingTriangles(const AABB& localAABB, Array& triangleVertices, @@ -176,6 +163,12 @@ class ConcaveMeshShape : public ConcaveShape { /// Destructor virtual ~ConcaveMeshShape() override = default; + // Return the integer data of leaf node of the dynamic AABB tree + int32 getDynamicAABBTreeNodeDataInt(int32 nodeID) const; + + /// Compute the scaled faces normals + void computeScaledVerticesNormals(); + public: /// Deleted copy-constructor @@ -184,17 +177,34 @@ class ConcaveMeshShape : public ConcaveShape { /// Deleted assignment operator ConcaveMeshShape& operator=(const ConcaveMeshShape& shape) = delete; - /// Return the number of sub parts contained in this mesh - uint32 getNbSubparts() const; - - /// Return the number of triangles in a sub part of the mesh - uint32 getNbTriangles(uint32 subPart) const; + /// Set the scale of the shape + virtual void setScale(const Vector3& scale) override; + + /// Return the number of vertices in the mesh + uint32 getNbVertices() const; + + /// Return the number of triangles of the mesh + uint32 getNbTriangles() const; /// Return the indices of the three vertices of a given triangle in the array - void getTriangleVerticesIndices(uint32 subPart, uint32 triangleIndex, uint32* outVerticesIndices) const; + void getTriangleVerticesIndices(uint32 triangleIndex, uint32& outV1Index, uint32& outV2Index, + uint32& outV3Index) const; + + /// Return the coordinates of the three vertices of a given triangle face + void getTriangleVertices(uint32 triangleIndex, Vector3& outV1, Vector3& outV2, Vector3& outV3) const; + + /// Return the normals of the three vertices of a given triangle face + void getTriangleVerticesNormals(uint32 triangleIndex, Vector3& outN1, + Vector3& outN2, Vector3& outN3) const; + + /// Return the coordinates of a given vertex + const Vector3 getVertex(uint32 vertexIndex) const; + + /// Return the normal of a given vertex + const Vector3& getVertexNormal(uint32 vertexIndex) const; /// Return the local bounds of the shape in x, y and z directions. - virtual void getLocalBounds(Vector3& min, Vector3& max) const override; + virtual AABB getLocalBounds() const override; /// Return the string representation of the shape virtual std::string to_string() const override; @@ -219,53 +229,33 @@ RP3D_FORCE_INLINE size_t ConcaveMeshShape::getSizeInBytes() const { return sizeof(ConcaveMeshShape); } -// Return the local bounds of the shape in x, y and z directions. -// This method is used to compute the AABB of the box -/** - * @param min The minimum bounds of the shape in local-space coordinates - * @param max The maximum bounds of the shape in local-space coordinates - */ -RP3D_FORCE_INLINE void ConcaveMeshShape::getLocalBounds(Vector3& min, Vector3& max) const { - - // Get the AABB of the whole tree - AABB treeAABB = mDynamicAABBTree.getRootAABB(); - - min = treeAABB.getMin(); - max = treeAABB.getMax(); -} - // Called when a overlapping node has been found during the call to // DynamicAABBTree:reportAllShapesOverlappingWithAABB() RP3D_FORCE_INLINE void ConvexTriangleAABBOverlapCallback::notifyOverlappingNode(int nodeId) { // Get the node data (triangle index and mesh subpart index) - int32* data = mDynamicAABBTree.getNodeDataInt(nodeId); + int32 data = mConcaveMeshShape.getDynamicAABBTreeNodeDataInt(nodeId); // Get the triangle vertices for this node from the concave mesh shape Vector3 trianglePoints[3]; - mConcaveMeshShape.getTriangleVertices(data[0], data[1], trianglePoints); + mConcaveMeshShape.getTriangleVertices(data, trianglePoints[0], trianglePoints[1], trianglePoints[2]); // Get the vertices normals of the triangle Vector3 verticesNormals[3]; - mConcaveMeshShape.getTriangleVerticesNormals(data[0], data[1], verticesNormals); + mConcaveMeshShape.getTriangleVerticesNormals(data, verticesNormals[0], verticesNormals[1], verticesNormals[2]); // Call the callback to test narrow-phase collision with this triangle - mTriangleTestCallback.testTriangle(trianglePoints, verticesNormals, mConcaveMeshShape.computeTriangleShapeId(data[0], data[1])); + mTriangleTestCallback.testTriangle(trianglePoints, verticesNormals, mConcaveMeshShape.computeTriangleShapeId(data)); } -#ifdef IS_RP3D_PROFILING_ENABLED - -// Set the profiler -RP3D_FORCE_INLINE void ConcaveMeshShape::setProfiler(Profiler* profiler) { +// Compute the shape Id for a given triangle of the mesh +RP3D_FORCE_INLINE uint32 ConcaveMeshShape::computeTriangleShapeId(uint32 triangleIndex) const { - CollisionShape::setProfiler(profiler); + RP3D_PROFILE("ConcaveMeshShape::computeTriangleShapeId()", mProfiler); - mDynamicAABBTree.setProfiler(profiler); + return getNbTriangles() + triangleIndex; } - -#endif - } #endif diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveShape.h index 0f9837dbc..5ff2c5b11 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConcaveShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -99,7 +99,7 @@ class ConcaveShape : public CollisionShape { const Vector3& getScale() const; /// Set the scale of the shape - void setScale(const Vector3& scale); + virtual void setScale(const Vector3& scale); /// Return the local inertia tensor of the collision shape virtual Vector3 getLocalInertiaTensor(decimal mass) const override; diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexMeshShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexMeshShape.h index ab09099ed..d0aa148e0 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexMeshShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexMeshShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -29,7 +29,7 @@ // Libraries #include #include -#include +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -51,25 +51,19 @@ class ConvexMeshShape : public ConvexPolyhedronShape { // -------------------- Attributes -------------------- // - /// Polyhedron structure of the mesh - PolyhedronMesh* mPolyhedronMesh; + /// Convex mesh + ConvexMesh* mConvexMesh; - /// Mesh minimum bounds in the three local x, y and z directions - Vector3 mMinBounds; - - /// Mesh maximum bounds in the three local x, y and z directions - Vector3 mMaxBounds; - - /// Scale of the mesh + /// Scale to apply to the mesh Vector3 mScale; + /// Array with the scaled face normals + Array mScaledFacesNormals; + // -------------------- Methods -------------------- // /// Constructor - ConvexMeshShape(PolyhedronMesh* polyhedronMesh, MemoryAllocator& allocator, const Vector3& scale = Vector3(1,1,1)); - - /// Recompute the bounds of the mesh - void recalculateBounds(); + ConvexMeshShape(ConvexMesh* convexMesh, MemoryAllocator& allocator, const Vector3& scale = Vector3(1,1,1)); /// Return a local support point in a given direction without the object margin. virtual Vector3 getLocalSupportPointWithoutMargin(const Vector3& direction) const override; @@ -83,6 +77,9 @@ class ConvexMeshShape : public ConvexPolyhedronShape { /// Return the number of bytes used by the collision shape virtual size_t getSizeInBytes() const override; + // Compute the scaled faces normals + void computeScaledFacesNormals(); + /// Destructor virtual ~ConvexMeshShape() override = default; @@ -103,36 +100,36 @@ class ConvexMeshShape : public ConvexPolyhedronShape { void setScale(const Vector3& scale); /// Return the local bounds of the shape in x, y and z directions - virtual void getLocalBounds(Vector3& min, Vector3& max) const override; + virtual AABB getLocalBounds() const override; /// Return the local inertia tensor of the collision shape. virtual Vector3 getLocalInertiaTensor(decimal mass) const override; - /// Return the number of faces of the polyhedron + /// Return the number of faces of the mesh virtual uint32 getNbFaces() const override; - /// Return a given face of the polyhedron + /// Return a given face of the mesh virtual const HalfEdgeStructure::Face& getFace(uint32 faceIndex) const override; - /// Return the number of vertices of the polyhedron + /// Return the number of vertices of the mesh virtual uint32 getNbVertices() const override; - /// Return a given vertex of the polyhedron + /// Return a given vertex of the mesh virtual const HalfEdgeStructure::Vertex& getVertex(uint32 vertexIndex) const override; - /// Return the number of half-edges of the polyhedron + /// Return the number of half-edges of the mesh virtual uint32 getNbHalfEdges() const override; - /// Return a given half-edge of the polyhedron + /// Return a given half-edge of the mesh virtual const HalfEdgeStructure::Edge& getHalfEdge(uint32 edgeIndex) const override; /// Return the position of a given vertex virtual Vector3 getVertexPosition(uint32 vertexIndex) const override; - /// Return the normal vector of a given face of the polyhedron + /// Return the normal vector of a given face of the mesh virtual Vector3 getFaceNormal(uint32 faceIndex) const override; - /// Return the centroid of the polyhedron + /// Return the centroid of the mesh virtual Vector3 getCentroid() const override; /// Compute and return the volume of the collision shape @@ -160,19 +157,13 @@ RP3D_FORCE_INLINE const Vector3& ConvexMeshShape::getScale() const { /// Note that you might want to recompute the inertia tensor and center of mass of the body /// after changing the scale of a collision shape RP3D_FORCE_INLINE void ConvexMeshShape::setScale(const Vector3& scale) { + mScale = scale; - recalculateBounds(); - notifyColliderAboutChangedSize(); -} -// Return the local bounds of the shape in x, y and z directions -/** - * @param min The minimum bounds of the shape in local-space coordinates - * @param max The maximum bounds of the shape in local-space coordinates - */ -RP3D_FORCE_INLINE void ConvexMeshShape::getLocalBounds(Vector3& min, Vector3& max) const { - min = mMinBounds; - max = mMaxBounds; + // Recompute the scaled face normals + computeScaledFacesNormals(); + + notifyColliderAboutChangedSize(); } // Return the local inertia tensor of the collision shape. @@ -182,69 +173,62 @@ RP3D_FORCE_INLINE void ConvexMeshShape::getLocalBounds(Vector3& min, Vector3& ma * @param mass Mass to use to compute the inertia tensor of the collision shape */ RP3D_FORCE_INLINE Vector3 ConvexMeshShape::getLocalInertiaTensor(decimal mass) const { - const decimal factor = (decimal(1.0) / decimal(3.0)) * mass; - const Vector3 realExtent = decimal(0.5) * (mMaxBounds - mMinBounds); - assert(realExtent.x > 0 && realExtent.y > 0 && realExtent.z > 0); - const decimal xSquare = realExtent.x * realExtent.x; - const decimal ySquare = realExtent.y * realExtent.y; - const decimal zSquare = realExtent.z * realExtent.z; - return Vector3(factor * (ySquare + zSquare), factor * (xSquare + zSquare), factor * (xSquare + ySquare)); + return mConvexMesh->getLocalInertiaTensor(mass, mScale); } -// Return the number of faces of the polyhedron +// Return the number of faces of the mesh RP3D_FORCE_INLINE uint32 ConvexMeshShape::getNbFaces() const { - return mPolyhedronMesh->getHalfEdgeStructure().getNbFaces(); + return mConvexMesh->getHalfEdgeStructure().getNbFaces(); } -// Return a given face of the polyhedron +// Return a given face of the mesh RP3D_FORCE_INLINE const HalfEdgeStructure::Face& ConvexMeshShape::getFace(uint32 faceIndex) const { assert(faceIndex < getNbFaces()); - return mPolyhedronMesh->getHalfEdgeStructure().getFace(faceIndex); + return mConvexMesh->getHalfEdgeStructure().getFace(faceIndex); } -// Return the number of vertices of the polyhedron +// Return the number of vertices of the mesh RP3D_FORCE_INLINE uint32 ConvexMeshShape::getNbVertices() const { - return mPolyhedronMesh->getHalfEdgeStructure().getNbVertices(); + return mConvexMesh->getHalfEdgeStructure().getNbVertices(); } -// Return a given vertex of the polyhedron +// Return a given vertex of the mesh RP3D_FORCE_INLINE const HalfEdgeStructure::Vertex& ConvexMeshShape::getVertex(uint32 vertexIndex) const { assert(vertexIndex < getNbVertices()); - return mPolyhedronMesh->getHalfEdgeStructure().getVertex(vertexIndex); + return mConvexMesh->getHalfEdgeStructure().getVertex(vertexIndex); } -// Return the number of half-edges of the polyhedron +// Return the number of half-edges of the mesh RP3D_FORCE_INLINE uint32 ConvexMeshShape::getNbHalfEdges() const { - return mPolyhedronMesh->getHalfEdgeStructure().getNbHalfEdges(); + return mConvexMesh->getHalfEdgeStructure().getNbHalfEdges(); } -// Return a given half-edge of the polyhedron +// Return a given half-edge of the mesh RP3D_FORCE_INLINE const HalfEdgeStructure::Edge& ConvexMeshShape::getHalfEdge(uint32 edgeIndex) const { assert(edgeIndex < getNbHalfEdges()); - return mPolyhedronMesh->getHalfEdgeStructure().getHalfEdge(edgeIndex); + return mConvexMesh->getHalfEdgeStructure().getHalfEdge(edgeIndex); } // Return the position of a given vertex RP3D_FORCE_INLINE Vector3 ConvexMeshShape::getVertexPosition(uint32 vertexIndex) const { assert(vertexIndex < getNbVertices()); - return mPolyhedronMesh->getVertex(vertexIndex) * mScale; + return mConvexMesh->getVertex(vertexIndex) * mScale; } -// Return the normal vector of a given face of the polyhedron +// Return the normal vector of a given face of the mesh RP3D_FORCE_INLINE Vector3 ConvexMeshShape::getFaceNormal(uint32 faceIndex) const { assert(faceIndex < getNbFaces()); - return mPolyhedronMesh->getFaceNormal(faceIndex); + return mScaledFacesNormals[faceIndex]; } -// Return the centroid of the polyhedron +// Return the centroid of the mesh RP3D_FORCE_INLINE Vector3 ConvexMeshShape::getCentroid() const { - return mPolyhedronMesh->getCentroid() * mScale; + return mConvexMesh->getCentroid() * mScale; } - // Compute and return the volume of the collision shape RP3D_FORCE_INLINE decimal ConvexMeshShape::getVolume() const { - return mPolyhedronMesh->getVolume(); + return mConvexMesh->getVolume() * mScale.x * mScale.y * mScale.z; } } diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexPolyhedronShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexPolyhedronShape.h index 43953c4c6..3c89130ae 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexPolyhedronShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexPolyhedronShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexShape.h index 21dc377b8..3aff4fa6c 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/ConvexShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/HeightFieldShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/HeightFieldShape.h index c13964e89..e6972b4c3 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/HeightFieldShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/HeightFieldShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -28,6 +28,7 @@ // Libraries #include +#include #include namespace reactphysics3d { @@ -51,60 +52,17 @@ class HeightFieldShape : public ConcaveShape { public: - /// Data type for the height data of the height field - enum class HeightDataType {HEIGHT_FLOAT_TYPE, HEIGHT_DOUBLE_TYPE, HEIGHT_INT_TYPE}; - protected: // -------------------- Attributes -------------------- // - /// Number of columns in the grid of the height field - int mNbColumns; - - /// Number of rows in the grid of the height field - int mNbRows; - - /// Height field width - decimal mWidth; - - /// Height field length - decimal mLength; - - /// Minimum height of the height field - decimal mMinHeight; - - /// Maximum height of the height field - decimal mMaxHeight; - - /// Up axis direction (0 => x, 1 => y, 2 => z) - int mUpAxis; - - /// Height values scale for height field with integer height values - decimal mIntegerHeightScale; - - /// Data type of the height values - HeightDataType mHeightDataType; - - /// Array of data with all the height values of the height field - const void* mHeightFieldData; - - /// Local AABB of the height field (without scaling) - AABB mAABB; - - /// Reference to the half-edge structure - HalfEdgeStructure& mTriangleHalfEdgeStructure; + /// Height-field + HeightField* mHeightField; // -------------------- Methods -------------------- // /// Constructor - HeightFieldShape(int nbGridColumns, int nbGridRows, decimal minHeight, decimal maxHeight, - const void* heightFieldData, HeightDataType dataType, MemoryAllocator& allocator, - HalfEdgeStructure& triangleHalfEdgeStructure, int upAxis = 1, decimal integerHeightScale = 1.0f, - const Vector3& scaling = Vector3(1,1,1)); - - /// Raycast a single triangle of the height-field - bool raycastTriangle(const Ray& ray, const Vector3& p1, const Vector3& p2, const Vector3& p3, uint32 shapeId, - Collider *collider, RaycastInfo& raycastInfo, decimal &smallestHitFraction, MemoryAllocator& allocator) const; + HeightFieldShape(HeightField* heightfield, MemoryAllocator& allocator, const Vector3& scaling = Vector3(1,1,1)); /// Raycast method with feedback information virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* collider, MemoryAllocator& allocator) const override; @@ -112,20 +70,6 @@ class HeightFieldShape : public ConcaveShape { /// Return the number of bytes used by the collision shape virtual size_t getSizeInBytes() const override; - /// Return the three vertices coordinates (in the array outTriangleVertices) of a triangle - /// given the start vertex index pointer of the triangle. - void getTriangleVerticesWithIndexPointer(int32 subPart, int32 triangleIndex, - Vector3* outTriangleVertices) const; - - /// Compute the min/max grid coords corresponding to the intersection of the AABB of the height field and the AABB to collide - void computeMinMaxGridCoordinates(int* minCoords, int* maxCoords, const AABB& aabbToCollide) const; - - /// Compute the shape Id for a given triangle - uint32 computeTriangleShapeId(uint32 iIndex, uint32 jIndex, uint32 secondTriangleIncrement) const; - - /// Compute the first grid cell of the heightfield intersected by a ray - bool computeEnteringRayGridCoordinates(const Ray& ray, int& i, int& j, Vector3& outHitPoint) const; - /// Destructor virtual ~HeightFieldShape() override = default; @@ -137,23 +81,14 @@ class HeightFieldShape : public ConcaveShape { /// Deleted assignment operator HeightFieldShape& operator=(const HeightFieldShape& shape) = delete; - /// Return the number of rows in the height field - int getNbRows() const; + /// Return a pointer to the internal height-field + HeightField* getHeightField() const; - /// Return the number of columns in the height field - int getNbColumns() const; - /// Return the vertex (local-coordinates) of the height field at a given (x,y) position - Vector3 getVertexAt(int x, int y) const; - - /// Return the height of a given (x,y) point in the height field - decimal getHeightAt(int x, int y) const; - - /// Return the type of height value in the height field - HeightDataType getHeightDataType() const; + Vector3 getVertexAt(uint32 x, uint32 y) const; /// Return the local bounds of the shape in x, y and z directions. - virtual void getLocalBounds(Vector3& min, Vector3& max) const override; + virtual AABB getLocalBounds() const override; /// Use a callback method on all triangles of the concave shape inside a given AABB virtual void computeOverlappingTriangles(const AABB& localAABB, Array& triangleVertices, @@ -170,19 +105,9 @@ class HeightFieldShape : public ConcaveShape { friend class PhysicsCommon; }; -// Return the number of rows in the height field -RP3D_FORCE_INLINE int HeightFieldShape::getNbRows() const { - return mNbRows; -} - -// Return the number of columns in the height field -RP3D_FORCE_INLINE int HeightFieldShape::getNbColumns() const { - return mNbColumns; -} - -// Return the type of height value in the height field -RP3D_FORCE_INLINE HeightFieldShape::HeightDataType HeightFieldShape::getHeightDataType() const { - return mHeightDataType; +// Return a pointer to the internal height-field +RP3D_FORCE_INLINE HeightField* HeightFieldShape::getHeightField() const { + return mHeightField; } // Return the number of bytes used by the collision shape @@ -190,24 +115,9 @@ RP3D_FORCE_INLINE size_t HeightFieldShape::getSizeInBytes() const { return sizeof(HeightFieldShape); } -// Return the height of a given (x,y) point in the height field -RP3D_FORCE_INLINE decimal HeightFieldShape::getHeightAt(int x, int y) const { - - assert(x >= 0 && x < mNbColumns); - assert(y >= 0 && y < mNbRows); - - switch(mHeightDataType) { - case HeightDataType::HEIGHT_FLOAT_TYPE : return decimal(((float*)mHeightFieldData)[y * mNbColumns + x]); - case HeightDataType::HEIGHT_DOUBLE_TYPE : return decimal(((double*)mHeightFieldData)[y * mNbColumns + x]); - case HeightDataType::HEIGHT_INT_TYPE : return decimal(((int*)mHeightFieldData)[y * mNbColumns + x] * mIntegerHeightScale); - default: assert(false); return 0; - } -} - -// Compute the shape Id for a given triangle -RP3D_FORCE_INLINE uint32 HeightFieldShape::computeTriangleShapeId(uint32 iIndex, uint32 jIndex, uint32 secondTriangleIncrement) const { - - return (jIndex * (mNbColumns - 1) + iIndex) * 2 + secondTriangleIncrement; +// Return the vertex (local-coordinates) of the height field at a given (x,y) position +RP3D_FORCE_INLINE Vector3 HeightFieldShape::getVertexAt(uint32 x, uint32 y) const { + return mHeightField->getVertexAt(x, y) * mScale; } } diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/SphereShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/SphereShape.h index f06166d9c..f7382669d 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/SphereShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/SphereShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -29,12 +29,13 @@ // Libraries #include #include +#include // ReactPhysics3D namespace namespace reactphysics3d { // Declarations -class CollisionBody; +class Body; // Class SphereShape /** @@ -88,7 +89,7 @@ class SphereShape : public ConvexShape { virtual bool isPolyhedron() const override; /// Return the local bounds of the shape in x, y and z directions. - virtual void getLocalBounds(Vector3& min, Vector3& max) const override; + virtual AABB getLocalBounds() const override; /// Return the local inertia tensor of the collision shape virtual Vector3 getLocalInertiaTensor(decimal mass) const override; @@ -96,8 +97,8 @@ class SphereShape : public ConvexShape { /// Compute and return the volume of the collision shape virtual decimal getVolume() const override; - /// Update the AABB of a body using its collision shape - virtual void computeAABB(AABB& aabb, const Transform& transform) const override; + /// Compute the transformed AABB + virtual AABB computeTransformedAABB(const Transform& transform) const override; /// Return the string representation of the shape virtual std::string to_string() const override; @@ -154,20 +155,12 @@ RP3D_FORCE_INLINE Vector3 SphereShape::getLocalSupportPointWithoutMargin(const V // Return the local bounds of the shape in x, y and z directions. // This method is used to compute the AABB of the box /** - * @param min The minimum bounds of the shape in local-space coordinates - * @param max The maximum bounds of the shape in local-space coordinates + * @return The AABB with the min/max bounds of the shape */ -RP3D_FORCE_INLINE void SphereShape::getLocalBounds(Vector3& min, Vector3& max) const { +RP3D_FORCE_INLINE AABB SphereShape::getLocalBounds() const { - // Maximum bounds - max.x = mMargin; - max.y = mMargin; - max.z = mMargin; - - // Minimum bounds - min.x = -mMargin; - min.y = min.x; - min.z = min.x; + return AABB(Vector3(-mMargin, -mMargin, -mMargin), + Vector3(mMargin, mMargin, mMargin)); } // Return the local inertia tensor of the sphere diff --git a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/TriangleShape.h b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/TriangleShape.h index 3767b7aff..584eb4bee 100644 --- a/ext/reactphysics3d/include/reactphysics3d/collision/shapes/TriangleShape.h +++ b/ext/reactphysics3d/include/reactphysics3d/collision/shapes/TriangleShape.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -28,6 +28,7 @@ // Libraries #include +#include #include /// ReactPhysics3D namespace @@ -127,13 +128,13 @@ class TriangleShape : public ConvexPolyhedronShape { TriangleShape& operator=(const TriangleShape& shape) = delete; /// Return the local bounds of the shape in x, y and z directions. - virtual void getLocalBounds(Vector3& min, Vector3& max) const override; + virtual AABB getLocalBounds() const override; /// Return the local inertia tensor of the collision shape virtual Vector3 getLocalInertiaTensor(decimal mass) const override; - /// Update the AABB of a body using its collision shape - virtual void computeAABB(AABB& aabb, const Transform& transform) const override; + /// Compute the transformed AABB of the collision shape given a transform + virtual AABB computeTransformedAABB(const Transform& transform) const override; /// Return the raycast test type (front, back, front-back) TriangleRaycastSide getRaycastTestType() const; @@ -185,7 +186,7 @@ class TriangleShape : public ConvexPolyhedronShape { friend class ConcaveMeshRaycastCallback; friend class TriangleOverlapCallback; friend class MiddlePhaseTriangleCallback; - friend class HeightFieldShape; + friend class HeightField; friend class CollisionDetectionSystem; }; @@ -201,28 +202,27 @@ RP3D_FORCE_INLINE Vector3 TriangleShape::getLocalSupportPointWithoutMargin(const } // Return the local bounds of the shape in x, y and z directions. -// This method is used to compute the AABB of the box /** - * @param min The minimum bounds of the shape in local-space coordinates - * @param max The maximum bounds of the shape in local-space coordinates + * @return The AABB with the min/max bounds of the shape */ -RP3D_FORCE_INLINE void TriangleShape::getLocalBounds(Vector3& min, Vector3& max) const { +RP3D_FORCE_INLINE AABB TriangleShape::getLocalBounds() const { const Vector3 xAxis(mPoints[0].x, mPoints[1].x, mPoints[2].x); const Vector3 yAxis(mPoints[0].y, mPoints[1].y, mPoints[2].y); const Vector3 zAxis(mPoints[0].z, mPoints[1].z, mPoints[2].z); - min.setAllValues(xAxis.getMinValue(), yAxis.getMinValue(), zAxis.getMinValue()); - max.setAllValues(xAxis.getMaxValue(), yAxis.getMaxValue(), zAxis.getMaxValue()); + Vector3 min(xAxis.getMinValue(), yAxis.getMinValue(), zAxis.getMinValue()); + Vector3 max(xAxis.getMaxValue(), yAxis.getMaxValue(), zAxis.getMaxValue()); min -= Vector3(mMargin, mMargin, mMargin); max += Vector3(mMargin, mMargin, mMargin); + + return AABB(min, max); } // Return the local inertia tensor of the triangle shape /** - * @param[out] tensor The 3x3 inertia tensor matrix of the shape in local-space - * coordinates * @param mass Mass to use to compute the inertia tensor of the collision shape + * @return A vector with the three diagonal values of the local inertia tensor */ RP3D_FORCE_INLINE Vector3 TriangleShape::getLocalInertiaTensor(decimal /*mass*/) const { return Vector3(0, 0, 0); diff --git a/ext/reactphysics3d/include/reactphysics3d/components/BallAndSocketJointComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/BallAndSocketJointComponents.h index dc6954746..654662f77 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/BallAndSocketJointComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/BallAndSocketJointComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -144,7 +144,7 @@ class BallAndSocketJointComponents : public Components { virtual ~BallAndSocketJointComponents() override = default; /// Add a component - void addComponent(Entity jointEntity, bool isSleeping, const BallAndSocketJointComponent& component); + void addComponent(Entity jointEntity, bool isDisabled, const BallAndSocketJointComponent& component); /// Return a pointer to a given joint BallAndSocketJoint* getJoint(Entity jointEntity) const; diff --git a/ext/reactphysics3d/include/reactphysics3d/components/CollisionBodyComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/BodyComponents.h similarity index 72% rename from ext/reactphysics3d/include/reactphysics3d/components/CollisionBodyComponents.h rename to ext/reactphysics3d/include/reactphysics3d/components/BodyComponents.h index 282558c7e..3d59b8351 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/CollisionBodyComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/BodyComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -38,14 +38,14 @@ namespace reactphysics3d { // Class declarations class MemoryAllocator; class EntityManager; -class CollisionBody; +class Body; -// Class CollisionBodyComponents +// Class BodyComponents /** - * This class represent the component of the ECS that contains data about a collision body. + * This class represent the component of the ECS that contains data about a body. * The components of the sleeping entities (bodies) are always stored at the end of the array. */ -class CollisionBodyComponents : public Components { +class BodyComponents : public Components { private: @@ -55,7 +55,7 @@ class CollisionBodyComponents : public Components { Entity* mBodiesEntities; /// Array of pointers to the corresponding bodies - CollisionBody** mBodies; + Body** mBodies; /// Array with the colliders of each body Array* mColliders; @@ -66,6 +66,9 @@ class CollisionBodyComponents : public Components { /// Array of pointers that can be used to attach user data to the body void** mUserData; + /// For each body, true if it has a least one simulation collider + bool* mHasSimulationCollider; + // -------------------- Methods -------------------- // /// Allocate memory for a given number of components @@ -83,12 +86,12 @@ class CollisionBodyComponents : public Components { public: /// Structure for the data of a collision body component - struct CollisionBodyComponent { + struct BodyComponent { - CollisionBody* body; + Body* body; /// Constructor - CollisionBodyComponent(CollisionBody* body) : body(body) { + BodyComponent(Body* body) : body(body) { } }; @@ -96,13 +99,13 @@ class CollisionBodyComponents : public Components { // -------------------- Methods -------------------- // /// Constructor - CollisionBodyComponents(MemoryAllocator& allocator); + BodyComponents(MemoryAllocator& allocator); /// Destructor - virtual ~CollisionBodyComponents() override = default; + virtual ~BodyComponents() override = default; /// Add a component - void addComponent(Entity bodyEntity, bool isSleeping, const CollisionBodyComponent& component); + void addComponent(Entity bodyEntity, bool isDisabled, const BodyComponent& component); /// Add a collider to a body component void addColliderToBody(Entity bodyEntity, Entity colliderEntity); @@ -111,7 +114,7 @@ class CollisionBodyComponents : public Components { void removeColliderFromBody(Entity bodyEntity, Entity colliderEntity); /// Return a pointer to a body - CollisionBody* getBody(Entity bodyEntity); + Body* getBody(Entity bodyEntity); /// Return the array of colliders of a body const Array& getColliders(Entity bodyEntity) const; @@ -127,10 +130,20 @@ class CollisionBodyComponents : public Components { /// Set the user data associated with the body void setUserData(Entity bodyEntity, void* userData) const; + + /// Return true if the body has at least one simulation collider + bool getHasSimulationCollider(Entity bodyEntity) const; + + /// Set whether the body has at least one simulation collider + void setHasSimulationCollider(Entity bodyEntity, bool hasSimulationCollider) const; + + // -------------------- Friendship -------------------- // + + friend class Body; }; // Add a collider to a body component -RP3D_FORCE_INLINE void CollisionBodyComponents::addColliderToBody(Entity bodyEntity, Entity colliderEntity) { +RP3D_FORCE_INLINE void BodyComponents::addColliderToBody(Entity bodyEntity, Entity colliderEntity) { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -138,7 +151,7 @@ RP3D_FORCE_INLINE void CollisionBodyComponents::addColliderToBody(Entity bodyEnt } // Remove a collider from a body component -RP3D_FORCE_INLINE void CollisionBodyComponents::removeColliderFromBody(Entity bodyEntity, Entity colliderEntity) { +RP3D_FORCE_INLINE void BodyComponents::removeColliderFromBody(Entity bodyEntity, Entity colliderEntity) { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -146,7 +159,7 @@ RP3D_FORCE_INLINE void CollisionBodyComponents::removeColliderFromBody(Entity bo } // Return a pointer to a body -RP3D_FORCE_INLINE CollisionBody *CollisionBodyComponents::getBody(Entity bodyEntity) { +RP3D_FORCE_INLINE Body* BodyComponents::getBody(Entity bodyEntity) { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -154,7 +167,7 @@ RP3D_FORCE_INLINE CollisionBody *CollisionBodyComponents::getBody(Entity bodyEnt } // Return the array of colliders of a body -RP3D_FORCE_INLINE const Array& CollisionBodyComponents::getColliders(Entity bodyEntity) const { +RP3D_FORCE_INLINE const Array& BodyComponents::getColliders(Entity bodyEntity) const { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -162,7 +175,7 @@ RP3D_FORCE_INLINE const Array& CollisionBodyComponents::getColliders(Ent } // Return true if the body is active -RP3D_FORCE_INLINE bool CollisionBodyComponents::getIsActive(Entity bodyEntity) const { +RP3D_FORCE_INLINE bool BodyComponents::getIsActive(Entity bodyEntity) const { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -170,7 +183,7 @@ RP3D_FORCE_INLINE bool CollisionBodyComponents::getIsActive(Entity bodyEntity) c } // Set the value to know if the body is active -RP3D_FORCE_INLINE void CollisionBodyComponents::setIsActive(Entity bodyEntity, bool isActive) const { +RP3D_FORCE_INLINE void BodyComponents::setIsActive(Entity bodyEntity, bool isActive) const { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -178,7 +191,7 @@ RP3D_FORCE_INLINE void CollisionBodyComponents::setIsActive(Entity bodyEntity, b } // Return the user data associated with the body -RP3D_FORCE_INLINE void* CollisionBodyComponents::getUserData(Entity bodyEntity) const { +RP3D_FORCE_INLINE void* BodyComponents::getUserData(Entity bodyEntity) const { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -186,13 +199,27 @@ RP3D_FORCE_INLINE void* CollisionBodyComponents::getUserData(Entity bodyEntity) } // Set the user data associated with the body -RP3D_FORCE_INLINE void CollisionBodyComponents::setUserData(Entity bodyEntity, void* userData) const { +RP3D_FORCE_INLINE void BodyComponents::setUserData(Entity bodyEntity, void* userData) const { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); mUserData[mMapEntityToComponentIndex[bodyEntity]] = userData; } +// Return true if the body has at least one simulation collider +RP3D_FORCE_INLINE bool BodyComponents::getHasSimulationCollider(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + return mHasSimulationCollider[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Set whether the body has at least one simulation collider +RP3D_FORCE_INLINE void BodyComponents::setHasSimulationCollider(Entity bodyEntity, bool hasSimulationCollider) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + mHasSimulationCollider[mMapEntityToComponentIndex[bodyEntity]] = hasSimulationCollider; +} + } #endif diff --git a/ext/reactphysics3d/include/reactphysics3d/components/ColliderComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/ColliderComponents.h index 32c013f87..6c793dcd7 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/ColliderComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/ColliderComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -100,10 +100,15 @@ class ColliderComponents : public Components { /// True if the collider is a trigger bool* mIsTrigger; + /// True if the collider is simulation collider + bool* mIsSimulationCollider; + + /// True if the collider is a world query collider + bool* mIsWorldQueryCollider; + /// Array with the material of each collider Material* mMaterials; - // -------------------- Methods -------------------- // /// Allocate memory for a given number of components @@ -153,7 +158,7 @@ class ColliderComponents : public Components { virtual ~ColliderComponents() override = default; /// Add a component - void addComponent(Entity colliderEntity, bool isSleeping, const ColliderComponent& component); + void addComponent(Entity colliderEntity, bool isDisabled, const ColliderComponent& component); /// Return the body entity of a given collider Entity getBody(Entity colliderEntity) const; @@ -209,6 +214,18 @@ class ColliderComponents : public Components { /// Set whether a collider is a trigger void setIsTrigger(Entity colliderEntity, bool isTrigger); + /// Return true if a collider is a simulation collider + bool getIsSimulationCollider(Entity colliderEntity) const; + + /// Set whether a collider is a simulation collider + void setIsSimulationCollider(Entity colliderEntity, bool isSimulationCollider); + + /// Return true if a collider is a world query collider + bool getIsWorldQueryCollider(Entity colliderEntity) const; + + /// Set whether a collider is a world query collider + void setIsWorldQueryCollider(Entity colliderEntity, bool isWorldQueryCollider); + /// Return a reference to the material of a collider Material& getMaterial(Entity colliderEntity); @@ -370,6 +387,38 @@ RP3D_FORCE_INLINE void ColliderComponents::setIsTrigger(Entity colliderEntity, b mIsTrigger[mMapEntityToComponentIndex[colliderEntity]] = isTrigger; } +// Return true if a collider is a simulation collider +RP3D_FORCE_INLINE bool ColliderComponents::getIsSimulationCollider(Entity colliderEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(colliderEntity)); + + return mIsSimulationCollider[mMapEntityToComponentIndex[colliderEntity]]; +} + +// Set whether a collider is a simulation collider +RP3D_FORCE_INLINE void ColliderComponents::setIsSimulationCollider(Entity colliderEntity, bool isSimulationCollider) { + + assert(mMapEntityToComponentIndex.containsKey(colliderEntity)); + + mIsSimulationCollider[mMapEntityToComponentIndex[colliderEntity]] = isSimulationCollider; +} + +// Return true if a collider is a world query collider +RP3D_FORCE_INLINE bool ColliderComponents::getIsWorldQueryCollider(Entity colliderEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(colliderEntity)); + + return mIsWorldQueryCollider[mMapEntityToComponentIndex[colliderEntity]]; +} + +// Set whether a collider is a world query collider +RP3D_FORCE_INLINE void ColliderComponents::setIsWorldQueryCollider(Entity colliderEntity, bool isWorldQueryCollider) { + + assert(mMapEntityToComponentIndex.containsKey(colliderEntity)); + + mIsWorldQueryCollider[mMapEntityToComponentIndex[colliderEntity]] = isWorldQueryCollider; +} + // Return a reference to the material of a collider RP3D_FORCE_INLINE Material& ColliderComponents::getMaterial(Entity colliderEntity) { diff --git a/ext/reactphysics3d/include/reactphysics3d/components/Components.h b/ext/reactphysics3d/include/reactphysics3d/components/Components.h index 34cdd4dcc..1e41be5e8 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/Components.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/Components.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -63,6 +63,9 @@ class Components { /// Size (in bytes) of a single component size_t mComponentDataSize; + /// Size (in bytes) to allocate to make sure we can offset the components array to keep alignment + size_t mAlignmentMarginSize; + /// Number of allocated components uint32 mNbAllocatedComponents; @@ -77,7 +80,7 @@ class Components { uint32 mDisabledStartIndex; /// Compute the index where we need to insert the new component - uint32 prepareAddComponent(bool isSleeping); + uint32 prepareAddComponent(bool isDisabled); /// Allocate memory for a given number of components virtual void allocate(uint32 nbComponentsToAllocate)=0; @@ -96,11 +99,14 @@ class Components { // -------------------- Methods -------------------- // /// Constructor - Components(MemoryAllocator& allocator, size_t componentDataSize); + Components(MemoryAllocator& allocator, size_t componentDataSize, size_t alignmentMarginSize); /// Destructor virtual ~Components(); + /// Initialize the components: + void init(); + /// Remove a component void removeComponent(Entity entity); @@ -137,7 +143,7 @@ RP3D_FORCE_INLINE bool Components::hasComponent(Entity entity) const { return mMapEntityToComponentIndex.containsKey(entity); } -// Return true if there is a component for a given entiy and if so set the entity index +// Return true if there is a component for a given entity and if so set the entity index RP3D_FORCE_INLINE bool Components::hasComponentGetIndex(Entity entity, uint32& entityIndex) const { auto it = mMapEntityToComponentIndex.find(entity); diff --git a/ext/reactphysics3d/include/reactphysics3d/components/FixedJointComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/FixedJointComponents.h index eedbeda2a..f7236dcff 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/FixedJointComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/FixedJointComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -131,7 +131,7 @@ class FixedJointComponents : public Components { virtual ~FixedJointComponents() override = default; /// Add a component - void addComponent(Entity jointEntity, bool isSleeping, const FixedJointComponent& component); + void addComponent(Entity jointEntity, bool isDisabled, const FixedJointComponent& component); /// Return a pointer to a given joint FixedJoint* getJoint(Entity jointEntity) const; diff --git a/ext/reactphysics3d/include/reactphysics3d/components/HingeJointComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/HingeJointComponents.h index b4ecbedbb..f8177e12a 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/HingeJointComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/HingeJointComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -202,7 +202,7 @@ class HingeJointComponents : public Components { virtual ~HingeJointComponents() override = default; /// Add a component - void addComponent(Entity jointEntity, bool isSleeping, const HingeJointComponent& component); + void addComponent(Entity jointEntity, bool isDisabled, const HingeJointComponent& component); /// Return a pointer to a given joint HingeJoint* getJoint(Entity jointEntity) const; diff --git a/ext/reactphysics3d/include/reactphysics3d/components/JointComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/JointComponents.h index f593d2701..a79a254b9 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/JointComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/JointComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -120,7 +120,7 @@ class JointComponents : public Components { virtual ~JointComponents() override = default; /// Add a component - void addComponent(Entity jointEntity, bool isSleeping, const JointComponent& component); + void addComponent(Entity jointEntity, bool isDisabled, const JointComponent& component); /// Return the entity of the first body of a joint Entity getBody1Entity(Entity jointEntity) const; diff --git a/ext/reactphysics3d/include/reactphysics3d/components/RigidBodyComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/RigidBodyComponents.h index afe114b63..2443c832e 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/RigidBodyComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/RigidBodyComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -32,6 +32,8 @@ #include #include #include +#include +#include // ReactPhysics3D namespace namespace reactphysics3d { @@ -196,7 +198,7 @@ class RigidBodyComponents : public Components { virtual ~RigidBodyComponents() override = default; /// Add a component - void addComponent(Entity bodyEntity, bool isSleeping, const RigidBodyComponent& component); + void addComponent(Entity bodyEntity, bool isDisabled, const RigidBodyComponent& component); /// Return a pointer to a rigid body RigidBody* getRigidBody(Entity bodyEntity); @@ -273,6 +275,9 @@ class RigidBodyComponents : public Components { /// Return the inverse world inertia tensor of an entity const Matrix3x3& getInertiaTensorWorldInverse(Entity bodyEntity); + /// Set the inverse world inertia tensor of an entity + void setInertiaTensorWorldInverse(Entity bodyEntity, const Matrix3x3& inertiaTensor); + /// Set the external force of an entity void setExternalForce(Entity bodyEntity, const Vector3& externalForce); @@ -372,6 +377,9 @@ class RigidBodyComponents : public Components { /// A an associated contact pairs into the contact pairs array of the body void addContacPair(Entity bodyEntity, uint32 contactPairIndex); + /// Remove all the contact pairs of a body + void removeAllContacPairs(Entity bodyEntity); + // -------------------- Friendship -------------------- // friend class PhysicsWorld; @@ -556,6 +564,14 @@ RP3D_FORCE_INLINE const Matrix3x3& RigidBodyComponents::getInertiaTensorWorldInv return mInverseInertiaTensorsWorld[mMapEntityToComponentIndex[bodyEntity]]; } +// Set the inverse world inertia tensor of an entity +RP3D_FORCE_INLINE void RigidBodyComponents::setInertiaTensorWorldInverse(Entity bodyEntity, const Matrix3x3& inertiaTensor) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mInverseInertiaTensorsWorld[mMapEntityToComponentIndex[bodyEntity]] = inertiaTensor; +} + // Set the external force of an entity RP3D_FORCE_INLINE void RigidBodyComponents::setExternalForce(Entity bodyEntity, const Vector3& externalForce) { @@ -846,6 +862,13 @@ RP3D_FORCE_INLINE void RigidBodyComponents::addContacPair(Entity bodyEntity, uin mContactPairs[mMapEntityToComponentIndex[bodyEntity]].add(contactPairIndex); } +// Remove all the contact pairs of a body +RP3D_FORCE_INLINE void RigidBodyComponents::removeAllContacPairs(Entity bodyEntity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + mContactPairs[mMapEntityToComponentIndex[bodyEntity]].clear(); +} + } #endif diff --git a/ext/reactphysics3d/include/reactphysics3d/components/SliderJointComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/SliderJointComponents.h index ff6842600..fecbed1b3 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/SliderJointComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/SliderJointComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -217,7 +217,7 @@ class SliderJointComponents : public Components { virtual ~SliderJointComponents() override = default; /// Add a component - void addComponent(Entity jointEntity, bool isSleeping, const SliderJointComponent& component); + void addComponent(Entity jointEntity, bool isDisabled, const SliderJointComponent& component); /// Return a pointer to a given joint SliderJoint* getJoint(Entity jointEntity) const; diff --git a/ext/reactphysics3d/include/reactphysics3d/components/TransformComponents.h b/ext/reactphysics3d/include/reactphysics3d/components/TransformComponents.h index d4dc576b2..d9a1e7d37 100644 --- a/ext/reactphysics3d/include/reactphysics3d/components/TransformComponents.h +++ b/ext/reactphysics3d/include/reactphysics3d/components/TransformComponents.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -93,7 +93,7 @@ class TransformComponents : public Components { virtual ~TransformComponents() override = default; /// Add a component - void addComponent(Entity bodyEntity, bool isSleeping, const TransformComponent& component); + void addComponent(Entity bodyEntity, bool isDisabled, const TransformComponent& component); /// Return the transform of an entity Transform& getTransform(Entity bodyEntity) const; diff --git a/ext/reactphysics3d/include/reactphysics3d/configuration.h b/ext/reactphysics3d/include/reactphysics3d/configuration.h index 6281c93cc..1481c8497 100644 --- a/ext/reactphysics3d/include/reactphysics3d/configuration.h +++ b/ext/reactphysics3d/include/reactphysics3d/configuration.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -33,9 +33,23 @@ #include #include #include +#include #include #include +// OS +#if defined(_WIN32) || defined(_WIN64) + #define RP3D_PLATFORM_WINDOWS +#elif defined(__APPLE__) + #define RP3D_PLATFORM_APPLE +#elif defined(__ANDROID__) + #define RP3D_PLATFORM_ANDROID +#elif defined(__linux__) + #define RP3D_PLATFORM_LINUX +#else + #define RP3D_PLATFORM_UNKNOWN +#endif + // Compilers #if defined(_MSC_VER) #define RP3D_COMPILER_VISUAL_STUDIO @@ -76,6 +90,7 @@ using int64 = std::int64_t; using uint64 = std::uint64_t; struct Entity; + using bodypair = Pair; // ------------------- Enumerations ------------------- // @@ -119,19 +134,22 @@ constexpr decimal DYNAMIC_TREE_FAT_AABB_INFLATE_PERCENTAGE = decimal(0.08); constexpr uint8 NB_MAX_CONTACT_POINTS_IN_NARROWPHASE_INFO = 16; /// Maximum number of contact manifolds in an overlapping pair -constexpr uint8 NB_MAX_CONTACT_MANIFOLDS = 3; +constexpr uint8 NB_MAX_CONTACT_MANIFOLDS = 4; /// Maximum number of potential contact manifolds in an overlapping pair -constexpr uint8 NB_MAX_POTENTIAL_CONTACT_MANIFOLDS = 4 * NB_MAX_CONTACT_MANIFOLDS; +constexpr uint8 NB_MAX_POTENTIAL_CONTACT_MANIFOLDS = 64; /// Maximum number of contact points in potential contact manifold -constexpr uint16 NB_MAX_CONTACT_POINTS_IN_POTENTIAL_MANIFOLD = 256; +constexpr uint8 NB_MAX_CONTACT_POINTS_IN_POTENTIAL_MANIFOLD = 255; /// Distance threshold to consider that two contact points in a manifold are the same constexpr decimal SAME_CONTACT_POINT_DISTANCE_THRESHOLD = decimal(0.01); +/// Global alignment (in bytes) that all allocators must enforce +constexpr uint8 GLOBAL_ALIGNMENT = 16; + /// Current version of ReactPhysics3D -const std::string RP3D_VERSION = std::string("0.9.0"); +const std::string RP3D_VERSION = std::string("0.10.1"); } diff --git a/ext/reactphysics3d/include/reactphysics3d/constraint/BallAndSocketJoint.h b/ext/reactphysics3d/include/reactphysics3d/constraint/BallAndSocketJoint.h index 9f37ef3e6..915b1124f 100644 --- a/ext/reactphysics3d/include/reactphysics3d/constraint/BallAndSocketJoint.h +++ b/ext/reactphysics3d/include/reactphysics3d/constraint/BallAndSocketJoint.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/constraint/ContactPoint.h b/ext/reactphysics3d/include/reactphysics3d/constraint/ContactPoint.h index fcbcb1583..281ebb19e 100644 --- a/ext/reactphysics3d/include/reactphysics3d/constraint/ContactPoint.h +++ b/ext/reactphysics3d/include/reactphysics3d/constraint/ContactPoint.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -35,7 +35,7 @@ namespace reactphysics3d { // Declarations -class CollisionBody; +class Body; // Class ContactPoint /** diff --git a/ext/reactphysics3d/include/reactphysics3d/constraint/FixedJoint.h b/ext/reactphysics3d/include/reactphysics3d/constraint/FixedJoint.h index b020230e2..a3b2543af 100644 --- a/ext/reactphysics3d/include/reactphysics3d/constraint/FixedJoint.h +++ b/ext/reactphysics3d/include/reactphysics3d/constraint/FixedJoint.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/constraint/HingeJoint.h b/ext/reactphysics3d/include/reactphysics3d/constraint/HingeJoint.h index 8c1e8c452..71b4b249b 100644 --- a/ext/reactphysics3d/include/reactphysics3d/constraint/HingeJoint.h +++ b/ext/reactphysics3d/include/reactphysics3d/constraint/HingeJoint.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/constraint/Joint.h b/ext/reactphysics3d/include/reactphysics3d/constraint/Joint.h index 0e2c62f10..77e5a7481 100644 --- a/ext/reactphysics3d/include/reactphysics3d/constraint/Joint.h +++ b/ext/reactphysics3d/include/reactphysics3d/constraint/Joint.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/constraint/SliderJoint.h b/ext/reactphysics3d/include/reactphysics3d/constraint/SliderJoint.h index a0eb557bf..142eeaa36 100644 --- a/ext/reactphysics3d/include/reactphysics3d/constraint/SliderJoint.h +++ b/ext/reactphysics3d/include/reactphysics3d/constraint/SliderJoint.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/Array.h b/ext/reactphysics3d/include/reactphysics3d/containers/Array.h index 89801ca13..b7f064558 100755 --- a/ext/reactphysics3d/include/reactphysics3d/containers/Array.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/Array.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -95,19 +95,19 @@ class Array { /// Deferencable reference operator*() { - assert(mCurrentIndex >= 0 && mCurrentIndex < mSize); + assert(mCurrentIndex < mSize); return mBuffer[mCurrentIndex]; } /// Const Deferencable const_reference operator*() const { - assert(mCurrentIndex >= 0 && mCurrentIndex < mSize); + assert(mCurrentIndex < mSize); return mBuffer[mCurrentIndex]; } /// Deferencable const_pointer operator->() const { - assert(mCurrentIndex >= 0 && mCurrentIndex < mSize); + assert(mCurrentIndex < mSize); return &(mBuffer[mCurrentIndex]); } @@ -190,7 +190,7 @@ class Array { /// Equality operator (it == end()) bool operator==(const Iterator& iterator) const { - assert(mCurrentIndex >= 0 && mCurrentIndex <= mSize); + assert(mCurrentIndex <= mSize); // If both iterators points to the end of the array if (mCurrentIndex == mSize && iterator.mCurrentIndex == iterator.mSize) { @@ -251,6 +251,9 @@ class Array { if (capacity <= mCapacity) return; + // Make sure capacity is an integral multiple of alignment + capacity = std::ceil(capacity / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Allocate memory for the new array void* newMemory = mAllocator.allocate(capacity * sizeof(T)); T* destination = static_cast(newMemory); @@ -283,7 +286,7 @@ class Array { // If we need to allocate more memory if (mSize == mCapacity) { - reserve(mCapacity == 0 ? 1 : mCapacity * 2); + reserve(mCapacity == 0 ? GLOBAL_ALIGNMENT : mCapacity * 2); } // Use the constructor to construct the element @@ -298,7 +301,7 @@ class Array { // If we need to allocate more memory if (mSize == mCapacity) { - reserve(mCapacity == 0 ? 1 : mCapacity * 2); + reserve(mCapacity == 0 ? GLOBAL_ALIGNMENT : mCapacity * 2); } // Construct the element directly at its location in the array @@ -433,13 +436,13 @@ class Array { /// Overloaded index operator T& operator[](const uint64 index) { - assert(index >= 0 && index < mSize); + assert(index < mSize); return mBuffer[index]; } /// Overloaded const index operator const T& operator[](const uint64 index) const { - assert(index >= 0 && index < mSize); + assert(index < mSize); return mBuffer[index]; } diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/Deque.h b/ext/reactphysics3d/include/reactphysics3d/containers/Deque.h index 8c4f55e2c..2a7977b14 100644 --- a/ext/reactphysics3d/include/reactphysics3d/containers/Deque.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/Deque.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -52,36 +52,19 @@ class Deque { private: - // -------------------- Constants -------------------- // - - /// Number of items in a chunk - const uint8 CHUNK_NB_ITEMS = 17; - - /// First item index in a chunk - const uint8 CHUNK_FIRST_ITEM_INDEX = CHUNK_NB_ITEMS / 2; - // -------------------- Attributes -------------------- // - /// Array of chunks - T** mChunks; + /// Buffer of elements + T* mBuffer; /// Number of current elements in the deque uint64 mSize; - /// Number of chunks - uint64 mNbChunks; - - /// Index of the chunk with the first element of the deque - uint64 mFirstChunkIndex; + /// Capacity + uint64 mCapacity; - /// Index of the chunk with the last element of the deque - uint64 mLastChunkIndex; - - /// Index of the first element in the first chunk - uint8 mFirstItemIndex; - - /// Index of the last element in the last chunk - uint8 mLastItemIndex; + /// Index in the buffer of the first item of the deque + uint64 mFirstItemIndex; /// Memory allocator MemoryAllocator& mAllocator; @@ -89,87 +72,52 @@ class Deque { // -------------------- Methods -------------------- // /// Return a reference to an item at the given virtual index in range [0; mSize-1] - T& getItem(uint64 virtualIndex) const { - - // If the virtual index is valid - if (virtualIndex < mSize) { + T& getItem(uint64 index) const { - uint64 chunkIndex = mFirstChunkIndex; - uint64 itemIndex = mFirstItemIndex; + // Ensure the virtual index is valid + assert(index < mSize); - const uint64 nbItemsFirstChunk = CHUNK_NB_ITEMS - mFirstItemIndex; - if (virtualIndex < nbItemsFirstChunk) { - itemIndex += virtualIndex; - } - else { - - virtualIndex -= nbItemsFirstChunk; - chunkIndex++; - - chunkIndex += virtualIndex / CHUNK_NB_ITEMS; - itemIndex = virtualIndex % CHUNK_NB_ITEMS; - } - - return mChunks[chunkIndex][itemIndex]; - } - else { - assert(false); - } + return mBuffer[mFirstItemIndex + index]; } /// Add more chunks - void expandChunks(uint64 atLeastNbChunks = 0) { + void reserve(uint64 capacity) { - // If it is not necessary to expand the chunks - if (atLeastNbChunks > 0 && atLeastNbChunks <= mNbChunks) { - return; - } + if (capacity <= mCapacity) return; - uint64 newNbChunks = mNbChunks == 0 ? 3 : 2 * mNbChunks - 1; - if (atLeastNbChunks > 0 && newNbChunks < atLeastNbChunks) { - newNbChunks = uint64(atLeastNbChunks / 2) * 2 + 1; - } - const uint64 halfNbChunksToAdd = mNbChunks == 0 ? 1 : (mNbChunks - 1) / 2; + // Make sure capacity is an integral multiple of alignment + capacity = std::ceil(capacity / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; - // Allocate memory for the new array of pointers to chunk - void* newMemory = mAllocator.allocate(newNbChunks * sizeof(T*)); - assert(newMemory != nullptr); - T** newChunks = static_cast(newMemory); + // Allocate memory for the new array + void* newMemory = mAllocator.allocate(capacity * sizeof(T)); + T* destination = static_cast(newMemory); - // If chunks have already been allocated - if (mNbChunks > 0) { + const uint64 newStartIndex = capacity / 2 - 1; - // Copy the pointers to the previous chunks to the new allocated memory location - std::uninitialized_copy(mChunks, mChunks + mNbChunks, newChunks + halfNbChunksToAdd); + if (mBuffer != nullptr) { - // Release the previously allocated memory - mAllocator.release(mChunks, mNbChunks * sizeof(T*)); - } + if (mSize > 0) { - mChunks = newChunks; + // Copy the elements to the new allocated memory location + std::uninitialized_copy(mBuffer + mFirstItemIndex, mBuffer + mFirstItemIndex + mSize, destination + newStartIndex); - // If we need to allocate the first chunk (in the middle of the chunks array) - if (mNbChunks == 0) { - mChunks[newNbChunks / 2] = static_cast(mAllocator.allocate(sizeof(T) * CHUNK_NB_ITEMS)); - assert(mChunks[newNbChunks / 2] != nullptr); - } - - mNbChunks = newNbChunks; + // Destruct the previous items + for (uint64 i=0; i < mSize; i++) { + mBuffer[mFirstItemIndex + i].~T(); + } + } - // Allocate memory for each new chunk - for (uint64 i=0; i < halfNbChunksToAdd; i++) { + // Release the previously allocated memory + mAllocator.release(mBuffer, mCapacity * sizeof(T)); + } - // Allocate memory for the new chunk - mChunks[i] = static_cast(mAllocator.allocate(sizeof(T) * CHUNK_NB_ITEMS)); - assert(mChunks[i] != nullptr); + mBuffer = destination; + assert(mBuffer != nullptr); - mChunks[mNbChunks - 1 - i] = static_cast(mAllocator.allocate(sizeof(T) * CHUNK_NB_ITEMS)); - assert(mChunks[mNbChunks - 1 -i] != nullptr); - } + mCapacity = capacity; // Update the first and last chunk index - mFirstChunkIndex += halfNbChunksToAdd; - mLastChunkIndex += halfNbChunksToAdd; + mFirstItemIndex = newStartIndex; } public: @@ -197,7 +145,7 @@ class Deque { using iterator_category = std::random_access_iterator_tag; /// Constructor - Iterator(const Deque* deque, uint64 virtualIndex) : mVirtualIndex(virtualIndex), mDeque(deque) { + Iterator(const Deque* deque, uint64 index) : mVirtualIndex(index), mDeque(deque) { } @@ -318,35 +266,28 @@ class Deque { /// Constructor Deque(MemoryAllocator& allocator) - : mChunks(nullptr), mSize(0), mNbChunks(0), mFirstChunkIndex(1), - mLastChunkIndex(1), mFirstItemIndex(CHUNK_FIRST_ITEM_INDEX), - mLastItemIndex(CHUNK_FIRST_ITEM_INDEX), mAllocator(allocator) { + : mBuffer(nullptr), mSize(0), mCapacity(0), mFirstItemIndex(0), mAllocator(allocator) { - // Allocate memory for the chunks array - expandChunks(); } /// Copy constructor Deque(const Deque& deque) - : mSize(0), mNbChunks(0), mFirstChunkIndex(1), - mLastChunkIndex(1), mFirstItemIndex(CHUNK_FIRST_ITEM_INDEX), - mLastItemIndex(CHUNK_FIRST_ITEM_INDEX), mAllocator(deque.mAllocator) { + : mBuffer(nullptr), mSize(0), mCapacity(0), mFirstItemIndex(deque.mFirstItemIndex), + mAllocator(deque.mAllocator) { - // Allocate memory for the array of chunks - expandChunks(deque.mNbChunks); + // Allocate memory + reserve(deque.mCapacity); if (deque.mSize > 0) { - const uint64 dequeHalfSize1 = std::ceil(deque.mSize / 2.0f); - const uint64 dequeHalfSize2 = deque.mSize - dequeHalfSize1; - // Add the items into the deque - for(uint64 i=0; i < dequeHalfSize1; i++) { - addFront(deque[dequeHalfSize1 - 1 - i]); - } - for(uint64 i=0; i < dequeHalfSize2; i++) { - addBack(deque[dequeHalfSize1 + i]); + for(uint64 i=0; i < deque.mSize; i++) { + + // Construct the element at its location in the buffer + new (static_cast(&(mBuffer[deque.mFirstItemIndex + i]))) T(deque.mBuffer[deque.mFirstItemIndex + i]); } + + mSize = deque.mSize; } } @@ -355,76 +296,58 @@ class Deque { clear(); - // Release each chunk - for (uint64 i=0; i < mNbChunks; i++) { + if (mCapacity > 0) { - mAllocator.release(mChunks[i], sizeof(T) * CHUNK_NB_ITEMS); + assert(mBuffer != nullptr); + + // Release the chunks array + mAllocator.release(mBuffer, sizeof(T) * mCapacity); } - // Release the chunks array - mAllocator.release(mChunks, sizeof(T*) * mNbChunks); + mCapacity = 0; + mBuffer = nullptr; } /// Add an element at the end of the deque void addBack(const T& element) { // If we need to add the item in a another chunk - if (mLastItemIndex == CHUNK_NB_ITEMS - 1) { - - // If we need to add more chunks - if (mLastChunkIndex == mNbChunks - 1) { + if (mFirstItemIndex + mSize >= mCapacity) { - // Add more chunks - expandChunks(); - } - - mLastItemIndex = 0; - mLastChunkIndex++; - } - else if (mSize != 0) { - mLastItemIndex++; + reserve(mCapacity == 0 ? GLOBAL_ALIGNMENT : mCapacity * 2); } + assert(mFirstItemIndex + mSize < mCapacity); + // Construct the element at its location in the chunk - new (static_cast(&(mChunks[mLastChunkIndex][mLastItemIndex]))) T(element); + new (static_cast(&(mBuffer[mFirstItemIndex + mSize]))) T(element); mSize++; - assert(mFirstChunkIndex >= 0 && mLastChunkIndex < mNbChunks); - assert(mFirstItemIndex >= 0 && mFirstItemIndex < CHUNK_NB_ITEMS); - assert(mLastItemIndex >= 0 && mLastItemIndex < CHUNK_NB_ITEMS); - assert(mFirstChunkIndex <= mLastChunkIndex); + assert(mFirstItemIndex + mSize <= mCapacity); + assert(mSize <= mCapacity); } /// Add an element at the front of the deque void addFront(const T& element) { - // If we need to add the item in another chunk + // If we need to add the item in a another chunk if (mFirstItemIndex == 0) { - // If we need to add more chunks - if (mFirstChunkIndex == 0) { + reserve(mCapacity == 0 ? GLOBAL_ALIGNMENT : mCapacity * 2); + } - // Add more chunks - expandChunks(); - } + assert(mFirstItemIndex > 0); - mFirstItemIndex = CHUNK_NB_ITEMS - 1; - mFirstChunkIndex--; - } - else if (mSize != 0) { - mFirstItemIndex--; - } + mFirstItemIndex--; // Construct the element at its location in the chunk - new (static_cast(&(mChunks[mFirstChunkIndex][mFirstItemIndex]))) T(element); + new (static_cast(&(mBuffer[mFirstItemIndex]))) T(element); mSize++; - assert(mFirstChunkIndex >= 0 && mLastChunkIndex < mNbChunks); - assert(mFirstItemIndex >= 0 && mFirstItemIndex < CHUNK_NB_ITEMS); - assert(mLastItemIndex >= 0 && mLastItemIndex < CHUNK_NB_ITEMS); - assert(mFirstChunkIndex <= mLastChunkIndex); + assert(mFirstItemIndex + mSize <= mCapacity); + assert(mSize <= mCapacity); } /// Remove the first element of the deque @@ -433,28 +356,17 @@ class Deque { if (mSize > 0) { // Call the destructor of the first element - mChunks[mFirstChunkIndex][mFirstItemIndex].~T(); + mBuffer[mFirstItemIndex].~T(); mSize--; + mFirstItemIndex++; if (mSize == 0) { - mFirstChunkIndex = mNbChunks / 2; - mFirstItemIndex = CHUNK_FIRST_ITEM_INDEX; - mLastChunkIndex = mFirstChunkIndex; - mLastItemIndex = CHUNK_FIRST_ITEM_INDEX; - } - else if (mFirstItemIndex == CHUNK_NB_ITEMS - 1){ - mFirstChunkIndex++; - mFirstItemIndex = 0; - } - else { - mFirstItemIndex++; + mFirstItemIndex = mCapacity / 2 - 1; } - assert(mFirstChunkIndex >= 0 && mLastChunkIndex < mNbChunks); - assert(mFirstItemIndex >= 0 && mFirstItemIndex < CHUNK_NB_ITEMS); - assert(mLastItemIndex >= 0 && mLastItemIndex < CHUNK_NB_ITEMS); - assert(mFirstChunkIndex <= mLastChunkIndex); + assert(mFirstItemIndex + mSize <= mCapacity); + assert(mSize <= mCapacity); } } @@ -464,41 +376,29 @@ class Deque { if (mSize > 0) { // Call the destructor of the last element - mChunks[mLastChunkIndex][mLastItemIndex].~T(); + mBuffer[mFirstItemIndex + mSize - 1].~T(); mSize--; if (mSize == 0) { - mFirstChunkIndex = mNbChunks / 2; - mFirstItemIndex = CHUNK_FIRST_ITEM_INDEX; - mLastChunkIndex = mFirstChunkIndex; - mLastItemIndex = CHUNK_FIRST_ITEM_INDEX; - } - else if (mLastItemIndex == 0){ - mLastChunkIndex--; - mLastItemIndex = CHUNK_NB_ITEMS - 1; - } - else { - mLastItemIndex--; + mFirstItemIndex = mCapacity / 2 - 1; } - assert(mFirstChunkIndex >= 0 && mLastChunkIndex < mNbChunks); - assert(mFirstItemIndex >= 0 && mFirstItemIndex < CHUNK_NB_ITEMS); - assert(mLastItemIndex >= 0 && mLastItemIndex < CHUNK_NB_ITEMS); - assert(mFirstChunkIndex <= mLastChunkIndex); + assert(mFirstItemIndex + mSize <= mCapacity); + assert(mSize <= mCapacity); } } /// Return a reference to the first item of the deque const T& getFront() const { assert(mSize > 0); - return mChunks[mFirstChunkIndex][mFirstItemIndex]; + return mBuffer[mFirstItemIndex]; } /// Return a reference to the last item of the deque const T& getBack() const { assert(mSize > 0); - return mChunks[mLastChunkIndex][mLastItemIndex]; + return mBuffer[mFirstItemIndex + mSize - 1]; } /// Clear the elements of the deque @@ -512,11 +412,7 @@ class Deque { } mSize = 0; - - mFirstChunkIndex = mNbChunks / 2; - mLastChunkIndex = mFirstChunkIndex; - mFirstItemIndex = CHUNK_FIRST_ITEM_INDEX; - mLastItemIndex = CHUNK_FIRST_ITEM_INDEX; + mFirstItemIndex = mCapacity / 2 - 1; } } @@ -565,27 +461,25 @@ class Deque { // Clear all the elements clear(); - if (deque.mSize > 0) { - - // Number of used chunks - const uint64 nbUsedChunks = deque.mLastChunkIndex - deque.mFirstChunkIndex + 1; - - // Expand the chunk if necessary - expandChunks(nbUsedChunks); + reserve(deque.mCapacity); - const uint64 dequeHalfSize1 = std::ceil(deque.mSize / 2.0f); - const uint64 dequeHalfSize2 = deque.mSize - dequeHalfSize1; + if (deque.mSize > 0) { // Add the items into the deque - for(uint64 i=0; i < dequeHalfSize1; i++) { - addFront(deque[dequeHalfSize1 - 1 - i]); - } - for(uint64 i=0; i < dequeHalfSize2; i++) { - addBack(deque[dequeHalfSize1 + i]); + for(uint64 i=0; i < deque.mSize; i++) { + + // Construct the element at its location in the buffer + new (static_cast(&(mBuffer[mFirstItemIndex + i]))) T(deque.mBuffer[deque.mFirstItemIndex + i]); } + } + + mSize = deque.mSize; } + assert(mFirstItemIndex + mSize <= mCapacity); + assert(mSize <= mCapacity); + return *this; } diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/LinkedList.h b/ext/reactphysics3d/include/reactphysics3d/containers/LinkedList.h index bf6a3066a..391073819 100644 --- a/ext/reactphysics3d/include/reactphysics3d/containers/LinkedList.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/LinkedList.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -66,6 +66,9 @@ class LinkedList { /// Memory allocator used to allocate the list elements MemoryAllocator& mAllocator; + /// Size to allocate for a single element + size_t mElementAllocationSize; + public: // -------------------- Methods -------------------- // @@ -73,6 +76,8 @@ class LinkedList { /// Constructor LinkedList(MemoryAllocator& allocator) : mListHead(nullptr), mAllocator(allocator) { + // Make sure capacity is an integral multiple of alignment + mElementAllocationSize = std::ceil(sizeof(ListElement) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; } /// Destructor @@ -88,7 +93,6 @@ class LinkedList { /// Remove all the elements of the list void reset(); - }; // Return the first element of the list @@ -100,7 +104,8 @@ RP3D_FORCE_INLINE typename LinkedList::ListElement* LinkedList::getListHea // Insert an element at the beginning of the linked list template RP3D_FORCE_INLINE void LinkedList::insert(const T& data) { - ListElement* element = new (mAllocator.allocate(sizeof(ListElement))) ListElement(data, mListHead); + + ListElement* element = new (mAllocator.allocate(mElementAllocationSize)) ListElement(data, mListHead); mListHead = element; } @@ -112,7 +117,7 @@ RP3D_FORCE_INLINE void LinkedList::reset() { ListElement* element = mListHead; while (element != nullptr) { ListElement* nextElement = element->next; - mAllocator.release(element, sizeof(ListElement)); + mAllocator.release(element, mElementAllocationSize); element = nextElement; } diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/Map.h b/ext/reactphysics3d/include/reactphysics3d/containers/Map.h index aadf9fa6e..4aaee4b09 100755 --- a/ext/reactphysics3d/include/reactphysics3d/containers/Map.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/Map.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -276,8 +276,12 @@ class Map { uint64* newBuckets = static_cast(mAllocator.allocate(capacity * sizeof(uint64))); // Allocate memory for the entries - const uint64 nbAllocatedEntries = static_cast(capacity * double(DEFAULT_LOAD_FACTOR)); + uint64 nbAllocatedEntries = static_cast(capacity * double(DEFAULT_LOAD_FACTOR)); assert(nbAllocatedEntries > 0); + + // Make sure capacity is an integral multiple of alignment + nbAllocatedEntries = std::ceil(nbAllocatedEntries / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + Pair* newEntries = static_cast*>(mAllocator.allocate(nbAllocatedEntries * sizeof(Pair))); uint64* newNextEntries = static_cast(mAllocator.allocate(nbAllocatedEntries * sizeof(uint64))); @@ -383,7 +387,6 @@ class Map { } else { assert(false); - throw std::runtime_error("The key and value pair already exists in the map"); } } } @@ -567,10 +570,7 @@ class Map { const uint64 entry = findEntry(key); - if (entry == INVALID_INDEX) { - assert(false); - throw std::runtime_error("No item with given key has been found in the map"); - } + assert(entry != INVALID_INDEX); return mEntries[entry].second; } @@ -580,10 +580,7 @@ class Map { const uint64 entry = findEntry(key); - if (entry == INVALID_INDEX) { - assert(false); - throw std::runtime_error("No item with given key has been found in the map"); - } + assert(entry != INVALID_INDEX); return mEntries[entry].second; } diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/Pair.h b/ext/reactphysics3d/include/reactphysics3d/containers/Pair.h index b69f465d9..ee26b1a5a 100644 --- a/ext/reactphysics3d/include/reactphysics3d/containers/Pair.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/Pair.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -27,8 +27,6 @@ #define REACTPHYSICS3D_PAIR_H // Libraries -#include -#include #include #include #include @@ -72,9 +70,9 @@ class Pair { } -// Hash function for a reactphysics3d Pair namespace std { + // Hash function for a reactphysics3d Pair template struct hash> { size_t operator()(const reactphysics3d::Pair& pair) const { diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/Set.h b/ext/reactphysics3d/include/reactphysics3d/containers/Set.h index 34129ffb5..ea894ab67 100755 --- a/ext/reactphysics3d/include/reactphysics3d/containers/Set.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/Set.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -275,8 +275,12 @@ class Set { uint64* newBuckets = static_cast(mAllocator.allocate(capacity * sizeof(uint64))); // Allocate memory for the entries - const uint64 nbAllocatedEntries = static_cast(capacity * double(DEFAULT_LOAD_FACTOR)); + uint64 nbAllocatedEntries = static_cast(capacity * double(DEFAULT_LOAD_FACTOR)); assert(nbAllocatedEntries > 0); + + // Make sure capacity is an integral multiple of alignment + nbAllocatedEntries = std::ceil(nbAllocatedEntries / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + V* newEntries = static_cast(mAllocator.allocate(nbAllocatedEntries * sizeof(V))); uint64* newNextEntries = static_cast(mAllocator.allocate(nbAllocatedEntries * sizeof(uint64))); @@ -470,7 +474,7 @@ class Set { /// Return an array with all the values of the set Array toArray(MemoryAllocator& arrayAllocator) const { - Array array(arrayAllocator); + Array array(arrayAllocator, size()); for (auto it = begin(); it != end(); ++it) { array.add(*it); diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/Stack.h b/ext/reactphysics3d/include/reactphysics3d/containers/Stack.h index 2919a7b0e..cdbe26ffa 100644 --- a/ext/reactphysics3d/include/reactphysics3d/containers/Stack.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/Stack.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -61,6 +61,9 @@ class Stack { /// Allocate more memory void allocate(uint64 capacity) { + // Make sure capacity is an integral multiple of alignment + capacity = std::ceil(capacity / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + T* newArray = static_cast(mAllocator.allocate(capacity * sizeof(T))); assert(newArray != nullptr); @@ -168,6 +171,14 @@ class Stack { return item; } + /// Return the top element of the stack + T& top() { + + assert(mNbElements > 0); + + return mArray[mNbElements-1]; + } + /// Return the number of items in the stack uint64 size() const { return mNbElements; diff --git a/ext/reactphysics3d/include/reactphysics3d/containers/containers_common.h b/ext/reactphysics3d/include/reactphysics3d/containers/containers_common.h index 74da7cf12..e8ae38b40 100644 --- a/ext/reactphysics3d/include/reactphysics3d/containers/containers_common.h +++ b/ext/reactphysics3d/include/reactphysics3d/containers/containers_common.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -29,7 +29,6 @@ // Libraries #include #include -#include namespace reactphysics3d { diff --git a/ext/reactphysics3d/include/reactphysics3d/decimal.h b/ext/reactphysics3d/include/reactphysics3d/decimal.h index c501fc3fb..ef15a5ab6 100644 --- a/ext/reactphysics3d/include/reactphysics3d/decimal.h +++ b/ext/reactphysics3d/include/reactphysics3d/decimal.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/Entity.h b/ext/reactphysics3d/include/reactphysics3d/engine/Entity.h index 9b4181c4e..cf4535606 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/Entity.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/Entity.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -125,9 +125,9 @@ RP3D_FORCE_INLINE bool Entity::operator!=(const Entity& entity) const { } -// Hash function for a reactphysics3d Entity namespace std { + // Hash function for a reactphysics3d Entity template <> struct hash { size_t operator()(const reactphysics3d::Entity& entity) const { diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/EntityManager.h b/ext/reactphysics3d/include/reactphysics3d/engine/EntityManager.h index 35a13fcd2..1dc21d622 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/EntityManager.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/EntityManager.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/EventListener.h b/ext/reactphysics3d/include/reactphysics3d/engine/EventListener.h index 7802e892b..f4a758a88 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/EventListener.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/EventListener.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/Island.h b/ext/reactphysics3d/include/reactphysics3d/engine/Island.h index dd145f93e..efcb08977 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/Island.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/Island.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/Islands.h b/ext/reactphysics3d/include/reactphysics3d/engine/Islands.h index 560e90aec..e70a29969 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/Islands.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/Islands.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/Material.h b/ext/reactphysics3d/include/reactphysics3d/engine/Material.h index 5a2a998f3..3445e182e 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/Material.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/Material.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -90,7 +90,7 @@ class Material { // ---------- Friendship ---------- // friend class Collider; - friend class CollisionBody; + friend class Body; friend class RigidBody; }; diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/OverlappingPairs.h b/ext/reactphysics3d/include/reactphysics3d/engine/OverlappingPairs.h index 03b728403..b1c0ed39b 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/OverlappingPairs.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/OverlappingPairs.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include @@ -85,7 +85,7 @@ struct LastFrameCollisionInfo { /// Constructor LastFrameCollisionInfo() - :isValid(false), isObsolete(false), wasColliding(false), wasUsingGJK(false), gjkSeparatingAxis(Vector3(0, 1, 0)), + :isValid(false), isObsolete(false), wasColliding(false), wasUsingGJK(false), wasUsingSAT(false), gjkSeparatingAxis(Vector3(0, 1, 0)), satIsAxisFacePolyhedron1(false), satIsAxisFacePolyhedron2(false), satMinAxisFaceIndex(0), satMinEdge1Index(0), satMinEdge2Index(0) { @@ -99,11 +99,20 @@ struct LastFrameCollisionInfo { * the two colliders start to overlap and is destroyed when they do not * overlap anymore. Each contains a contact manifold that * store all the contact points between the two bodies. + * + * Once the two bodies of an overlapping pair are disabled (sleeping or static), + * we put the overlapping pair to the array of disabled pairs. We keep it so that + * we can awake the other body if one body is destroyed and we can also correclty + * track the lost contacts between bodies even after they go to sleep. */ class OverlappingPairs { public: + // Struct OverlappingPair + /** + * A base overlapping pair + */ struct OverlappingPair { /// Ids of the convex vs convex pairs @@ -135,12 +144,15 @@ class OverlappingPairs { /// True if the colliders of the overlapping pair are colliding in the current frame bool collidingInCurrentFrame; + /// True if at least one body of the pair is awake or not static + bool isEnabled; + /// Constructor OverlappingPair(uint64 pairId, int32 broadPhaseId1, int32 broadPhaseId2, Entity collider1, Entity collider2, - NarrowPhaseAlgorithmType narrowPhaseAlgorithmType) + NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, bool isEnabled) : pairID(pairId), broadPhaseId1(broadPhaseId1), broadPhaseId2(broadPhaseId2), collider1(collider1) , collider2(collider2), needToTestOverlap(false), narrowPhaseAlgorithmType(narrowPhaseAlgorithmType), collidingInPreviousFrame(false), - collidingInCurrentFrame(false) { + collidingInCurrentFrame(false), isEnabled(isEnabled) { } @@ -148,7 +160,10 @@ class OverlappingPairs { virtual ~OverlappingPair() = default; }; - // Overlapping pair between two convex colliders + // Struct ConvexOverlappingPair + /** + * An overlapping pair between two convex colliders + */ struct ConvexOverlappingPair : public OverlappingPair { /// Temporal coherence collision data for each overlapping collision shapes of this pair. @@ -159,13 +174,16 @@ class OverlappingPairs { /// Constructor ConvexOverlappingPair(uint64 pairId, int32 broadPhaseId1, int32 broadPhaseId2, Entity collider1, Entity collider2, - NarrowPhaseAlgorithmType narrowPhaseAlgorithmType) - : OverlappingPair(pairId, broadPhaseId1, broadPhaseId2, collider1, collider2, narrowPhaseAlgorithmType) { + NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, bool isEnabled) + : OverlappingPair(pairId, broadPhaseId1, broadPhaseId2, collider1, collider2, narrowPhaseAlgorithmType, isEnabled) { } }; - // Overlapping pair between two a convex collider and a concave collider + // Struct ConvexOverlappingPair + /** + * An overlapping pair between a convex collider and a concave collider + */ struct ConcaveOverlappingPair : public OverlappingPair { private: @@ -187,9 +205,10 @@ class OverlappingPairs { /// Constructor ConcaveOverlappingPair(uint64 pairId, int32 broadPhaseId1, int32 broadPhaseId2, Entity collider1, Entity collider2, NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, - bool isShape1Convex, MemoryAllocator& poolAllocator, MemoryAllocator& heapAllocator) - : OverlappingPair(pairId, broadPhaseId1, broadPhaseId2, collider1, collider2, narrowPhaseAlgorithmType), mPoolAllocator(&poolAllocator), - isShape1Convex(isShape1Convex), lastFrameCollisionInfos(heapAllocator, 16) { + bool isShape1Convex, MemoryAllocator& poolAllocator, MemoryAllocator& heapAllocator, bool isEnabled, + bool allocateLastFrameCollisionInfos = true) + : OverlappingPair(pairId, broadPhaseId1, broadPhaseId2, collider1, collider2, narrowPhaseAlgorithmType, isEnabled), mPoolAllocator(&poolAllocator), + isShape1Convex(isShape1Convex), lastFrameCollisionInfos(heapAllocator, allocateLastFrameCollisionInfos ? 16 : 0) { } @@ -225,7 +244,10 @@ class OverlappingPairs { auto it = lastFrameCollisionInfos.find(shapesId); if (it == lastFrameCollisionInfos.end()) { - LastFrameCollisionInfo* lastFrameInfo = new (mPoolAllocator->allocate(sizeof(LastFrameCollisionInfo))) LastFrameCollisionInfo(); + // Make sure capacity is an integral multiple of alignment + const size_t allocatedMemory = std::ceil(sizeof(LastFrameCollisionInfo) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + + LastFrameCollisionInfo* lastFrameInfo = new (mPoolAllocator->allocate(allocatedMemory)) LastFrameCollisionInfo(); // Add it into the map of collision infos lastFrameCollisionInfos.add(Pair(shapesId, lastFrameInfo)); @@ -253,8 +275,11 @@ class OverlappingPairs { // Call the destructor it->second->LastFrameCollisionInfo::~LastFrameCollisionInfo(); + // Make sure capacity is an integral multiple of alignment + const size_t allocatedMemory = std::ceil(sizeof(LastFrameCollisionInfo) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Release memory - mPoolAllocator->release(it->second, sizeof(LastFrameCollisionInfo)); + mPoolAllocator->release(it->second, allocatedMemory); it = lastFrameCollisionInfos.remove(it); } @@ -285,17 +310,29 @@ class OverlappingPairs { /// Array of convex vs concave overlapping pairs Array mConcavePairs; - /// Map a pair id to the internal array index + /// Array of disabled convex overlapping pairs (pairs with both bodies disabled) + Array mDisabledConvexPairs; + + /// Array of disabled concave overlapping pairs (pairs with both bodies disabled) + Array mDisabledConcavePairs; + + /// Map a convex pair id to the internal array index Map mMapConvexPairIdToPairIndex; - /// Map a pair id to the internal array index + /// Map a concave pair id to the internal array index Map mMapConcavePairIdToPairIndex; + /// Map a disabled convex pair id to the internal array index + Map mMapDisabledConvexPairIdToPairIndex; + + /// Map a disable concave pair id to the internal array index + Map mMapDisabledConcavePairIdToPairIndex; + /// Reference to the colliders components ColliderComponents& mColliderComponents; - /// Reference to the collision body components - CollisionBodyComponents& mCollisionBodyComponents; + /// Reference to the body components + BodyComponents& mBodyComponents; /// Reference to the rigid bodies components RigidBodyComponents& mRigidBodyComponents; @@ -321,13 +358,19 @@ class OverlappingPairs { /// Swap two pairs in the array void swapPairs(uint64 index1, uint64 index2); + /// Remove a disabled convex overlapping pair + void removeDisabledConvexPairWithIndex(uint64 pairIndex, bool removeFromColliders); + + /// Remove a disabled concave overlapping pair + void removeDisabledConcavePairWithIndex(uint64 pairIndex, bool removeFromColliders); + public: // -------------------- Methods -------------------- // /// Constructor OverlappingPairs(MemoryManager& memoryManager, ColliderComponents& colliderComponents, - CollisionBodyComponents& collisionBodyComponents, + BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, Set& noCollisionPairs, CollisionDispatch& collisionDispatch); @@ -340,14 +383,38 @@ class OverlappingPairs { /// Deleted assignment operator OverlappingPairs& operator=(const OverlappingPairs& pair) = delete; + /// Enable an overlapping pair (because at least one body of the pair is awaken or not static anymore) + void enablePair(uint64 pairId); + + /// Disable an overlapping pair (because both bodies of the pair are disabled) + void disablePair(uint64 pairId); + + /// Enable a convex overlapping pair + void enableConvexPairWithIndex(uint64 pairIndex); + + /// Disable a convex overlapping pair (because both bodies of the pair are disabled) + void disableConvexPairWithIndex(uint64 pairIndex); + + /// Enable a concave overlapping pair + void enableConcavePairWithIndex(uint64 pairIndex); + + /// Disable a concave overlapping pair (because both bodies of the pair are disabled) + void disableConcavePairWithIndex(uint64 pairIndex); + + /// Return true if a given pair is disabled (both bodies of the pair are disabled) + bool isPairDisabled(uint64 pairId) const; + /// Add an overlapping pair uint64 addPair(uint32 collider1Index, uint32 collider2Index, bool isConvexVsConvex); - /// Remove a component at a given index + /// Remove an overlapping pair void removePair(uint64 pairId); - /// Remove a pair - void removePair(uint64 pairIndex, bool isConvexVsConvex); + /// Remove a convex pair at a given index + void removeConvexPairWithIndex(uint64 pairIndex, bool removeFromColliders = true); + + // Remove a concave pair at a given index + void removeConcavePairWithIndex(uint64 pairIndex, bool removeFromColliders = true); /// Delete all the obsolete last frame collision info void clearObsoleteLastFrameCollisionInfos(); @@ -413,10 +480,23 @@ RP3D_FORCE_INLINE OverlappingPairs::OverlappingPair* OverlappingPairs::getOverla if (it != mMapConcavePairIdToPairIndex.end()) { return &(mConcavePairs[static_cast(it->second)]); } + it = mMapDisabledConvexPairIdToPairIndex.find(pairId); + if (it != mMapDisabledConvexPairIdToPairIndex.end()) { + return &(mDisabledConvexPairs[static_cast(it->second)]); + } + it = mMapDisabledConcavePairIdToPairIndex.find(pairId); + if (it != mMapDisabledConcavePairIdToPairIndex.end()) { + return &(mDisabledConcavePairs[static_cast(it->second)]); + } return nullptr; } +// Return true if a given pair is disabled (both bodies of the pair are disabled) +RP3D_FORCE_INLINE bool OverlappingPairs::isPairDisabled(uint64 pairId) const { + return mMapDisabledConvexPairIdToPairIndex.containsKey(pairId) || mMapDisabledConcavePairIdToPairIndex.containsKey(pairId); +} + #ifdef IS_RP3D_PROFILING_ENABLED // Set the profiler diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsCommon.h b/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsCommon.h index 2505fb7d0..2c10f7723 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsCommon.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsCommon.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -36,11 +36,17 @@ #include #include #include +#include +#include #include +#include +#include /// ReactPhysics3D namespace namespace reactphysics3d { +class VertexArray; + // Class PhysicsCommon /** * This class is a singleton that needs to be instanciated once at the beginning. @@ -77,12 +83,15 @@ class PhysicsCommon { /// Set of height field shapes Set mHeightFieldShapes; - /// Set of polyhedron meshes - Set mPolyhedronMeshes; + /// Set of convex meshes + Set mConvexMeshes; /// Set of triangle meshes Set mTriangleMeshes; + /// Set of height-fields + Set mHeightFields; + /// Pointer to the current logger static Logger* mLogger; @@ -127,12 +136,15 @@ class PhysicsCommon { /// Delete a concave mesh shape void deleteConcaveMeshShape(ConcaveMeshShape* concaveMeshShape); - /// Delete a polyhedron mesh - void deletePolyhedronMesh(PolyhedronMesh* polyhedronMesh); + /// Delete a convex mesh + void deleteConvexMesh(ConvexMesh* convexMesh); /// Delete a triangle mesh void deleteTriangleMesh(TriangleMesh* triangleMesh); + /// Delete a height-field + void deleteHeightField(HeightField* heightField); + /// Delete a default logger void deleteDefaultLogger(DefaultLogger* logger); @@ -191,16 +203,19 @@ class PhysicsCommon { void destroyCapsuleShape(CapsuleShape* capsuleShape); /// Create and return a convex mesh shape - ConvexMeshShape* createConvexMeshShape(PolyhedronMesh* polyhedronMesh, const Vector3& scaling = Vector3(1,1,1)); + ConvexMeshShape* createConvexMeshShape(ConvexMesh* convexMesh, const Vector3& scaling = Vector3(1,1,1)); /// Destroy a convex mesh shape void destroyConvexMeshShape(ConvexMeshShape* convexMeshShape); + /// Create and return a height-field + HeightField* createHeightField(int nbGridColumns, int nbGridRows, const void* heightFieldData, + HeightField::HeightDataType dataType, std::vector& messages, + decimal integerHeightScale = 1.0f); + /// Create and return a height-field shape - HeightFieldShape* createHeightFieldShape(int nbGridColumns, int nbGridRows, decimal minHeight, decimal maxHeight, - const void* heightFieldData, HeightFieldShape::HeightDataType dataType, - int upAxis = 1, decimal integerHeightScale = 1.0f, - const Vector3& scaling = Vector3(1,1,1)); + HeightFieldShape* createHeightFieldShape(HeightField* heightField, + const Vector3& scaling = Vector3(1,1,1)); /// Destroy a height-field shape void destroyHeightFieldShape(HeightFieldShape* heightFieldShape); @@ -211,18 +226,24 @@ class PhysicsCommon { /// Destroy a concave mesh shape void destroyConcaveMeshShape(ConcaveMeshShape* concaveMeshShape); - /// Create a polyhedron mesh - PolyhedronMesh* createPolyhedronMesh(PolygonVertexArray* polygonVertexArray); + /// Create a convex mesh from a PolygonVertexArray describing vertices and faces + ConvexMesh* createConvexMesh(const PolygonVertexArray& polygonVertexArray, std::vector& messages); + + /// Create a convex mesh from an array of vertices (automatically computing the convex hull using QuickHull) + ConvexMesh* createConvexMesh(const VertexArray& vertexArray, std::vector& messages); - /// Destroy a polyhedron mesh - void destroyPolyhedronMesh(PolyhedronMesh* polyhedronMesh); + /// Destroy a convex mesh + void destroyConvexMesh(ConvexMesh* convexMesh); /// Create a triangle mesh - TriangleMesh* createTriangleMesh(); + TriangleMesh* createTriangleMesh(const TriangleVertexArray& triangleVertexArray, std::vector& messages); /// Destroy a triangle mesh void destroyTriangleMesh(TriangleMesh* triangleMesh); + /// Destroy a height-field + void destroyHeightField(HeightField* heightField); + /// Create and return a new default logger DefaultLogger* createDefaultLogger(); diff --git a/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsWorld.h b/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsWorld.h index 3462506d9..b1a3baea4 100644 --- a/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsWorld.h +++ b/ext/reactphysics3d/include/reactphysics3d/engine/PhysicsWorld.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -180,8 +180,11 @@ class PhysicsWorld { /// True if debug rendering is enabled bool mIsDebugRenderingEnabled; + /// True if the gravity force is on + bool mIsGravityEnabled; + /// Collision Body Components - CollisionBodyComponents mCollisionBodyComponents; + BodyComponents mBodyComponents; /// Rigid Body Components RigidBodyComponents mRigidBodyComponents; @@ -211,7 +214,7 @@ class PhysicsWorld { CollisionDetectionSystem mCollisionDetection; /// All the collision bodies of the world - Array mCollisionBodies; + Array mCollisionBodies; /// Pointer to an event listener object EventListener* mEventListener; @@ -257,9 +260,6 @@ class PhysicsWorld { /// All the rigid bodies of the physics world Array mRigidBodies; - /// True if the gravity force is on - bool mIsGravityEnabled; - /// Sleep linear velocity threshold decimal mSleepLinearVelocity; @@ -309,12 +309,6 @@ class PhysicsWorld { // -------------------- Methods -------------------- // - /// Create a collision body - CollisionBody* createCollisionBody(const Transform& transform); - - /// Destroy a collision body - void destroyCollisionBody(CollisionBody* collisionBody); - /// Get the collision dispatch configuration CollisionDispatch& getCollisionDispatch(); @@ -322,19 +316,19 @@ class PhysicsWorld { void raycast(const Ray& ray, RaycastCallback* raycastCallback, unsigned short raycastWithCategoryMaskBits = 0xFFFF) const; /// Return true if two bodies overlap (collide) - bool testOverlap(CollisionBody* body1, CollisionBody* body2); + bool testOverlap(Body* body1, Body* body2); /// Report all the bodies that overlap (collide) with the body in parameter - void testOverlap(CollisionBody* body, OverlapCallback& overlapCallback); + void testOverlap(Body* body, OverlapCallback& overlapCallback); /// Report all the bodies that overlap (collide) in the world void testOverlap(OverlapCallback& overlapCallback); /// Test collision and report contacts between two bodies. - void testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback); + void testCollision(Body* body1, Body* body2, CollisionCallback& callback); /// Test collision and report all the contacts involving the body in parameter - void testCollision(CollisionBody* body, CollisionCallback& callback); + void testCollision(Body* body, CollisionCallback& callback); /// Test collision and report contacts between each colliding bodies in the world void testCollision(CollisionCallback& callback); @@ -426,15 +420,6 @@ class PhysicsWorld { /// Set an event listener object to receive events callbacks. void setEventListener(EventListener* eventListener); - /// Return the number of CollisionBody in the physics world - uint32 getNbCollisionBodies() const; - - /// Return a constant pointer to a given CollisionBody of the world - const CollisionBody* getCollisionBody(uint32 index) const; - - /// Return a pointer to a given CollisionBody of the world - CollisionBody* getCollisionBody(uint32 index) ; - /// Return the number of RigidBody in the physics world uint32 getNbRigidBodies() const; @@ -463,7 +448,7 @@ class PhysicsWorld { // -------------------- Friendship -------------------- // friend class CollisionDetectionSystem; - friend class CollisionBody; + friend class Body; friend class Collider; friend class ConvexMeshShape; friend class CollisionCallback::ContactPair; @@ -514,7 +499,7 @@ RP3D_FORCE_INLINE void PhysicsWorld::raycast(const Ray& ray, * @param body2 Pointer to the second body to test * @param callback Pointer to the object with the callback method */ -RP3D_FORCE_INLINE void PhysicsWorld::testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback) { +RP3D_FORCE_INLINE void PhysicsWorld::testCollision(Body* body1, Body* body2, CollisionCallback& callback) { mCollisionDetection.testCollision(body1, body2, callback); } @@ -527,7 +512,7 @@ RP3D_FORCE_INLINE void PhysicsWorld::testCollision(CollisionBody* body1, Collisi * @param body Pointer to the body against which we need to test collision * @param callback Pointer to the object with the callback method to report contacts */ -RP3D_FORCE_INLINE void PhysicsWorld::testCollision(CollisionBody* body, CollisionCallback& callback) { +RP3D_FORCE_INLINE void PhysicsWorld::testCollision(Body* body, CollisionCallback& callback) { mCollisionDetection.testCollision(body, callback); } @@ -551,7 +536,7 @@ RP3D_FORCE_INLINE void PhysicsWorld::testCollision(CollisionCallback& callback) * @param body Pointer to the collision body to test overlap with * @param overlapCallback Pointer to the callback class to report overlap */ -RP3D_FORCE_INLINE void PhysicsWorld::testOverlap(CollisionBody* body, OverlapCallback& overlapCallback) { +RP3D_FORCE_INLINE void PhysicsWorld::testOverlap(Body* body, OverlapCallback& overlapCallback) { mCollisionDetection.testOverlap(body, overlapCallback); } @@ -679,15 +664,6 @@ RP3D_FORCE_INLINE void PhysicsWorld::setEventListener(EventListener* eventListen mEventListener = eventListener; } -// Return the number of CollisionBody in the physics world -/// Note that even if a RigidBody is also a collision body, this method does not return the rigid bodies -/** - * @return The number of collision bodies in the physics world - */ -RP3D_FORCE_INLINE uint32 PhysicsWorld::getNbCollisionBodies() const { - return static_cast(mCollisionBodies.size()); -} - // Return the number of RigidBody in the physics world /** * @return The number of rigid bodies in the physics world diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix2x2.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix2x2.h index a736d2a1c..641001279 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix2x2.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix2x2.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix3x3.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix3x3.h index 607bd996d..9ea7b803f 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix3x3.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/Matrix3x3.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/Quaternion.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/Quaternion.h index 5526d1680..0a6d178f8 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/Quaternion.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/Quaternion.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/Ray.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/Ray.h index 262fd3644..fcef0a5cd 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/Ray.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/Ray.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/Transform.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/Transform.h index 78fcbfc53..b2885b036 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/Transform.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/Transform.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector2.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector2.h index 93fb00feb..27cbd13aa 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector2.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector2.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector3.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector3.h index 61ed30cd7..80a472725 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector3.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/Vector3.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -154,6 +154,9 @@ struct Vector3 { /// Return the zero vector static Vector3 zero(); + /// Function to test if two vectors are (almost) equal + static bool approxEqual(const Vector3& vec1, const Vector3& vec2, decimal epsilon = MACHINE_EPSILON); + // -------------------- Friends -------------------- // friend Vector3 operator+(const Vector3& vector1, const Vector3& vector2); @@ -390,9 +393,9 @@ RP3D_FORCE_INLINE Vector3 Vector3::zero() { } // Function to test if two vectors are (almost) equal -RP3D_FORCE_INLINE bool approxEqual(const Vector3& vec1, const Vector3& vec2, decimal epsilon = MACHINE_EPSILON) { - return approxEqual(vec1.x, vec2.x, epsilon) && approxEqual(vec1.y, vec2.y, epsilon) && - approxEqual(vec1.z, vec2.z, epsilon); +RP3D_FORCE_INLINE bool Vector3::approxEqual(const Vector3& vec1, const Vector3& vec2, decimal epsilon) { + return reactphysics3d::approxEqual(vec1.x, vec2.x, epsilon) && reactphysics3d::approxEqual(vec1.y, vec2.y, epsilon) && + reactphysics3d::approxEqual(vec1.z, vec2.z, epsilon); } } diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics.h index 289e91ef4..93d44463c 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_common.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_common.h index 8f7419f06..b956af87b 100644 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_common.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_common.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_functions.h b/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_functions.h index d6f73c3cd..1c1aca7ae 100755 --- a/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_functions.h +++ b/ext/reactphysics3d/include/reactphysics3d/mathematics/mathematics_functions.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/memory/DefaultAllocator.h b/ext/reactphysics3d/include/reactphysics3d/memory/DefaultAllocator.h index ec2ef0aef..d4b1b9d22 100644 --- a/ext/reactphysics3d/include/reactphysics3d/memory/DefaultAllocator.h +++ b/ext/reactphysics3d/include/reactphysics3d/memory/DefaultAllocator.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -28,15 +28,20 @@ // Libraries #include +#include #include +#include #include +#include /// ReactPhysics3D namespace namespace reactphysics3d { // Class DefaultAllocator /** - * This class represents a default memory allocator that uses default malloc/free methods + * This class represents a default memory allocator that uses standard C++ functions + * to allocated 16-bytes aligned memory. + * */ class DefaultAllocator : public MemoryAllocator { @@ -49,15 +54,37 @@ class DefaultAllocator : public MemoryAllocator { DefaultAllocator& operator=(DefaultAllocator& allocator) = default; /// Allocate memory of a given size (in bytes) and return a pointer to the - /// allocated memory. + /// allocated memory. The returned allocated memory must be 16 bytes aligned. virtual void* allocate(size_t size) override { - return std::malloc(size); + assert(size % GLOBAL_ALIGNMENT == 0); + +// If compiler is Visual Studio +#ifdef RP3D_PLATFORM_WINDOWS + + // Visual Studio doesn't not support standard std:aligned_alloc() method from C++ 17 + return _aligned_malloc(size, GLOBAL_ALIGNMENT); +#else + + // Return 16-bytes aligned memory + void* address = nullptr; + posix_memalign(&address, GLOBAL_ALIGNMENT, size); + return address; +#endif } /// Release previously allocated memory. virtual void release(void* pointer, size_t /*size*/) override { - std::free(pointer); + + // If compiler is Visual Studio +#ifdef RP3D_COMPILER_VISUAL_STUDIO + + // Visual Studio doesn't not support standard std:aligned_alloc() method from c++ 17 + return _aligned_free(pointer); +#else + + return std::free(pointer); +#endif } }; diff --git a/ext/reactphysics3d/include/reactphysics3d/memory/HeapAllocator.h b/ext/reactphysics3d/include/reactphysics3d/memory/HeapAllocator.h index 940853fdb..3e504fa76 100644 --- a/ext/reactphysics3d/include/reactphysics3d/memory/HeapAllocator.h +++ b/ext/reactphysics3d/include/reactphysics3d/memory/HeapAllocator.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -57,26 +57,33 @@ class HeapAllocator : public MemoryAllocator { // -------------------- Attributes -------------------- // - /// Size in bytes of the allocated memory unit - size_t size; - - /// True if the memory unit is currently allocated - bool isAllocated; - /// Pointer to the previous memory unit MemoryUnitHeader* previousUnit; /// Pointer to the next memory unit MemoryUnitHeader* nextUnit; + /// Pointer to the previous free (not allocated) memory unit + MemoryUnitHeader* previousFreeUnit; + + /// Pointer to the next free (not allocated) memory unit + MemoryUnitHeader* nextFreeUnit; + + /// Size in bytes of the allocated memory unit + size_t size; + /// True if the next memory unit has been allocated with the same call to malloc() bool isNextContiguousMemory; + /// True if the memory unit is currently allocated + bool isAllocated = false; + // -------------------- Methods -------------------- // - MemoryUnitHeader(size_t size, MemoryUnitHeader* previousUnit, MemoryUnitHeader* nextUnit, bool isNextContiguousMemory) - : size(size), isAllocated(false), previousUnit(previousUnit), - nextUnit(nextUnit), isNextContiguousMemory(isNextContiguousMemory) { + MemoryUnitHeader(size_t size, MemoryUnitHeader* previousUnit, MemoryUnitHeader* nextUnit, + MemoryUnitHeader* previousFreeUnit, MemoryUnitHeader* nextFreeUnit, bool isNextContiguousMemory) + : previousUnit(previousUnit), nextUnit(nextUnit), previousFreeUnit(previousFreeUnit), nextFreeUnit(nextFreeUnit), size(size), + isNextContiguousMemory(isNextContiguousMemory) { assert(size > 0); } @@ -101,8 +108,8 @@ class HeapAllocator : public MemoryAllocator { /// Pointer to the first memory unit of the linked-list MemoryUnitHeader* mMemoryUnits; - /// Pointer to a cached free memory unit - MemoryUnitHeader* mCachedFreeUnit; + /// Pointer to the first item of the linked-list of free units + MemoryUnitHeader* mFreeUnits; #ifndef NDEBUG /// This variable is incremented by one when the allocate() method has been @@ -118,12 +125,21 @@ class HeapAllocator : public MemoryAllocator { /// left over space. The second unit is put into the free memory units void splitMemoryUnit(MemoryUnitHeader* unit, size_t size); - // Merge two contiguous memory units that are not allocated. + /// Add the unit from the linked-list of free units + void addToFreeUnits(MemoryUnitHeader* unit); + + /// Remove the unit from the linked-list of free units + void removeFromFreeUnits(MemoryUnitHeader* unit); + + /// Merge two contiguous memory units that are not allocated. void mergeUnits(MemoryUnitHeader* unit1, MemoryUnitHeader* unit2); /// Reserve more memory for the allocator void reserve(size_t sizeToAllocate); + /// Return the next aligned memory address + void* computeAlignedAddress(void* unalignedAddress); + public : // -------------------- Methods -------------------- // diff --git a/ext/reactphysics3d/include/reactphysics3d/memory/MemoryAllocator.h b/ext/reactphysics3d/include/reactphysics3d/memory/MemoryAllocator.h index 9111f3d7e..27ae26322 100644 --- a/ext/reactphysics3d/include/reactphysics3d/memory/MemoryAllocator.h +++ b/ext/reactphysics3d/include/reactphysics3d/memory/MemoryAllocator.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -28,6 +28,7 @@ // Libraries #include +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -50,11 +51,17 @@ class MemoryAllocator { MemoryAllocator& operator=(MemoryAllocator& allocator) = default; /// Allocate memory of a given size (in bytes) and return a pointer to the - /// allocated memory. + /// allocated memory. The return allocated memory must be 16 bytes aligned. virtual void* allocate(size_t size)=0; /// Release previously allocated memory. virtual void release(void* pointer, size_t size)=0; + + /// Given a pointer to memory, this method returns the next aligned address + static void* alignAddress(void* pointer, uint8 alignment); + + /// Given a pointer to memory, this method returns the next aligned address and also output the alignment offset + static void* alignAddress(void* pointer, uint8 alignment, ptrdiff_t& alignmentOffset); }; } diff --git a/ext/reactphysics3d/include/reactphysics3d/memory/MemoryManager.h b/ext/reactphysics3d/include/reactphysics3d/memory/MemoryManager.h index cf6505bbb..b53460772 100644 --- a/ext/reactphysics3d/include/reactphysics3d/memory/MemoryManager.h +++ b/ext/reactphysics3d/include/reactphysics3d/memory/MemoryManager.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -104,14 +104,18 @@ class MemoryManager { // Allocate memory of a given type RP3D_FORCE_INLINE void* MemoryManager::allocate(AllocationType allocationType, size_t size) { + void* allocatedMemory = nullptr; + switch (allocationType) { - case AllocationType::Base: return mBaseAllocator->allocate(size); - case AllocationType::Pool: return mPoolAllocator.allocate(size); - case AllocationType::Heap: return mHeapAllocator.allocate(size); - case AllocationType::Frame: return mSingleFrameAllocator.allocate(size); + case AllocationType::Base: allocatedMemory = mBaseAllocator->allocate(size); break; + case AllocationType::Pool: allocatedMemory = mPoolAllocator.allocate(size); break; + case AllocationType::Heap: allocatedMemory = mHeapAllocator.allocate(size); break; + case AllocationType::Frame: allocatedMemory = mSingleFrameAllocator.allocate(size); break; } - return nullptr; + assert(allocatedMemory == nullptr || reinterpret_cast(allocatedMemory) % 16 == 0); + + return allocatedMemory; } // Release previously allocated memory. diff --git a/ext/reactphysics3d/include/reactphysics3d/memory/PoolAllocator.h b/ext/reactphysics3d/include/reactphysics3d/memory/PoolAllocator.h index 95f56a1fd..7898545a0 100644 --- a/ext/reactphysics3d/include/reactphysics3d/memory/PoolAllocator.h +++ b/ext/reactphysics3d/include/reactphysics3d/memory/PoolAllocator.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -81,13 +81,16 @@ class PoolAllocator : public MemoryAllocator { /// Number of heaps static const int NB_HEAPS = 128; + /// Minimum unit size + static const size_t MIN_UNIT_SIZE = GLOBAL_ALIGNMENT; + /// Maximum memory unit size. An allocation request of a size smaller or equal to /// this size will be handled using the small block allocator. However, for an /// allocation request larger than the maximum block size, the standard malloc() /// will be used. - static const size_t MAX_UNIT_SIZE = 1024; + static const size_t MAX_UNIT_SIZE = NB_HEAPS * MIN_UNIT_SIZE; - /// Size a memory chunk + /// Size of a memory chunk static const size_t BLOCK_SIZE = 16 * MAX_UNIT_SIZE; // -------------------- Attributes -------------------- // diff --git a/ext/reactphysics3d/include/reactphysics3d/memory/SingleFrameAllocator.h b/ext/reactphysics3d/include/reactphysics3d/memory/SingleFrameAllocator.h index b53b8a4a0..9455bb36c 100644 --- a/ext/reactphysics3d/include/reactphysics3d/memory/SingleFrameAllocator.h +++ b/ext/reactphysics3d/include/reactphysics3d/memory/SingleFrameAllocator.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -45,10 +45,6 @@ class SingleFrameAllocator : public MemoryAllocator { // -------------------- Constants -------------------- // - /// Number of frames to wait before shrinking the allocated - /// memory if too much is allocated - static const int NB_FRAMES_UNTIL_SHRINK = 120; - /// Initial size (in bytes) of the single frame allocator size_t INIT_SINGLE_FRAME_ALLOCATOR_NB_BYTES = 1048576; // 1Mb @@ -69,10 +65,6 @@ class SingleFrameAllocator : public MemoryAllocator { /// Pointer to the next available memory location in the buffer size_t mCurrentOffset; - /// Current number of frames since we detected too much memory - /// is allocated - size_t mNbFramesTooMuchAllocated; - /// True if we need to allocate more memory in the next reset() call bool mNeedToAllocatedMore; diff --git a/ext/reactphysics3d/include/reactphysics3d/reactphysics3d.h b/ext/reactphysics3d/include/reactphysics3d/reactphysics3d.h index 3fe4a8fa8..e429a88af 100644 --- a/ext/reactphysics3d/include/reactphysics3d/reactphysics3d.h +++ b/ext/reactphysics3d/include/reactphysics3d/reactphysics3d.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -26,7 +26,7 @@ /******************************************************************************** * ReactPhysics3D * -* Version 0.9.0 * +* Version 0.10.1 * * http://www.reactphysics3d.com * * Daniel Chappuis * ********************************************************************************/ @@ -37,7 +37,7 @@ // Libraries #include #include -#include +#include #include #include #include @@ -50,14 +50,16 @@ #include #include #include -#include +#include #include #include #include #include -#include +#include +#include #include #include +#include #include #include #include @@ -65,6 +67,7 @@ #include #include #include +#include /// Alias to the ReactPhysics3D namespace namespace rp3d = reactphysics3d; diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/BroadPhaseSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/BroadPhaseSystem.h index d06b5cb3b..4311b3082 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/BroadPhaseSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/BroadPhaseSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -41,12 +41,15 @@ namespace reactphysics3d { // Declarations class CollisionDetectionSystem; class BroadPhaseSystem; -class CollisionBody; +class Body; class Collider; class MemoryManager; class Profiler; // class AABBOverlapCallback +/** + * This class represents a callback when two AABB overlap + */ class AABBOverlapCallback : public DynamicAABBTreeOverlapCallback { public: diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/CollisionDetectionSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/CollisionDetectionSystem.h index 7e17e28b2..ed91dc9f1 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/CollisionDetectionSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/CollisionDetectionSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -27,7 +27,7 @@ #define REACTPHYSICS3D_COLLISION_DETECTION_SYSTEM_H // Libraries -#include +#include #include #include #include @@ -160,9 +160,6 @@ class CollisionDetectionSystem { /// Pointer to the contact points of the current frame (either mContactPoints1 or mContactPoints2) Array* mCurrentContactPoints; - /// Array with the indices of all the contact pairs that have at least one CollisionBody - Array mCollisionBodyContactPairsIndices; - /// Number of potential contact manifolds in the previous frame uint32 mNbPreviousPotentialContactManifolds; @@ -172,6 +169,9 @@ class CollisionDetectionSystem { /// Reference to the half-edge structure of the triangle polyhedron HalfEdgeStructure& mTriangleHalfEdgeStructure; + /// Allocated size for a triangle shape + static const size_t mTriangleShapeAllocatedSize; + #ifdef IS_RP3D_PROFILING_ENABLED /// Pointer to the profiler @@ -185,7 +185,7 @@ class CollisionDetectionSystem { void computeBroadPhase(); /// Compute the middle-phase collision detection - void computeMiddlePhase(NarrowPhaseInput& narrowPhaseInput, bool needToReportContacts); + void computeMiddlePhase(NarrowPhaseInput& narrowPhaseInput, bool needToReportContacts, bool isWorldQuery); // Compute the middle-phase collision detection void computeMiddlePhaseCollisionSnapshot(Array& convexPairs, Array& concavePairs, NarrowPhaseInput& narrowPhaseInput, @@ -213,6 +213,18 @@ class CollisionDetectionSystem { /// Remove pairs that are not overlapping anymore void removeNonOverlappingPairs(); + /// Disable an overlapping pair (because both bodies of the pair are disabled) + void disableOverlappingPair(uint64 pairId); + + /// Remove an overlapping pair + void removeOverlappingPair(uint64 pairId, bool notifyLostContact); + + /// Remove a convex overlapping pair at a given index + void removeConvexOverlappingPairWithIndex(uint64 pairIndex); + + /// Remove a concave overlapping pair at a given index + void removeConcaveOverlappingPairWithIndex(uint64 pairIndex); + /// Add a lost contact pair (pair of colliders that are not in contact anymore) void addLostContactPair(OverlappingPairs::OverlappingPair& overlappingPair); @@ -300,7 +312,7 @@ class CollisionDetectionSystem { /// Constructor CollisionDetectionSystem(PhysicsWorld* world, ColliderComponents& collidersComponents, - TransformComponents& transformComponents, CollisionBodyComponents& collisionBodyComponents, RigidBodyComponents& rigidBodyComponents, + TransformComponents& transformComponents, BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, MemoryManager& memoryManager, HalfEdgeStructure& triangleHalfEdgeStructure); /// Destructor @@ -350,19 +362,19 @@ class CollisionDetectionSystem { unsigned short raycastWithCategoryMaskBits) const; /// Return true if two bodies (collide) overlap - bool testOverlap(CollisionBody* body1, CollisionBody* body2); + bool testOverlap(Body* body1, Body* body2); /// Report all the bodies that overlap (collide) with the body in parameter - void testOverlap(CollisionBody* body, OverlapCallback& callback); + void testOverlap(Body* body, OverlapCallback& callback); /// Report all the bodies that overlap (collide) in the world void testOverlap(OverlapCallback& overlapCallback); /// Test collision and report contacts between two bodies. - void testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback); + void testCollision(Body* body1, Body* body2, CollisionCallback& callback); /// Test collision and report all the contacts involving the body in parameter - void testCollision(CollisionBody* body, CollisionCallback& callback); + void testCollision(Body* body, CollisionCallback& callback); /// Test collision and report contacts between each colliding bodies in the world void testCollision(CollisionCallback& callback); diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/ConstraintSolverSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/ConstraintSolverSystem.h index 11b6cbc74..ab3201ddf 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/ConstraintSolverSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/ConstraintSolverSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/ContactSolverSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/ContactSolverSystem.h index 46b1f22d4..20caafc5e 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/ContactSolverSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/ContactSolverSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -47,7 +47,7 @@ struct Islands; class RigidBody; class Collider; class PhysicsWorld; -class CollisionBodyComponents; +class BodyComponents; class DynamicsComponents; class RigidBodyComponents; class ColliderComponents; @@ -322,7 +322,7 @@ class ContactSolverSystem { Array* mAllContactPoints; /// Reference to the body components - CollisionBodyComponents& mBodyComponents; + BodyComponents& mBodyComponents; /// Reference to the dynamics components RigidBodyComponents& mRigidBodyComponents; @@ -362,7 +362,7 @@ class ContactSolverSystem { // -------------------- Methods -------------------- // /// Constructor - ContactSolverSystem(MemoryManager& memoryManager, PhysicsWorld& world, Islands& islands, CollisionBodyComponents& bodyComponents, + ContactSolverSystem(MemoryManager& memoryManager, PhysicsWorld& world, Islands& islands, BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, ColliderComponents& colliderComponents, decimal& restitutionVelocityThreshold); /// Destructor diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/DynamicsSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/DynamicsSystem.h index f82c51204..921ffe8d7 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/DynamicsSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/DynamicsSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -28,7 +28,7 @@ // Libraries #include -#include +#include #include #include #include @@ -52,7 +52,7 @@ class DynamicsSystem { PhysicsWorld& mWorld; /// Reference to the collision body components - CollisionBodyComponents& mCollisionBodyComponents; + BodyComponents& mBodyComponents; /// Reference to the rigid body components RigidBodyComponents& mRigidBodyComponents; @@ -80,7 +80,7 @@ class DynamicsSystem { // -------------------- Methods -------------------- // /// Constructor - DynamicsSystem(PhysicsWorld& world, CollisionBodyComponents& collisionBodyComponents, + DynamicsSystem(PhysicsWorld& world, BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, TransformComponents& transformComponents, ColliderComponents& colliderComponents, bool& isGravityEnabled, Vector3& gravity); diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/SolveBallAndSocketJointSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/SolveBallAndSocketJointSystem.h index aa1bf9b98..84441fd61 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/SolveBallAndSocketJointSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/SolveBallAndSocketJointSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/SolveFixedJointSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/SolveFixedJointSystem.h index b897404ff..278809d9c 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/SolveFixedJointSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/SolveFixedJointSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/SolveHingeJointSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/SolveHingeJointSystem.h index 6fcb84b15..dc7083c9f 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/SolveHingeJointSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/SolveHingeJointSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/systems/SolveSliderJointSystem.h b/ext/reactphysics3d/include/reactphysics3d/systems/SolveSliderJointSystem.h index a08b65e2b..ebe64ffcb 100644 --- a/ext/reactphysics3d/include/reactphysics3d/systems/SolveSliderJointSystem.h +++ b/ext/reactphysics3d/include/reactphysics3d/systems/SolveSliderJointSystem.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/utils/DebugRenderer.h b/ext/reactphysics3d/include/reactphysics3d/utils/DebugRenderer.h index 2d22dba3d..779c7472d 100644 --- a/ext/reactphysics3d/include/reactphysics3d/utils/DebugRenderer.h +++ b/ext/reactphysics3d/include/reactphysics3d/utils/DebugRenderer.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -31,7 +31,6 @@ #include #include #include -#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -40,6 +39,7 @@ namespace reactphysics3d { class ConcaveMeshShape; class ConvexMeshShape; class HeightFieldShape; +class BoxShape; class Collider; class PhysicsWorld; @@ -86,6 +86,9 @@ class DebugRenderer : public EventListener { /// Display the contact normals CONTACT_NORMAL = 1 << 4, + + /// Display the face normals of the collision shapes + COLLISION_SHAPE_NORMAL = 1 << 5, }; /// Struture that represents a line of the DebugRenderer @@ -151,10 +154,13 @@ class DebugRenderer : public EventListener { /// Default radius of the sphere displayed to represent contact points static constexpr decimal DEFAULT_CONTACT_POINT_SPHERE_RADIUS = decimal(0.1); - /// Default radius of the sphere displayed to represent contact points + /// Default length for the displayed contacts normals static constexpr decimal DEFAULT_CONTACT_NORMAL_LENGTH = decimal(1.0); - // -------------------- Attributes -------------------- // + /// Default length for the displayed faces normals of the collision shapes + static constexpr decimal DEFAULT_COLLISION_SHAPE_NORMAL_LENGTH = decimal(1.0); + + // -------------------- Attributes -------------------- // /// Memory allocator MemoryAllocator& mAllocator; @@ -174,16 +180,19 @@ class DebugRenderer : public EventListener { /// Radius of the sphere displayed to represent contact points decimal mContactPointSphereRadius; - /// Lenght of contact normal + /// Length of contact normal decimal mContactNormalLength; + /// Length of collision shape face normal + decimal mCollisionShapeNormalLength; + // -------------------- Methods -------------------- // /// Draw an AABB void drawAABB(const AABB& aabb, uint32 color); /// Draw a box - void drawBox(const Transform& transform, const Vector3& extents, uint32 color); + void drawBox(const Transform& transform, const BoxShape* boxShape, uint32 colorShape, uint32 colorShapeNormals); /// Draw a sphere void drawSphere(const Vector3& position, decimal radius, uint32 color); @@ -192,16 +201,18 @@ class DebugRenderer : public EventListener { void drawCapsule(const Transform& transform, decimal radius, decimal height, uint32 color); /// Draw a convex mesh - void drawConvexMesh(const Transform& transform, const ConvexMeshShape* convexMesh, uint32 color); + void drawConvexMesh(const Transform& transform, const ConvexMeshShape* convexMesh, uint32 colorShape, uint32 colorShapeNormals); /// Draw a concave mesh shape - void drawConcaveMeshShape(const Transform& transform, const ConcaveMeshShape* concaveMeshShape, uint32 color); + void drawConcaveMeshShape(const Transform& transform, const ConcaveMeshShape* concaveMeshShape, + uint32 colorShape, uint32 colorShapeNormals); /// Draw a height field shape - void drawHeightFieldShape(const Transform& transform, const HeightFieldShape* heightFieldShape, uint32 color); + void drawHeightFieldShape(const Transform& transform, const HeightFieldShape* heightFieldShape, + uint32 colorShape, uint32 colorShapeNormals); /// Draw the collision shape of a collider - void drawCollisionShapeOfCollider(const Collider* collider, uint32 color); + void drawCollisionShapeOfCollider(const Collider* collider); public : @@ -366,9 +377,9 @@ RP3D_FORCE_INLINE void DebugRenderer::setContactNormalLength(decimal contactNorm } -// Hash function for a DebugItem namespace std { + // Hash function for a DebugItem template <> struct hash { size_t operator()(const reactphysics3d::DebugRenderer::DebugItem& debugItem) const { diff --git a/ext/reactphysics3d/include/reactphysics3d/utils/DefaultLogger.h b/ext/reactphysics3d/include/reactphysics3d/utils/DefaultLogger.h index f4d2e927e..1088d1e92 100644 --- a/ext/reactphysics3d/include/reactphysics3d/utils/DefaultLogger.h +++ b/ext/reactphysics3d/include/reactphysics3d/utils/DefaultLogger.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -36,6 +36,7 @@ #include #include #include +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -80,8 +81,33 @@ class DefaultLogger : public Logger { /// Format a log message virtual std::string format(const time_t& time, const std::string& physicsWorldName, const std::string& message, Level level, Category category, const char* filename, int lineNumber) = 0; + + /// Return the current date and time + std::tm getLocalTime(const std::time_t& time) const { + + std::tm bt = std::tm(); + + // This is because std::localtime is not thread-safe + +#if defined(__unix__) + localtime_r(&time, &bt); +#elif defined(_MSC_VER) + localtime_s(&bt, &time); +#else + static std::mutex mtx; + std::lock_guard lock(mtx); + bt = *std::localtime(&time); +#endif + + return bt; + } + }; + // Class TextFormatter + /** + * Format the logs with simple text + */ class TextFormatter : public Formatter { public: @@ -101,12 +127,14 @@ class DefaultLogger : public Logger { // Get current date auto now = std::chrono::system_clock::now(); - auto time = std::chrono::system_clock::to_time_t(now); + std::time_t time = std::chrono::system_clock::to_time_t(now); + + auto localTime = getLocalTime(time); std::stringstream ss; ss << "ReactPhysics3D Logs" << std::endl; ss << "ReactPhysics3D Version: " << RP3D_VERSION << std::endl; - ss << "Date: " << std::put_time(std::localtime(&time), "%Y-%m-%d") << std::endl; + ss << "Date: " << std::put_time(&localTime, "%Y-%m-%d") << std::endl; ss << "---------------------------------------------------------" << std::endl; return ss.str(); @@ -117,8 +145,10 @@ class DefaultLogger : public Logger { Level level, Category category, const char* filename, int lineNumber) override { std::stringstream ss; + auto localTime = getLocalTime(time); + // Time - ss << std::put_time(std::localtime(&time), "%X") << " "; + ss << std::put_time(&localTime, "%X") << " "; // World ss << "World:" << physicsWorldName << std::endl; @@ -142,6 +172,10 @@ class DefaultLogger : public Logger { } }; + // Class HtmlFormatter + /** + * Format the logs with HTML + */ class HtmlFormatter : public Formatter { private: @@ -152,6 +186,7 @@ class DefaultLogger : public Logger { // Get current date auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); + auto localTime = getLocalTime(time); std::stringstream ss; ss << "" << std::endl; @@ -164,7 +199,7 @@ class DefaultLogger : public Logger { ss << "

ReactPhysics3D Logs

" << std::endl; ss << "
" << std::endl; ss << "

ReactPhysics3D version: " << RP3D_VERSION << "

" << std::endl; - ss << "

Date: " << std::put_time(std::localtime(&time), "%Y-%m-%d") << "

" << std::endl; + ss << "

Date: " << std::put_time(&localTime, "%Y-%m-%d") << "

" << std::endl; ss << "
" << std::endl; ss << "
"; @@ -275,11 +310,13 @@ class DefaultLogger : public Logger { std::stringstream ss; + auto localTime = getLocalTime(time); + ss << "
"; // Time ss << "
"; - ss << std::put_time(std::localtime(&time), "%X"); + ss << std::put_time(&localTime, "%X"); ss << "
"; // Message @@ -315,7 +352,10 @@ class DefaultLogger : public Logger { }; - /// Log destination + // Class Destination + /** + * destination for the logs + */ class Destination { public: @@ -344,6 +384,10 @@ class DefaultLogger : public Logger { virtual size_t getSizeBytes() const=0; }; + // Class FileDestination + /** + * File destination for the logs + */ class FileDestination : public Destination { private: @@ -360,9 +404,7 @@ class DefaultLogger : public Logger { :Destination(maxLevelFlag, formatter), mFilePath(filePath), mFileStream(filePath, std::ios::binary) { - if(!mFileStream.is_open()) { - throw(std::runtime_error("ReactPhysics3D Logger: Unable to open an output stream to file " + mFilePath)); - } + assert(mFileStream.is_open()); // Write the header mFileStream << formatter->getHeader() << std::endl; @@ -396,7 +438,10 @@ class DefaultLogger : public Logger { } }; - /// Stream destination to output the logs into a stream + // Class TextFormatter + /** + * Stream destination for the logs + */ class StreamDestination : public Destination { private: @@ -487,9 +532,9 @@ class DefaultLogger : public Logger { } -// Hash function for struct VerticesPair namespace std { + // Hash function for struct VerticesPair template<> struct hash { size_t operator()(const reactphysics3d::DefaultLogger::Format format) const { diff --git a/ext/reactphysics3d/include/reactphysics3d/utils/Logger.h b/ext/reactphysics3d/include/reactphysics3d/utils/Logger.h index bf6a0d422..38cd5152a 100644 --- a/ext/reactphysics3d/include/reactphysics3d/utils/Logger.h +++ b/ext/reactphysics3d/include/reactphysics3d/utils/Logger.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/include/reactphysics3d/utils/Message.h b/ext/reactphysics3d/include/reactphysics3d/utils/Message.h new file mode 100644 index 000000000..be39494c4 --- /dev/null +++ b/ext/reactphysics3d/include/reactphysics3d/utils/Message.h @@ -0,0 +1,63 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_MESSAGE_H +#define REACTPHYSICS3D_MESSAGE_H + +// Libraries +#include + +/// ReactPhysics3D namespace +namespace reactphysics3d { + +// Structure Message +/** + * This structure represent a message that can be returned to the user + */ +struct Message { + + public: + + /// Type of message + enum class Type {Error = 1, Warning = 2, Information = 4}; + + /// Message text + std::string text; + + // Type (error, warning, information) + Type type; + + // -------------------- Methods -------------------- // + + /// Constructor + Message(std::string text, Type type = Type::Error) : text(text), type(type) { + + } +}; + +} + +#endif + diff --git a/ext/reactphysics3d/include/reactphysics3d/utils/Profiler.h b/ext/reactphysics3d/include/reactphysics3d/utils/Profiler.h index b65a19d66..8aad066ee 100644 --- a/ext/reactphysics3d/include/reactphysics3d/utils/Profiler.h +++ b/ext/reactphysics3d/include/reactphysics3d/utils/Profiler.h @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -229,9 +229,7 @@ class Profiler { :Destination(format), mFilePath(filePath), mFileStream(filePath, std::ios::binary) { - if(!mFileStream.is_open()) { - throw(std::runtime_error("ReactPhysics3D Logger: Unable to open an output stream to file " + mFilePath)); - } + assert(mFileStream.is_open()); } /// Destructor diff --git a/ext/reactphysics3d/include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h b/ext/reactphysics3d/include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h new file mode 100644 index 000000000..a7a86b0cd --- /dev/null +++ b/ext/reactphysics3d/include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h @@ -0,0 +1,397 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_QH_HALF_EDGE_STRUCTURE_MESH_H +#define REACTPHYSICS3D_QH_HALF_EDGE_STRUCTURE_MESH_H + +// Libraries +#include +#include +#include + +namespace reactphysics3d { + +// Class QHHalfEdgeStructure +/** + * This class describes a polyhedron mesh made of faces and vertices. + * The faces do not have to be triangles. Note that in the linked-list of + * half-edges an edge must always follow its twin edge. This way, on closed + * mesh, we can always iterate every two half-edges to find the unique edges. + * This class is only used for QuickHull algorithm. + */ +class QHHalfEdgeStructure { + + public: + + // Forward declaration + struct Vertex; + struct Face; + + using VerticesPair = Pair; + using EdgeVertices = Pair; + + // Struct Edge + /** + * An half-edge + */ + struct Edge { + + /// Vertex at the beginning of the edge + Vertex* startVertex; + + /// Vertex at the end of the edge + Vertex* endVertex; + + /// Adjacent face of the edge + Face* face; + + /// Previous edge in the linked-list of edges + Edge* previousEdge; + + /// Next edge in the linked-list of edges + Edge* nextEdge; + + /// Previous edge around the face of the edge + Edge* previousFaceEdge; + + /// Next edge around the face of the edge + Edge* nextFaceEdge; + + /// Twin edge + Edge* twinEdge; + + Edge(Vertex* startVertex, Vertex* endVertex, Face* face) + :startVertex(startVertex), endVertex(endVertex), face(face), previousEdge(nullptr), nextEdge(nullptr), + previousFaceEdge(nullptr), nextFaceEdge(nullptr), twinEdge(nullptr) {} + + bool isValid() const { + + bool isValid = true; + + isValid &= face != nullptr; + isValid &= previousFaceEdge != nullptr; + isValid &= nextFaceEdge != nullptr; + isValid &= previousFaceEdge->nextFaceEdge == this; + isValid &= nextFaceEdge->previousFaceEdge == this; + isValid &= twinEdge != nullptr; + isValid &= twinEdge->twinEdge == this; + isValid &= startVertex == twinEdge->endVertex; + isValid &= endVertex == twinEdge->startVertex; + isValid &= endVertex == nextFaceEdge->startVertex; + isValid &= startVertex == previousFaceEdge->endVertex; + + return isValid; + } + }; + + // Struct Face + /** + * A face + */ + struct Face { + + /// Pointer to the next face + Face* nextFace; + + /// Pointer to the previous face + Face* previousFace; + + /// One half-edge of the face + Edge* edge; + + /// Face normal + Vector3 normal; + + /// Center of the face (average of the face vertices) + Vector3 centroid; + + /// Area of the face + decimal area; + + /// Array with some remaining points visible from this face that need to be processed + Array conflictPoints; + + /// Constructor + Face(MemoryAllocator& allocator) + : nextFace(nullptr), previousFace(nullptr), edge(nullptr), normal(0, 0, 0), area(0), conflictPoints(allocator, 8) { + + } + + /// Return a vertex of the face + const Vertex* getVertex() const { + return edge->startVertex; + } + + /// Recalculate the face centroid and normal to better fit its new vertices (using Newell method) + void recalculateFace(const Array& points) { + + centroid.setToZero(); + normal.setToZero(); + uint32 nbVertices = 0; + + // For each vertex of the face + const QHHalfEdgeStructure::Edge* firstFaceEdge = edge; + const QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge; + do { + + const Vector3 v1 = points[faceEdge->startVertex->externalIndex]; + const Vector3 v2 = points[faceEdge->endVertex->externalIndex]; + centroid += v1; + normal += Vector3((v1.y - v2.y) * (v1.z + v2.z), + (v1.z - v2.z) * (v1.x + v2.x), + (v1.x - v2.x) * (v1.y + v2.y)); + + nbVertices++; + + faceEdge = faceEdge->nextFaceEdge; + + } while(faceEdge != firstFaceEdge); + + assert(nbVertices > 0); + + centroid = centroid / nbVertices; + const decimal normalLength = normal.length(); + assert(normalLength > 0); + normal = normal / normalLength; + area = normalLength * decimal(0.5); + } + + /// Return a string with the vertices of the face + std::string verticesString() const { + + std::string verticesString = "("; + + const Edge* firstFaceEdge = edge; + const Edge* faceEdge = edge; + do { + + verticesString += std::to_string(faceEdge->startVertex->externalIndex); + + faceEdge = faceEdge->nextFaceEdge; + + if (faceEdge != firstFaceEdge) { + verticesString += ","; + } + + } while(faceEdge != firstFaceEdge); + verticesString += ")"; + + return verticesString; + } + + /// Return true if the face is a triangle + bool isTriangle() { + + return edge->nextFaceEdge->nextFaceEdge->nextFaceEdge == edge; + } + + /// Return true if the face structure is valid (for debugging purpose) + bool isValid() { + bool isValid = true; + + isValid &= approxEqual(normal.lengthSquare(), 1.0, 0.01); + isValid &= edge->face == this; + + const QHHalfEdgeStructure::Edge* firstFaceEdge = edge; + const QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge; + do { + + if (faceEdge->face != this) { + return false; + } + + faceEdge = faceEdge->nextFaceEdge; + + } while(faceEdge != firstFaceEdge); + + + return isValid; + } + + }; + + // Struct Vertex + /** + * A vertex + */ + struct Vertex { + + /// Index of the vertex point in the user vertex array + uint32 externalIndex; + + /// Pointer to the previous vertex + Vertex* previousVertex; + + /// Pointer to the next vertex + Vertex* nextVertex; + + /// Constructor + Vertex(uint32 externalIndex, Vertex* previousVertex, Vertex* nextVertex) + : externalIndex(externalIndex), previousVertex(previousVertex), + nextVertex(nextVertex) { } + }; + + private: + + // ---------- Attributes ---------- // + + /// Reference to a memory allocator + MemoryAllocator& mAllocator; + + /// Map a pair of vertice to the corresponding edge + Map mMapVerticesToEdge; + + /// Number of faces + uint32 mNbFaces = 0; + + /// Number of half-edges + uint32 mNbHalfEdges = 0; + + /// Number of vertices + uint32 mNbVertices = 0; + + /// Linked-list of faces + Face* mFaces = nullptr; + + /// Linked-list of vertices + Vertex* mVertices = nullptr; + + /// Linked-list of half-edges + Edge* mHalfEdges = nullptr; + + static const size_t mVertexAllocatedSize; + static const size_t mEdgeAllocatedSize; + static const size_t mFaceAllocatedSize; + + // ---------- Methods ---------- // + + /// Add an edge before another one in the linked-list of edges + void addEdgeToLinkedListBefore(Edge* newEdge, Edge* edge); + + /// Remove an half-edge from the linked-list of half-edges + void removeEdgeFromLinkedList(Edge* edge); + + /// Add a face to the linked-list of faces + void addFaceToLinkedList(Face* face); + + /// Remove a face from the linked-list of faces + void removeFaceFromLinkedList(Face* face); + + public: + + // ---------- Methods ---------- // + + /// Constructor + QHHalfEdgeStructure(MemoryAllocator& allocator) :mAllocator(allocator), mMapVerticesToEdge(allocator) {} + + /// Destructor + ~QHHalfEdgeStructure(); + + /// Add a vertex + Vertex* addVertex(uint32 externalIndex); + + /// Add a face + Face* addFace(const Array& faceVertices, const Array& points, MemoryAllocator& allocator); + + /// Remove a face + void removeFace(Face* face); + + /// Delete the face + void deleteFace(Face* face); + + /// Remove an half-edge + void removeHalfEdge(Edge* edge); + + /// Remove a vertex + void removeVertex(Vertex* vertex); + + /// Return the number of faces + uint32 getNbFaces() const; + + /// Return the number of half-edges + uint32 getNbHalfEdges() const; + + /// Return the number of vertices + uint32 getNbVertices() const; + + /// Return a pointer to the first face in the linked-list of faces + const Face* getFaces() const; + + /// Return a pointer to the first half-edge in the linked-list of half-edges + const Edge* getHalfEdges() const; + + /// Return a pointer to the first vertex in the linked-list of vertices + const Vertex* getVertices() const; + + /// Return true if the half-edge structure is valid (for debugging purpose) + bool isValid() const; + + /// Return a string representation of the half-edge structure + std::string to_string() const; +}; + +// Return the number of faces +/** + * @return The number of faces in the polyhedron + */ +RP3D_FORCE_INLINE uint32 QHHalfEdgeStructure::getNbFaces() const { + return mNbFaces; +} + +// Return the number of edges +/** + * @return The number of edges in the polyhedron + */ +RP3D_FORCE_INLINE uint32 QHHalfEdgeStructure::getNbHalfEdges() const { + return mNbHalfEdges; +} + +// Return the number of vertices +/** + * @return The number of vertices in the polyhedron + */ +RP3D_FORCE_INLINE uint32 QHHalfEdgeStructure::getNbVertices() const { + return mNbVertices; +} + +// Return a pointer to the first face in the linked-list of faces +RP3D_FORCE_INLINE const QHHalfEdgeStructure::Face* QHHalfEdgeStructure::getFaces() const { + return mFaces; +} + +// Return a pointer to the first half-edge in the linked-list of half-edges +RP3D_FORCE_INLINE const QHHalfEdgeStructure::Edge* QHHalfEdgeStructure::getHalfEdges() const { + return mHalfEdges; +} + +// Return a pointer to the first vertex in the linked-list of vertices +RP3D_FORCE_INLINE const QHHalfEdgeStructure::Vertex* QHHalfEdgeStructure::getVertices() const { + return mVertices; +} + +} + +#endif + diff --git a/ext/reactphysics3d/include/reactphysics3d/utils/quickhull/QuickHull.h b/ext/reactphysics3d/include/reactphysics3d/utils/quickhull/QuickHull.h new file mode 100644 index 000000000..d393cba40 --- /dev/null +++ b/ext/reactphysics3d/include/reactphysics3d/utils/quickhull/QuickHull.h @@ -0,0 +1,184 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_QUICKHULL_H +#define REACTPHYSICS3D_QUICKHULL_H + +// Libraries +#include +#include +#include +#include +#include + +/// ReactPhysics3D namespace +namespace reactphysics3d { + +// Declarations +class ConvexMesh; +class VertexArray; +struct Message; +template +class Array; + +// Class QuickHull +// This algorithm is based on 'Implementing Quickhull' presentation at GDC from Dirk Gregorius +/** + * This class implements the Quickhull algorithm to compute a convex mesh from a set of 3D points + */ +class QuickHull { + + private: + + // Structure Candidate + // This is used during the process to find the horizon + struct CandidateFace { + + const QHHalfEdgeStructure::Face* face; + const QHHalfEdgeStructure::Edge* startEdge; + const QHHalfEdgeStructure::Edge* currentEdge; + + // Constructor + CandidateFace(const QHHalfEdgeStructure::Face* face, const QHHalfEdgeStructure::Edge* edge) + :face(face), startEdge(edge), currentEdge(edge) {} + }; + + // -------------------- Constants -------------------- // + + static const uint32 INVALID_VERTEX_INDEX = -1; + static const uint32 INVALID_FACE_INDEX = -1; + + // -------------------- Attributes -------------------- // + + // -------------------- Methods -------------------- // + + // Compute the initial tetrahedron convex hull + static bool computeInitialHull(Array& points, QHHalfEdgeStructure& convexHull, + Array& initialFaces, + Array& orphanPointsIndices, + MemoryAllocator& allocator, std::vector& errors); + + /// Extract the points from the array + static void extractPoints(const VertexArray& vertexArray, Array& outArray); + + /// Add a vertex to the current convex hull to expand it + static void addVertexToHull(uint32 vertexIndex, QHHalfEdgeStructure::Face* face, Array& points, + QHHalfEdgeStructure& convexHull, decimal epsilon, + MemoryAllocator& allocator); + + /// Build the new faces that contain the new vertex and the horizon edges + static void buildNewFaces(uint32 newVertexIndex, Array& horizonVertices, + QHHalfEdgeStructure& convexHull, + Array& points, + Array& newFaces, + MemoryAllocator& allocator); + + /// Delete all the faces visible from the vertex to be added + static void deleteVisibleFaces(const Array& visibleFaces, + QHHalfEdgeStructure& convexHull, + Array& orphanPoints, + const Array& horizonVertices, + MemoryAllocator& allocator); + + /// Find the horizon (edges) forming the separation between the faces that are visible from the vertex and the faces that are not visible + static void findHorizon(const Vector3& vertex, QHHalfEdgeStructure::Face *face, + MemoryAllocator& allocator, + Array& outHorizonVertices, + Array& outVisibleFaces, + decimal epsilon); + + /// Fix faces that are forming a concave or coplanar shape (by giving priority to large faces) + static void mergeLargeConcaveFaces(QHHalfEdgeStructure& convexHull, Array& newFaces, + const Array& points, decimal epsilon, Set& deletedFaces); + + /// Fix faces that are forming a concave or coplanar shape + static void mergeConcaveFaces(QHHalfEdgeStructure& convexHull, Array& newFaces, + const Array& points, decimal epsilon, + Set& deletedFaces); + + /// Merge two faces that are concave at a given edge + static void mergeConcaveFacesAtEdge(QHHalfEdgeStructure::Edge* edge, QHHalfEdgeStructure& convexHull, + const Array& points, Set& deletedFaces); + + /// Fix topological issues (if any) that might have been created during faces merge + static void fixTopologicalIssues(QHHalfEdgeStructure& convexHull, QHHalfEdgeStructure::Face* face, + const Array& points, Set& deletedFaces); + + /// Fix topological issue at a given edge + static void fixTopologicalIssueAtEdge(QHHalfEdgeStructure& convexHull, QHHalfEdgeStructure::Face* face, + QHHalfEdgeStructure::Edge* inEdge, const Array& points, + Set& deletedFaces); + + /// Remove duplicated vertices in the input array of points + static void removeDuplicatedVertices(Array& points, MemoryAllocator& allocator); + + /// Return the index of the next vertex candidate to be added to the hull + static void findNextVertexCandidate(Array& points, uint32& outNextVertexIndex, + QHHalfEdgeStructure& convexHull, + QHHalfEdgeStructure::Face*& outNextFace, decimal epsilon); + + /// Find the closest face for a given vertex and add this vertex to the remaining closest points for this face + static void findFarthestFaceForVertex(uint32 vertexIndex, Array& faces, Array& points, + decimal epsilon, Set& deletedFaces); + + /// Take all the points closest to the old face and add them to the closest faces among the new faces that replace the old face + static void associateOrphanPointsToNewFaces(Array& orphanPointsIndices, + Array& newFaces, + Array& points, decimal epsilon, Set& deletedFaces); + + /// Return true if the vertex is part of horizon edges + static bool testIsVertexInHorizon(QHHalfEdgeStructure::Vertex* vertex, const Array& horizonVertices); + + /// Return true if a given edge is convex and false otherwise + static bool testIsConvexEdge(const QHHalfEdgeStructure::Edge* edge, decimal epsilon); + + /// Compute the center of a face (the average of face vertices) + static Vector3 computeFaceCenter(QHHalfEdgeStructure::Face* face, const Array& points); + + /// Compute the final PolygonVertexArray from the convex hull half-edge structure + static void computeFinalPolygonVertexArray(const QHHalfEdgeStructure& convexHull, + const Array& points, + PolygonVertexArray& outPolygonVertexArray, + Array& outVertices, Array& outIndices, + Array& outFaces, + MemoryAllocator& allocator); + + public: + + // -------------------- Methods -------------------- // + + /// Compute the convex hull of a set of points and returns true if there are no errors + static bool computeConvexHull(const VertexArray& vertexArray, PolygonVertexArray& outPolygonVertexArray, + Array& outVertices, Array& outIndices, + Array& outFaces, + MemoryAllocator& allocator, std::vector& errors); + + + +}; + +} + +#endif diff --git a/ext/reactphysics3d/src/body/CollisionBody.cpp b/ext/reactphysics3d/src/body/Body.cpp similarity index 72% rename from ext/reactphysics3d/src/body/CollisionBody.cpp rename to ext/reactphysics3d/src/body/Body.cpp index 95f0afc0a..891ec3174 100644 --- a/ext/reactphysics3d/src/body/CollisionBody.cpp +++ b/ext/reactphysics3d/src/body/Body.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -24,7 +24,7 @@ ********************************************************************************/ // Libraries -#include +#include #include #include #include @@ -36,12 +36,11 @@ using namespace reactphysics3d; // Constructor /** - * @param transform The transform of the body - * @param world The physics world where the body is created - * @param id ID of the body + * @param world The reference to the physics world where the body is created + * @param entity Entity of the body */ -CollisionBody::CollisionBody(PhysicsWorld& world, Entity entity) - : mEntity(entity), mWorld(world) { +Body::Body(PhysicsWorld& world, Entity entity) + : mEntity(entity), mWorld(world), mIsDebugEnabled(false) { #ifdef IS_RP3D_PROFILING_ENABLED @@ -51,7 +50,7 @@ CollisionBody::CollisionBody(PhysicsWorld& world, Entity entity) } // Destructor -CollisionBody::~CollisionBody() { +Body::~Body() { } @@ -67,7 +66,7 @@ CollisionBody::~CollisionBody() { * local-space of the collider into the local-space of the body * @return A pointer to the collider that has been created */ -Collider* CollisionBody::addCollider(CollisionShape* collisionShape, const Transform& transform) { +Collider* Body::addCollider(CollisionShape* collisionShape, const Transform& transform) { // Create a new entity for the collider Entity colliderEntity = mWorld.mEntityManager.createEntity(); @@ -84,19 +83,16 @@ Collider* CollisionBody::addCollider(CollisionShape* collisionShape, const Trans sizeof(Collider))) Collider(colliderEntity, this, mWorld.mMemoryManager); // Add the collider component to the entity of the body - Vector3 localBoundsMin; - Vector3 localBoundsMax; - // TODO : Maybe this method can directly returns an AABB - collisionShape->getLocalBounds(localBoundsMin, localBoundsMax); + AABB shapeAABB = collisionShape->getLocalBounds(); const Transform localToWorldTransform = mWorld.mTransformComponents.getTransform(mEntity) * transform; Material material(mWorld.mConfig.defaultFrictionCoefficient, mWorld.mConfig.defaultBounciness); - ColliderComponents::ColliderComponent colliderComponent(mEntity, collider, AABB(localBoundsMin, localBoundsMax), + ColliderComponents::ColliderComponent colliderComponent(mEntity, collider, shapeAABB, transform, collisionShape, 0x0001, 0xFFFF, localToWorldTransform, material); - bool isActive = mWorld.mCollisionBodyComponents.getIsActive(mEntity); + bool isActive = mWorld.mBodyComponents.getIsActive(mEntity); mWorld.mCollidersComponents.addComponent(colliderEntity, !isActive, colliderComponent); - mWorld.mCollisionBodyComponents.addColliderToBody(mEntity, colliderEntity); + mWorld.mBodyComponents.addColliderToBody(mEntity, colliderEntity); // Assign the collider with the collision shape collisionShape->addCollider(collider); @@ -110,8 +106,7 @@ Collider* CollisionBody::addCollider(CollisionShape* collisionShape, const Trans #endif // Compute the world-space AABB of the new collision shape - AABB aabb; - collisionShape->computeAABB(aabb, mWorld.mTransformComponents.getTransform(mEntity) * transform); + const AABB aabb = collisionShape->computeTransformedAABB(mWorld.mTransformComponents.getTransform(mEntity) * transform); // Notify the collision detection about this new collision shape mWorld.mCollisionDetection.addCollider(collider, aabb); @@ -131,34 +126,34 @@ Collider* CollisionBody::addCollider(CollisionShape* collisionShape, const Trans /** * @return The number of colliders associated with this body */ -uint32 CollisionBody::getNbColliders() const { - return static_cast(mWorld.mCollisionBodyComponents.getColliders(mEntity).size()); +uint32 Body::getNbColliders() const { + return static_cast(mWorld.mBodyComponents.getColliders(mEntity).size()); } // Return a const pointer to a given collider of the body /** -* @param index Index of a Collider of the body -* @return The const pointer of a given collider of the body +* @param colliderIndex Index of a Collider of the body +* @return The const pointer to the requested collider */ -const Collider* CollisionBody::getCollider(uint32 colliderIndex) const { +const Collider* Body::getCollider(uint32 colliderIndex) const { assert(colliderIndex < getNbColliders()); - Entity colliderEntity = mWorld.mCollisionBodyComponents.getColliders(mEntity)[colliderIndex]; + Entity colliderEntity = mWorld.mBodyComponents.getColliders(mEntity)[colliderIndex]; return mWorld.mCollidersComponents.getCollider(colliderEntity); } // Return a pointer to a given collider of the body /** -* @param index Index of a Collider of the body -* @return The pointer of a given collider of the body +* @param colliderIndex Index of a Collider of the body +* @return The pointer to the requested collider */ -Collider* CollisionBody::getCollider(uint32 colliderIndex) { +Collider* Body::getCollider(uint32 colliderIndex) { assert(colliderIndex < getNbColliders()); - Entity colliderEntity = mWorld.mCollisionBodyComponents.getColliders(mEntity)[colliderIndex]; + Entity colliderEntity = mWorld.mBodyComponents.getColliders(mEntity)[colliderIndex]; return mWorld.mCollidersComponents.getCollider(colliderEntity); } @@ -168,7 +163,7 @@ Collider* CollisionBody::getCollider(uint32 colliderIndex) { /** * @param collider The pointer of the collider you want to remove */ -void CollisionBody::removeCollider(Collider* collider) { +void Body::removeCollider(Collider* collider) { RP3D_LOG(mWorld.mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(mEntity.id) + ": Collider " + std::to_string(collider->getBroadPhaseId()) + " removed from body", __FILE__, __LINE__); @@ -178,7 +173,7 @@ void CollisionBody::removeCollider(Collider* collider) { mWorld.mCollisionDetection.removeCollider(collider); } - mWorld.mCollisionBodyComponents.removeColliderFromBody(mEntity, collider->getEntity()); + mWorld.mBodyComponents.removeColliderFromBody(mEntity, collider->getEntity()); // Unassign the collider from the collision shape collider->getCollisionShape()->removeCollider(collider); @@ -192,16 +187,21 @@ void CollisionBody::removeCollider(Collider* collider) { // Call the constructor of the collider collider->~Collider(); + // Update whether the body still has a simulation collider + if (mWorld.mBodyComponents.getHasSimulationCollider(mEntity)) { + updateHasSimulationCollider(); + } + // Release allocated memory for the collider mWorld.mMemoryManager.release(MemoryManager::AllocationType::Pool, collider, sizeof(Collider)); } // Remove all the colliders -void CollisionBody::removeAllColliders() { +void Body::removeAllColliders() { // Look for the collider that contains the collision shape in parameter. // Note that we need to copy the array of collider entities because we are deleting them in a loop. - const Array collidersEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array collidersEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < collidersEntities.size(); i++) { removeCollider(mWorld.mCollidersComponents.getCollider(collidersEntities[i])); @@ -213,16 +213,16 @@ void CollisionBody::removeAllColliders() { * @return The current transformation of the body that transforms the local-space * of the body into world-space */ -const Transform& CollisionBody::getTransform() const { +const Transform& Body::getTransform() const { return mWorld.mTransformComponents.getTransform(mEntity); } // Update the broad-phase state for this body (because it has moved for instance) -void CollisionBody::updateBroadPhaseState() const { +void Body::updateBroadPhaseState() const { // For all the colliders of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); const uint32 nbColliderEntities = static_cast(colliderEntities.size()); for (uint32 i=0; i < nbColliderEntities; i++) { @@ -240,12 +240,12 @@ void CollisionBody::updateBroadPhaseState() const { /** * @param isActive True if you want to activate the body */ -void CollisionBody::setIsActive(bool isActive) { +void Body::setIsActive(bool isActive) { // If the state does not change - if (mWorld.mCollisionBodyComponents.getIsActive(mEntity) == isActive) return; + if (mWorld.mBodyComponents.getIsActive(mEntity) == isActive) return; - mWorld.mCollisionBodyComponents.setIsActive(mEntity, isActive); + mWorld.mBodyComponents.setIsActive(mEntity, isActive); // If we have to activate the body if (isActive) { @@ -253,14 +253,13 @@ void CollisionBody::setIsActive(bool isActive) { const Transform& transform = mWorld.mTransformComponents.getTransform(mEntity); // For each collider of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < colliderEntities.size(); i++) { Collider* collider = mWorld.mCollidersComponents.getCollider(colliderEntities[i]); // Compute the world-space AABB of the new collision shape - AABB aabb; - collider->getCollisionShape()->computeAABB(aabb, transform * mWorld.mCollidersComponents.getLocalToBodyTransform(collider->getEntity())); + const AABB aabb = collider->getCollisionShape()->computeTransformedAABB(transform * mWorld.mCollidersComponents.getLocalToBodyTransform(collider->getEntity())); // Add the collider to the collision detection mWorld.mCollisionDetection.addCollider(collider, aabb); @@ -269,7 +268,7 @@ void CollisionBody::setIsActive(bool isActive) { else { // If we have to deactivate the body // For each collider of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < colliderEntities.size(); i++) { Collider* collider = mWorld.mCollidersComponents.getCollider(colliderEntities[i]); @@ -289,10 +288,10 @@ void CollisionBody::setIsActive(bool isActive) { // Ask the broad-phase to test again the collision shapes of the body for collision // (as if the body has moved). -void CollisionBody::askForBroadPhaseCollisionCheck() const { +void Body::askForBroadPhaseCollisionCheck() const { // For all the colliders of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); const uint32 nbColliderEntities = static_cast(colliderEntities.size()); for (uint32 i=0; i < nbColliderEntities; i++) { @@ -308,10 +307,10 @@ void CollisionBody::askForBroadPhaseCollisionCheck() const { * @param worldPoint The point to test (in world-space coordinates) * @return True if the point is inside the body */ -bool CollisionBody::testPointInside(const Vector3& worldPoint) const { +bool Body::testPointInside(const Vector3& worldPoint) const { // For each collider of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < colliderEntities.size(); i++) { Collider* collider = mWorld.mCollidersComponents.getCollider(colliderEntities[i]); @@ -331,16 +330,16 @@ bool CollisionBody::testPointInside(const Vector3& worldPoint) const { * (valid only if the method returned true) * @return True if the ray hit the body and false otherwise */ -bool CollisionBody::raycast(const Ray& ray, RaycastInfo& raycastInfo) { +bool Body::raycast(const Ray& ray, RaycastInfo& raycastInfo) { // If the body is not active, it cannot be hit by rays - if (!mWorld.mCollisionBodyComponents.getIsActive(mEntity)) return false; + if (!mWorld.mBodyComponents.getIsActive(mEntity)) return false; bool isHit = false; Ray rayTemp(ray); // For each collider of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); const uint32 nbColliderEntities = static_cast(colliderEntities.size()); for (uint32 i=0; i < nbColliderEntities; i++) { @@ -360,17 +359,15 @@ bool CollisionBody::raycast(const Ray& ray, RaycastInfo& raycastInfo) { /** * @return The axis-aligned bounding box (AABB) of the body in world-space coordinates */ -AABB CollisionBody::getAABB() const { - - AABB bodyAABB; +AABB Body::getAABB() const { - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); - if (colliderEntities.size() == 0) return bodyAABB; + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); + if (colliderEntities.size() == 0) return AABB(); const Transform& transform = mWorld.mTransformComponents.getTransform(mEntity); Collider* collider = mWorld.mCollidersComponents.getCollider(colliderEntities[0]); - collider->getCollisionShape()->computeAABB(bodyAABB, transform * collider->getLocalToBodyTransform()); + AABB bodyAABB = collider->getCollisionShape()->computeTransformedAABB(transform * collider->getLocalToBodyTransform()); // For each collider of the body const uint32 nbColliderEntities = static_cast(colliderEntities.size()); @@ -379,8 +376,7 @@ AABB CollisionBody::getAABB() const { Collider* collider = mWorld.mCollidersComponents.getCollider(colliderEntities[i]); // Compute the world-space AABB of the collider - AABB aabb; - collider->getCollisionShape()->computeAABB(aabb, transform * collider->getLocalToBodyTransform()); + const AABB aabb = collider->getCollisionShape()->computeTransformedAABB(transform * collider->getLocalToBodyTransform()); // Merge the collider AABB with the current body AABB bodyAABB.mergeWithAABB(aabb); @@ -394,7 +390,7 @@ AABB CollisionBody::getAABB() const { * @param transform The transformation of the body that transforms the local-space * of the body into world-space */ -void CollisionBody::setTransform(const Transform& transform) { +void Body::setTransform(const Transform& transform) { // Update the transform of the body mWorld.mTransformComponents.setTransform(mEntity, transform); @@ -410,24 +406,24 @@ void CollisionBody::setTransform(const Transform& transform) { /** * @return True if the body currently active and false otherwise */ -bool CollisionBody::isActive() const { - return mWorld.mCollisionBodyComponents.getIsActive(mEntity); +bool Body::isActive() const { + return mWorld.mBodyComponents.getIsActive(mEntity); } // Return a pointer to the user data attached to this body /** * @return A pointer to the user data you have attached to the body */ -void* CollisionBody::getUserData() const { - return mWorld.mCollisionBodyComponents.getUserData(mEntity); +void* Body::getUserData() const { + return mWorld.mBodyComponents.getUserData(mEntity); } // Attach user data to this body /** * @param userData A pointer to the user data you want to attach to the body */ -void CollisionBody::setUserData(void* userData) { - mWorld.mCollisionBodyComponents.setUserData(mEntity, userData); +void Body::setUserData(void* userData) { + mWorld.mBodyComponents.setUserData(mEntity, userData); } // Return the world-space coordinates of a point given the local-space coordinates of the body @@ -435,7 +431,7 @@ void CollisionBody::setUserData(void* userData) { * @param localPoint A point in the local-space coordinates of the body * @return The point in world-space coordinates */ -Vector3 CollisionBody::getWorldPoint(const Vector3& localPoint) const { +Vector3 Body::getWorldPoint(const Vector3& localPoint) const { return mWorld.mTransformComponents.getTransform(mEntity) * localPoint; } @@ -444,7 +440,7 @@ Vector3 CollisionBody::getWorldPoint(const Vector3& localPoint) const { * @param localVector A vector in the local-space coordinates of the body * @return The vector in world-space coordinates */ -Vector3 CollisionBody::getWorldVector(const Vector3& localVector) const { +Vector3 Body::getWorldVector(const Vector3& localVector) const { return mWorld.mTransformComponents.getTransform(mEntity).getOrientation() * localVector; } @@ -453,7 +449,7 @@ Vector3 CollisionBody::getWorldVector(const Vector3& localVector) const { * @param worldPoint A point in world-space coordinates * @return The point in the local-space coordinates of the body */ -Vector3 CollisionBody::getLocalPoint(const Vector3& worldPoint) const { +Vector3 Body::getLocalPoint(const Vector3& worldPoint) const { return mWorld.mTransformComponents.getTransform(mEntity).getInverse() * worldPoint; } @@ -462,6 +458,59 @@ Vector3 CollisionBody::getLocalPoint(const Vector3& worldPoint) const { * @param worldVector A vector in world-space coordinates * @return The vector in the local-space coordinates of the body */ -Vector3 CollisionBody::getLocalVector(const Vector3& worldVector) const { +Vector3 Body::getLocalVector(const Vector3& worldVector) const { return mWorld.mTransformComponents.getTransform(mEntity).getOrientation().getInverse() * worldVector; } + +// Update whether the body has at least one simulation provider +void Body::updateHasSimulationCollider() { + + // For each collider of the body + const uint32 bodyIndex = mWorld.mBodyComponents.getEntityIndex(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.mColliders[bodyIndex]; + for (uint32 i=0; i < colliderEntities.size(); i++) { + + // Get the currently overlapping pairs for this collider + const bool isSimulationCollider = mWorld.mCollidersComponents.getIsSimulationCollider(colliderEntities[i]); + + if (isSimulationCollider) { + mWorld.mBodyComponents.mHasSimulationCollider[bodyIndex] = true; + return; + } + } +} + +#ifdef IS_RP3D_PROFILING_ENABLED + +// Set the profiler +void Body::setProfiler(Profiler* profiler) { + + mProfiler = profiler; + + // Set the profiler for each collider + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); + for (uint32 i=0; i < colliderEntities.size(); i++) { + + Collider* collider = mWorld.mCollidersComponents.getCollider(colliderEntities[i]); + + collider->setProfiler(profiler); + } +} + +#endif + +// Set whether to compute debug information on this body +/** + * @param enabled Set to true if this body should have it's debug information computed + */ +void Body::setIsDebugEnabled(bool enabled) { + mIsDebugEnabled = enabled; +} + +// Returns true if this collision body is computing debug information +/** + * @return Returns true if this body is computing debug information + */ +bool Body::isDebugEnabled() const { + return mIsDebugEnabled; +} diff --git a/ext/reactphysics3d/src/body/RigidBody.cpp b/ext/reactphysics3d/src/body/RigidBody.cpp index 4e574907b..da1627072 100644 --- a/ext/reactphysics3d/src/body/RigidBody.cpp +++ b/ext/reactphysics3d/src/body/RigidBody.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -35,11 +35,10 @@ using namespace reactphysics3d; // Constructor /** -* @param transform The transformation of the body * @param world The world where the body has been added -* @param id The ID of the body +* @param entity The entity of the rigidbody */ -RigidBody::RigidBody(PhysicsWorld& world, Entity entity) : CollisionBody(world, entity) { +RigidBody::RigidBody(PhysicsWorld& world, Entity entity) : Body(world, entity) { } @@ -76,6 +75,10 @@ void RigidBody::setType(BodyType type) { // Reset the velocity to zero mWorld.mRigidBodyComponents.setLinearVelocity(mEntity, Vector3::zero()); mWorld.mRigidBodyComponents.setAngularVelocity(mEntity, Vector3::zero()); + + const Transform& transform = getTransform(); + mWorld.mRigidBodyComponents.setConstrainedPosition(mEntity, transform.getPosition()); + mWorld.mRigidBodyComponents.setConstrainedOrientation(mEntity, transform.getOrientation()); } // If it is a static or a kinematic body @@ -84,6 +87,7 @@ void RigidBody::setType(BodyType type) { // Reset the inverse mass and inverse inertia tensor to zero mWorld.mRigidBodyComponents.setMassInverse(mEntity, decimal(0)); mWorld.mRigidBodyComponents.setInverseInertiaTensorLocal(mEntity, Vector3::zero()); + mWorld.mRigidBodyComponents.setInertiaTensorWorldInverse(mEntity, Matrix3x3::zero()); } else { // If it is a dynamic body @@ -104,11 +108,17 @@ void RigidBody::setType(BodyType type) { mWorld.mRigidBodyComponents.setInverseInertiaTensorLocal(mEntity, inverseInertiaTensorLocal); } + // Disable/Enable the body if necessary (components of static bodies are disabled) + mWorld.setBodyDisabled(mEntity, type == BodyType::STATIC); + // Awake the body setIsSleeping(false); - // Update the active status of currently overlapping pairs - resetOverlappingPairs(); + if (type == BodyType::STATIC) { + + // Disable overlapping pairs if both bodies are disabled (sleeping or static) + checkForDisabledOverlappingPairs(); + } // Reset the force and torque on the body mWorld.mRigidBodyComponents.setExternalForce(mEntity, Vector3::zero()); @@ -383,7 +393,7 @@ Vector3 RigidBody::computeCenterOfMass() const { Vector3 centerOfMassLocal(0, 0, 0); // Compute the local center of mass - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < colliderEntities.size(); i++) { const uint32 colliderIndex = mWorld.mCollidersComponents.getEntityIndex(colliderEntities[i]); @@ -415,7 +425,7 @@ void RigidBody::computeMassAndInertiaTensorLocal(Vector3& inertiaTensorLocal, de const Vector3 centerOfMassLocal = mWorld.mRigidBodyComponents.getCenterOfMassLocal(mEntity); // Compute the inertia tensor using all the colliders - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < colliderEntities.size(); i++) { const uint32 colliderIndex = mWorld.mCollidersComponents.getEntityIndex(colliderEntities[i]); @@ -495,7 +505,7 @@ void RigidBody::updateMassFromColliders() { decimal totalMass = decimal(0.0); // Compute the total mass of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < colliderEntities.size(); i++) { const uint32 colliderIndex = mWorld.mCollidersComponents.getEntityIndex(colliderEntities[i]); @@ -665,18 +675,15 @@ Collider* RigidBody::addCollider(CollisionShape* collisionShape, const Transform sizeof(Collider))) Collider(colliderEntity, this, mWorld.mMemoryManager); // Add the collider component to the entity of the body - Vector3 localBoundsMin; - Vector3 localBoundsMax; - // TODO : Maybe this method can directly returns an AABB - collisionShape->getLocalBounds(localBoundsMin, localBoundsMax); + AABB shapeAABB = collisionShape->getLocalBounds(); const Transform localToWorldTransform = mWorld.mTransformComponents.getTransform(mEntity) * transform; Material material(mWorld.mConfig.defaultFrictionCoefficient, mWorld.mConfig.defaultBounciness); - ColliderComponents::ColliderComponent colliderComponent(mEntity, collider, AABB(localBoundsMin, localBoundsMax), + ColliderComponents::ColliderComponent colliderComponent(mEntity, collider, shapeAABB, transform, collisionShape, 0x0001, 0xFFFF, localToWorldTransform, material); - bool isSleeping = mWorld.mRigidBodyComponents.getIsSleeping(mEntity); - mWorld.mCollidersComponents.addComponent(colliderEntity, isSleeping, colliderComponent); + bool isDisabled = mWorld.mRigidBodyComponents.getIsEntityDisabled(mEntity); + mWorld.mCollidersComponents.addComponent(colliderEntity, isDisabled, colliderComponent); - mWorld.mCollisionBodyComponents.addColliderToBody(mEntity, colliderEntity); + mWorld.mBodyComponents.addColliderToBody(mEntity, colliderEntity); // Assign the collider with the collision shape collisionShape->addCollider(collider); @@ -690,8 +697,7 @@ Collider* RigidBody::addCollider(CollisionShape* collisionShape, const Transform #endif // Compute the world-space AABB of the new collision shape - AABB aabb; - collisionShape->computeAABB(aabb, mWorld.mTransformComponents.getTransform(mEntity) * transform); + AABB aabb = collisionShape->computeTransformedAABB(mWorld.mTransformComponents.getTransform(mEntity) * transform); // Notify the collision detection about this new collision shape mWorld.mCollisionDetection.addCollider(collider, aabb); @@ -703,6 +709,8 @@ Collider* RigidBody::addCollider(CollisionShape* collisionShape, const Transform "Collider " + std::to_string(collider->getBroadPhaseId()) + ": collisionShape=" + collider->getCollisionShape()->to_string(), __FILE__, __LINE__); + mWorld.mBodyComponents.setHasSimulationCollider(mEntity, true); + // Return a pointer to the collider return collider; } @@ -714,8 +722,11 @@ Collider* RigidBody::addCollider(CollisionShape* collisionShape, const Transform */ void RigidBody::removeCollider(Collider* collider) { + // Awake all the sleeping neighbor bodies of the current one + awakeNeighborDisabledBodies(); + // Remove the collision shape - CollisionBody::removeCollider(collider); + Body::removeCollider(collider); } // Set the variable to know if the gravity is applied to this rigid body @@ -833,7 +844,13 @@ void RigidBody::setTransform(const Transform& transform) { linearVelocity += angularVelocity.cross(centerOfMassWorld - oldCenterOfMass); mWorld.mRigidBodyComponents.setLinearVelocity(mEntity, linearVelocity); - CollisionBody::setTransform(transform); + if (getType() == BodyType::STATIC) { + + mWorld.mRigidBodyComponents.setConstrainedPosition(mEntity, transform.getPosition()); + mWorld.mRigidBodyComponents.setConstrainedOrientation(mEntity, transform.getOrientation()); + } + + Body::setTransform(transform); // Awake the body if it is sleeping setIsSleeping(false); @@ -987,8 +1004,11 @@ void RigidBody::setIsSleeping(bool isSleeping) { if (isBodySleeping == isSleeping) return; + // A static body is always awake + if (mWorld.mRigidBodyComponents.getBodyType(mEntity) == BodyType::STATIC) return; + // If the body is not active, do nothing (it is sleeping) - if (!mWorld.mCollisionBodyComponents.getIsActive(mEntity)) { + if (!mWorld.mBodyComponents.getIsActive(mEntity)) { assert(isBodySleeping); return; } @@ -1007,41 +1027,119 @@ void RigidBody::setIsSleeping(bool isSleeping) { // Notify all the components mWorld.setBodyDisabled(mEntity, isSleeping); - // Update the currently overlapping pairs - resetOverlappingPairs(); - if (isSleeping) { + // Disable overlapping pairs if both bodies are disabled (sleeping or static) + checkForDisabledOverlappingPairs(); + mWorld.mRigidBodyComponents.setLinearVelocity(mEntity, Vector3::zero()); mWorld.mRigidBodyComponents.setAngularVelocity(mEntity, Vector3::zero()); mWorld.mRigidBodyComponents.setExternalForce(mEntity, Vector3::zero()); mWorld.mRigidBodyComponents.setExternalTorque(mEntity, Vector3::zero()); } + else { + + // We need to enable the currently disabled overlapping pairs + enableOverlappingPairs(); + + // Make sure the broad-phase with recompute the overlapping pairs with this body + askForBroadPhaseCollisionCheck(); + } RP3D_LOG(mWorld.mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(mEntity.id) + ": Set isSleeping=" + (isSleeping ? "true" : "false"), __FILE__, __LINE__); } -// Remove all the overlapping pairs in which this body is involved and ask the broad-phase to recompute pairs in the next frame -void RigidBody::resetOverlappingPairs() { +// Enable the currently disabled overlapping pairs +void RigidBody::enableOverlappingPairs() { // For each collider of the body - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); for (uint32 i=0; i < colliderEntities.size(); i++) { // Get the currently overlapping pairs for this collider Array overlappingPairs = mWorld.mCollidersComponents.getOverlappingPairs(colliderEntities[i]); + // We enable all the overlapping pairs (there should be only disabled overlapping pairs at this point) const uint64 nbOverlappingPairs = overlappingPairs.size(); for (uint64 j=0; j < nbOverlappingPairs; j++) { - mWorld.mCollisionDetection.mOverlappingPairs.removePair(overlappingPairs[j]); + OverlappingPairs::OverlappingPair* pair = mWorld.mCollisionDetection.mOverlappingPairs.getOverlappingPair(overlappingPairs[j]); + + if (!pair->isEnabled) { + + mWorld.mCollisionDetection.mOverlappingPairs.enablePair(overlappingPairs[j]); + } } } +} + +// Awake the disabled neighbor bodies +void RigidBody::awakeNeighborDisabledBodies() { + + // For each collider of the body + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); + for (uint32 i=0; i < colliderEntities.size(); i++) { + + // Get the currently overlapping pairs for this collider + Array overlappingPairs = mWorld.mCollidersComponents.getOverlappingPairs(colliderEntities[i]); + + const uint64 nbOverlappingPairs = overlappingPairs.size(); + for (uint64 j=0; j < nbOverlappingPairs; j++) { + + OverlappingPairs::OverlappingPair* pair = mWorld.mCollisionDetection.mOverlappingPairs.getOverlappingPair(overlappingPairs[j]); + + // If both collider where colliding in the previous frame + if (pair->collidingInPreviousFrame) { + + const Entity body1Entity = mWorld.mCollidersComponents.getBody(pair->collider1); + const Entity body2Entity = mWorld.mCollidersComponents.getBody(pair->collider2); + + const bool isCurrentBody1 = mEntity == body1Entity; - // Make sure we recompute the overlapping pairs with this body in the next frame - askForBroadPhaseCollisionCheck(); + const bool isNeighborDisabled = mWorld.mRigidBodyComponents.getIsEntityDisabled(isCurrentBody1 ? body2Entity : body1Entity); + + // If both bodies of the pair are disabled we awake the neighbor body + if (isNeighborDisabled) { + + // Awake the neighbor colliding body + RigidBody* neighborBody = mWorld.mRigidBodyComponents.getRigidBody(isCurrentBody1 ? body2Entity : body1Entity); + neighborBody->setIsSleeping(false); + } + } + } + } +} + +// Disable the overlapping pairs if both bodies are disabled (sleeping or static) +void RigidBody::checkForDisabledOverlappingPairs() { + + // For each collider of the body + const Array& colliderEntities = mWorld.mBodyComponents.getColliders(mEntity); + for (uint32 i=0; i < colliderEntities.size(); i++) { + + // Get the currently overlapping pairs for this collider + Array overlappingPairs = mWorld.mCollidersComponents.getOverlappingPairs(colliderEntities[i]); + + const uint64 nbOverlappingPairs = overlappingPairs.size(); + for (uint64 j=0; j < nbOverlappingPairs; j++) { + + OverlappingPairs::OverlappingPair* pair = mWorld.mCollisionDetection.mOverlappingPairs.getOverlappingPair(overlappingPairs[j]); + + const Entity body1Entity = mWorld.mCollidersComponents.getBody(pair->collider1); + const Entity body2Entity = mWorld.mCollidersComponents.getBody(pair->collider2); + + const bool isBody1Disabled = mWorld.mRigidBodyComponents.getIsEntityDisabled(body1Entity); + const bool isBody2Disabled = mWorld.mRigidBodyComponents.getIsEntityDisabled(body2Entity); + + // If both bodies of the pair are disabled, we disable the overlapping pair + if (isBody1Disabled && isBody2Disabled) { + + mWorld.mCollisionDetection.disableOverlappingPair(overlappingPairs[j]); + } + } + } } // Set whether or not the body is allowed to go to sleep @@ -1082,29 +1180,9 @@ bool RigidBody::isSleeping() const { void RigidBody::setIsActive(bool isActive) { // If the state does not change - if (mWorld.mCollisionBodyComponents.getIsActive(mEntity) == isActive) return; + if (mWorld.mBodyComponents.getIsActive(mEntity) == isActive) return; setIsSleeping(!isActive); - CollisionBody::setIsActive(isActive); + Body::setIsActive(isActive); } - -#ifdef IS_RP3D_PROFILING_ENABLED - - -// Set the profiler -void RigidBody::setProfiler(Profiler* profiler) { - - CollisionBody::setProfiler(profiler); - - // Set the profiler for each collider - const Array& colliderEntities = mWorld.mCollisionBodyComponents.getColliders(mEntity); - for (uint32 i=0; i < colliderEntities.size(); i++) { - - Collider* collider = mWorld.mCollidersComponents.getCollider(colliderEntities[i]); - - collider->setProfiler(profiler); - } -} - -#endif diff --git a/ext/reactphysics3d/src/collision/Collider.cpp b/ext/reactphysics3d/src/collision/Collider.cpp index c615b27c5..53bc1374e 100644 --- a/ext/reactphysics3d/src/collision/Collider.cpp +++ b/ext/reactphysics3d/src/collision/Collider.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -35,12 +35,11 @@ using namespace reactphysics3d; // Constructor /** - * @param body Pointer to the parent body - * @param shape Pointer to the collision shape - * @param transform Transformation from collision shape local-space to body local-space - * @param mass Mass of the collision shape (in kilograms) + * @param entity Entity of the collider + * @param body Pointer to the body + * @param memoryManager Reference to the memory manager */ -Collider::Collider(Entity entity, CollisionBody* body, MemoryManager& memoryManager) +Collider::Collider(Entity entity, Body* body, MemoryManager& memoryManager) :mMemoryManager(memoryManager), mEntity(entity), mBody(body), mUserData(nullptr) { @@ -112,7 +111,7 @@ void Collider::setLocalToBodyTransform(const Transform& transform) { const Transform& bodyTransform = mBody->mWorld.mTransformComponents.getTransform(mBody->getEntity()); mBody->mWorld.mCollidersComponents.setLocalToWorldTransform(mEntity, bodyTransform * transform); - RigidBody* rigidBody = static_cast(mBody); + RigidBody* rigidBody = dynamic_cast(mBody); if (rigidBody != nullptr) { rigidBody->setIsSleeping(false); } @@ -129,9 +128,8 @@ void Collider::setLocalToBodyTransform(const Transform& transform) { * @return The AABB of the collider in world-space */ const AABB Collider::getWorldAABB() const { - AABB aabb; CollisionShape* collisionShape = mBody->mWorld.mCollidersComponents.getCollisionShape(mEntity); - collisionShape->computeAABB(aabb, getLocalToWorldTransform()); + const AABB aabb = collisionShape->computeTransformedAABB(getLocalToWorldTransform()); return aabb; } @@ -178,7 +176,7 @@ bool Collider::raycast(const Ray& ray, RaycastInfo& raycastInfo) { if (!mBody->isActive()) return false; // Convert the ray into the local-space of the collision shape - const Transform localToWorldTransform = mBody->mWorld.mCollidersComponents.getLocalToWorldTransform(mEntity); + const Transform& localToWorldTransform = mBody->mWorld.mCollidersComponents.getLocalToWorldTransform(mEntity); const Transform worldToLocalTransform = localToWorldTransform.getInverse(); Ray rayLocal(worldToLocalTransform * ray.point1, worldToLocalTransform * ray.point2, ray.maxFraction); @@ -244,11 +242,61 @@ bool Collider::getIsTrigger() const { } // Set whether the collider is a trigger +/// Note that a collider cannot be a simulation collider and a trigger at the same time. If you set +/// this to true, this collider will stop being a simulation collider. /** * @param isTrigger True if you want to set this collider as a trigger and false otherwise */ void Collider::setIsTrigger(bool isTrigger) const { mBody->mWorld.mCollidersComponents.setIsTrigger(mEntity, isTrigger); + + // The collider cannot be a simulation collider anymore + if (isTrigger && getIsSimulationCollider()) { + setIsSimulationCollider(false); + } +} + +// Return true if the collider can generate contacts for the simulation of the associated body +bool Collider::getIsSimulationCollider() const { + return mBody->mWorld.mCollidersComponents.getIsSimulationCollider(mEntity); +} + +// Set whether the collider can generate contacts for the simulation of the associated body +/// Note that a collider cannot be a simulation collider and a trigger at the same time. If you set +/// this to true, this collider will stop being a trigger. +/** + * @param isSimulationCollider True if this collider can generate contacts for the simulation of the + * associated body. + */ +void Collider::setIsSimulationCollider(bool isSimulationCollider) const { + + mBody->mWorld.mCollidersComponents.setIsSimulationCollider(mEntity, isSimulationCollider); + + if (isSimulationCollider) { + + mBody->mWorld.mBodyComponents.setHasSimulationCollider(mBody->getEntity(), true); + } + else { + mBody->updateHasSimulationCollider(); + } + + // The collider cannot be a trigger anymore + if (isSimulationCollider && getIsTrigger()) { + setIsTrigger(false); + } +} + +// Return true if the collider will be part of results of queries on the PhysicsWorld +bool Collider::getIsWorldQueryCollider() const { + return mBody->mWorld.mCollidersComponents.getIsWorldQueryCollider(mEntity); +} + +// Set whether the collider will be part of results of queries on the PhysicsWorld +/** + * @param isWorldQueryCollider True if this collider will be part of results of queries on the PhysicsWorld + */ +void Collider::setIsWorldQueryCollider(bool isWorldQueryCollider) const { + mBody->mWorld.mCollidersComponents.setIsWorldQueryCollider(mEntity, isWorldQueryCollider); } // Return a reference to the material properties of the collider diff --git a/ext/reactphysics3d/src/collision/CollisionCallback.cpp b/ext/reactphysics3d/src/collision/CollisionCallback.cpp index ade108fb6..9eb109c90 100644 --- a/ext/reactphysics3d/src/collision/CollisionCallback.cpp +++ b/ext/reactphysics3d/src/collision/CollisionCallback.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -46,13 +46,13 @@ CollisionCallback::ContactPair::ContactPair(const reactphysics3d::ContactPair& c } // Return a pointer to the first body in contact -CollisionBody* CollisionCallback::ContactPair::getBody1() const { - return static_cast(mWorld.mCollisionBodyComponents.getBody(mContactPair.body1Entity)); +Body* CollisionCallback::ContactPair::getBody1() const { + return static_cast(mWorld.mBodyComponents.getBody(mContactPair.body1Entity)); } // Return a pointer to the second body in contact -CollisionBody* CollisionCallback::ContactPair::getBody2() const { - return static_cast(mWorld.mCollisionBodyComponents.getBody(mContactPair.body2Entity)); +Body* CollisionCallback::ContactPair::getBody2() const { + return static_cast(mWorld.mBodyComponents.getBody(mContactPair.body2Entity)); } // Return a pointer to the first collider in contact (in body 1) diff --git a/ext/reactphysics3d/src/collision/ContactManifold.cpp b/ext/reactphysics3d/src/collision/ContactManifold.cpp index a11f84104..09f9fa828 100644 --- a/ext/reactphysics3d/src/collision/ContactManifold.cpp +++ b/ext/reactphysics3d/src/collision/ContactManifold.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/ConvexMesh.cpp b/ext/reactphysics3d/src/collision/ConvexMesh.cpp new file mode 100644 index 000000000..e301ea6a5 --- /dev/null +++ b/ext/reactphysics3d/src/collision/ConvexMesh.cpp @@ -0,0 +1,264 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace reactphysics3d; + + +// Constructor +/** + * Create a convex mesh given an array of polygons. + * @param polygonVertexArray Pointer to the array of polygons and their vertices + */ +ConvexMesh::ConvexMesh(MemoryAllocator& allocator) + : mMemoryAllocator(allocator), mHalfEdgeStructure(allocator, 6, 8, 24), + mVertices(allocator), mFacesNormals(allocator), mVolume(0) { + +} + +// Initialize a mesh and returns errors if any +bool ConvexMesh::init(const PolygonVertexArray& polygonVertexArray, std::vector& errors) { + + bool isValid = true; + + // Reserve memory for the vertices, faces and edges + mVertices.reserve(polygonVertexArray.getNbVertices()); + mFacesNormals.reserve(polygonVertexArray.getNbFaces()); + mHalfEdgeStructure.reserve(polygonVertexArray.getNbFaces(), polygonVertexArray.getNbVertices(), + (polygonVertexArray.getNbVertices() + polygonVertexArray.getNbFaces() - 2) * 2); + + // Copy the vertices from the PolygonVertexArray into the mesh + isValid &= copyVertices(polygonVertexArray, errors); + + // Create the half-edge structure of the mesh + isValid &= createHalfEdgeStructure(polygonVertexArray, errors); + + // Compute the faces normals + isValid &= computeFacesNormals(errors); + + // Compute the volume of the mesh + computeVolume(); + + return isValid; +} + +// Copy the vertices into the mesh +bool ConvexMesh::copyVertices(const PolygonVertexArray& polygonVertexArray, std::vector& errors) { + + bool isValid = true; + + mCentroid.setToZero(); + + if (polygonVertexArray.getNbVertices() > 0) { + mBounds.setMin(polygonVertexArray.getVertex(0)); + mBounds.setMax( polygonVertexArray.getVertex(0)); + } + + // For each vertex + for (uint32 i=0 ; i < polygonVertexArray.getNbVertices(); i++) { + + const Vector3 vertex = polygonVertexArray.getVertex(i); + mVertices.add(vertex); + + // Compute centroid + mCentroid += vertex; + + // Inflate the bounds of the mesh with the vertex (if necessary) + mBounds.inflateWithPoint(vertex); + } + + if (getNbVertices() > 0) { + + mCentroid /= static_cast(getNbVertices()); + } + else { + + errors.push_back(Message("The mesh does not have any vertices")); + + isValid = false; + } + + return isValid; +} + +// Create the half-edge structure of the mesh +/// This method returns true if the mesh is valid or false otherwise +bool ConvexMesh::createHalfEdgeStructure(const PolygonVertexArray& polygonVertexArray, std::vector& errors) { + + bool isValid = true; + + // For each vertex of the mesh + const uint32 nbVertices = polygonVertexArray.getNbVertices(); + for (uint32 v=0; v < nbVertices; v++) { + mHalfEdgeStructure.addVertex(v); + } + + uint32 nbEdges = 0; + + // For each polygon face of the mesh + const uint32 nbFaces = polygonVertexArray.getNbFaces(); + for (uint32 f=0; f < nbFaces; f++) { + + // Get the polygon face + PolygonVertexArray::PolygonFace* face = polygonVertexArray.getPolygonFace(f); + + Array faceVertices(mMemoryAllocator, face->nbVertices); + + // For each vertex of the face + for (uint32 v=0; v < face->nbVertices; v++) { + faceVertices.add(polygonVertexArray.getVertexIndexInFace(f, v)); + } + + nbEdges += face->nbVertices; + + // If the face has less than three vertices + if (faceVertices.size() < 3) { + + // Create a new error + errors.push_back(Message(std::string("The face with index " + std::to_string(f) + " has less than three vertices"))); + + isValid = false; + + RP3D_LOG("PhysicsCommon", Logger::Level::Error, Logger::Category::PhysicCommon, + "Error when creating a ConvexMesh: Mesh has a face with less than three vertices.", __FILE__, __LINE__); + } + + // Addd the face into the half-edge structure + mHalfEdgeStructure.addFace(faceVertices); + } + + nbEdges /= 2; + + // If the mesh is not valid (check Euler formula V + F - E = 2) (has duplicated vertices) + if (2 + nbEdges - polygonVertexArray.getNbFaces() != polygonVertexArray.getNbVertices()) { + + RP3D_LOG("PhysicsCommon", Logger::Level::Error, Logger::Category::PhysicCommon, + "Error when creating a ConvexMesh: input PolygonVertexArray is not valid. Mesh with duplicated vertices is not supported.", __FILE__, __LINE__); + + // Create a new error + errors.push_back(Message(std::string("Convex Mesh might have duplicated vertices (invalid Euler formula)"))); + + isValid = false; + } + + //if (isValid) { + + // Initialize the half-edge structure + mHalfEdgeStructure.computeHalfEdges(); + //} + + return isValid; +} + +// Compute the faces normals +bool ConvexMesh::computeFacesNormals(std::vector& errors) { + + bool isValid = true; + + mFacesNormals.clear(); + + // For each face + const uint32 nbFaces = mHalfEdgeStructure.getNbFaces(); + for (uint32 f=0; f < nbFaces; f++) { + + const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(f); + + if (face.faceVertices.size() >= 3) { + + mFacesNormals.add(computeFaceNormal(f)); + decimal normalLength = mFacesNormals[f].length(); + + if (normalLength > MACHINE_EPSILON) { + + mFacesNormals[f] /= normalLength; + } + else { + isValid = false; + errors.push_back(Message("Face with index " + std::to_string(f) + " has a zero area")); + } + } + } + + return isValid; +} + +// Compute and return the face normal (not normalized) +Vector3 ConvexMesh::computeFaceNormal(uint32 faceIndex) const { + + Vector3 normal(0, 0, 0); + + const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(faceIndex); + assert(face.faceVertices.size() >= 3); + + // Use Newell's method to compute the face normal + for (uint32 i = face.faceVertices.size() - 1, j = 0; j < face.faceVertices.size(); i = j, j++) { + + const Vector3& v1 = getVertex(face.faceVertices[i]); + const Vector3& v2 = getVertex(face.faceVertices[j]); + + normal += Vector3((v1.y - v2.y) * (v1.z + v2.z), + (v1.z - v2.z) * (v1.x + v2.x), + (v1.x - v2.x) * (v1.y + v2.y)); + } + + return normal; +} + +// Return the minimum bounds of the mesh in the x,y,z direction +/** + * @return The three mimimum bounds of the mesh in the x,y,z direction + */ +const AABB& ConvexMesh::getBounds() const { + return mBounds; +} + +// Compute the volume of the convex mesh +/// We use the divergence theorem to compute the volume of the convex mesh using a sum over its faces. +void ConvexMesh::computeVolume() { + + decimal sum = 0.0; + + // For each face of the mesh + for (uint32 f=0; f < getNbFaces(); f++) { + + const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(f); + const decimal faceArea = computeFaceNormal(f).length() * decimal(0.5); + const Vector3 faceNormal = mFacesNormals[f]; + const Vector3& faceVertex = getVertex(face.faceVertices[0]); + + sum += faceVertex.dot(faceNormal) * faceArea; + } + + mVolume = std::abs(sum) / decimal(3.0); +} diff --git a/ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp b/ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp index 7a0818406..eb8320a49 100644 --- a/ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp +++ b/ext/reactphysics3d/src/collision/HalfEdgeStructure.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -32,7 +32,7 @@ using namespace reactphysics3d; // Initialize the structure (when all vertices and faces have been added) -void HalfEdgeStructure::init() { +void HalfEdgeStructure::computeHalfEdges() { Map edges(mAllocator); Map nextEdges(mAllocator); @@ -119,3 +119,71 @@ void HalfEdgeStructure::init() { mFaces[f].edgeIndex = mapEdgeToIndex[mapFaceIndexToEdgeKey[f]]; } } + +// Reserve some memory for vertices, faces and edges +void HalfEdgeStructure::reserve(uint32 facesCapacity, uint32 verticesCapacity, uint32 edgesCapacity) { + + mFaces.reserve(facesCapacity); + mVertices.reserve(verticesCapacity); + mEdges.reserve(edgesCapacity); + +} + +// Return a string representation of the half-edge structure +std::string HalfEdgeStructure::to_string() const { + + std::string faces = "Faces{"; + for (uint32 i=0; i < mFaces.size(); i++) { + + faces += "Face" + std::to_string(i) + "("; + + const Face& face = mFaces[i]; + + for (uint32 v=0; v < face.faceVertices.size(); v++) { + + faces += std::to_string(mVertices[face.faceVertices[v]].vertexPointIndex); + + if (v < face.faceVertices.size() - 1) { + faces += ","; + } + } + + faces += ")"; + + if (i < mFaces.size() - 1) { + faces += ","; + } + } + + faces += "}"; + + std::string edges = "Edges{"; + for (uint32 i=0; i < mEdges.size(); i++) { + + edges += "Edge" + std::to_string(i) + "("; + + const Edge& edge = mEdges[i]; + const Edge& twinEdge = mEdges[edge.twinEdgeIndex]; + + edges += std::to_string(mVertices[edge.vertexIndex].vertexPointIndex) + "," + std::to_string(mVertices[twinEdge.vertexIndex].vertexPointIndex) + ")"; + + if (i < mEdges.size() - 1) { + edges += ","; + } + } + + std::string vertices = "Vertices{"; + for (uint32 i=0; i < mVertices.size(); i++) { + + vertices += "Vertex" + std::to_string(i) + "("; + + const Vertex& vertex = mVertices[i]; + + vertices += std::to_string(vertex.vertexPointIndex) + ")"; + + if (i < mVertices.size() - 1) { + vertices += ","; + } + } + return "HalfEdgeStructure(" + faces + ",\n" + edges + ",\n" + vertices + ")"; +} diff --git a/ext/reactphysics3d/src/collision/HeightField.cpp b/ext/reactphysics3d/src/collision/HeightField.cpp new file mode 100644 index 000000000..4ee27006f --- /dev/null +++ b/ext/reactphysics3d/src/collision/HeightField.cpp @@ -0,0 +1,428 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include +#include +#include +#include +#include +#include +#include + +using namespace reactphysics3d; + +// Constructor +HeightField::HeightField(MemoryAllocator& allocator, HalfEdgeStructure& triangleHalfEdgeStructure) + : mAllocator(allocator), mHeightFieldData(allocator), + mTriangleHalfEdgeStructure(triangleHalfEdgeStructure) { + +} + +// Initialize the height-field +bool HeightField::init(int nbGridColumns, int nbGridRows, + const void* heightFieldData, HeightDataType dataType, + std::vector& messages, decimal integerHeightScale) { + + bool isValid = true; + + if (nbGridColumns < 2 || nbGridRows < 2) { + + // Add a warning message for the user + messages.push_back(Message("The number of grid columns and grid rows must be at least two", Message::Type::Error)); + return false; + } + + mHeightFieldData.reserve(nbGridColumns * nbGridRows); + mHeightFieldData.addWithoutInit(nbGridColumns * nbGridRows); + + mNbColumns = nbGridColumns; + mNbRows = nbGridRows; + mWidth = static_cast(nbGridColumns - 1); + mLength = static_cast(nbGridRows - 1); + mIntegerHeightScale = integerHeightScale; + mHeightDataType = dataType; + + // Copy the height values from the user into the height-field + copyData(heightFieldData); + + assert(mMinHeight <= mMaxHeight); + + const decimal halfHeight = (mMaxHeight - mMinHeight) * decimal(0.5); + assert(halfHeight >= 0); + + assert(mWidth >= 1); + assert(mLength >= 1); + + // Compute the local AABB of the height field + mBounds.setMin(Vector3(-mWidth * decimal(0.5), -halfHeight, -mLength * decimal(0.5))); + mBounds.setMax(Vector3(mWidth * decimal(0.5), halfHeight, mLength * decimal(0.5))); + + assert(mHeightFieldData.size() == mNbRows * mNbColumns); + + return isValid; +} + +// Copy the data from the user into the height-field array +void HeightField::copyData(const void* heightFieldData) { + + // For each height value + for (uint32 x=0; x < mNbColumns; x++) { + + for (uint32 y=0; y < mNbRows; y++) { + + decimal height = 0.0; + + switch(mHeightDataType) { + case HeightDataType::HEIGHT_FLOAT_TYPE: + height = decimal(((float*)heightFieldData)[y * mNbColumns + x]); + break; + case HeightDataType::HEIGHT_DOUBLE_TYPE: + height = decimal(((double*)heightFieldData)[y * mNbColumns + x]); + break; + case HeightDataType::HEIGHT_INT_TYPE: + height = decimal(((int*)heightFieldData)[y * mNbColumns + x] * mIntegerHeightScale); + break; + default: + assert(false); // This should never happen + } + + mHeightFieldData[y * mNbColumns + x] = height; + + if (x == 0 && y == 0) { + mMinHeight = height; + mMaxHeight = height; + } + + // Compute minimum height + if (height < mMinHeight) { + mMinHeight = height; + } + + // Compute maximum height + if (height > mMaxHeight) { + mMaxHeight = height; + } + } + } + + // Compute the height origin + mHeightOrigin = -(mMaxHeight - mMinHeight) * decimal(0.5) - mMinHeight; +} + +// Test collision with the triangles of the height field shape. The idea is to use the AABB +// of the body when need to test and see against which triangles of the height-field we need +// to test for collision. We compute the sub-grid points that are inside the other body's AABB +// and then for each rectangle in the sub-grid we generate two triangles that we use to test collision. +void HeightField::computeOverlappingTriangles(const AABB& aabb, Array& triangleVertices, + Array& triangleVerticesNormals, + Array& shapeIds, const Vector3& scale) const { + + RP3D_PROFILE("HeightField::computeOverlappingTriangles()", mProfiler); + + // Compute the integer grid coordinates inside the area we need to test for collision + uint32 minGridCoords[3]; + uint32 maxGridCoords[3]; + computeMinMaxGridCoordinates(minGridCoords, maxGridCoords, aabb); + + // Compute the starting and ending coords of the sub-grid according to the up axis + uint32 iMin = clamp(minGridCoords[0], 0, mNbColumns - 1); + uint32 iMax = clamp(maxGridCoords[0], 0, mNbColumns - 1); + uint32 jMin = clamp(minGridCoords[2], 0, mNbRows - 1); + uint32 jMax = clamp(maxGridCoords[2], 0, mNbRows - 1); + + assert(iMin < mNbColumns); + assert(iMax < mNbColumns); + assert(jMin < mNbRows); + assert(jMax < mNbRows); + + // For each sub-grid points (except the last ones one each dimension) + for (uint32 i = iMin; i < iMax; i++) { + for (uint32 j = jMin; j < jMax; j++) { + + // Compute the four point of the current quad + const Vector3 p1 = getVertexAt(i, j) * scale; + const Vector3 p2 = getVertexAt(i, j + 1) * scale; + const Vector3 p3 = getVertexAt(i + 1, j) * scale; + const Vector3 p4 = getVertexAt(i + 1, j + 1) * scale; + + // Generate the first triangle for the current grid rectangle + triangleVertices.add(p1); + triangleVertices.add(p2); + triangleVertices.add(p3); + + // Compute the triangle normal + Vector3 triangle1Normal = (p2 - p1).cross(p3 - p1).getUnit(); + + // Use the triangle face normal as vertices normals (this is an aproximation. The correct + // solution would be to compute all the normals of the neighbor triangles and use their + // weighted average (with incident angle as weight) at the vertices. However, this solution + // seems too expensive (it requires to compute the normal of all neighbor triangles instead + // and compute the angle of incident edges with asin(). Maybe we could also precompute the + // vertices normal at the HeightFieldShape constructor but it will require extra memory to + // store them. + triangleVerticesNormals.add(triangle1Normal); + triangleVerticesNormals.add(triangle1Normal); + triangleVerticesNormals.add(triangle1Normal); + + // Compute the shape ID + shapeIds.add(computeTriangleShapeId(i, j, 0)); + + // Generate the second triangle for the current grid rectangle + triangleVertices.add(p3); + triangleVertices.add(p2); + triangleVertices.add(p4); + + // Compute the triangle normal + Vector3 triangle2Normal = (p2 - p3).cross(p4 - p3).getUnit(); + + // Use the triangle face normal as vertices normals (this is an aproximation. The correct + // solution would be to compute all the normals of the neighbor triangles and use their + // weighted average (with incident angle as weight) at the vertices. However, this solution + // seems too expensive (it requires to compute the normal of all neighbor triangles instead + // and compute the angle of incident edges with asin(). Maybe we could also precompute the + // vertices normal at the HeightFieldShape constructor but it will require extra memory to + // store them. + triangleVerticesNormals.add(triangle2Normal); + triangleVerticesNormals.add(triangle2Normal); + triangleVerticesNormals.add(triangle2Normal); + + // Compute the shape ID + shapeIds.add(computeTriangleShapeId(i, j, 1)); + } + } +} + +// Compute the min/max grid coords corresponding to the intersection of the AABB of the height field and +// the AABB to collide +void HeightField::computeMinMaxGridCoordinates(uint32* minCoords, uint32* maxCoords, const AABB& aabbToCollide) const { + + // Clamp the min/max coords of the AABB to collide inside the height field AABB + Vector3 minPoint = Vector3::max(aabbToCollide.getMin(), mBounds.getMin()); + minPoint = Vector3::min(minPoint, mBounds.getMax()); + + Vector3 maxPoint = Vector3::min(aabbToCollide.getMax(), mBounds.getMax()); + maxPoint = Vector3::max(maxPoint, mBounds.getMin()); + + // Translate the min/max points such that the we compute grid points from [0 ... mNbWidthGridPoints] + // and from [0 ... mNbLengthGridPoints] because the AABB coordinates range are [-mWdith/2 ... mWidth/2] + // and [-mLength/2 ... mLength/2] + const Vector3 translateVec = mBounds.getExtent() * decimal(0.5); + minPoint += translateVec; + maxPoint += translateVec; + + assert(minPoint.x >= 0); + assert(minPoint.y >= 0); + assert(minPoint.z >= 0); + assert(maxPoint.x >= 0); + assert(maxPoint.y >= 0); + assert(maxPoint.z >= 0); + + // Convert the floating min/max coords of the AABB into closest integer + // grid values (note that we use the closest grid coordinate that is out + // of the AABB) + minCoords[0] = static_cast(minPoint.x + 0.5) - 1; + minCoords[1] = static_cast(minPoint.y + 0.5) - 1; + minCoords[2] = static_cast(minPoint.z + 0.5) - 1; + + maxCoords[0] = static_cast(maxPoint.x + 0.5) + 1; + maxCoords[1] = static_cast(maxPoint.y + 0.5) + 1; + maxCoords[2] = static_cast(maxPoint.z + 0.5) + 1; +} + +// Raycast method with feedback information +/// Note that only the first triangle hit by the ray in the mesh will be returned, even if +/// the ray hits many triangles. +bool HeightField::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* collider, TriangleRaycastSide testSide, + MemoryAllocator& allocator) const { + + RP3D_PROFILE("HeightField::raycast()", mProfiler); + + bool isHit = false; + + // Compute the grid coordinates where the ray is entering the AABB of the height field + int32 i, j; + Vector3 outHitGridPoint; + if (computeEnteringRayGridCoordinates(ray, i, j, outHitGridPoint)) { + + const int32 nbCellsI = mNbColumns - 1; + const int32 nbCellsJ = mNbRows - 1; + + const Vector3 aabbSize = mBounds.getExtent(); + + const Vector3 rayDirection = ray.point2 - ray.point1; + + int32 stepI = rayDirection.x > 0 ? 1 : (rayDirection.x < 0 ? -1 : 0); + int32 stepJ = rayDirection.z > 0 ? 1 : (rayDirection.z < 0 ? -1 : 0); + decimal nextI = static_cast(stepI >= 0 ? i + 1 : i); + decimal nextJ = static_cast(stepJ >= 0 ? j + 1 : j); + decimal sizeI = aabbSize.x / nbCellsI; + decimal sizeJ = aabbSize.z / nbCellsJ; + decimal tMaxI = ((nextI * sizeI) - outHitGridPoint.x) / rayDirection.x; + decimal tMaxJ = ((nextJ * sizeJ) - outHitGridPoint.z) / rayDirection.z; + decimal tDeltaI = sizeI / std::abs(rayDirection.x); + decimal tDeltaJ = sizeJ / std::abs(rayDirection.z); + + decimal smallestHitFraction = ray.maxFraction; + + while (i >= 0 && i < nbCellsI && j >= 0 && j < nbCellsJ) { + + // Compute the four point of the current quad + const Vector3 p1 = getVertexAt(i, j); + const Vector3 p2 = getVertexAt(i, j + 1); + const Vector3 p3 = getVertexAt(i + 1, j); + const Vector3 p4 = getVertexAt(i + 1, j + 1); + + // Raycast against the first triangle of the cell + uint32 shapeId = computeTriangleShapeId(i, j, 0); + isHit |= raycastTriangle(ray, p1, p2, p3, shapeId, collider, raycastInfo, smallestHitFraction, testSide, allocator); + + // Raycast against the second triangle of the cell + shapeId = computeTriangleShapeId(i, j, 1); + isHit |= raycastTriangle(ray, p3, p2, p4, shapeId, collider, raycastInfo, smallestHitFraction, testSide, allocator); + + if (stepI == 0 && stepJ == 0) break; + + if (tMaxI < tMaxJ) { + tMaxI += tDeltaI; + i += stepI; + } + else { + tMaxJ += tDeltaJ; + j += stepJ; + } + } + } + + return isHit; +} + +// Raycast a single triangle of the height-field +bool HeightField::raycastTriangle(const Ray& ray, const Vector3& p1, const Vector3& p2, const Vector3& p3, uint32 shapeId, + Collider* collider, RaycastInfo& raycastInfo, decimal& smallestHitFraction, + TriangleRaycastSide testSide, MemoryAllocator& allocator) const { + + // Generate the first triangle for the current grid rectangle + Vector3 triangleVertices[3] = {p1, p2, p3}; + + // Create a triangle collision shape + TriangleShape triangleShape(triangleVertices, shapeId, mTriangleHalfEdgeStructure, allocator); + triangleShape.setRaycastTestType(testSide); + +#ifdef IS_RP3D_PROFILING_ENABLED + + + // Set the profiler to the triangle shape + triangleShape.setProfiler(mProfiler); + +#endif + + // Ray casting test against the collision shape + RaycastInfo triangleRaycastInfo; + bool isTriangleHit = triangleShape.raycast(ray, triangleRaycastInfo, collider, allocator); + + // If the ray hit the collision shape + if (isTriangleHit && triangleRaycastInfo.hitFraction <= smallestHitFraction) { + + assert(triangleRaycastInfo.hitFraction >= decimal(0.0)); + + raycastInfo.body = triangleRaycastInfo.body; + raycastInfo.collider = triangleRaycastInfo.collider; + raycastInfo.hitFraction = triangleRaycastInfo.hitFraction; + raycastInfo.worldPoint = triangleRaycastInfo.worldPoint; + raycastInfo.worldNormal = triangleRaycastInfo.worldNormal; + raycastInfo.triangleIndex = -1; + + smallestHitFraction = triangleRaycastInfo.hitFraction; + + return true; + } + + return false; +} + +// Compute the first grid cell of the heightfield intersected by a ray. +/// This method returns true if the ray hit the AABB of the height field and false otherwise +bool HeightField::computeEnteringRayGridCoordinates(const Ray& ray, int32& i, int32& j, Vector3& outHitGridPoint) const { + + decimal stepI, stepJ; + const Vector3 aabbSize = mBounds.getExtent(); + + assert(mNbColumns > 0); + assert(mNbRows > 0); + + const int32 nbCellsI = mNbColumns - 1; + const int32 nbCellsJ = mNbRows - 1; + + if (mBounds.raycast(ray, outHitGridPoint)) { + + // Map the hit point into the grid range [0, mNbColumns - 1], [0, mNbRows - 1] + outHitGridPoint -= mBounds.getMin(); + + stepI = aabbSize.x / nbCellsI; + stepJ = aabbSize.z / nbCellsJ; + i = clamp(int(outHitGridPoint.x / stepI), 0, nbCellsI - 1); + j = clamp(int(outHitGridPoint.z / stepJ), 0, nbCellsJ - 1); + + assert(i >= 0 && i < nbCellsI); + assert(i >= 0 && j < nbCellsJ); + + return true; + } + + return false; +} + +// Return the vertex (local-coordinates) of the height field at a given (x,y) position +Vector3 HeightField::getVertexAt(uint32 x, uint32 y) const { + + // Get the height value + const decimal height = getHeightAt(x, y); + + const Vector3 vertex = Vector3(-mWidth * decimal(0.5) + x, mHeightOrigin + height, -mLength * decimal(0.5) + y); + + assert(mBounds.contains(vertex, decimal(0.0001))); + + return vertex; +} + +// Return the string representation of the shape +std::string HeightField::to_string() const { + + std::stringstream ss; + + ss << "HeightFiel{" << std::endl; + + ss << "nbColumns=" << mNbColumns << std::endl; + ss << ", nbRows=" << mNbRows << std::endl; + ss << ", width=" << mWidth << std::endl; + ss << ", length=" << mLength << std::endl; + ss << ", minHeight=" << mMinHeight << std::endl; + ss << ", maxHeight=" << mMaxHeight << std::endl; + ss << ", integerHeightScale=" << mIntegerHeightScale << std::endl; + ss << "}"; + + return ss.str(); +} diff --git a/ext/reactphysics3d/src/collision/OverlapCallback.cpp b/ext/reactphysics3d/src/collision/OverlapCallback.cpp index 2b1147eb1..8ef1c7aac 100644 --- a/ext/reactphysics3d/src/collision/OverlapCallback.cpp +++ b/ext/reactphysics3d/src/collision/OverlapCallback.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -47,13 +47,13 @@ Collider* OverlapCallback::OverlapPair::getCollider2() const { } // Return a pointer to the first body in contact -CollisionBody* OverlapCallback::OverlapPair::getBody1() const { - return static_cast(mWorld.mCollisionBodyComponents.getBody(mContactPair.body1Entity)); +Body* OverlapCallback::OverlapPair::getBody1() const { + return static_cast(mWorld.mBodyComponents.getBody(mContactPair.body1Entity)); } // Return a pointer to the second body in contact -CollisionBody* OverlapCallback::OverlapPair::getBody2() const { - return static_cast(mWorld.mCollisionBodyComponents.getBody(mContactPair.body2Entity)); +Body* OverlapCallback::OverlapPair::getBody2() const { + return static_cast(mWorld.mBodyComponents.getBody(mContactPair.body2Entity)); } // Return the corresponding type of event for this overlapping pair diff --git a/ext/reactphysics3d/src/collision/PolygonVertexArray.cpp b/ext/reactphysics3d/src/collision/PolygonVertexArray.cpp index d4d595eab..a5fba9e96 100644 --- a/ext/reactphysics3d/src/collision/PolygonVertexArray.cpp +++ b/ext/reactphysics3d/src/collision/PolygonVertexArray.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -25,9 +25,15 @@ // Libraries #include +#include using namespace reactphysics3d; +// Constructor +PolygonVertexArray::PolygonVertexArray() { + +} + // Constructor /// Note that your data will not be copied into the PolygonVertexArray and /// therefore, you need to make sure that those data are always valid during @@ -39,7 +45,7 @@ using namespace reactphysics3d; * @param indexesStart Pointer to the start of the face indices data * @param indexesStride The number of bytes between two consecutive face indices in the array * @param nbFaces The number of faces in the array - * @param nbFaces Pointer to the start of the faces data + * @param facesStart Pointer to the start of the faces data * @param vertexDataType Data type of the vertices data * @param indexDataType Data type of the face indices data */ @@ -47,6 +53,18 @@ PolygonVertexArray::PolygonVertexArray(uint32 nbVertices, const void* verticesSt const void* indexesStart, uint32 indexesStride, uint32 nbFaces, PolygonFace* facesStart, VertexDataType vertexDataType, IndexDataType indexDataType) { + + init(nbVertices, verticesStart, verticesStride, indexesStart, indexesStride, nbFaces, + facesStart, vertexDataType, indexDataType); + +} + +// Initialize the PolygonVertexArray +void PolygonVertexArray::init(uint32 nbVertices, const void* verticesStart, uint32 verticesStride, + const void* indexesStart, uint32 indexesStride, + uint32 nbFaces, PolygonFace* facesStart, + VertexDataType vertexDataType, IndexDataType indexDataType) { + mNbVertices = nbVertices; mVerticesStart = reinterpret_cast(verticesStart); mVerticesStride = verticesStride; @@ -85,3 +103,27 @@ uint32 PolygonVertexArray::getVertexIndexInFace(uint32 faceIndex32, uint32 noVer return 0; } + +// Return the coordinates of a given vertex +Vector3 PolygonVertexArray::getVertex(uint32 vertexIndex) const { + + Vector3 vertex; + + if (mVertexDataType == PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE) { + const float* vertices = (float*)(mVerticesStart + vertexIndex * mVerticesStride); + vertex.x = decimal(vertices[0]); + vertex.y = decimal(vertices[1]); + vertex.z = decimal(vertices[2]); + } + else if (mVertexDataType == PolygonVertexArray::VertexDataType::VERTEX_DOUBLE_TYPE) { + const double* vertices = (double*)(mVerticesStart + vertexIndex * mVerticesStride); + vertex.x = decimal(vertices[0]); + vertex.y = decimal(vertices[1]); + vertex.z = decimal(vertices[2]); + } + else { + assert(false); + } + + return vertex; +} diff --git a/ext/reactphysics3d/src/collision/PolyhedronMesh.cpp b/ext/reactphysics3d/src/collision/PolyhedronMesh.cpp deleted file mode 100644 index 58e08e742..000000000 --- a/ext/reactphysics3d/src/collision/PolyhedronMesh.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/******************************************************************************** -* ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * -********************************************************************************* -* * -* This software is provided 'as-is', without any express or implied warranty. * -* In no event will the authors be held liable for any damages arising from the * -* use of this software. * -* * -* Permission is granted to anyone to use this software for any purpose, * -* including commercial applications, and to alter it and redistribute it * -* freely, subject to the following restrictions: * -* * -* 1. The origin of this software must not be misrepresented; you must not claim * -* that you wrote the original software. If you use this software in a * -* product, an acknowledgment in the product documentation would be * -* appreciated but is not required. * -* * -* 2. Altered source versions must be plainly marked as such, and must not be * -* misrepresented as being the original software. * -* * -* 3. This notice may not be removed or altered from any source distribution. * -* * -********************************************************************************/ - -// Libraries -#include -#include -#include -#include -#include -#include - -using namespace reactphysics3d; - - -// Constructor -/** - * Create a polyhedron mesh given an array of polygons. - * @param polygonVertexArray Pointer to the array of polygons and their vertices - */ -PolyhedronMesh::PolyhedronMesh(PolygonVertexArray* polygonVertexArray, MemoryAllocator& allocator) - : mMemoryAllocator(allocator), mHalfEdgeStructure(allocator, polygonVertexArray->getNbFaces(), polygonVertexArray->getNbVertices(), - (polygonVertexArray->getNbFaces() + polygonVertexArray->getNbVertices() - 2) * 2), mFacesNormals(nullptr) { - - mPolygonVertexArray = polygonVertexArray; -} - -// Destructor -PolyhedronMesh::~PolyhedronMesh() { - - if (mFacesNormals != nullptr) { - - for (uint32 f=0; f < mHalfEdgeStructure.getNbFaces(); f++) { - mFacesNormals[f].~Vector3(); - } - - mMemoryAllocator.release(mFacesNormals, mHalfEdgeStructure.getNbFaces() * sizeof(Vector3)); - } -} - -/// Static factory method to create a polyhedron mesh. This methods returns null_ptr if the mesh is not valid -PolyhedronMesh* PolyhedronMesh::create(PolygonVertexArray* polygonVertexArray, MemoryAllocator& polyhedronMeshAllocator, MemoryAllocator& dataAllocator) { - - PolyhedronMesh* mesh = new (polyhedronMeshAllocator.allocate(sizeof(PolyhedronMesh))) PolyhedronMesh(polygonVertexArray, dataAllocator); - - // Create the half-edge structure of the mesh - bool isValid = mesh->createHalfEdgeStructure(); - - if (isValid) { - - // Compute the faces normals - mesh->computeFacesNormals(); - - // Compute the centroid - mesh->computeCentroid(); - } - else { - mesh->~PolyhedronMesh(); - polyhedronMeshAllocator.release(mesh, sizeof(PolyhedronMesh)); - mesh = nullptr; - } - - return mesh; -} - -// Create the half-edge structure of the mesh -/// This method returns true if the mesh is valid or false otherwise -bool PolyhedronMesh::createHalfEdgeStructure() { - - // For each vertex of the mesh - for (uint32 v=0; v < mPolygonVertexArray->getNbVertices(); v++) { - mHalfEdgeStructure.addVertex(v); - } - - uint32 nbEdges = 0; - - // For each polygon face of the mesh - for (uint32 f=0; f < mPolygonVertexArray->getNbFaces(); f++) { - - // Get the polygon face - PolygonVertexArray::PolygonFace* face = mPolygonVertexArray->getPolygonFace(f); - - Array faceVertices(mMemoryAllocator, face->nbVertices); - - // For each vertex of the face - for (uint32 v=0; v < face->nbVertices; v++) { - faceVertices.add(mPolygonVertexArray->getVertexIndexInFace(f, v)); - } - - nbEdges += face->nbVertices; - - assert(faceVertices.size() >= 3); - - // Addd the face into the half-edge structure - mHalfEdgeStructure.addFace(faceVertices); - } - - nbEdges /= 2; - - // If the mesh is valid (check Euler formula V + F - E = 2) and does not use duplicated vertices - if (2 + nbEdges - mPolygonVertexArray->getNbFaces() != mPolygonVertexArray->getNbVertices()) { - - RP3D_LOG("PhysicsCommon", Logger::Level::Error, Logger::Category::PhysicCommon, - "Error when creating a PolyhedronMesh: input PolygonVertexArray is not valid. Mesh with duplicated vertices is not supported.", __FILE__, __LINE__); - - assert(false); - - return false; - } - - // Initialize the half-edge structure - mHalfEdgeStructure.init(); - - // Create the face normals array - mFacesNormals = new (mMemoryAllocator.allocate(mHalfEdgeStructure.getNbFaces() * sizeof(Vector3))) Vector3[mHalfEdgeStructure.getNbFaces()]; - - return true; -} - -/// Return a vertex -/** - * @param index Index of a given vertex in the mesh - * @return The coordinates of a given vertex in the mesh - */ -Vector3 PolyhedronMesh::getVertex(uint32 index) const { - assert(index < getNbVertices()); - - // Get the vertex index in the array with all vertices - uint32 vertexIndex = mHalfEdgeStructure.getVertex(index).vertexPointIndex; - - PolygonVertexArray::VertexDataType vertexType = mPolygonVertexArray->getVertexDataType(); - const unsigned char* verticesStart = mPolygonVertexArray->getVerticesStart(); - uint32 vertexStride = mPolygonVertexArray->getVerticesStride(); - - Vector3 vertex; - if (vertexType == PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE) { - const float* vertices = (float*)(verticesStart + vertexIndex * vertexStride); - vertex.x = decimal(vertices[0]); - vertex.y = decimal(vertices[1]); - vertex.z = decimal(vertices[2]); - } - else if (vertexType == PolygonVertexArray::VertexDataType::VERTEX_DOUBLE_TYPE) { - const double* vertices = (double*)(verticesStart + vertexIndex * vertexStride); - vertex.x = decimal(vertices[0]); - vertex.y = decimal(vertices[1]); - vertex.z = decimal(vertices[2]); - } - else { - assert(false); - } - - return vertex; -} - -// Compute the faces normals -void PolyhedronMesh::computeFacesNormals() { - - // For each face - const uint32 nbFaces = mHalfEdgeStructure.getNbFaces(); - for (uint32 f=0; f < nbFaces; f++) { - const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(f); - - assert(face.faceVertices.size() >= 3); - - const Vector3 vec1 = getVertex(face.faceVertices[1]) - getVertex(face.faceVertices[0]); - const Vector3 vec2 = getVertex(face.faceVertices[2]) - getVertex(face.faceVertices[0]); - mFacesNormals[f] = vec1.cross(vec2); - mFacesNormals[f].normalize(); - } -} - -// Compute the centroid of the polyhedron -void PolyhedronMesh::computeCentroid() { - - mCentroid.setToZero(); - - for (uint32 v=0; v < getNbVertices(); v++) { - mCentroid += getVertex(v); - } - - mCentroid /= static_cast(getNbVertices()); -} - -// Compute and return the area of a face -decimal PolyhedronMesh::getFaceArea(uint32 faceIndex) const { - - Vector3 sumCrossProducts(0, 0, 0); - - const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(faceIndex); - assert(face.faceVertices.size() >= 3); - - Vector3 v1 = getVertex(face.faceVertices[0]); - - // For each vertex of the face - const uint32 nbFaceVertices = static_cast(face.faceVertices.size()); - for (uint32 i=2; i < nbFaceVertices; i++) { - - const Vector3 v2 = getVertex(face.faceVertices[i-1]); - const Vector3 v3 = getVertex(face.faceVertices[i]); - - const Vector3 v1v2 = v2 - v1; - const Vector3 v1v3 = v3 - v1; - - sumCrossProducts += v1v2.cross(v1v3); - } - - return decimal(0.5) * sumCrossProducts.length(); -} - -// Compute and return the volume of the polyhedron -/// We use the divergence theorem to compute the volume of the polyhedron using a sum over its faces. -decimal PolyhedronMesh::getVolume() const { - - decimal sum = 0.0; - - // For each face of the polyhedron - for (uint32 f=0; f < getNbFaces(); f++) { - - const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(f); - const decimal faceArea = getFaceArea(f); - const Vector3 faceNormal = mFacesNormals[f]; - const Vector3 faceVertex = getVertex(face.faceVertices[0]); - - sum += faceVertex.dot(faceNormal) * faceArea; - } - - return std::abs(sum) / decimal(3.0); -} diff --git a/ext/reactphysics3d/src/collision/RaycastInfo.cpp b/ext/reactphysics3d/src/collision/RaycastInfo.cpp index 92d7d4908..e79ae8cc4 100644 --- a/ext/reactphysics3d/src/collision/RaycastInfo.cpp +++ b/ext/reactphysics3d/src/collision/RaycastInfo.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/TriangleMesh.cpp b/ext/reactphysics3d/src/collision/TriangleMesh.cpp index 223b84f48..7c3029dfb 100644 --- a/ext/reactphysics3d/src/collision/TriangleMesh.cpp +++ b/ext/reactphysics3d/src/collision/TriangleMesh.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -25,15 +25,323 @@ // Libraries #include +#include +#include +#include using namespace reactphysics3d; // Constructor -TriangleMesh::TriangleMesh(MemoryAllocator& allocator) : mTriangleArrays(allocator) { +TriangleMesh::TriangleMesh(MemoryAllocator& allocator) + : mAllocator(allocator), mVertices(allocator), mTriangles(allocator), + mVerticesNormals(allocator), mDynamicAABBTree(allocator), mEpsilon(0) { } -// Destructor -TriangleMesh::~TriangleMesh() { +// Initialize the mesh using a TriangleVertexArray +bool TriangleMesh::init(const TriangleVertexArray& triangleVertexArray, std::vector& messages) { + bool isValid = true; + + // Reserve memory for the vertices, faces and edges + mVertices.reserve(triangleVertexArray.getNbVertices()); + mTriangles.reserve(triangleVertexArray.getNbTriangles() * 3); + mVerticesNormals.reserve(triangleVertexArray.getNbVertices()); + + computeEpsilon(triangleVertexArray); + + // Create the half-edge structure of the mesh + isValid &= copyData(triangleVertexArray, messages); + + // If the normals are not provided by the user + if (!triangleVertexArray.getHasNormals() && isValid) { + + // Compute the normals + computeVerticesNormals(); + } + + // Insert all the triangles into the dynamic AABB tree + initBVHTree(); + + return isValid; +} + +// Compute the epsilon value for this mesh +void TriangleMesh::computeEpsilon(const TriangleVertexArray& triangleVertexArray) { + + // Compute the bounds of the mesh + Vector3 max(0, 0, 0); + for (uint32 i=0 ; i < triangleVertexArray.getNbVertices(); i++) { + + const Vector3 vertex = triangleVertexArray.getVertex(i); + + decimal maxX = std::abs(vertex.x); + decimal maxY = std::abs(vertex.y); + decimal maxZ = std::abs(vertex.z); + if (maxX > max.x) max.x = maxX; + if (maxY > max.y) max.y = maxY; + if (maxZ > max.z) max.z = maxZ; + } + + // Compute the 'epsilon' value for this set of points + mEpsilon = 3 * (max.x + max.y + max.z) * MACHINE_EPSILON; +} + +// Copy the triangles faces +bool TriangleMesh::copyData(const TriangleVertexArray& triangleVertexArray, std::vector& messages) { + + bool isValid = true; + + assert(mEpsilon > 0); + + const decimal epsilonSquare = mEpsilon * mEpsilon; + + Array areUserVerticesUsed(mAllocator); + for (uint32 i=0 ; i < triangleVertexArray.getNbVertices(); i++) { + areUserVerticesUsed.add(false); + mVertices.add(Vector3::zero()); + mVerticesNormals.add(Vector3::zero()); + } + + // For each face + for (uint32 i=0 ; i < triangleVertexArray.getNbTriangles(); i++) { + + bool isValidFace = true; + uint32 vertexIndices[3]; + Vector3 vertexNormal[3] = {Vector3::zero(), Vector3::zero(), Vector3::zero()}; + + // Get the vertex indices from the user + triangleVertexArray.getTriangleVerticesIndices(i, vertexIndices[0], vertexIndices[1], vertexIndices[2]); + + for (int v=0; v < 3; v++) { + + if (vertexIndices[v] >= triangleVertexArray.getNbVertices()) { + + // Add an error message for the user + messages.push_back(Message("The face with index " + std::to_string(i) + + " has a vertex with index " + std::to_string(vertexIndices[v]) + + " but the TriangleVertexArray only has " + + std::to_string(triangleVertexArray.getNbVertices()) + " vertices")); + + isValid = false; + isValidFace = false; + } + } + + if (isValidFace) { + + // Check if the triangle area is not almost zero + const Vector3 v1 = triangleVertexArray.getVertex(vertexIndices[0]); + const Vector3 v2 = triangleVertexArray.getVertex(vertexIndices[1]); + const Vector3 v3 = triangleVertexArray.getVertex(vertexIndices[2]); + const Vector3 faceNormal = (v3 - v1).cross(v2 - v1); + const bool isFaceZeroArea = faceNormal.lengthSquare() < epsilonSquare; + if (isFaceZeroArea) { + + // Add a warning message for the user + messages.push_back(Message("The face with index " + std::to_string(i) + " has almost zero area. This triangle will not be part of the final collision shape.", + Message::Type::Warning)); + } + + // Check that edges lengths are not almost zero + decimal edgesLengthsSquare[3]; + edgesLengthsSquare[0] = (v2 - v1).lengthSquare(); + edgesLengthsSquare[1] = (v3 - v2).lengthSquare(); + edgesLengthsSquare[2] = (v1 - v3).lengthSquare(); + bool hasFaceZeroLengthEdge = edgesLengthsSquare[0] < epsilonSquare || edgesLengthsSquare[1] < epsilonSquare || + edgesLengthsSquare[2] < epsilonSquare; + if (hasFaceZeroLengthEdge) { + + // Add a warning message for the user + messages.push_back(Message("The face with index " + std::to_string(i) + " has an almost zero length edge. This triangle will not be part of the final collision shape.", + Message::Type::Warning)); + } + + // If the face does not have a zero area + if (!isFaceZeroArea && !hasFaceZeroLengthEdge) { + + // If the vertices normals are provided by the user + if (triangleVertexArray.getHasNormals()) { + + for (int v=0; v < 3; v++) { + + vertexNormal[v] = triangleVertexArray.getVertexNormal(vertexIndices[v]); + + // Check that the normal is not too small + if (vertexNormal[v].lengthSquare() < epsilonSquare) { + + messages.push_back(Message("The length of the provided normal for vertex with index " + std::to_string(vertexIndices[v]) + " is too small")) ; + isValid = false; + } + } + } + + // Add the vertices to the mesh + mVerticesNormals[vertexIndices[0]] = vertexNormal[0]; + mVertices[vertexIndices[0]] = v1; + mVerticesNormals[vertexIndices[1]] = vertexNormal[1]; + mVertices[vertexIndices[1]] = v2; + mVerticesNormals[vertexIndices[2]] = vertexNormal[2]; + mVertices[vertexIndices[2]] = v3; + + // Add the triangle to the mesh + mTriangles.add(vertexIndices[0]); + mTriangles.add(vertexIndices[1]); + mTriangles.add(vertexIndices[2]); + + areUserVerticesUsed[vertexIndices[0]] = true; + areUserVerticesUsed[vertexIndices[1]] = true; + areUserVerticesUsed[vertexIndices[2]] = true; + + assert(mVertices.size() == mVerticesNormals.size()); + } + } + } + + removeUnusedVertices(areUserVerticesUsed); + + if (mTriangles.size() == 0) { + + messages.push_back(Message("The mesh does not have any valid triangle faces")); + + isValid = false; + } + + assert(mTriangles.size() % 3 == 0); + + return isValid; +} + +// Remove the ununsed vertices (because they are not used in any triangles or +// are part of discarded triangles) +void TriangleMesh::removeUnusedVertices(Array& areUsedVertices) { + + // For each vertex of the user mesh + for (uint32 i=mVertices.size() - 1; i > 0; i--) { + + // If the vertex is not used + if (!areUsedVertices[i]) { + + mVertices.removeAt(i); + mVerticesNormals.removeAt(i); + + // For each triangle index of the mesh + for (uint32 t=0; t < mTriangles.size(); t++) { + + assert(mTriangles[t] != i); + + if (mTriangles[t] > i) { + mTriangles[t]--; + } + } + } + } +} + +// Insert all the triangles into the dynamic AABB tree +void TriangleMesh::initBVHTree() { + + assert(mTriangles.size() % 3 == 0); + + // TODO : Try to randomly add the triangles into the tree to obtain a better tree + + // For each triangle of the mesh + for (uint32 f=0; f < mTriangles.size() / 3; f++) { + + // Get the triangle vertices + Vector3 trianglePoints[3]; + trianglePoints[0] = mVertices[mTriangles[f * 3]]; + trianglePoints[1] = mVertices[mTriangles[f * 3 + 1]]; + trianglePoints[2] = mVertices[mTriangles[f * 3 + 2]]; + + // Create the AABB for the triangle + AABB aabb = AABB::createAABBForTriangle(trianglePoints); + + // Add the AABB with the index of the triangle into the dynamic AABB tree + mDynamicAABBTree.addObject(aabb, f); + } +} + +// Return the minimum bounds of the mesh in the x,y,z direction +/** + * @return The three mimimum bounds of the mesh in the x,y,z direction + */ +const AABB& TriangleMesh::getBounds() const { + return mDynamicAABBTree.getRootAABB(); +} + +// Compute the vertices normals +/// Compute the vertices normals if they are not provided by the user +/// The vertices normals are computed with weighted average of the associated +/// triangle face normal. The weights are the angle between the associated edges of neighbor triangle face. +void TriangleMesh::computeVerticesNormals() { + + // For each triangle face in the array + for (uint32 f=0; f < mTriangles.size() / 3; f++) { + + // Get the triangle vertices + Vector3 triangleVertices[3]; + assert(mTriangles[f * 3 + 2] < mVertices.size()); + triangleVertices[0] = mVertices[mTriangles[f * 3]]; + triangleVertices[1] = mVertices[mTriangles[f * 3 + 1]]; + triangleVertices[2] = mVertices[mTriangles[f * 3 + 2]]; + + // Edges lengths + decimal edgesLengths[3]; + edgesLengths[0] = (triangleVertices[1] - triangleVertices[0]).length(); + edgesLengths[1] = (triangleVertices[2] - triangleVertices[1]).length(); + edgesLengths[2] = (triangleVertices[0] - triangleVertices[2]).length(); + + // For each vertex of the face + for (uint32 v=0; v < 3; v++) { + + uint32 previousVertex = (v == 0) ? 2 : v-1; + uint32 nextVertex = (v == 2) ? 0 : v+1; + const Vector3 a = triangleVertices[nextVertex] - triangleVertices[v]; + const Vector3 b = triangleVertices[previousVertex] - triangleVertices[v]; + + // Weighted normal using angle + const decimal dotProduct = a.dot(b); + decimal lengthATimesLengthB = (edgesLengths[previousVertex] * edgesLengths[v]); + assert(lengthATimesLengthB * lengthATimesLengthB >= mEpsilon * mEpsilon); + + decimal cosA = dotProduct / lengthATimesLengthB; + cosA = std::min(std::max(cosA, decimal(0.0)), decimal(1.0)); // Angle inside a triangle should be in [0, pi] + const decimal angle = std::acos(cosA); + assert(angle >= decimal(0.0)); + + Vector3 normal = a.cross(b); + assert(normal.lengthSquare() > mEpsilon * mEpsilon); + normal.normalize(); + + // Add the normal component of this vertex into the normals array + mVerticesNormals[mTriangles[f * 3 + v]] += angle * normal; + } + } + + assert(mVertices.size() == mVerticesNormals.size()); + + // Normalize the computed vertices normals + for (uint32 v=0; v < mVertices.size(); v++) { + + assert(mVerticesNormals[v].lengthSquare() >= mEpsilon * mEpsilon); + + // Normalize the normal + mVerticesNormals[v].normalize(); + } +} + +// Report all shapes overlapping with the AABB given in parameter. +void TriangleMesh::reportAllShapesOverlappingWithAABB(const AABB& aabb, Array& overlappingNodes) { + mDynamicAABBTree.reportAllShapesOverlappingWithAABB(aabb, overlappingNodes); +} + +// Return the integer data of leaf node of the dynamic AABB tree +int32 TriangleMesh::getDynamicAABBTreeNodeDataInt(int32 nodeID) const { + return mDynamicAABBTree.getNodeDataInt(nodeID); +} + +// Ray casting method +void TriangleMesh::raycast(const Ray& ray, DynamicAABBTreeRaycastCallback& callback) const { + mDynamicAABBTree.raycast(ray, callback); } diff --git a/ext/reactphysics3d/src/collision/TriangleVertexArray.cpp b/ext/reactphysics3d/src/collision/TriangleVertexArray.cpp index 0e738ca5e..56a1cd7b4 100644 --- a/ext/reactphysics3d/src/collision/TriangleVertexArray.cpp +++ b/ext/reactphysics3d/src/collision/TriangleVertexArray.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -62,10 +62,7 @@ TriangleVertexArray::TriangleVertexArray(uint32 nbVertices, const void* vertices mVertexDataType = vertexDataType; mVertexNormaldDataType = NormalDataType::NORMAL_FLOAT_TYPE; mIndexDataType = indexDataType; - mAreVerticesNormalsProvidedByUser = false; - - // Compute the vertices normals because they are not provided by the user - computeVerticesNormals(); + mHasNormals = false; } // Constructor with vertices normals @@ -83,6 +80,7 @@ TriangleVertexArray::TriangleVertexArray(uint32 nbVertices, const void* vertices * @param indexesStart Pointer to the first triangle index * @param indexesStride Number of bytes between the beginning of two consecutive triangle indices * @param vertexDataType Type of data for the vertices (float, double) + * @param normalDataType Type of data for the normals (float, double) * @param indexDataType Type of data for the indices (short, int) */ TriangleVertexArray::TriangleVertexArray(uint32 nbVertices, const void* verticesStart, uint32 verticesStride, @@ -102,207 +100,47 @@ TriangleVertexArray::TriangleVertexArray(uint32 nbVertices, const void* vertices mVertexDataType = vertexDataType; mVertexNormaldDataType = normalDataType; mIndexDataType = indexDataType; - mAreVerticesNormalsProvidedByUser = true; + mHasNormals = true; assert(mVerticesNormalsStart != nullptr); } -// Destructor -TriangleVertexArray::~TriangleVertexArray() { - - // If the vertices normals have not been provided by the user - if (!mAreVerticesNormalsProvidedByUser) { - - // Release the allocated memory - const void* verticesNormalPointer = static_cast(mVerticesNormalsStart); - const float* verticesNormals = static_cast(verticesNormalPointer); - delete[] verticesNormals; - } -} - -// Compute the vertices normals when they are not provided by the user -/// The vertices normals are computed with weighted average of the associated -/// triangle face normal. The weights are the angle between the associated edges -/// of neighbor triangle face. -void TriangleVertexArray::computeVerticesNormals() { - - // Allocate memory for the vertices normals - float* verticesNormals = new float[mNbVertices * 3]; - - // Init vertices normals to zero - for (uint32 i=0; i decimal(MACHINE_EPSILON)) { - - decimal sinA = crossProduct.length() / edgeLengths; - sinA = std::min(std::max(sinA, decimal(0.0)), decimal(1.0)); - decimal arcSinA = std::asin(sinA); - assert(arcSinA >= decimal(0.0)); - Vector3 normalComponent = arcSinA * crossProduct; - - // Add the normal component of this vertex into the normals array - verticesNormals[verticesIndices[v] * 3] += normalComponent.x; - verticesNormals[verticesIndices[v] * 3 + 1] += normalComponent.y; - verticesNormals[verticesIndices[v] * 3 + 2] += normalComponent.z; - } - } - } - - // Normalize the computed vertices normals - for (uint32 v=0; v(verticesNormals); - mVerticesNormalsStart = static_cast(verticesNormalsPointer); -} - // Return the indices of the three vertices of a given triangle in the array /** * @param triangleIndex Index of a given triangle in the array - * @param[out] outVerticesIndices Pointer to the three output vertex indices + * @param[out] outV1Index Index of the first vertex of the triangle in the vertex array + * @param[out] outV2Index Index of the first vertex of the triangle in the vertex array + * @param[out] outV3Index Index of the first vertex of the triangle in the vertex array */ -void TriangleVertexArray::getTriangleVerticesIndices(uint32 triangleIndex, uint32* outVerticesIndices) const { +void TriangleVertexArray::getTriangleVerticesIndices(uint32 triangleIndex, uint32& outV1Index, uint32& outV2Index, uint32& outV3Index) const { assert(triangleIndex < mNbTriangles); const uchar* triangleIndicesPointer = mIndicesStart + triangleIndex * mIndicesStride; const void* startTriangleIndices = static_cast(triangleIndicesPointer); - // For each vertex of the triangle - for (uint32 i=0; i < 3; i++) { - - // Get the index of the current vertex in the triangle - if (mIndexDataType == TriangleVertexArray::IndexDataType::INDEX_INTEGER_TYPE) { - outVerticesIndices[i] = static_cast(startTriangleIndices)[i]; - } - else if (mIndexDataType == TriangleVertexArray::IndexDataType::INDEX_SHORT_TYPE) { - outVerticesIndices[i] = static_cast(startTriangleIndices)[i]; - } - else { - assert(false); - } + // Get the index of the current vertex in the triangle + if (mIndexDataType == TriangleVertexArray::IndexDataType::INDEX_INTEGER_TYPE) { + outV1Index = static_cast(startTriangleIndices)[0]; + outV2Index = static_cast(startTriangleIndices)[1]; + outV3Index = static_cast(startTriangleIndices)[2]; } -} - -// Return the three vertices coordinates of a triangle -/** - * @param triangleIndex Index of a given triangle in the array - * @param[out] outTriangleVertices Pointer to the three output vertex coordinates - */ -void TriangleVertexArray::getTriangleVertices(uint32 triangleIndex, Vector3* outTriangleVertices) const { - - assert(triangleIndex < mNbTriangles); - - // Get the three vertex index of the three vertices of the triangle - uint32 verticesIndices[3]; - getTriangleVerticesIndices(triangleIndex, verticesIndices); - - // For each vertex of the triangle - for (uint32 k=0; k < 3; k++) { - - const uchar* vertexPointerChar = mVerticesStart + verticesIndices[k] * mVerticesStride; - const void* vertexPointer = static_cast(vertexPointerChar); - - // Get the vertices components of the triangle - if (mVertexDataType == TriangleVertexArray::VertexDataType::VERTEX_FLOAT_TYPE) { - const float* vertices = static_cast(vertexPointer); - outTriangleVertices[k][0] = decimal(vertices[0]); - outTriangleVertices[k][1] = decimal(vertices[1]); - outTriangleVertices[k][2] = decimal(vertices[2]); - } - else if (mVertexDataType == TriangleVertexArray::VertexDataType::VERTEX_DOUBLE_TYPE) { - const double* vertices = static_cast(vertexPointer); - outTriangleVertices[k][0] = decimal(vertices[0]); - outTriangleVertices[k][1] = decimal(vertices[1]); - outTriangleVertices[k][2] = decimal(vertices[2]); - } - else { - assert(false); - } + else if (mIndexDataType == TriangleVertexArray::IndexDataType::INDEX_SHORT_TYPE) { + outV1Index = static_cast(startTriangleIndices)[0]; + outV2Index = static_cast(startTriangleIndices)[1]; + outV3Index = static_cast(startTriangleIndices)[2]; } -} - -// Return the three vertices normals of a triangle -/** - * @param triangleIndex Index of a given triangle in the array - * @param[out] outTriangleVerticesNormals Pointer to the three output vertex normals - */ -void TriangleVertexArray::getTriangleVerticesNormals(uint32 triangleIndex, Vector3* outTriangleVerticesNormals) const { - - assert(triangleIndex < mNbTriangles); - - // Get the three vertex index of the three vertices of the triangle - uint32 verticesIndices[3]; - getTriangleVerticesIndices(triangleIndex, verticesIndices); - - // For each vertex of the triangle - for (uint32 k=0; k < 3; k++) { - - const uchar* vertexNormalPointerChar = mVerticesNormalsStart + verticesIndices[k] * mVerticesNormalsStride; - const void* vertexNormalPointer = static_cast(vertexNormalPointerChar); - - // Get the normals from the array - if (mVertexNormaldDataType == TriangleVertexArray::NormalDataType::NORMAL_FLOAT_TYPE) { - const float* normal = static_cast(vertexNormalPointer); - outTriangleVerticesNormals[k][0] = decimal(normal[0]); - outTriangleVerticesNormals[k][1] = decimal(normal[1]); - outTriangleVerticesNormals[k][2] = decimal(normal[2]); - } - else if (mVertexNormaldDataType == TriangleVertexArray::NormalDataType::NORMAL_DOUBLE_TYPE) { - const double* normal = static_cast(vertexNormalPointer); - outTriangleVerticesNormals[k][0] = decimal(normal[0]); - outTriangleVerticesNormals[k][1] = decimal(normal[1]); - outTriangleVerticesNormals[k][2] = decimal(normal[2]); - } - else { - assert(false); - } + else { + assert(false); } } // Return a vertex of the array /** * @param vertexIndex Index of a given vertex of the array - * @param[out] outVertex Pointer to the output vertex coordinates + * @return The vertex coordinates */ -void TriangleVertexArray::getVertex(uint32 vertexIndex, Vector3* outVertex) { +Vector3 TriangleVertexArray::getVertex(uint32 vertexIndex) const { assert(vertexIndex < mNbVertices); @@ -311,16 +149,13 @@ void TriangleVertexArray::getVertex(uint32 vertexIndex, Vector3* outVertex) { // Get the vertices components of the triangle if (mVertexDataType == TriangleVertexArray::VertexDataType::VERTEX_FLOAT_TYPE) { + const float* vertices = static_cast(vertexPointer); - (*outVertex)[0] = decimal(vertices[0]); - (*outVertex)[1] = decimal(vertices[1]); - (*outVertex)[2] = decimal(vertices[2]); + return Vector3(decimal(vertices[0]), decimal(vertices[1]), decimal(vertices[2])); } else if (mVertexDataType == TriangleVertexArray::VertexDataType::VERTEX_DOUBLE_TYPE) { const double* vertices = static_cast(vertexPointer); - (*outVertex)[0] = decimal(vertices[0]); - (*outVertex)[1] = decimal(vertices[1]); - (*outVertex)[2] = decimal(vertices[2]); + return Vector3(decimal(vertices[0]), decimal(vertices[1]), decimal(vertices[2])); } else { assert(false); @@ -330,9 +165,9 @@ void TriangleVertexArray::getVertex(uint32 vertexIndex, Vector3* outVertex) { // Return a vertex normal of the array /** * @param vertexIndex Index of a given vertex of the array - * @param[out] outNormal Pointer to the output vertex normal + * @return The normal vector of the vertex */ -void TriangleVertexArray::getNormal(uint32 vertexIndex, Vector3* outNormal) { +Vector3 TriangleVertexArray::getVertexNormal(uint32 vertexIndex) const { assert(vertexIndex < mNbVertices); @@ -342,17 +177,15 @@ void TriangleVertexArray::getNormal(uint32 vertexIndex, Vector3* outNormal) { // Get the normals from the array if (mVertexNormaldDataType == TriangleVertexArray::NormalDataType::NORMAL_FLOAT_TYPE) { const float* normal = static_cast(vertexNormalPointer); - (*outNormal)[0] = decimal(normal[0]); - (*outNormal)[1] = decimal(normal[1]); - (*outNormal)[2] = decimal(normal[2]); + return Vector3(decimal(normal[0]), decimal(normal[1]), decimal(normal[2])); } else if (mVertexNormaldDataType == TriangleVertexArray::NormalDataType::NORMAL_DOUBLE_TYPE) { const double* normal = static_cast(vertexNormalPointer); - (*outNormal)[0] = decimal(normal[0]); - (*outNormal)[1] = decimal(normal[1]); - (*outNormal)[2] = decimal(normal[2]); + return Vector3(decimal(normal[0]), decimal(normal[1]), decimal(normal[2])); } else { assert(false); } + + return Vector3::zero(); } diff --git a/ext/reactphysics3d/src/collision/VertexArray.cpp b/ext/reactphysics3d/src/collision/VertexArray.cpp new file mode 100644 index 000000000..aa99b45b2 --- /dev/null +++ b/ext/reactphysics3d/src/collision/VertexArray.cpp @@ -0,0 +1,69 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include +#include + +using namespace reactphysics3d; + +// Constructor +/// Note that your data will not be copied into the PolygonVertexArray +/** + * @param start Pointer to the start of the vertices data + * @param stride The number of bytes between two consecutive vertices in the array + * @param nbVertices Number of vertices in the array + * @param dataType Data type of the vertices data + */ +VertexArray::VertexArray(const void* start, uint32 stride, uint32 nbVertices, DataType dataType) { + mNbVertices = nbVertices; + mStart = reinterpret_cast(start); + mStride = stride; + mDataType = dataType; +} + +// Return the coordinates of a given vertex +Vector3 VertexArray::getVertex(uint32 vertexIndex) const { + + Vector3 vertex; + + if (mDataType == DataType::VERTEX_FLOAT_TYPE) { + const float* vertices = (float*)(mStart + vertexIndex * mStride); + vertex.x = decimal(vertices[0]); + vertex.y = decimal(vertices[1]); + vertex.z = decimal(vertices[2]); + } + else if (mDataType == DataType::VERTEX_DOUBLE_TYPE) { + const double* vertices = (double*)(mStart + vertexIndex * mStride); + vertex.x = decimal(vertices[0]); + vertex.y = decimal(vertices[1]); + vertex.z = decimal(vertices[2]); + } + else { + assert(false); + } + + return vertex; +} diff --git a/ext/reactphysics3d/src/collision/broadphase/DynamicAABBTree.cpp b/ext/reactphysics3d/src/collision/broadphase/DynamicAABBTree.cpp index 333d922b2..5804567c2 100644 --- a/ext/reactphysics3d/src/collision/broadphase/DynamicAABBTree.cpp +++ b/ext/reactphysics3d/src/collision/broadphase/DynamicAABBTree.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -53,7 +53,7 @@ void DynamicAABBTree::init() { mRootNodeID = TreeNode::NULL_TREE_NODE; mNbNodes = 0; - mNbAllocatedNodes = 8; + mNbAllocatedNodes = GLOBAL_ALIGNMENT; // Allocate memory for the nodes of the tree mNodes = static_cast(mAllocator.allocate(static_cast(mNbAllocatedNodes) * sizeof(TreeNode))); @@ -147,6 +147,7 @@ int32 DynamicAABBTree::addObjectInternal(const AABB& aabb) { // Get the next available node (or allocate new ones if necessary) int32 nodeID = allocateNode(); + assert(nodeID >= 0); // Create the fat aabb to use in the tree (inflate the aabb by a constant percentage of its size) const Vector3 gap(aabb.getExtent() * mFatAABBInflatePercentage * decimal(0.5f)); @@ -160,8 +161,6 @@ int32 DynamicAABBTree::addObjectInternal(const AABB& aabb) { insertLeafNode(nodeID); assert(mNodes[nodeID].isLeaf()); - assert(nodeID >= 0); - // Return the Id of the node return nodeID; } @@ -590,7 +589,7 @@ int32 DynamicAABBTree::balanceSubTreeAtNode(int32 nodeID) { void DynamicAABBTree::reportAllShapesOverlappingWithShapes(const Array& nodesToTest, uint32 startIndex, size_t endIndex, Array>& outOverlappingNodes) const { - RP3D_PROFILE("DynamicAABBTree::reportAllShapesOverlappingWithAABB()", mProfiler); + RP3D_PROFILE("DynamicAABBTree::reportAllShapesOverlappingWithShapes()", mProfiler); // Create a stack with the nodes to visit Stack stack(mAllocator, 64); diff --git a/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp index 0fbe9b075..7110f3bb3 100755 --- a/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -182,19 +182,24 @@ bool CapsuleVsCapsuleAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseI if (closestPointsDistanceSquare > MACHINE_EPSILON) { decimal closestPointsDistance = std::sqrt(closestPointsDistanceSquare); - closestPointsSeg1ToSeg2 /= closestPointsDistance; + decimal penetrationDepth = sumRadius - closestPointsDistance; - const Vector3 contactPointCapsule1Local = capsule1ToCapsule2SpaceTransform.getInverse() * (closestPointCapsule1Seg + closestPointsSeg1ToSeg2 * capsule1Radius); - const Vector3 contactPointCapsule2Local = closestPointCapsule2Seg - closestPointsSeg1ToSeg2 * capsule2Radius; + // Make sure the penetration depth is not zero (even if the previous condition test was true the penetration depth can still be + // zero because of precision issue of the computation at the previous line) + if (penetrationDepth > 0) { - const Vector3 normalWorld = narrowPhaseInfoBatch.narrowPhaseInfos[batchIndex].shape2ToWorldTransform.getOrientation() * closestPointsSeg1ToSeg2; + closestPointsSeg1ToSeg2 /= closestPointsDistance; - decimal penetrationDepth = sumRadius - closestPointsDistance; + const Vector3 contactPointCapsule1Local = capsule1ToCapsule2SpaceTransform.getInverse() * (closestPointCapsule1Seg + closestPointsSeg1ToSeg2 * capsule1Radius); + const Vector3 contactPointCapsule2Local = closestPointCapsule2Seg - closestPointsSeg1ToSeg2 * capsule2Radius; - // Create the contact info object - narrowPhaseInfoBatch.addContactPoint(batchIndex, normalWorld, penetrationDepth, contactPointCapsule1Local, contactPointCapsule2Local); + const Vector3 normalWorld = narrowPhaseInfoBatch.narrowPhaseInfos[batchIndex].shape2ToWorldTransform.getOrientation() * closestPointsSeg1ToSeg2; + + // Create the contact info object + narrowPhaseInfoBatch.addContactPoint(batchIndex, normalWorld, penetrationDepth, contactPointCapsule1Local, contactPointCapsule2Local); + } } - else { // The segment are overlapping (degenerate case) + else if (sumRadius > 0){ // The segment are overlapping (degenerate case) // If the capsule segments are parralel if (areCapsuleInnerSegmentsParralel) { diff --git a/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp index 347eae65a..22eeaccd8 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -82,10 +82,8 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar // If we need to report contacts if (narrowPhaseInfoBatch.narrowPhaseInfos[batchIndex].reportContacts) { - bool noContact = false; - - // GJK has found a shallow contact. If the face of the polyhedron mesh is orthogonal to the - // capsule inner segment and parallel to the contact point normal, we would like to create + // GJK has found a shallow contact. If the normal of face of the polyhedron mesh is orthogonal to the + // capsule inner segment (the face normal is parallel to the contact point normal), we would like to create // two contact points instead of a single one (as in the deep contact case with SAT algorithm) // Get the contact point created by GJK @@ -116,13 +114,13 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar bool isFaceNormalInDirectionOfContactNormal = faceNormalWorld.dot(contactPoint.normal) > decimal(0.0); bool isFaceNormalInContactDirection = (isCapsuleShape1 && !isFaceNormalInDirectionOfContactNormal) || (!isCapsuleShape1 && isFaceNormalInDirectionOfContactNormal); - // If the polyhedron face normal is orthogonal to the capsule inner segment and parallel to the contact point normal and the face normal + // If the polyhedron face normal is orthogonal to the capsule inner segment (the face normal is parallel to the contact point normal) and the face normal // is in direction of the contact normal (from the polyhedron point of view). if (isFaceNormalInContactDirection && areOrthogonalVectors(faceNormalWorld, capsuleInnerSegmentDirection) && areParallelVectors(faceNormalWorld, contactPoint.normal)) { // Remove the previous contact point computed by GJK - narrowPhaseInfoBatch.resetContactPoints(batchIndex); + //narrowPhaseInfoBatch.resetContactPoints(batchIndex); const Transform capsuleToWorld = isCapsuleShape1 ? narrowPhaseInfoBatch.narrowPhaseInfos[batchIndex].shape1ToWorldTransform : narrowPhaseInfoBatch.narrowPhaseInfos[batchIndex].shape2ToWorldTransform; const Transform polyhedronToCapsuleTransform = capsuleToWorld.getInverse() * polyhedronToWorld; @@ -147,24 +145,13 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar polyhedronToCapsuleTransform, faceNormalWorld, separatingAxisCapsuleSpace, capsuleSegAPolyhedronSpace, capsuleSegBPolyhedronSpace, narrowPhaseInfoBatch, batchIndex, isCapsuleShape1); - if (!contactsFound) { - noContact = true; - narrowPhaseInfoBatch.narrowPhaseInfos[batchIndex].isColliding = false; + if (contactsFound) { break; } - - break; } } - - if (noContact) { - continue; - } } - lastFrameCollisionInfo->wasUsingSAT = false; - lastFrameCollisionInfo->wasUsingGJK = false; - // Colision found narrowPhaseInfoBatch.narrowPhaseInfos[batchIndex].isColliding = true; isCollisionFound = true; diff --git a/ext/reactphysics3d/src/collision/narrowphase/CollisionDispatch.cpp b/ext/reactphysics3d/src/collision/narrowphase/CollisionDispatch.cpp index fbdd51560..57f4eb3cf 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/CollisionDispatch.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/CollisionDispatch.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -31,13 +31,21 @@ using namespace reactphysics3d; // Constructor CollisionDispatch::CollisionDispatch(MemoryAllocator& allocator) : mAllocator(allocator) { + // Make sure to allocate memory with size that is multiple integral of alignment + mSphereVsSphereAllocatedSize = std::ceil(sizeof(SphereVsSphereAlgorithm) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + mSphereVsCapsuleAllocatedSize = std::ceil(sizeof(SphereVsCapsuleAlgorithm) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + mCapsuleVsCapsuleAllocatedSize = std::ceil(sizeof(CapsuleVsCapsuleAlgorithm) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + mSphereVsConvexPolyAllocatedSize = std::ceil(sizeof(SphereVsConvexPolyhedronAlgorithm) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + mCapsuleVsConvexPolyAllocatedSize = std::ceil(sizeof(CapsuleVsConvexPolyhedronAlgorithm) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + mConvexPolyVsConvexPolyAllocatedSize = std::ceil(sizeof(ConvexPolyhedronVsConvexPolyhedronAlgorithm) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Create the default narrow-phase algorithms - mSphereVsSphereAlgorithm = new (allocator.allocate(sizeof(SphereVsSphereAlgorithm))) SphereVsSphereAlgorithm(); - mSphereVsCapsuleAlgorithm = new (allocator.allocate(sizeof(SphereVsCapsuleAlgorithm))) SphereVsCapsuleAlgorithm(); - mCapsuleVsCapsuleAlgorithm = new (allocator.allocate(sizeof(CapsuleVsCapsuleAlgorithm))) CapsuleVsCapsuleAlgorithm(); - mSphereVsConvexPolyhedronAlgorithm = new (allocator.allocate(sizeof(SphereVsConvexPolyhedronAlgorithm))) SphereVsConvexPolyhedronAlgorithm(); - mCapsuleVsConvexPolyhedronAlgorithm = new (allocator.allocate(sizeof(CapsuleVsConvexPolyhedronAlgorithm))) CapsuleVsConvexPolyhedronAlgorithm(); - mConvexPolyhedronVsConvexPolyhedronAlgorithm = new (allocator.allocate(sizeof(ConvexPolyhedronVsConvexPolyhedronAlgorithm))) ConvexPolyhedronVsConvexPolyhedronAlgorithm(); + mSphereVsSphereAlgorithm = new (allocator.allocate(mSphereVsSphereAllocatedSize)) SphereVsSphereAlgorithm(); + mSphereVsCapsuleAlgorithm = new (allocator.allocate(mSphereVsCapsuleAllocatedSize)) SphereVsCapsuleAlgorithm(); + mCapsuleVsCapsuleAlgorithm = new (allocator.allocate(mCapsuleVsCapsuleAllocatedSize)) CapsuleVsCapsuleAlgorithm(); + mSphereVsConvexPolyhedronAlgorithm = new (allocator.allocate(mSphereVsConvexPolyAllocatedSize)) SphereVsConvexPolyhedronAlgorithm(); + mCapsuleVsConvexPolyhedronAlgorithm = new (allocator.allocate(mCapsuleVsConvexPolyAllocatedSize)) CapsuleVsConvexPolyhedronAlgorithm(); + mConvexPolyhedronVsConvexPolyhedronAlgorithm = new (allocator.allocate(mConvexPolyVsConvexPolyAllocatedSize)) ConvexPolyhedronVsConvexPolyhedronAlgorithm(); // Fill in the collision matrix fillInCollisionMatrix(); @@ -48,22 +56,22 @@ CollisionDispatch::~CollisionDispatch() { // Release allocated memory if (mIsSphereVsSphereDefault) { - mAllocator.release(mSphereVsSphereAlgorithm, sizeof(SphereVsSphereAlgorithm)); + mAllocator.release(mSphereVsSphereAlgorithm, mSphereVsSphereAllocatedSize); } if (mIsSphereVsCapsuleDefault) { - mAllocator.release(mSphereVsCapsuleAlgorithm, sizeof(SphereVsCapsuleAlgorithm)); + mAllocator.release(mSphereVsCapsuleAlgorithm, mSphereVsCapsuleAllocatedSize); } if (mIsCapsuleVsCapsuleDefault) { - mAllocator.release(mCapsuleVsCapsuleAlgorithm, sizeof(CapsuleVsCapsuleAlgorithm)); + mAllocator.release(mCapsuleVsCapsuleAlgorithm, mCapsuleVsCapsuleAllocatedSize); } if (mIsSphereVsConvexPolyhedronDefault) { - mAllocator.release(mSphereVsConvexPolyhedronAlgorithm, sizeof(SphereVsConvexPolyhedronAlgorithm)); + mAllocator.release(mSphereVsConvexPolyhedronAlgorithm, mSphereVsConvexPolyAllocatedSize); } if (mIsCapsuleVsConvexPolyhedronDefault) { - mAllocator.release(mCapsuleVsConvexPolyhedronAlgorithm, sizeof(CapsuleVsConvexPolyhedronAlgorithm)); + mAllocator.release(mCapsuleVsConvexPolyhedronAlgorithm, mCapsuleVsConvexPolyAllocatedSize); } if (mIsConvexPolyhedronVsConvexPolyhedronDefault) { - mAllocator.release(mConvexPolyhedronVsConvexPolyhedronAlgorithm, sizeof(ConvexPolyhedronVsConvexPolyhedronAlgorithm)); + mAllocator.release(mConvexPolyhedronVsConvexPolyhedronAlgorithm, mConvexPolyVsConvexPolyAllocatedSize); } } @@ -75,7 +83,7 @@ NarrowPhaseAlgorithmType CollisionDispatch::selectAlgorithm(int type1, int type2 CollisionShapeType shape2Type = static_cast(type2); if (type1 > type2) { - return NarrowPhaseAlgorithmType::None; + return NarrowPhaseAlgorithmType::NoCollisionTest; } // Sphere vs Sphere algorithm if (shape1Type == CollisionShapeType::SPHERE && shape2Type == CollisionShapeType::SPHERE) { @@ -103,7 +111,7 @@ NarrowPhaseAlgorithmType CollisionDispatch::selectAlgorithm(int type1, int type2 return NarrowPhaseAlgorithmType::ConvexPolyhedronVsConvexPolyhedron; } - return NarrowPhaseAlgorithmType::None; + return NarrowPhaseAlgorithmType::NoCollisionTest; } // Set the Sphere vs Sphere narrow-phase collision detection algorithm diff --git a/ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp index 9a3498254..b8462e5a6 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/narrowphase/GJK/GJKAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/GJK/GJKAlgorithm.cpp index 864458f21..e447c52ec 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/GJK/GJKAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/GJK/GJKAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -193,6 +193,7 @@ void GJKAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uin // object with the margins decimal dist = std::sqrt(distSquare); assert(dist > decimal(0.0)); + pA = (pA - (shape1->getMargin() / dist) * v); pB = body2Tobody1.getInverse() * (pB + (shape2->getMargin() / dist) * v); diff --git a/ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp b/ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp index 37f1ea826..a8ec4ee33 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/GJK/VoronoiSimplex.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp b/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp index f6f624991..06f13eeb5 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -32,6 +32,9 @@ using namespace reactphysics3d; +// TriangleShape allocated size +const size_t NarrowPhaseInfoBatch::mTriangleShapeAllocatedSize = std::ceil(sizeof(TriangleShape) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Constructor NarrowPhaseInfoBatch::NarrowPhaseInfoBatch(OverlappingPairs& overlappingPairs, MemoryAllocator& allocator) : mMemoryAllocator(allocator), mOverlappingPairs(overlappingPairs), narrowPhaseInfos(allocator){ @@ -63,11 +66,11 @@ void NarrowPhaseInfoBatch::clear() { // MiddlePhaseTriangleCallback::testTriangle() method) if (narrowPhaseInfos[i].collisionShape1->getName() == CollisionShapeName::TRIANGLE) { narrowPhaseInfos[i].collisionShape1->~CollisionShape(); - narrowPhaseInfos[i].collisionShapeAllocator->release(narrowPhaseInfos[i].collisionShape1, sizeof(TriangleShape)); + narrowPhaseInfos[i].collisionShapeAllocator->release(narrowPhaseInfos[i].collisionShape1, mTriangleShapeAllocatedSize); } if (narrowPhaseInfos[i].collisionShape2->getName() == CollisionShapeName::TRIANGLE) { narrowPhaseInfos[i].collisionShape2->~CollisionShape(); - narrowPhaseInfos[i].collisionShapeAllocator->release(narrowPhaseInfos[i].collisionShape2, sizeof(TriangleShape)); + narrowPhaseInfos[i].collisionShapeAllocator->release(narrowPhaseInfos[i].collisionShape2, mTriangleShapeAllocatedSize); } } diff --git a/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp b/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp index b748c4d0e..6f0b10258 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/NarrowPhaseInput.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/narrowphase/SAT/SATAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/SAT/SATAlgorithm.cpp index 6887b054b..841f19051 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/SAT/SATAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/SAT/SATAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -26,7 +26,7 @@ // Libraries #include #include -#include +#include #include #include #include @@ -438,6 +438,10 @@ bool SATAlgorithm::computeCapsulePolyhedronFaceContactPoints(uint32 referenceFac // If the clipped point is one that produce this penetration depth, we keep it if (clipPointPenDepth > penetrationDepth - capsuleRadius - decimal(0.001)) { + if (!contactFound) { + narrowPhaseInfoBatch.resetContactPoints(batchIndex); + } + contactFound = true; Vector3 contactPointPolyhedron = clipSegment[i] + delta; @@ -720,7 +724,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn // stability. To do this, we use a relative and absolute bias to move penetrationDepth2 a little bit to the right. // Now if: // penetrationDepth1 < penetrationDepth2: Nothing happens and we use axis of polygon 1 - // penetrationDepth1 ~ penetrationDepth2: Until penetrationDepth1 becomes significantly less than penetrationDepth2 we still use axis of polygon 1 + // penetrationDepth1 ~ penetrationDepth2: Until penetrationDepth2 becomes significantly less than penetrationDepth1 we still use axis of polygon 1 // penetrationDepth1 >> penetrationDepth2: penetrationDepth2 is now significantly less than penetrationDepth1 and we use polygon 2 axis if (penetrationDepth1 < penetrationDepth2 * SEPARATING_AXIS_RELATIVE_TOLERANCE + SEPARATING_AXIS_ABSOLUTE_TOLERANCE) { @@ -760,8 +764,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn const Vector3 edge2B = polyhedron2->getVertexPosition(polyhedron2->getHalfEdge(edge2.nextEdgeIndex).vertexIndex); const Vector3 edge2Direction = edge2B - edge2A; - // If the two edges build a minkowski face (and the cross product is - // therefore a candidate for separating axis + // If the two edges build a minkowski face (and the cross product is therefore a candidate for separating axis if (testEdgesBuildMinkowskiFace(polyhedron1, edge1, polyhedron2, edge2, polyhedron1ToPolyhedron2)) { Vector3 separatingAxisPolyhedron2Space; @@ -784,13 +787,13 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn } // If the current minimum penetration depth is along a face normal axis (isMinPenetrationFaceNormal=true) and we have found a new - // smaller pentration depth along an edge-edge cross-product axis we want to favor the face normal axis because contact manifolds between + // smaller penetration depth along an edge-edge cross-product axis we want to favor the face normal axis because contact manifolds between // faces have more contact points and therefore more stable than the single contact point of an edge-edge collision. It means that if the new minimum // penetration depth from the edge-edge contact is only a little bit smaller than the current minPenetrationDepth (from a face contact), we favor // the face contact and do not generate an edge-edge contact. However, if the new penetration depth from the edge-edge contact is really smaller than // the current one, we generate an edge-edge contact. // To do this, we use a relative and absolute bias to increase a little bit the new penetration depth from the edge-edge contact during the comparison test - if ((isMinPenetrationFaceNormal && penetrationDepth1 * SEPARATING_AXIS_RELATIVE_TOLERANCE + SEPARATING_AXIS_ABSOLUTE_TOLERANCE < minPenetrationDepth) || + if ((isMinPenetrationFaceNormal && penetrationDepth * SEPARATING_AXIS_RELATIVE_TOLERANCE + SEPARATING_AXIS_ABSOLUTE_TOLERANCE < minPenetrationDepth) || (!isMinPenetrationFaceNormal && penetrationDepth < minPenetrationDepth)) { minPenetrationDepth = penetrationDepth; diff --git a/ext/reactphysics3d/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp index 15d3e874f..5f90af9ab 100755 --- a/ext/reactphysics3d/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp index 9d1eae019..04ab86da1 100644 --- a/ext/reactphysics3d/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp b/ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp index 0a913f058..3a8a6c958 100755 --- a/ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp +++ b/ext/reactphysics3d/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/shapes/AABB.cpp b/ext/reactphysics3d/src/collision/shapes/AABB.cpp index 09554e39c..13e912ad1 100644 --- a/ext/reactphysics3d/src/collision/shapes/AABB.cpp +++ b/ext/reactphysics3d/src/collision/shapes/AABB.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/shapes/BoxShape.cpp b/ext/reactphysics3d/src/collision/shapes/BoxShape.cpp index 942e9536c..9efbfdece 100644 --- a/ext/reactphysics3d/src/collision/shapes/BoxShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/BoxShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -140,3 +140,12 @@ const HalfEdgeStructure::Edge& BoxShape::getHalfEdge(uint32 edgeIndex) const { assert(edgeIndex < getNbHalfEdges()); return mPhysicsCommon.mBoxShapeHalfEdgeStructure.getHalfEdge(edgeIndex); } + +// Return the local bounds of the shape in x, y and z directions +/// This method is used to compute the AABB of the box +/** + * @return The AABB of the shape + */ +AABB BoxShape::getLocalBounds() const { + return AABB(-mHalfExtents, mHalfExtents); +} diff --git a/ext/reactphysics3d/src/collision/shapes/CapsuleShape.cpp b/ext/reactphysics3d/src/collision/shapes/CapsuleShape.cpp index e8c249881..c774ca325 100644 --- a/ext/reactphysics3d/src/collision/shapes/CapsuleShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/CapsuleShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -82,6 +82,17 @@ bool CapsuleShape::testPointInside(const Vector3& localPoint, Collider* /*collid (xSquare + zSquare + diffYCenterSphere2 * diffYCenterSphere2) < squareRadius; } +// Return the local bounds of the shape in x, y and z directions +// This method is used to compute the AABB of the box +/** + * @return The AABB of the shape + */ +AABB CapsuleShape::getLocalBounds() const { + + return AABB(Vector3(-mMargin, -mHalfHeight - mMargin, -mMargin), + Vector3(mMargin, mHalfHeight + mMargin, mMargin)); +} + // Raycast method with feedback information bool CapsuleShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* collider, MemoryAllocator& /*allocator*/) const { diff --git a/ext/reactphysics3d/src/collision/shapes/CollisionShape.cpp b/ext/reactphysics3d/src/collision/shapes/CollisionShape.cpp index 47c63ad57..e3367f5e0 100644 --- a/ext/reactphysics3d/src/collision/shapes/CollisionShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/CollisionShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -26,7 +26,7 @@ // Libraries #include #include -#include +#include #include // We want to use the ReactPhysics3D namespace @@ -43,23 +43,20 @@ CollisionShape::CollisionShape(CollisionShapeName name, CollisionShapeType type, } -// Compute the world-space AABB of the collision shape given a transform from shape -// local-space to world-space. The technique is described in the book Real-Time Collision -// Detection by Christer Ericson. +// Compute the transformed AABB of the collision shape in another space +// The technique is described in the book Real-Time Collision +// Detection by Christer Ericson. This can be used to compute AABB of the collision shape +// in world-space for instance /** - * @param[out] aabb The axis-aligned bounding box (AABB) of the collision shape - * computed in world-space coordinates - * @param transform Transform from shape local-space to world-space used to compute - * the AABB of the collision shape + * @param transform Transform to use to for the space conversion + * @return The transformed axis-aligned bounding box (AABB) of the collision shape */ -void CollisionShape::computeAABB(AABB& aabb, const Transform& transform) const { +AABB CollisionShape::computeTransformedAABB(const Transform& transform) const { RP3D_PROFILE("CollisionShape::computeAABB()", mProfiler); // Get the local bounds in x,y and z direction - Vector3 minBounds; - Vector3 maxBounds; - getLocalBounds(minBounds, maxBounds); + AABB aabb = getLocalBounds(); const Vector3& translation = transform.getPosition(); Matrix3x3 matrix = transform.getOrientation().getMatrix(); @@ -74,8 +71,8 @@ void CollisionShape::computeAABB(AABB& aabb, const Transform& transform) const { resultMax[i] = translation[i]; for (int j=0; j<3; j++) { - decimal e = matrix[i][j] * minBounds[j]; - decimal f = matrix[i][j] * maxBounds[j]; + decimal e = matrix[i][j] * aabb.getMin()[j]; + decimal f = matrix[i][j] * aabb.getMax()[j]; if (e < f) { resultMin[i] += e; @@ -91,6 +88,8 @@ void CollisionShape::computeAABB(AABB& aabb, const Transform& transform) const { // Update the AABB with the new minimum and maximum coordinates aabb.setMin(resultMin); aabb.setMax(resultMax); + + return aabb; } /// Notify all the assign colliders that the size of the collision shape has changed diff --git a/ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp b/ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp index 3c4294c38..0843aff5d 100644 --- a/ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/ConcaveMeshShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -29,101 +29,106 @@ #include #include #include -#include using namespace reactphysics3d; // Constructor ConcaveMeshShape::ConcaveMeshShape(TriangleMesh* triangleMesh, MemoryAllocator& allocator, HalfEdgeStructure& triangleHalfEdgeStructure, const Vector3& scaling) - : ConcaveShape(CollisionShapeName::TRIANGLE_MESH, allocator, scaling), mDynamicAABBTree(allocator), mTriangleHalfEdgeStructure(triangleHalfEdgeStructure) { + : ConcaveShape(CollisionShapeName::TRIANGLE_MESH, allocator, scaling), mTriangleHalfEdgeStructure(triangleHalfEdgeStructure), + mScaledVerticesNormals(allocator, triangleMesh->getNbVertices()) { mTriangleMesh = triangleMesh; mRaycastTestType = TriangleRaycastSide::FRONT; - // Insert all the triangles into the dynamic AABB tree - initBVHTree(); + computeScaledVerticesNormals(); } -// Insert all the triangles into the dynamic AABB tree -void ConcaveMeshShape::initBVHTree() { +// Set the scale of the shape +void ConcaveMeshShape::setScale(const Vector3& scale) { - // TODO : Try to randomly add the triangles into the tree to obtain a better tree + ConcaveShape::setScale(scale); - // For each sub-part of the mesh - for (uint32 subPart=0; subPartgetNbSubparts(); subPart++) { + // Recompute the scale vertices normals + computeScaledVerticesNormals(); +} - // Get the triangle vertex array of the current sub-part - TriangleVertexArray* triangleVertexArray = mTriangleMesh->getSubpart(subPart); +// Compute the scaled faces normals +void ConcaveMeshShape::computeScaledVerticesNormals() { - // For each triangle of the concave mesh - for (uint32 triangleIndex=0; triangleIndexgetNbTriangles(); triangleIndex++) { + mScaledVerticesNormals.clear(); - Vector3 trianglePoints[3]; + // For each vertex + const uint32 nbVertices = mTriangleMesh->getNbVertices(); + for (uint32 v=0; v < nbVertices; v++) { - // Get the triangle vertices - triangleVertexArray->getTriangleVertices(triangleIndex, trianglePoints); + Vector3 normal = mTriangleMesh->getVertexNormal(v); - // Create the AABB for the triangle - AABB aabb = AABB::createAABBForTriangle(trianglePoints); + // Scale the normal + normal = Vector3(1.0 / mScale.x, 1.0 / mScale.y, 1.0 / mScale.z) * normal; - // Add the AABB with the index of the triangle into the dynamic AABB tree - mDynamicAABBTree.addObject(aabb, subPart, triangleIndex); - } + // Normalize the normal + const decimal normalLength = normal.length(); + assert(normalLength > MACHINE_EPSILON); + normal /= normalLength; + + mScaledVerticesNormals.add(normal); } } // Return the three vertices coordinates (in the array outTriangleVertices) of a triangle -void ConcaveMeshShape::getTriangleVertices(uint32 subPart, uint32 triangleIndex, Vector3* outTriangleVertices) const { - - // Get the triangle vertex array of the current sub-part - TriangleVertexArray* triangleVertexArray = mTriangleMesh->getSubpart(subPart); +void ConcaveMeshShape::getTriangleVertices(uint32 triangleIndex, Vector3& outV1, Vector3& outV2, Vector3& outV3) const { // Get the vertices coordinates of the triangle - triangleVertexArray->getTriangleVertices(triangleIndex, outTriangleVertices); + mTriangleMesh->getTriangleVertices(triangleIndex, outV1, outV2, outV3); // Apply the scaling factor to the vertices - outTriangleVertices[0].x *= mScale.x; - outTriangleVertices[0].y *= mScale.y; - outTriangleVertices[0].z *= mScale.z; - outTriangleVertices[1].x *= mScale.x; - outTriangleVertices[1].y *= mScale.y; - outTriangleVertices[1].z *= mScale.z; - outTriangleVertices[2].x *= mScale.x; - outTriangleVertices[2].y *= mScale.y; - outTriangleVertices[2].z *= mScale.z; + outV1 = outV1 * mScale; + outV2 = outV2 * mScale; + outV3 = outV3 * mScale; } // Return the three vertex normals (in the array outVerticesNormals) of a triangle -void ConcaveMeshShape::getTriangleVerticesNormals(uint32 subPart, uint32 triangleIndex, Vector3* outVerticesNormals) const { +void ConcaveMeshShape::getTriangleVerticesNormals(uint32 triangleIndex, Vector3& outN1, Vector3& outN2, Vector3& outN3) const { + + assert(triangleIndex < mTriangleMesh->getNbTriangles()); - // Get the triangle vertex array of the current sub-part - TriangleVertexArray* triangleVertexArray = mTriangleMesh->getSubpart(subPart); + // Get the triangle vertices indices + uint32 v1, v2, v3; + mTriangleMesh->getTriangleVerticesIndices(triangleIndex, v1, v2, v3); - // Get the vertices normals of the triangle - triangleVertexArray->getTriangleVerticesNormals(triangleIndex, outVerticesNormals); + // Return the scaled vertices normals + outN1 = mScaledVerticesNormals[v1]; + outN2 = mScaledVerticesNormals[v2]; + outN3 = mScaledVerticesNormals[v3]; } // Return the indices of the three vertices of a given triangle in the array -void ConcaveMeshShape::getTriangleVerticesIndices(uint32 subPart, uint32 triangleIndex, uint32* outVerticesIndices) const { - - // Get the triangle vertex array of the current sub-part - TriangleVertexArray* triangleVertexArray = mTriangleMesh->getSubpart(subPart); +void ConcaveMeshShape::getTriangleVerticesIndices(uint32 triangleIndex, uint32& outV1Index, + uint32& outV2Index, uint32& outV3Index) const { - // Get the vertices normals of the triangle - triangleVertexArray->getTriangleVerticesIndices(triangleIndex, outVerticesIndices); + mTriangleMesh->getTriangleVerticesIndices(triangleIndex, outV1Index, outV2Index, outV3Index); } -// Return the number of sub parts contained in this mesh -uint32 ConcaveMeshShape::getNbSubparts() const -{ - return mTriangleMesh->getNbSubparts(); +// Return the number of vertices in the mesh +uint32 ConcaveMeshShape::getNbVertices() const { + return mTriangleMesh->getNbVertices(); } - + // Return the number of triangles in a sub part of the mesh -uint32 ConcaveMeshShape::getNbTriangles(uint32 subPart) const -{ - assert(mTriangleMesh->getSubpart(subPart)); - return mTriangleMesh->getSubpart(subPart)->getNbTriangles(); +uint32 ConcaveMeshShape::getNbTriangles() const { + return mTriangleMesh->getNbTriangles(); +} + +// Return the coordinates of a given vertex +const Vector3 ConcaveMeshShape::getVertex(uint32 vertexIndex) const { + assert(vertexIndex < mTriangleMesh->getNbVertices()); + return mTriangleMesh->getVertex(vertexIndex) * mScale; +} + +// Return the normal of a given vertex +const Vector3& ConcaveMeshShape::getVertexNormal(uint32 vertexIndex) const { + assert(vertexIndex < mTriangleMesh->getNbVertices()); + return mScaledVerticesNormals[vertexIndex]; } // Compute all the triangles of the mesh that are overlapping with the AABB in parameter @@ -140,7 +145,7 @@ void ConcaveMeshShape::computeOverlappingTriangles(const AABB& localAABB, Array< // Compute the nodes of the internal AABB tree that are overlapping with the AABB Array overlappingNodes(allocator, 64); - mDynamicAABBTree.reportAllShapesOverlappingWithAABB(aabb, overlappingNodes); + mTriangleMesh->reportAllShapesOverlappingWithAABB(aabb, overlappingNodes); const uint32 nbOverlappingNodes = static_cast(overlappingNodes.size()); @@ -154,16 +159,17 @@ void ConcaveMeshShape::computeOverlappingTriangles(const AABB& localAABB, Array< int nodeId = overlappingNodes[i]; // Get the node data (triangle index and mesh subpart index) - int32* data = mDynamicAABBTree.getNodeDataInt(nodeId); + int32 data = mTriangleMesh->getDynamicAABBTreeNodeDataInt(nodeId); // Get the triangle vertices for this node from the concave mesh shape - getTriangleVertices(data[0], data[1], &(triangleVertices[i * 3])); + getTriangleVertices(data, triangleVertices[i * 3], triangleVertices[i * 3 + 1], triangleVertices[i * 3 + 2]); // Get the vertices normals of the triangle - getTriangleVerticesNormals(data[0], data[1], &(triangleVerticesNormals[i * 3])); + getTriangleVerticesNormals(data, triangleVerticesNormals[i * 3], triangleVerticesNormals[i * 3 + 1], + triangleVerticesNormals[i * 3 + 2]); // Compute the triangle shape ID - shapeIds.add(computeTriangleShapeId(data[0], data[1])); + shapeIds.add(computeTriangleShapeId(data)); } } @@ -180,11 +186,10 @@ bool ConcaveMeshShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collide Ray scaledRay(ray.point1 * inverseScale, ray.point2 * inverseScale, ray.maxFraction); // Create the callback object that will compute ray casting against triangles - ConcaveMeshRaycastCallback raycastCallback(mDynamicAABBTree, *this, collider, raycastInfo, scaledRay, mScale, allocator); + ConcaveMeshRaycastCallback raycastCallback(*this, collider, raycastInfo, ray, allocator); #ifdef IS_RP3D_PROFILING_ENABLED - // Set the profiler raycastCallback.setProfiler(mProfiler); @@ -192,32 +197,15 @@ bool ConcaveMeshShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collide // Ask the Dynamic AABB Tree to report all AABB nodes that are hit by the ray. // The raycastCallback object will then compute ray casting against the triangles - // in the hit AABBs. - mDynamicAABBTree.raycast(scaledRay, raycastCallback); + // in the hit AABBs. Note that we use the inverse scaled ray here because AABBs of the TriangleMesh + // are stored without scaling + mTriangleMesh->raycast(scaledRay, raycastCallback); raycastCallback.raycastTriangles(); return raycastCallback.getIsHit(); } -// Compute the shape Id for a given triangle of the mesh -uint32 ConcaveMeshShape::computeTriangleShapeId(uint32 subPart, uint32 triangleIndex) const { - - RP3D_PROFILE("ConcaveMeshShape::computeTriangleShapeId()", mProfiler); - - uint32 shapeId = 0; - - uint32 i=0; - while (i < subPart) { - - shapeId += mTriangleMesh->getSubpart(i)->getNbTriangles(); - - i++; - } - - return shapeId + triangleIndex; -} - // Collect all the AABB nodes that are hit by the ray in the Dynamic AABB Tree decimal ConcaveMeshRaycastCallback::raycastBroadPhaseShape(int32 nodeId, const Ray& ray) { @@ -236,23 +224,22 @@ void ConcaveMeshRaycastCallback::raycastTriangles() { for (it = mHitAABBNodes.begin(); it != mHitAABBNodes.end(); ++it) { // Get the node data (triangle index and mesh subpart index) - int32* data = mDynamicAABBTree.getNodeDataInt(*it); + int32 data = mConcaveMeshShape.getDynamicAABBTreeNodeDataInt(*it); // Get the triangle vertices for this node from the concave mesh shape Vector3 trianglePoints[3]; - mConcaveMeshShape.getTriangleVertices(data[0], data[1], trianglePoints); + mConcaveMeshShape.getTriangleVertices(data, trianglePoints[0], trianglePoints[1], trianglePoints[2]); // Get the vertices normals of the triangle Vector3 verticesNormals[3]; - mConcaveMeshShape.getTriangleVerticesNormals(data[0], data[1], verticesNormals); + mConcaveMeshShape.getTriangleVerticesNormals(data, verticesNormals[0], verticesNormals[1], verticesNormals[2]); // Create a triangle collision shape - TriangleShape triangleShape(trianglePoints, verticesNormals, mConcaveMeshShape.computeTriangleShapeId(data[0], data[1]), mConcaveMeshShape.mTriangleHalfEdgeStructure, mAllocator); + TriangleShape triangleShape(trianglePoints, verticesNormals, mConcaveMeshShape.computeTriangleShapeId(data), mConcaveMeshShape.mTriangleHalfEdgeStructure, mAllocator); triangleShape.setRaycastTestType(mConcaveMeshShape.getRaycastTestType()); #ifdef IS_RP3D_PROFILING_ENABLED - // Set the profiler to the triangle shape triangleShape.setProfiler(mProfiler); @@ -270,10 +257,9 @@ void ConcaveMeshRaycastCallback::raycastTriangles() { mRaycastInfo.body = raycastInfo.body; mRaycastInfo.collider = raycastInfo.collider; mRaycastInfo.hitFraction = raycastInfo.hitFraction; - mRaycastInfo.worldPoint = raycastInfo.worldPoint * mMeshScale; + mRaycastInfo.worldPoint = raycastInfo.worldPoint; mRaycastInfo.worldNormal = raycastInfo.worldNormal; - mRaycastInfo.meshSubpart = data[0]; - mRaycastInfo.triangleIndex = data[1]; + mRaycastInfo.triangleIndex = data; smallestHitFraction = raycastInfo.hitFraction; mIsHit = true; @@ -281,67 +267,90 @@ void ConcaveMeshRaycastCallback::raycastTriangles() { } } +// Return the local bounds of the shape in x, y and z directions. +// This method is used to compute the AABB of the box +/** + * @param min The minimum bounds of the shape in local-space coordinates + * @param max The maximum bounds of the shape in local-space coordinates + */ +AABB ConcaveMeshShape::getLocalBounds() const { + + // Get the AABB of the whole tree + AABB aabb = mTriangleMesh->getBounds(); + + // Apply the scale factor + aabb.applyScale(mScale); + + return aabb; +} + +// Return the integer data of leaf node of the dynamic AABB tree +int32 ConcaveMeshShape::getDynamicAABBTreeNodeDataInt(int32 nodeID) const { + return mTriangleMesh->getDynamicAABBTreeNodeDataInt(nodeID); +} + // Return the string representation of the shape std::string ConcaveMeshShape::to_string() const { std::stringstream ss; ss << "ConcaveMeshShape{" << std::endl; - ss << "nbSubparts=" << mTriangleMesh->getNbSubparts() << std::endl; - // Vertices array - for (uint32 subPart=0; subPartgetNbSubparts(); subPart++) { + ss << "nbVertices=" << getNbVertices() << std::endl; + ss << "nbTriangles=" << getNbTriangles() << std::endl; - // Get the triangle vertex array of the current sub-part - TriangleVertexArray* triangleVertexArray = mTriangleMesh->getSubpart(subPart); + ss << "vertices=["; - ss << "subpart" << subPart << "={" << std::endl; - ss << "nbVertices=" << triangleVertexArray->getNbVertices() << std::endl; - ss << "nbTriangles=" << triangleVertexArray->getNbTriangles() << std::endl; + // For each triangle of the concave mesh + for (uint32 v=0; vgetVertex(v); - // For each triangle of the concave mesh - for (uint32 v=0; vgetNbVertices(); v++) { + ss << vertex.to_string() << ", "; + } - Vector3 vertex; - triangleVertexArray->getVertex(v, &vertex); + ss << "], " << std::endl; - ss << vertex.to_string() << ", "; - } + ss << "normals=["; - ss << "], " << std::endl; + // For each vertex of the concave mesh + for (uint32 v=0; vgetNbVertices(); v++) { + ss << normal.to_string() << ", "; + } - Vector3 normal; - triangleVertexArray->getNormal(v, &normal); + ss << "], " << std::endl; - ss << normal.to_string() << ", "; - } + ss << "triangles=["; - ss << "], " << std::endl; + // For each triangle of the concave mesh + // For each triangle of the concave mesh + for (uint32 triangleIndex=0; triangleIndex < getNbTriangles(); triangleIndex++) { - ss << "triangles=["; + uint32 indices[3]; - // For each triangle of the concave mesh - // For each triangle of the concave mesh - for (uint32 triangleIndex=0; triangleIndexgetNbTriangles(); triangleIndex++) { + mTriangleMesh->getTriangleVerticesIndices(triangleIndex, indices[0], indices[1], indices[2]); - uint32 indices[3]; + ss << "(" << indices[0] << "," << indices[1] << "," << indices[2] << "), "; + } - triangleVertexArray->getTriangleVerticesIndices(triangleIndex, indices); + ss << "], " << std::endl; - ss << "(" << indices[0] << "," << indices[1] << "," << indices[2] << "), "; - } + ss << "}" << std::endl; - ss << "], " << std::endl; + return ss.str(); +} - ss << "}" << std::endl; - } +#ifdef IS_RP3D_PROFILING_ENABLED - return ss.str(); +// Set the profiler +void ConcaveMeshShape::setProfiler(Profiler* profiler) { + + CollisionShape::setProfiler(profiler); + + mTriangleMesh->setProfiler(profiler); } + +#endif diff --git a/ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp b/ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp index 1c321c406..96a148523 100644 --- a/ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/ConcaveShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -39,14 +39,13 @@ ConcaveShape::ConcaveShape(CollisionShapeName name, MemoryAllocator& allocator, // Compute and return the volume of the collision shape /// Note that we approximate the volume of a concave shape with the volume of its AABB decimal ConcaveShape::getVolume() const { - Vector3 minBounds, maxBounds; // Compute the local bounds - getLocalBounds(minBounds, maxBounds); + AABB aabb = getLocalBounds(); - const decimal lengthX = maxBounds.x - minBounds.x; - const decimal lengthY = maxBounds.y - minBounds.y; - const decimal lengthZ = maxBounds.z - minBounds.z; + const decimal lengthX = aabb.getMax().x - aabb.getMin().x; + const decimal lengthY = aabb.getMax().y - aabb.getMin().y; + const decimal lengthZ = aabb.getMax().z - aabb.getMin().z; // Approximate the volume of the concave shape as the volume of its AABB return lengthX * lengthY * lengthZ; diff --git a/ext/reactphysics3d/src/collision/shapes/ConvexMeshShape.cpp b/ext/reactphysics3d/src/collision/shapes/ConvexMeshShape.cpp index d57f01351..ee6c33e41 100644 --- a/ext/reactphysics3d/src/collision/shapes/ConvexMeshShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/ConvexMeshShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -39,12 +39,34 @@ using namespace reactphysics3d; * @param stride Stride between the beginning of two elements in the vertices array * @param margin Collision margin (in meters) around the collision shape */ -ConvexMeshShape::ConvexMeshShape(PolyhedronMesh* polyhedronMesh, MemoryAllocator& allocator, const Vector3& scale) - : ConvexPolyhedronShape(CollisionShapeName::CONVEX_MESH, allocator), mPolyhedronMesh(polyhedronMesh), - mMinBounds(0, 0, 0), mMaxBounds(0, 0, 0), mScale(scale) { +ConvexMeshShape::ConvexMeshShape(ConvexMesh* convexMesh, MemoryAllocator& allocator, const Vector3& scale) + : ConvexPolyhedronShape(CollisionShapeName::CONVEX_MESH, allocator), mConvexMesh(convexMesh), + mScale(scale), mScaledFacesNormals(allocator, convexMesh->getNbFaces()) { - // Recalculate the bounds of the mesh - recalculateBounds(); + computeScaledFacesNormals(); +} + +// Compute the scaled faces normals +void ConvexMeshShape::computeScaledFacesNormals() { + + mScaledFacesNormals.clear(); + + // For each face + const uint32 nbFaces = mConvexMesh->getNbFaces(); + for (uint32 f=0; f < nbFaces; f++) { + + Vector3 normal = mConvexMesh->getFaceNormal(f); + + // Scale the normal + normal = Vector3(1.0 / mScale.x, 1.0 / mScale.y, 1.0 / mScale.z) * normal; + + // Normalize the normal + const decimal normalLength = normal.length(); + assert(normalLength > MACHINE_EPSILON); + normal /= normalLength; + + mScaledFacesNormals.add(normal); + } } // Return a local support point in a given direction without the object margin. @@ -61,10 +83,10 @@ Vector3 ConvexMeshShape::getLocalSupportPointWithoutMargin(const Vector3& direct uint32 indexMaxDotProduct = 0; // For each vertex of the mesh - for (uint32 i=0; igetNbVertices(); i++) { + for (uint32 i=0; igetNbVertices(); i++) { // Compute the dot product of the current vertex - decimal dotProduct = direction.dot(mPolyhedronMesh->getVertex(i)); + decimal dotProduct = direction.dot(mConvexMesh->getVertex(i)); // If the current dot product is larger than the maximum one if (dotProduct > maxDotProduct) { @@ -76,31 +98,7 @@ Vector3 ConvexMeshShape::getLocalSupportPointWithoutMargin(const Vector3& direct assert(maxDotProduct >= decimal(0.0)); // Return the vertex with the largest dot product in the support direction - return mPolyhedronMesh->getVertex(indexMaxDotProduct) * mScale; -} - -// Recompute the bounds of the mesh -void ConvexMeshShape::recalculateBounds() { - - mMinBounds = mPolyhedronMesh->getVertex(0); - mMaxBounds = mPolyhedronMesh->getVertex(0); - - // For each vertex of the mesh - for (uint32 i=1; igetNbVertices(); i++) { - - if (mPolyhedronMesh->getVertex(i).x > mMaxBounds.x) mMaxBounds.x = mPolyhedronMesh->getVertex(i).x; - if (mPolyhedronMesh->getVertex(i).x < mMinBounds.x) mMinBounds.x = mPolyhedronMesh->getVertex(i).x; - - if (mPolyhedronMesh->getVertex(i).y > mMaxBounds.y) mMaxBounds.y = mPolyhedronMesh->getVertex(i).y; - if (mPolyhedronMesh->getVertex(i).y < mMinBounds.y) mMinBounds.y = mPolyhedronMesh->getVertex(i).y; - - if (mPolyhedronMesh->getVertex(i).z > mMaxBounds.z) mMaxBounds.z = mPolyhedronMesh->getVertex(i).z; - if (mPolyhedronMesh->getVertex(i).z < mMinBounds.z) mMinBounds.z = mPolyhedronMesh->getVertex(i).z; - } - - // Apply the local scaling factor - mMaxBounds = mMaxBounds * mScale; - mMinBounds = mMinBounds * mScale; + return mConvexMesh->getVertex(indexMaxDotProduct) * mScale; } // Raycast method with feedback information @@ -116,15 +114,15 @@ bool ConvexMeshShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider Vector3 currentFaceNormal; bool isIntersectionFound = false; - const HalfEdgeStructure& halfEdgeStructure = mPolyhedronMesh->getHalfEdgeStructure(); + const HalfEdgeStructure& halfEdgeStructure = mConvexMesh->getHalfEdgeStructure(); // For each face of the convex mesh - for (uint32 f=0; f < mPolyhedronMesh->getNbFaces(); f++) { + for (uint32 f=0; f < mConvexMesh->getNbFaces(); f++) { const HalfEdgeStructure::Face& face = halfEdgeStructure.getFace(f); - const Vector3 faceNormal = mPolyhedronMesh->getFaceNormal(f); + const Vector3& faceNormal = getFaceNormal(f); const HalfEdgeStructure::Vertex& faceVertex = halfEdgeStructure.getVertex(face.faceVertices[0]); - const Vector3 facePoint = mPolyhedronMesh->getVertex(faceVertex.vertexPointIndex); + const Vector3& facePoint = mConvexMesh->getVertex(faceVertex.vertexPointIndex); decimal denom = faceNormal.dot(direction); decimal planeD = faceNormal.dot(facePoint); decimal dist = planeD - faceNormal.dot(ray.point1); @@ -186,15 +184,15 @@ bool ConvexMeshShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider // Return true if a point is inside the collision shape bool ConvexMeshShape::testPointInside(const Vector3& localPoint, Collider* /*collider*/) const { - const HalfEdgeStructure& halfEdgeStructure = mPolyhedronMesh->getHalfEdgeStructure(); + const HalfEdgeStructure& halfEdgeStructure = mConvexMesh->getHalfEdgeStructure(); // For each face plane of the convex mesh - for (uint32 f=0; f < mPolyhedronMesh->getNbFaces(); f++) { + for (uint32 f=0; f < mConvexMesh->getNbFaces(); f++) { const HalfEdgeStructure::Face& face = halfEdgeStructure.getFace(f); - const Vector3 faceNormal = mPolyhedronMesh->getFaceNormal(f); + const Vector3& faceNormal = getFaceNormal(f); const HalfEdgeStructure::Vertex& faceVertex = halfEdgeStructure.getVertex(face.faceVertices[0]); - const Vector3 facePoint = mPolyhedronMesh->getVertex(faceVertex.vertexPointIndex); + const Vector3& facePoint = mConvexMesh->getVertex(faceVertex.vertexPointIndex); // If the point is out of the face plane, it is outside of the convex mesh if (computePointToPlaneDistance(localPoint, faceNormal, facePoint) > decimal(0.0)) return false; @@ -203,29 +201,39 @@ bool ConvexMeshShape::testPointInside(const Vector3& localPoint, Collider* /*col return true; } +// Return the local bounds of the shape in x, y and z directions +/** + * @return The AABB with the min/max bounds + */ +AABB ConvexMeshShape::getLocalBounds() const { + AABB aabb = mConvexMesh->getBounds(); + aabb.applyScale(mScale); + return aabb; +} + // Return the string representation of the shape std::string ConvexMeshShape::to_string() const { std::stringstream ss; ss << "ConvexMeshShape{" << std::endl; - ss << "nbVertices=" << mPolyhedronMesh->getNbVertices() << std::endl; - ss << "nbFaces=" << mPolyhedronMesh->getNbFaces() << std::endl; + ss << "nbVertices=" << mConvexMesh->getNbVertices() << std::endl; + ss << "nbFaces=" << mConvexMesh->getNbFaces() << std::endl; ss << "vertices=["; - for (uint32 v=0; v < mPolyhedronMesh->getNbVertices(); v++) { + for (uint32 v=0; v < mConvexMesh->getNbVertices(); v++) { - Vector3 vertex = mPolyhedronMesh->getVertex(v); + const Vector3& vertex = mConvexMesh->getVertex(v); ss << vertex.to_string(); - if (v != mPolyhedronMesh->getNbVertices() - 1) { + if (v != mConvexMesh->getNbVertices() - 1) { ss << ", "; } } ss << "], faces=["; - HalfEdgeStructure halfEdgeStruct = mPolyhedronMesh->getHalfEdgeStructure(); - for (uint32 f=0; f < mPolyhedronMesh->getNbFaces(); f++) { + HalfEdgeStructure halfEdgeStruct = mConvexMesh->getHalfEdgeStructure(); + for (uint32 f=0; f < mConvexMesh->getNbFaces(); f++) { const HalfEdgeStructure::Face& face = halfEdgeStruct.getFace(f); diff --git a/ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp b/ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp index 1aca3a56c..dcdd88223 100644 --- a/ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/ConvexPolyhedronShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp b/ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp index e59c7632a..20198f540 100644 --- a/ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/ConvexShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp b/ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp index 1cf5d4e32..2765732f3 100644 --- a/ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/HeightFieldShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -33,60 +33,24 @@ using namespace reactphysics3d; // Constructor /** - * @param nbGridColumns Number of columns in the grid of the height field - * @param nbGridRows Number of rows in the grid of the height field - * @param minHeight Minimum height value of the height field - * @param maxHeight Maximum height value of the height field - * @param heightFieldData Pointer to the first height value data (note that values are shared and not copied) - * @param dataType Data type for the height values (int, float, double) - * @param upAxis Integer representing the up axis direction (0 for x, 1 for y and 2 for z) - * @param integerHeightScale Scaling factor used to scale the height values (only when height values type is integer) + * @param heightField Pointer to a height-field + * @param scaling Scaling factor that needs to be applied to the height-field + * @param allocator Memory allocator used to allocated memory inside the HeightFieldShape */ -HeightFieldShape::HeightFieldShape(int nbGridColumns, int nbGridRows, decimal minHeight, decimal maxHeight, - const void* heightFieldData, HeightDataType dataType, MemoryAllocator& allocator, - HalfEdgeStructure& triangleHalfEdgeStructure, int upAxis, - decimal integerHeightScale, const Vector3& scaling) - : ConcaveShape(CollisionShapeName::HEIGHTFIELD, allocator, scaling), mNbColumns(nbGridColumns), mNbRows(nbGridRows), - mWidth(static_cast(nbGridColumns - 1)), mLength(static_cast(nbGridRows - 1)), mMinHeight(minHeight), - mMaxHeight(maxHeight), mUpAxis(upAxis), mIntegerHeightScale(integerHeightScale), - mHeightDataType(dataType), mTriangleHalfEdgeStructure(triangleHalfEdgeStructure) { +HeightFieldShape::HeightFieldShape(HeightField* heightField, MemoryAllocator& allocator, const Vector3& scaling) + : ConcaveShape(CollisionShapeName::HEIGHTFIELD, allocator, scaling), + mHeightField(heightField) { - assert(nbGridColumns >= 2); - assert(nbGridRows >= 2); - assert(mWidth >= 1); - assert(mLength >= 1); - assert(minHeight <= maxHeight); - assert(upAxis == 0 || upAxis == 1 || upAxis == 2); - - mHeightFieldData = heightFieldData; - - decimal halfHeight = (mMaxHeight - mMinHeight) * decimal(0.5); - assert(halfHeight >= 0); - - // Compute the local AABB of the height field - if (mUpAxis == 0) { - mAABB.setMin(Vector3(-halfHeight, -mWidth * decimal(0.5), -mLength * decimal(0.5))); - mAABB.setMax(Vector3(halfHeight, mWidth * decimal(0.5), mLength* decimal(0.5))); - } - else if (mUpAxis == 1) { - mAABB.setMin(Vector3(-mWidth * decimal(0.5), -halfHeight, -mLength * decimal(0.5))); - mAABB.setMax(Vector3(mWidth * decimal(0.5), halfHeight, mLength * decimal(0.5))); - } - else if (mUpAxis == 2) { - mAABB.setMin(Vector3(-mWidth * decimal(0.5), -mLength * decimal(0.5), -halfHeight)); - mAABB.setMax(Vector3(mWidth * decimal(0.5), mLength * decimal(0.5), halfHeight)); - } } // Return the local bounds of the shape in x, y and z directions. -// This method is used to compute the AABB of the box /** - * @param min The minimum bounds of the shape in local-space coordinates - * @param max The maximum bounds of the shape in local-space coordinates + * @return The AABB with the min/max bounds of the shape */ -void HeightFieldShape::getLocalBounds(Vector3& min, Vector3& max) const { - min = mAABB.getMin() * mScale; - max = mAABB.getMax() * mScale; +AABB HeightFieldShape::getLocalBounds() const { + AABB aabb = mHeightField->getBounds(); + aabb.applyScale(mScale); + return aabb; } // Test collision with the triangles of the height field shape. The idea is to use the AABB @@ -103,131 +67,7 @@ void HeightFieldShape::computeOverlappingTriangles(const AABB& localAABB, Array< Vector3 inverseScale(decimal(1.0) / mScale.x, decimal(1.0) / mScale.y, decimal(1.0) / mScale.z); AABB aabb(localAABB.getMin() * inverseScale, localAABB.getMax() * inverseScale); - // Compute the integer grid coordinates inside the area we need to test for collision - int minGridCoords[3]; - int maxGridCoords[3]; - computeMinMaxGridCoordinates(minGridCoords, maxGridCoords, aabb); - - // Compute the starting and ending coords of the sub-grid according to the up axis - int iMin = 0; - int iMax = 0; - int jMin = 0; - int jMax = 0; - switch(mUpAxis) { - case 0 : iMin = clamp(minGridCoords[1], 0, mNbColumns - 1); - iMax = clamp(maxGridCoords[1], 0, mNbColumns - 1); - jMin = clamp(minGridCoords[2], 0, mNbRows - 1); - jMax = clamp(maxGridCoords[2], 0, mNbRows - 1); - break; - case 1 : iMin = clamp(minGridCoords[0], 0, mNbColumns - 1); - iMax = clamp(maxGridCoords[0], 0, mNbColumns - 1); - jMin = clamp(minGridCoords[2], 0, mNbRows - 1); - jMax = clamp(maxGridCoords[2], 0, mNbRows - 1); - break; - case 2 : iMin = clamp(minGridCoords[0], 0, mNbColumns - 1); - iMax = clamp(maxGridCoords[0], 0, mNbColumns - 1); - jMin = clamp(minGridCoords[1], 0, mNbRows - 1); - jMax = clamp(maxGridCoords[1], 0, mNbRows - 1); - break; - } - - assert(iMin >= 0 && iMin < mNbColumns); - assert(iMax >= 0 && iMax < mNbColumns); - assert(jMin >= 0 && jMin < mNbRows); - assert(jMax >= 0 && jMax < mNbRows); - - // For each sub-grid points (except the last ones one each dimension) - for (int i = iMin; i < iMax; i++) { - for (int j = jMin; j < jMax; j++) { - - // Compute the four point of the current quad - const Vector3 p1 = getVertexAt(i, j); - const Vector3 p2 = getVertexAt(i, j + 1); - const Vector3 p3 = getVertexAt(i + 1, j); - const Vector3 p4 = getVertexAt(i + 1, j + 1); - - // Generate the first triangle for the current grid rectangle - triangleVertices.add(p1); - triangleVertices.add(p2); - triangleVertices.add(p3); - - // Compute the triangle normal - Vector3 triangle1Normal = (p2 - p1).cross(p3 - p1).getUnit(); - - // Use the triangle face normal as vertices normals (this is an aproximation. The correct - // solution would be to compute all the normals of the neighbor triangles and use their - // weighted average (with incident angle as weight) at the vertices. However, this solution - // seems too expensive (it requires to compute the normal of all neighbor triangles instead - // and compute the angle of incident edges with asin(). Maybe we could also precompute the - // vertices normal at the HeightFieldShape constructor but it will require extra memory to - // store them. - triangleVerticesNormals.add(triangle1Normal); - triangleVerticesNormals.add(triangle1Normal); - triangleVerticesNormals.add(triangle1Normal); - - // Compute the shape ID - shapeIds.add(computeTriangleShapeId(i, j, 0)); - - // Generate the second triangle for the current grid rectangle - triangleVertices.add(p3); - triangleVertices.add(p2); - triangleVertices.add(p4); - - // Compute the triangle normal - Vector3 triangle2Normal = (p2 - p3).cross(p4 - p3).getUnit(); - - // Use the triangle face normal as vertices normals (this is an aproximation. The correct - // solution would be to compute all the normals of the neighbor triangles and use their - // weighted average (with incident angle as weight) at the vertices. However, this solution - // seems too expensive (it requires to compute the normal of all neighbor triangles instead - // and compute the angle of incident edges with asin(). Maybe we could also precompute the - // vertices normal at the HeightFieldShape constructor but it will require extra memory to - // store them. - triangleVerticesNormals.add(triangle2Normal); - triangleVerticesNormals.add(triangle2Normal); - triangleVerticesNormals.add(triangle2Normal); - - // Compute the shape ID - shapeIds.add(computeTriangleShapeId(i, j, 1)); - } - } -} - -// Compute the min/max grid coords corresponding to the intersection of the AABB of the height field and -// the AABB to collide -void HeightFieldShape::computeMinMaxGridCoordinates(int* minCoords, int* maxCoords, const AABB& aabbToCollide) const { - - // Clamp the min/max coords of the AABB to collide inside the height field AABB - Vector3 minPoint = Vector3::max(aabbToCollide.getMin(), mAABB.getMin()); - minPoint = Vector3::min(minPoint, mAABB.getMax()); - - Vector3 maxPoint = Vector3::min(aabbToCollide.getMax(), mAABB.getMax()); - maxPoint = Vector3::max(maxPoint, mAABB.getMin()); - - // Translate the min/max points such that the we compute grid points from [0 ... mNbWidthGridPoints] - // and from [0 ... mNbLengthGridPoints] because the AABB coordinates range are [-mWdith/2 ... mWidth/2] - // and [-mLength/2 ... mLength/2] - const Vector3 translateVec = mAABB.getExtent() * decimal(0.5); - minPoint += translateVec; - maxPoint += translateVec; - - assert(minPoint.x >= 0); - assert(minPoint.y >= 0); - assert(minPoint.z >= 0); - assert(maxPoint.x >= 0); - assert(maxPoint.y >= 0); - assert(maxPoint.z >= 0); - - // Convert the floating min/max coords of the AABB into closest integer - // grid values (note that we use the closest grid coordinate that is out - // of the AABB) - minCoords[0] = static_cast(minPoint.x + 0.5) - 1; - minCoords[1] = static_cast(minPoint.y + 0.5) - 1; - minCoords[2] = static_cast(minPoint.z + 0.5) - 1; - - maxCoords[0] = static_cast(maxPoint.x + 0.5) + 1; - maxCoords[1] = static_cast(maxPoint.y + 0.5) + 1; - maxCoords[2] = static_cast(maxPoint.z + 0.5) + 1; + mHeightField->computeOverlappingTriangles(aabb, triangleVertices, triangleVerticesNormals, shapeIds, mScale); } // Raycast method with feedback information @@ -237,135 +77,15 @@ bool HeightFieldShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collide RP3D_PROFILE("HeightFieldShape::raycast()", mProfiler); - // Apply the concave mesh inverse scale factor because the mesh is stored without scaling + // Apply the height-field scale inverse scale factor because the mesh is stored without scaling // inside the dynamic AABB tree const Vector3 inverseScale(decimal(1.0) / mScale.x, decimal(1.0) / mScale.y, decimal(1.0) / mScale.z); Ray scaledRay(ray.point1 * inverseScale, ray.point2 * inverseScale, ray.maxFraction); - bool isHit = false; - - // Compute the grid coordinates where the ray is entering the AABB of the height field - int i, j; - Vector3 outHitGridPoint; - if (computeEnteringRayGridCoordinates(scaledRay, i, j, outHitGridPoint)) { - - const int nbCellsI = mNbColumns - 1; - const int nbCellsJ = mNbRows - 1; - - const Vector3 aabbSize = mAABB.getExtent(); - - const Vector3 rayDirection = scaledRay.point2 - scaledRay.point1; - - int stepI, stepJ; - decimal tMaxI, tMaxJ, nextI, nextJ, tDeltaI, tDeltaJ, sizeI, sizeJ; - - switch(mUpAxis) { - case 0 : stepI = rayDirection.y > 0 ? 1 : (rayDirection.y < 0 ? -1 : 0); - stepJ = rayDirection.z > 0 ? 1 : (rayDirection.z < 0 ? -1 : 0); - nextI = static_cast(stepI >= 0 ? i + 1 : i); - nextJ = static_cast(stepJ >= 0 ? j + 1 : j); - sizeI = aabbSize.y / nbCellsI; - sizeJ = aabbSize.z / nbCellsJ; - tMaxI = ((nextI * sizeI) - outHitGridPoint.y) / rayDirection.y; - tMaxJ = ((nextJ * sizeJ) - outHitGridPoint.z) / rayDirection.z; - tDeltaI = sizeI / std::abs(rayDirection.y); - tDeltaJ = sizeJ / std::abs(rayDirection.z); - break; - case 1 : stepI = rayDirection.x > 0 ? 1 : (rayDirection.x < 0 ? -1 : 0); - stepJ = rayDirection.z > 0 ? 1 : (rayDirection.z < 0 ? -1 : 0); - nextI = static_cast(stepI >= 0 ? i + 1 : i); - nextJ = static_cast(stepJ >= 0 ? j + 1 : j); - sizeI = aabbSize.x / nbCellsI; - sizeJ = aabbSize.z / nbCellsJ; - tMaxI = ((nextI * sizeI) - outHitGridPoint.x) / rayDirection.x; - tMaxJ = ((nextJ * sizeJ) - outHitGridPoint.z) / rayDirection.z; - tDeltaI = sizeI / std::abs(rayDirection.x); - tDeltaJ = sizeJ / std::abs(rayDirection.z); - break; - case 2 : stepI = rayDirection.x > 0 ? 1 : (rayDirection.x < 0 ? -1 : 0); - stepJ = rayDirection.y > 0 ? 1 : (rayDirection.y < 0 ? -1 : 0); - nextI = static_cast(stepI >= 0 ? i + 1 : i); - nextJ = static_cast(stepJ >= 0 ? j + 1 : j); - sizeI = aabbSize.x / nbCellsI; - sizeJ = aabbSize.y / nbCellsJ; - tMaxI = ((nextI * sizeI) - outHitGridPoint.x) / rayDirection.x; - tMaxJ = ((nextJ * sizeJ) - outHitGridPoint.y) / rayDirection.y; - tDeltaI = sizeI / std::abs(rayDirection.x); - tDeltaJ = sizeJ / std::abs(rayDirection.y); - break; - } - - decimal smallestHitFraction = ray.maxFraction; - - while (i >= 0 && i < nbCellsI && j >= 0 && j < nbCellsJ) { - - // Compute the four point of the current quad - const Vector3 p1 = getVertexAt(i, j); - const Vector3 p2 = getVertexAt(i, j + 1); - const Vector3 p3 = getVertexAt(i + 1, j); - const Vector3 p4 = getVertexAt(i + 1, j + 1); - - // Raycast against the first triangle of the cell - uint32 shapeId = computeTriangleShapeId(i, j, 0); - isHit |= raycastTriangle(ray, p1, p2, p3, shapeId, collider, raycastInfo, smallestHitFraction, allocator); + if (mHeightField->raycast(scaledRay, raycastInfo, collider, getRaycastTestType(), allocator)) { - // Raycast against the second triangle of the cell - shapeId = computeTriangleShapeId(i, j, 1); - isHit |= raycastTriangle(ray, p3, p2, p4, shapeId, collider, raycastInfo, smallestHitFraction, allocator); - - if (stepI == 0 && stepJ == 0) break; - - if (tMaxI < tMaxJ) { - tMaxI += tDeltaI; - i += stepI; - } - else { - tMaxJ += tDeltaJ; - j += stepJ; - } - } - } - - return isHit; -} - -// Raycast a single triangle of the height-field -bool HeightFieldShape::raycastTriangle(const Ray& ray, const Vector3& p1, const Vector3& p2, const Vector3& p3, uint32 shapeId, - Collider* collider, RaycastInfo& raycastInfo, decimal& smallestHitFraction, MemoryAllocator& allocator) const { - - // Generate the first triangle for the current grid rectangle - Vector3 triangleVertices[3] = {p1, p2, p3}; - - // Create a triangle collision shape - TriangleShape triangleShape(triangleVertices, shapeId, mTriangleHalfEdgeStructure, allocator); - triangleShape.setRaycastTestType(getRaycastTestType()); - -#ifdef IS_RP3D_PROFILING_ENABLED - - - // Set the profiler to the triangle shape - triangleShape.setProfiler(mProfiler); - -#endif - - // Ray casting test against the collision shape - RaycastInfo triangleRaycastInfo; - bool isTriangleHit = triangleShape.raycast(ray, triangleRaycastInfo, collider, allocator); - - // If the ray hit the collision shape - if (isTriangleHit && triangleRaycastInfo.hitFraction <= smallestHitFraction) { - - assert(triangleRaycastInfo.hitFraction >= decimal(0.0)); - - raycastInfo.body = triangleRaycastInfo.body; - raycastInfo.collider = triangleRaycastInfo.collider; - raycastInfo.hitFraction = triangleRaycastInfo.hitFraction; - raycastInfo.worldPoint = triangleRaycastInfo.worldPoint; - raycastInfo.worldNormal = triangleRaycastInfo.worldNormal; - raycastInfo.meshSubpart = -1; - raycastInfo.triangleIndex = -1; - - smallestHitFraction = triangleRaycastInfo.hitFraction; + // Scale back the contact point because we used a ray scale with inverse height-field scale + raycastInfo.worldPoint = raycastInfo.worldPoint * mScale; return true; } @@ -373,75 +93,6 @@ bool HeightFieldShape::raycastTriangle(const Ray& ray, const Vector3& p1, const return false; } -// Compute the first grid cell of the heightfield intersected by a ray. -/// This method returns true if the ray hit the AABB of the height field and false otherwise -bool HeightFieldShape::computeEnteringRayGridCoordinates(const Ray& ray, int& i, int& j, Vector3& outHitGridPoint) const { - - decimal stepI, stepJ; - const Vector3 aabbSize = mAABB.getExtent(); - - assert(mNbColumns > 0); - assert(mNbRows > 0); - - const int nbCellsI = mNbColumns - 1; - const int nbCellsJ = mNbRows - 1; - - if (mAABB.raycast(ray, outHitGridPoint)) { - - // Map the hit point into the grid range [0, mNbColumns - 1], [0, mNbRows - 1] - outHitGridPoint -= mAABB.getMin(); - - switch(mUpAxis) { - case 0 : stepI = aabbSize.y / nbCellsI; - stepJ = aabbSize.z / nbCellsJ; - i = clamp(int(outHitGridPoint.y / stepI), 0, nbCellsI - 1); - j = clamp(int(outHitGridPoint.z / stepJ), 0, nbCellsJ - 1); - break; - case 1 : stepI = aabbSize.x / nbCellsI; - stepJ = aabbSize.z / nbCellsJ; - i = clamp(int(outHitGridPoint.x / stepI), 0, nbCellsI - 1); - j = clamp(int(outHitGridPoint.z / stepJ), 0, nbCellsJ - 1); - break; - case 2 : stepI = aabbSize.x / nbCellsI; - stepJ = aabbSize.y / nbCellsJ; - i = clamp(int(outHitGridPoint.x / stepI), 0, nbCellsI - 1); - j = clamp(int(outHitGridPoint.y / stepJ), 0, nbCellsJ - 1); - break; - } - - assert(i >= 0 && i < nbCellsI); - assert(j >= 0 && j < nbCellsJ); - return true; - } - - return false; -} - -// Return the vertex (local-coordinates) of the height field at a given (x,y) position -Vector3 HeightFieldShape::getVertexAt(int x, int y) const { - - // Get the height value - const decimal height = getHeightAt(x, y); - - // Height values origin - const decimal heightOrigin = -(mMaxHeight - mMinHeight) * decimal(0.5) - mMinHeight; - - Vector3 vertex; - switch (mUpAxis) { - case 0: vertex = Vector3(heightOrigin + height, -mWidth * decimal(0.5) + x, -mLength * decimal(0.5) + y); - break; - case 1: vertex = Vector3(-mWidth * decimal(0.5) + x, heightOrigin + height, -mLength * decimal(0.5) + y); - break; - case 2: vertex = Vector3(-mWidth * decimal(0.5) + x, -mLength * decimal(0.5) + y, heightOrigin + height); - break; - default: assert(false); - } - - assert(mAABB.contains(vertex)); - - return vertex * mScale; -} - // Return the string representation of the shape std::string HeightFieldShape::to_string() const { @@ -449,14 +100,8 @@ std::string HeightFieldShape::to_string() const { ss << "HeightFieldShape{" << std::endl; - ss << "nbColumns=" << mNbColumns << std::endl; - ss << ", nbRows=" << mNbRows << std::endl; - ss << ", width=" << mWidth << std::endl; - ss << ", length=" << mLength << std::endl; - ss << ", minHeight=" << mMinHeight << std::endl; - ss << ", maxHeight=" << mMaxHeight << std::endl; - ss << ", upAxis=" << mUpAxis << std::endl; - ss << ", integerHeightScale=" << mIntegerHeightScale << std::endl; + ss << "scaling=" << mScale.to_string() << std::endl; + ss << ", HeightField=" << mHeightField->to_string() << std::endl; ss << "}"; return ss.str(); diff --git a/ext/reactphysics3d/src/collision/shapes/SphereShape.cpp b/ext/reactphysics3d/src/collision/shapes/SphereShape.cpp index 86d0c99ad..515f95686 100644 --- a/ext/reactphysics3d/src/collision/shapes/SphereShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/SphereShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -41,22 +41,20 @@ SphereShape::SphereShape(decimal radius, MemoryAllocator& allocator) assert(radius > decimal(0.0)); } -// Update the AABB of a body using its collision shape +// Compute the transformed AABB of the collision shape given a transform /** - * @param[out] aabb The axis-aligned bounding box (AABB) of the collision shape - * computed in world-space coordinates - * @param transform Transform used to compute the AABB of the collision shape + * @param transform Transform used to for the space conversion + * @return aabb The transformed axis-aligned bounding box (AABB) of the collision shape */ -void SphereShape::computeAABB(AABB& aabb, const Transform& transform) const { +AABB SphereShape::computeTransformedAABB(const Transform& transform) const { RP3D_PROFILE("SphereShape::computeAABB()", mProfiler); // Get the local extents in x,y and z direction Vector3 extents(mMargin, mMargin, mMargin); - // Update the AABB with the new minimum and maximum coordinates - aabb.setMin(transform.getPosition() - extents); - aabb.setMax(transform.getPosition() + extents); + // Return the AABB with the new minimum and maximum coordinates + return AABB(transform.getPosition() - extents, transform.getPosition() + extents); } // Raycast method with feedback information diff --git a/ext/reactphysics3d/src/collision/shapes/TriangleShape.cpp b/ext/reactphysics3d/src/collision/shapes/TriangleShape.cpp index eff1b0add..349ef873b 100644 --- a/ext/reactphysics3d/src/collision/shapes/TriangleShape.cpp +++ b/ext/reactphysics3d/src/collision/shapes/TriangleShape.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -46,6 +46,9 @@ TriangleShape::TriangleShape(const Vector3* vertices, const Vector3* verticesNor // Compute the triangle normal mNormal = (vertices[1] - vertices[0]).cross(vertices[2] - vertices[0]); + + assert(mNormal.length() > MACHINE_EPSILON); + mNormal.normalize(); mVerticesNormals[0] = verticesNormals[0]; @@ -109,16 +112,16 @@ void TriangleShape::computeSmoothMeshContact(Vector3 localContactPointTriangle, // Re-align the local contact point on the other shape such that it is aligned along the new contact normal Vector3 otherShapePointTriangleSpace = localContactPointTriangle - triangleLocalNormal * penetrationDepth; Vector3 otherShapePoint = worldToOtherShapeTransform * triangleShapeToWorldTransform * otherShapePointTriangleSpace; + outNewLocalContactPointOtherShape.setAllValues(otherShapePoint.x, otherShapePoint.y, otherShapePoint.z); } -// Update the AABB of a body using its collision shape +// Compute the transformed AABB of the collision shape given a transform /** - * @param[out] aabb The axis-aligned bounding box (AABB) of the collision shape - * computed in world-space coordinates - * @param transform Transform used to compute the AABB of the collision shape + * @param transform Transform to use for the space conversion + * @return aabb The transformed axis-aligned bounding box (AABB) of the collision shape */ -void TriangleShape::computeAABB(AABB& aabb, const Transform& transform) const { +AABB TriangleShape::computeTransformedAABB(const Transform& transform) const { RP3D_PROFILE("TriangleShape::computeAABB()", mProfiler); @@ -129,8 +132,9 @@ void TriangleShape::computeAABB(AABB& aabb, const Transform& transform) const { const Vector3 xAxis(worldPoint1.x, worldPoint2.x, worldPoint3.x); const Vector3 yAxis(worldPoint1.y, worldPoint2.y, worldPoint3.y); const Vector3 zAxis(worldPoint1.z, worldPoint2.z, worldPoint3.z); - aabb.setMin(Vector3(xAxis.getMinValue(), yAxis.getMinValue(), zAxis.getMinValue())); - aabb.setMax(Vector3(xAxis.getMaxValue(), yAxis.getMaxValue(), zAxis.getMaxValue())); + + return AABB(Vector3(xAxis.getMinValue(), yAxis.getMinValue(), zAxis.getMinValue()), + Vector3(xAxis.getMaxValue(), yAxis.getMaxValue(), zAxis.getMaxValue())); } // Raycast method with feedback information @@ -183,14 +187,15 @@ bool TriangleShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* // Compute the barycentric coordinates (u, v, w) to determine the // intersection point R, R = u * a + v * b + w * c - decimal denom = decimal(1.0) / (u + v + w); + const decimal denom = decimal(1.0) / (u + v + w); u *= denom; v *= denom; w *= denom; // Compute the local hit point using the barycentric coordinates const Vector3 localHitPoint = u * mPoints[0] + v * mPoints[1] + w * mPoints[2]; - const decimal hitFraction = (localHitPoint - ray.point1).length() / pq.length(); + const Vector3 point1ToHitPoint = localHitPoint - ray.point1; + const decimal hitFraction = point1ToHitPoint.dot(pq) / pq.lengthSquare(); if (hitFraction < decimal(0.0) || hitFraction > ray.maxFraction) return false; diff --git a/ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp b/ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp index 6f843e786..0850edbfb 100644 --- a/ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp +++ b/ext/reactphysics3d/src/components/BallAndSocketJointComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -38,10 +38,9 @@ BallAndSocketJointComponents::BallAndSocketJointComponents(MemoryAllocator& allo sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Matrix3x3) + sizeof(Matrix3x3) + sizeof(Vector3) + sizeof(Matrix3x3) + sizeof(Vector3) + sizeof(bool) + sizeof(decimal) + - sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(bool) + sizeof(Vector3)) { + sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(bool) + sizeof(Vector3), + 18 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -49,32 +48,55 @@ void BallAndSocketJointComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newJointEntities = static_cast(newBuffer); - BallAndSocketJoint** newJoints = reinterpret_cast(newJointEntities + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody1 = reinterpret_cast(newJoints + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody2 = reinterpret_cast(newLocalAnchorPointBody1 + nbComponentsToAllocate); - Vector3* newR1World = reinterpret_cast(newLocalAnchorPointBody2 + nbComponentsToAllocate); - Vector3* newR2World = reinterpret_cast(newR1World + nbComponentsToAllocate); - Matrix3x3* newI1 = reinterpret_cast(newR2World + nbComponentsToAllocate); - Matrix3x3* newI2 = reinterpret_cast(newI1 + nbComponentsToAllocate); - Vector3* newBiasVector = reinterpret_cast(newI2 + nbComponentsToAllocate); - Matrix3x3* newInverseMassMatrix = reinterpret_cast(newBiasVector + nbComponentsToAllocate); - Vector3* newImpulse = reinterpret_cast(newInverseMassMatrix + nbComponentsToAllocate); - bool* newIsConeLimitEnabled = reinterpret_cast(newImpulse + nbComponentsToAllocate); - decimal* newConeLimitImpulse = reinterpret_cast(newIsConeLimitEnabled + nbComponentsToAllocate); - decimal* newConeLimitHalfAngle = reinterpret_cast(newConeLimitImpulse + nbComponentsToAllocate); - decimal* newInverseMassMatrixConeLimit = reinterpret_cast(newConeLimitHalfAngle + nbComponentsToAllocate); - decimal* newBConeLimit = reinterpret_cast(newInverseMassMatrixConeLimit + nbComponentsToAllocate); - bool* newIsConeLimitViolated = reinterpret_cast(newBConeLimit + nbComponentsToAllocate); - Vector3* newConeLimitACrossB = reinterpret_cast(newIsConeLimitViolated + nbComponentsToAllocate); + assert(reinterpret_cast(newJointEntities) % GLOBAL_ALIGNMENT == 0); + BallAndSocketJoint** newJoints = reinterpret_cast(MemoryAllocator::alignAddress(newJointEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newJoints) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody1 = reinterpret_cast(MemoryAllocator::alignAddress(newJoints + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody1) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody2 = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody2) % GLOBAL_ALIGNMENT == 0); + Vector3* newR1World = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR1World) % GLOBAL_ALIGNMENT == 0); + Vector3* newR2World = reinterpret_cast(MemoryAllocator::alignAddress(newR1World + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR2World) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI1 = reinterpret_cast(MemoryAllocator::alignAddress(newR2World + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI1) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI2 = reinterpret_cast(MemoryAllocator::alignAddress(newI1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI2) % GLOBAL_ALIGNMENT == 0); + Vector3* newBiasVector = reinterpret_cast(MemoryAllocator::alignAddress(newI2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBiasVector) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newInverseMassMatrix = reinterpret_cast(MemoryAllocator::alignAddress(newBiasVector + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrix) % GLOBAL_ALIGNMENT == 0); + Vector3* newImpulse = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrix + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulse) % GLOBAL_ALIGNMENT == 0); + bool* newIsConeLimitEnabled = reinterpret_cast(MemoryAllocator::alignAddress(newImpulse + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsConeLimitEnabled) % GLOBAL_ALIGNMENT == 0); + decimal* newConeLimitImpulse = reinterpret_cast(MemoryAllocator::alignAddress(newIsConeLimitEnabled + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newConeLimitImpulse) % GLOBAL_ALIGNMENT == 0); + decimal* newConeLimitHalfAngle = reinterpret_cast(MemoryAllocator::alignAddress(newConeLimitImpulse + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newConeLimitHalfAngle) % GLOBAL_ALIGNMENT == 0); + decimal* newInverseMassMatrixConeLimit = reinterpret_cast(MemoryAllocator::alignAddress(newConeLimitHalfAngle + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixConeLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newBConeLimit = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixConeLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBConeLimit) % GLOBAL_ALIGNMENT == 0); + bool* newIsConeLimitViolated = reinterpret_cast(MemoryAllocator::alignAddress(newBConeLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsConeLimitViolated) % GLOBAL_ALIGNMENT == 0); + Vector3* newConeLimitACrossB = reinterpret_cast(MemoryAllocator::alignAddress(newIsConeLimitViolated + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newConeLimitACrossB) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newConeLimitACrossB + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -126,10 +148,10 @@ void BallAndSocketJointComponents::allocate(uint32 nbComponentsToAllocate) { } // Add a component -void BallAndSocketJointComponents::addComponent(Entity jointEntity, bool isSleeping, const BallAndSocketJointComponent& component) { +void BallAndSocketJointComponents::addComponent(Entity jointEntity, bool isDisabled, const BallAndSocketJointComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mJointEntities + index) Entity(jointEntity); diff --git a/ext/reactphysics3d/src/components/CollisionBodyComponents.cpp b/ext/reactphysics3d/src/components/BodyComponents.cpp similarity index 69% rename from ext/reactphysics3d/src/components/CollisionBodyComponents.cpp rename to ext/reactphysics3d/src/components/BodyComponents.cpp index 9606cfaad..8aae02380 100644 --- a/ext/reactphysics3d/src/components/CollisionBodyComponents.cpp +++ b/ext/reactphysics3d/src/components/BodyComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -24,7 +24,7 @@ ********************************************************************************/ // Libraries -#include +#include #include #include #include @@ -33,42 +33,53 @@ using namespace reactphysics3d; // Constructor -CollisionBodyComponents::CollisionBodyComponents(MemoryAllocator& allocator) - :Components(allocator, sizeof(Entity) + sizeof(CollisionBody*) + sizeof(Array) + - sizeof(bool) + sizeof(void*)) { +BodyComponents::BodyComponents(MemoryAllocator& allocator) + :Components(allocator, sizeof(Entity) + sizeof(Body*) + sizeof(Array) + + sizeof(bool) + sizeof(void*) + sizeof(bool), 6 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components -void CollisionBodyComponents::allocate(uint32 nbComponentsToAllocate) { +void BodyComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newBodiesEntities = static_cast(newBuffer); - CollisionBody** newBodies = reinterpret_cast(newBodiesEntities + nbComponentsToAllocate); - Array* newColliders = reinterpret_cast*>(newBodies + nbComponentsToAllocate); - bool* newIsActive = reinterpret_cast(newColliders + nbComponentsToAllocate); - void** newUserData = reinterpret_cast(newIsActive + nbComponentsToAllocate); + assert(reinterpret_cast(newBodiesEntities) % GLOBAL_ALIGNMENT == 0); + Body** newBodies = reinterpret_cast(MemoryAllocator::alignAddress(newBodiesEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBodies) % GLOBAL_ALIGNMENT == 0); + Array* newColliders = reinterpret_cast*>(MemoryAllocator::alignAddress(newBodies + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newColliders) % GLOBAL_ALIGNMENT == 0); + bool* newIsActive = reinterpret_cast(MemoryAllocator::alignAddress(newColliders + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsActive) % GLOBAL_ALIGNMENT == 0); + void** newUserData = reinterpret_cast(MemoryAllocator::alignAddress(newIsActive + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newUserData) % GLOBAL_ALIGNMENT == 0); + bool* newHasSimulationCollider = reinterpret_cast(MemoryAllocator::alignAddress(newUserData + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newHasSimulationCollider) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newHasSimulationCollider + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { // Copy component data from the previous buffer to the new one memcpy(newBodiesEntities, mBodiesEntities, mNbComponents * sizeof(Entity)); - memcpy(newBodies, mBodies, mNbComponents * sizeof(CollisionBody*)); + memcpy(newBodies, mBodies, mNbComponents * sizeof(Body*)); memcpy(newColliders, mColliders, mNbComponents * sizeof(Array)); memcpy(newIsActive, mIsActive, mNbComponents * sizeof(bool)); memcpy(newUserData, mUserData, mNbComponents * sizeof(void*)); + memcpy(newHasSimulationCollider, mHasSimulationCollider, mNbComponents * sizeof(bool)); // Deallocate previous memory mMemoryAllocator.release(mBuffer, mNbAllocatedComponents * mComponentDataSize); @@ -80,14 +91,15 @@ void CollisionBodyComponents::allocate(uint32 nbComponentsToAllocate) { mColliders = newColliders; mIsActive = newIsActive; mUserData = newUserData; + mHasSimulationCollider = newHasSimulationCollider; mNbAllocatedComponents = nbComponentsToAllocate; } // Add a component -void CollisionBodyComponents::addComponent(Entity bodyEntity, bool isSleeping, const CollisionBodyComponent& component) { +void BodyComponents::addComponent(Entity bodyEntity, bool isDisabled, const BodyComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mBodiesEntities + index) Entity(bodyEntity); @@ -95,6 +107,7 @@ void CollisionBodyComponents::addComponent(Entity bodyEntity, bool isSleeping, c new (mColliders + index) Array(mMemoryAllocator); mIsActive[index] = true; mUserData[index] = nullptr; + mHasSimulationCollider[index] = false; // Map the entity with the new component lookup index mMapEntityToComponentIndex.add(Pair(bodyEntity, index)); @@ -107,7 +120,7 @@ void CollisionBodyComponents::addComponent(Entity bodyEntity, bool isSleeping, c // Move a component from a source to a destination index in the components array // The destination location must contain a constructed object -void CollisionBodyComponents::moveComponentToIndex(uint32 srcIndex, uint32 destIndex) { +void BodyComponents::moveComponentToIndex(uint32 srcIndex, uint32 destIndex) { const Entity entity = mBodiesEntities[srcIndex]; @@ -117,6 +130,7 @@ void CollisionBodyComponents::moveComponentToIndex(uint32 srcIndex, uint32 destI new (mColliders + destIndex) Array(mColliders[srcIndex]); mIsActive[destIndex] = mIsActive[srcIndex]; mUserData[destIndex] = mUserData[srcIndex]; + mHasSimulationCollider[destIndex] = mHasSimulationCollider[srcIndex]; // Destroy the source component destroyComponent(srcIndex); @@ -130,14 +144,15 @@ void CollisionBodyComponents::moveComponentToIndex(uint32 srcIndex, uint32 destI } // Swap two components in the array -void CollisionBodyComponents::swapComponents(uint32 index1, uint32 index2) { +void BodyComponents::swapComponents(uint32 index1, uint32 index2) { // Copy component 1 data Entity entity1(mBodiesEntities[index1]); - CollisionBody* body1 = mBodies[index1]; + Body* body1 = mBodies[index1]; Array colliders1(mColliders[index1]); bool isActive1 = mIsActive[index1]; void* userData1 = mUserData[index1]; + bool hasSimulationCollider = mHasSimulationCollider[index1]; // Destroy component 1 destroyComponent(index1); @@ -150,6 +165,7 @@ void CollisionBodyComponents::swapComponents(uint32 index1, uint32 index2) { mBodies[index2] = body1; mIsActive[index2] = isActive1; mUserData[index2] = userData1; + mHasSimulationCollider[index2] = hasSimulationCollider; // Update the entity to component index mapping mMapEntityToComponentIndex.add(Pair(entity1, index2)); @@ -160,7 +176,7 @@ void CollisionBodyComponents::swapComponents(uint32 index1, uint32 index2) { } // Destroy a component at a given index -void CollisionBodyComponents::destroyComponent(uint32 index) { +void BodyComponents::destroyComponent(uint32 index) { Components::destroyComponent(index); diff --git a/ext/reactphysics3d/src/components/ColliderComponents.cpp b/ext/reactphysics3d/src/components/ColliderComponents.cpp index 51e1b8dc2..7b0fefe81 100644 --- a/ext/reactphysics3d/src/components/ColliderComponents.cpp +++ b/ext/reactphysics3d/src/components/ColliderComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -38,10 +38,8 @@ ColliderComponents::ColliderComponents(MemoryAllocator& allocator) :Components(allocator, sizeof(Entity) + sizeof(Entity) + sizeof(Collider*) + sizeof(int32) + sizeof(Transform) + sizeof(CollisionShape*) + sizeof(unsigned short) + sizeof(unsigned short) + sizeof(Transform) + sizeof(Array) + sizeof(bool) + - sizeof(bool) + sizeof(Material)) { + sizeof(bool) + sizeof(bool) + sizeof(bool) + sizeof(Material), 15 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -49,27 +47,48 @@ void ColliderComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newCollidersEntities = static_cast(newBuffer); - Entity* newBodiesEntities = reinterpret_cast(newCollidersEntities + nbComponentsToAllocate); - Collider** newColliders = reinterpret_cast(newBodiesEntities + nbComponentsToAllocate); - int32* newBroadPhaseIds = reinterpret_cast(newColliders + nbComponentsToAllocate); - Transform* newLocalToBodyTransforms = reinterpret_cast(newBroadPhaseIds + nbComponentsToAllocate); - CollisionShape** newCollisionShapes = reinterpret_cast(newLocalToBodyTransforms + nbComponentsToAllocate); - unsigned short* newCollisionCategoryBits = reinterpret_cast(newCollisionShapes + nbComponentsToAllocate); - unsigned short* newCollideWithMaskBits = reinterpret_cast(newCollisionCategoryBits + nbComponentsToAllocate); - Transform* newLocalToWorldTransforms = reinterpret_cast(newCollideWithMaskBits + nbComponentsToAllocate); - Array* newOverlappingPairs = reinterpret_cast*>(newLocalToWorldTransforms + nbComponentsToAllocate); - bool* hasCollisionShapeChangedSize = reinterpret_cast(newOverlappingPairs + nbComponentsToAllocate); - bool* isTrigger = reinterpret_cast(hasCollisionShapeChangedSize + nbComponentsToAllocate); - Material* materials = reinterpret_cast(isTrigger + nbComponentsToAllocate); + Entity* newBodiesEntities = reinterpret_cast(MemoryAllocator::alignAddress(newCollidersEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBodiesEntities) % GLOBAL_ALIGNMENT == 0); + Collider** newColliders = reinterpret_cast(MemoryAllocator::alignAddress(newBodiesEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newColliders) % GLOBAL_ALIGNMENT == 0); + int32* newBroadPhaseIds = reinterpret_cast(MemoryAllocator::alignAddress(newColliders + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBroadPhaseIds) % GLOBAL_ALIGNMENT == 0); + Transform* newLocalToBodyTransforms = reinterpret_cast(MemoryAllocator::alignAddress(newBroadPhaseIds + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalToBodyTransforms) % GLOBAL_ALIGNMENT == 0); + CollisionShape** newCollisionShapes = reinterpret_cast(MemoryAllocator::alignAddress(newLocalToBodyTransforms + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newCollisionShapes) % GLOBAL_ALIGNMENT == 0); + unsigned short* newCollisionCategoryBits = reinterpret_cast(MemoryAllocator::alignAddress(newCollisionShapes + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newCollisionCategoryBits) % GLOBAL_ALIGNMENT == 0); + unsigned short* newCollideWithMaskBits = reinterpret_cast(MemoryAllocator::alignAddress(newCollisionCategoryBits + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newCollideWithMaskBits) % GLOBAL_ALIGNMENT == 0); + Transform* newLocalToWorldTransforms = reinterpret_cast(MemoryAllocator::alignAddress(newCollideWithMaskBits + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalToWorldTransforms) % GLOBAL_ALIGNMENT == 0); + Array* newOverlappingPairs = reinterpret_cast*>(MemoryAllocator::alignAddress(newLocalToWorldTransforms + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newOverlappingPairs) % GLOBAL_ALIGNMENT == 0); + bool* hasCollisionShapeChangedSize = reinterpret_cast(MemoryAllocator::alignAddress(newOverlappingPairs + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(hasCollisionShapeChangedSize) % GLOBAL_ALIGNMENT == 0); + bool* isTrigger = reinterpret_cast(MemoryAllocator::alignAddress(hasCollisionShapeChangedSize + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(isTrigger) % GLOBAL_ALIGNMENT == 0); + bool* isSimulationCollider = reinterpret_cast(MemoryAllocator::alignAddress(isTrigger + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(isSimulationCollider) % GLOBAL_ALIGNMENT == 0); + bool* isWorldQueryCollider = reinterpret_cast(MemoryAllocator::alignAddress(isSimulationCollider + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(isWorldQueryCollider) % GLOBAL_ALIGNMENT == 0); + Material* materials = reinterpret_cast(MemoryAllocator::alignAddress(isWorldQueryCollider + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(materials) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(materials + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -87,6 +106,8 @@ void ColliderComponents::allocate(uint32 nbComponentsToAllocate) { memcpy(newOverlappingPairs, mOverlappingPairs, mNbComponents * sizeof(Array)); memcpy(hasCollisionShapeChangedSize, mHasCollisionShapeChangedSize, mNbComponents * sizeof(bool)); memcpy(isTrigger, mIsTrigger, mNbComponents * sizeof(bool)); + memcpy(isSimulationCollider, mIsSimulationCollider, mNbComponents * sizeof(bool)); + memcpy(isWorldQueryCollider, mIsWorldQueryCollider, mNbComponents * sizeof(bool)); memcpy(materials, mMaterials, mNbComponents * sizeof(Material)); // Deallocate previous memory @@ -94,7 +115,6 @@ void ColliderComponents::allocate(uint32 nbComponentsToAllocate) { } mBuffer = newBuffer; - mCollidersEntities = newCollidersEntities; mBodiesEntities = newBodiesEntities; mCollidersEntities = newCollidersEntities; mColliders = newColliders; @@ -107,16 +127,18 @@ void ColliderComponents::allocate(uint32 nbComponentsToAllocate) { mOverlappingPairs = newOverlappingPairs; mHasCollisionShapeChangedSize = hasCollisionShapeChangedSize; mIsTrigger = isTrigger; + mIsSimulationCollider = isSimulationCollider; + mIsWorldQueryCollider = isWorldQueryCollider; mMaterials = materials; mNbAllocatedComponents = nbComponentsToAllocate; } // Add a component -void ColliderComponents::addComponent(Entity colliderEntity, bool isSleeping, const ColliderComponent& component) { +void ColliderComponents::addComponent(Entity colliderEntity, bool isDisabled, const ColliderComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mCollidersEntities + index) Entity(colliderEntity); @@ -131,6 +153,8 @@ void ColliderComponents::addComponent(Entity colliderEntity, bool isSleeping, co new (mOverlappingPairs + index) Array(mMemoryAllocator); mHasCollisionShapeChangedSize[index] = false; mIsTrigger[index] = false; + mIsSimulationCollider[index] = true; + mIsWorldQueryCollider[index] = true; mMaterials[index] = component.material; // Map the entity with the new component lookup index @@ -160,6 +184,8 @@ void ColliderComponents::moveComponentToIndex(uint32 srcIndex, uint32 destIndex) new (mOverlappingPairs + destIndex) Array(mOverlappingPairs[srcIndex]); mHasCollisionShapeChangedSize[destIndex] = mHasCollisionShapeChangedSize[srcIndex]; mIsTrigger[destIndex] = mIsTrigger[srcIndex]; + mIsSimulationCollider[destIndex] = mIsSimulationCollider[srcIndex]; + mIsWorldQueryCollider[destIndex] = mIsWorldQueryCollider[srcIndex]; mMaterials[destIndex] = mMaterials[srcIndex]; // Destroy the source component @@ -189,6 +215,8 @@ void ColliderComponents::swapComponents(uint32 index1, uint32 index2) { Array overlappingPairs = mOverlappingPairs[index1]; bool hasCollisionShapeChangedSize = mHasCollisionShapeChangedSize[index1]; bool isTrigger = mIsTrigger[index1]; + bool isSimulationCollider = mIsSimulationCollider[index1]; + bool isWorldQueryCollider = mIsWorldQueryCollider[index1]; Material material = mMaterials[index1]; // Destroy component 1 @@ -209,6 +237,8 @@ void ColliderComponents::swapComponents(uint32 index1, uint32 index2) { new (mOverlappingPairs + index2) Array(overlappingPairs); mHasCollisionShapeChangedSize[index2] = hasCollisionShapeChangedSize; mIsTrigger[index2] = isTrigger; + mIsSimulationCollider[index2] = isSimulationCollider; + mIsWorldQueryCollider[index2] = isWorldQueryCollider; mMaterials[index2] = material; // Update the entity to component index mapping diff --git a/ext/reactphysics3d/src/components/Components.cpp b/ext/reactphysics3d/src/components/Components.cpp index f09d4fe2b..a920a78f4 100644 --- a/ext/reactphysics3d/src/components/Components.cpp +++ b/ext/reactphysics3d/src/components/Components.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -31,9 +31,9 @@ using namespace reactphysics3d; // Constructor -Components::Components(MemoryAllocator& allocator, size_t componentDataSize) +Components::Components(MemoryAllocator& allocator, size_t componentDataSize, size_t alignmentMarginSize) : mMemoryAllocator(allocator), mNbComponents(0), mComponentDataSize(componentDataSize), - mNbAllocatedComponents(0), mBuffer(nullptr), mMapEntityToComponentIndex(allocator), + mAlignmentMarginSize(alignmentMarginSize), mNbAllocatedComponents(0), mBuffer(nullptr), mMapEntityToComponentIndex(allocator), mDisabledStartIndex(0) { } @@ -57,8 +57,15 @@ Components::~Components() { } } +// Initialize the components: +void Components::init() { + + // Allocate memory for the components data + allocate(INIT_NB_ALLOCATED_COMPONENTS); +} + // Compute the index where we need to insert the new component -uint32 Components::prepareAddComponent(bool isSleeping) { +uint32 Components::prepareAddComponent(bool isDisabled) { // If we need to allocate more components if (mNbComponents == mNbAllocatedComponents) { @@ -68,7 +75,7 @@ uint32 Components::prepareAddComponent(bool isSleeping) { uint32 index; // If the component to add is part of a disabled entity or there are no disabled entity - if (isSleeping) { + if (isDisabled) { // Add the component at the end of the array index = mNbComponents; diff --git a/ext/reactphysics3d/src/components/FixedJointComponents.cpp b/ext/reactphysics3d/src/components/FixedJointComponents.cpp index 0794c0587..18ddf65c9 100644 --- a/ext/reactphysics3d/src/components/FixedJointComponents.cpp +++ b/ext/reactphysics3d/src/components/FixedJointComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -38,10 +38,8 @@ FixedJointComponents::FixedJointComponents(MemoryAllocator& allocator) sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Matrix3x3) + sizeof(Matrix3x3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Matrix3x3) + sizeof(Matrix3x3) + - sizeof(Vector3) + sizeof(Vector3) + sizeof(Quaternion)) { + sizeof(Vector3) + sizeof(Vector3) + sizeof(Quaternion), 15 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -49,29 +47,48 @@ void FixedJointComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newJointEntities = static_cast(newBuffer); - FixedJoint** newJoints = reinterpret_cast(newJointEntities + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody1 = reinterpret_cast(newJoints + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody2 = reinterpret_cast(newLocalAnchorPointBody1 + nbComponentsToAllocate); - Vector3* newR1World = reinterpret_cast(newLocalAnchorPointBody2 + nbComponentsToAllocate); - Vector3* newR2World = reinterpret_cast(newR1World + nbComponentsToAllocate); - Matrix3x3* newI1 = reinterpret_cast(newR2World + nbComponentsToAllocate); - Matrix3x3* newI2 = reinterpret_cast(newI1 + nbComponentsToAllocate); - Vector3* newImpulseTranslation = reinterpret_cast(newI2 + nbComponentsToAllocate); - Vector3* newImpulseRotation = reinterpret_cast(newImpulseTranslation + nbComponentsToAllocate); - Matrix3x3* newInverseMassMatrixTranslation = reinterpret_cast(newImpulseRotation + nbComponentsToAllocate); - Matrix3x3* newInverseMassMatrixRotation = reinterpret_cast(newInverseMassMatrixTranslation + nbComponentsToAllocate); - Vector3* newBiasTranslation = reinterpret_cast(newInverseMassMatrixRotation + nbComponentsToAllocate); - Vector3* newBiasRotation = reinterpret_cast(newBiasTranslation + nbComponentsToAllocate); - Quaternion* newInitOrientationDifferenceInv = reinterpret_cast(newBiasRotation + nbComponentsToAllocate); + FixedJoint** newJoints = reinterpret_cast(MemoryAllocator::alignAddress(newJointEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newJoints) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody1 = reinterpret_cast(MemoryAllocator::alignAddress(newJoints + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody1) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody2 = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody2) % GLOBAL_ALIGNMENT == 0); + Vector3* newR1World = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR1World) % GLOBAL_ALIGNMENT == 0); + Vector3* newR2World = reinterpret_cast(MemoryAllocator::alignAddress(newR1World + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR2World) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI1 = reinterpret_cast(MemoryAllocator::alignAddress(newR2World + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI1) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI2 = reinterpret_cast(MemoryAllocator::alignAddress(newI1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI1) % GLOBAL_ALIGNMENT == 0); + Vector3* newImpulseTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newI2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseTranslation) % GLOBAL_ALIGNMENT == 0); + Vector3* newImpulseRotation = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseRotation) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newInverseMassMatrixTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixTranslation) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newInverseMassMatrixRotation = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixRotation) % GLOBAL_ALIGNMENT == 0); + Vector3* newBiasTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBiasTranslation) % GLOBAL_ALIGNMENT == 0); + Vector3* newBiasRotation = reinterpret_cast(MemoryAllocator::alignAddress(newBiasTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBiasRotation) % GLOBAL_ALIGNMENT == 0); + Quaternion* newInitOrientationDifferenceInv = reinterpret_cast(MemoryAllocator::alignAddress(newBiasRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInitOrientationDifferenceInv) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newInitOrientationDifferenceInv + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -117,10 +134,10 @@ void FixedJointComponents::allocate(uint32 nbComponentsToAllocate) { } // Add a component -void FixedJointComponents::addComponent(Entity jointEntity, bool isSleeping, const FixedJointComponent& /*component*/) { +void FixedJointComponents::addComponent(Entity jointEntity, bool isDisabled, const FixedJointComponent& /*component*/) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mJointEntities + index) Entity(jointEntity); diff --git a/ext/reactphysics3d/src/components/HingeJointComponents.cpp b/ext/reactphysics3d/src/components/HingeJointComponents.cpp index e55a9d2d1..2bd324770 100644 --- a/ext/reactphysics3d/src/components/HingeJointComponents.cpp +++ b/ext/reactphysics3d/src/components/HingeJointComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -43,10 +43,8 @@ HingeJointComponents::HingeJointComponents(MemoryAllocator& allocator) sizeof(Vector3) + sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(bool) + sizeof(bool) + sizeof(decimal) + sizeof(decimal) + - sizeof(bool) + sizeof(bool) + sizeof(decimal) + sizeof(decimal)) { + sizeof(bool) + sizeof(bool) + sizeof(decimal) + sizeof(decimal), 35 * GLOBAL_ALIGNMENT ) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -54,49 +52,88 @@ void HingeJointComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newJointEntities = static_cast(newBuffer); - HingeJoint** newJoints = reinterpret_cast(newJointEntities + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody1 = reinterpret_cast(newJoints + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody2 = reinterpret_cast(newLocalAnchorPointBody1 + nbComponentsToAllocate); - Vector3* newR1World = reinterpret_cast(newLocalAnchorPointBody2 + nbComponentsToAllocate); - Vector3* newR2World = reinterpret_cast(newR1World + nbComponentsToAllocate); - Matrix3x3* newI1 = reinterpret_cast(newR2World + nbComponentsToAllocate); - Matrix3x3* newI2 = reinterpret_cast(newI1 + nbComponentsToAllocate); - Vector3* newImpulseTranslation = reinterpret_cast(newI2 + nbComponentsToAllocate); - Vector2* newImpulseRotation = reinterpret_cast(newImpulseTranslation + nbComponentsToAllocate); - Matrix3x3* newInverseMassMatrixTranslation = reinterpret_cast(newImpulseRotation + nbComponentsToAllocate); - Matrix2x2* newInverseMassMatrixRotation = reinterpret_cast(newInverseMassMatrixTranslation + nbComponentsToAllocate); - Vector3* newBiasTranslation = reinterpret_cast(newInverseMassMatrixRotation + nbComponentsToAllocate); - Vector2* newBiasRotation = reinterpret_cast(newBiasTranslation + nbComponentsToAllocate); - Quaternion* newInitOrientationDifferenceInv = reinterpret_cast(newBiasRotation + nbComponentsToAllocate); - Vector3* newHingeLocalAxisBody1 = reinterpret_cast(newInitOrientationDifferenceInv + nbComponentsToAllocate); - Vector3* newHingeLocalAxisBody2 = reinterpret_cast(newHingeLocalAxisBody1 + nbComponentsToAllocate); - Vector3* newA1 = reinterpret_cast(newHingeLocalAxisBody2 + nbComponentsToAllocate); - Vector3* newB2CrossA1 = reinterpret_cast(newA1 + nbComponentsToAllocate); - Vector3* newC2CrossA1 = reinterpret_cast(newB2CrossA1 + nbComponentsToAllocate); - decimal* newImpulseLowerLimit = reinterpret_cast(newC2CrossA1 + nbComponentsToAllocate); - decimal* newImpulseUpperLimit = reinterpret_cast(newImpulseLowerLimit + nbComponentsToAllocate); - decimal* newImpulseMotor = reinterpret_cast(newImpulseUpperLimit + nbComponentsToAllocate); - decimal* newInverseMassMatrixLimitMotor = reinterpret_cast(newImpulseMotor + nbComponentsToAllocate); - decimal* newInverseMassMatrixMotor = reinterpret_cast(newInverseMassMatrixLimitMotor + nbComponentsToAllocate); - decimal* newBLowerLimit = reinterpret_cast(newInverseMassMatrixMotor + nbComponentsToAllocate); - decimal* newBUpperLimit = reinterpret_cast(newBLowerLimit + nbComponentsToAllocate); - bool* newIsLimitEnabled = reinterpret_cast(newBUpperLimit + nbComponentsToAllocate); - bool* newIsMotorEnabled = reinterpret_cast(newIsLimitEnabled + nbComponentsToAllocate); - decimal* newLowerLimit = reinterpret_cast(newIsMotorEnabled + nbComponentsToAllocate); - decimal* newUpperLimit = reinterpret_cast(newLowerLimit + nbComponentsToAllocate); - bool* newIsLowerLimitViolated = reinterpret_cast(newUpperLimit + nbComponentsToAllocate); - bool* newIsUpperLimitViolated = reinterpret_cast(newIsLowerLimitViolated + nbComponentsToAllocate); - decimal* newMotorSpeed = reinterpret_cast(newIsUpperLimitViolated + nbComponentsToAllocate); - decimal* newMaxMotorTorque = reinterpret_cast(newMotorSpeed + nbComponentsToAllocate); + HingeJoint** newJoints = reinterpret_cast(MemoryAllocator::alignAddress(newJointEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newJoints) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody1 = reinterpret_cast(MemoryAllocator::alignAddress(newJoints + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody1) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody2 = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody2) % GLOBAL_ALIGNMENT == 0); + Vector3* newR1World = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR1World) % GLOBAL_ALIGNMENT == 0); + Vector3* newR2World = reinterpret_cast(MemoryAllocator::alignAddress(newR1World + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR2World) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI1 = reinterpret_cast(MemoryAllocator::alignAddress(newR2World + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI1) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI2 = reinterpret_cast(MemoryAllocator::alignAddress(newI1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI2) % GLOBAL_ALIGNMENT == 0); + Vector3* newImpulseTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newI2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseTranslation) % GLOBAL_ALIGNMENT == 0); + Vector2* newImpulseRotation = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseRotation) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newInverseMassMatrixTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixTranslation) % GLOBAL_ALIGNMENT == 0); + Matrix2x2* newInverseMassMatrixRotation = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixRotation) % GLOBAL_ALIGNMENT == 0); + Vector3* newBiasTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBiasTranslation) % GLOBAL_ALIGNMENT == 0); + Vector2* newBiasRotation = reinterpret_cast(MemoryAllocator::alignAddress(newBiasTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBiasRotation) % GLOBAL_ALIGNMENT == 0); + Quaternion* newInitOrientationDifferenceInv = reinterpret_cast(MemoryAllocator::alignAddress(newBiasRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInitOrientationDifferenceInv) % GLOBAL_ALIGNMENT == 0); + Vector3* newHingeLocalAxisBody1 = reinterpret_cast(MemoryAllocator::alignAddress(newInitOrientationDifferenceInv + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newHingeLocalAxisBody1) % GLOBAL_ALIGNMENT == 0); + Vector3* newHingeLocalAxisBody2 = reinterpret_cast(MemoryAllocator::alignAddress(newHingeLocalAxisBody1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newHingeLocalAxisBody2) % GLOBAL_ALIGNMENT == 0); + Vector3* newA1 = reinterpret_cast(MemoryAllocator::alignAddress(newHingeLocalAxisBody2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newA1) % GLOBAL_ALIGNMENT == 0); + Vector3* newB2CrossA1 = reinterpret_cast(MemoryAllocator::alignAddress(newA1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newB2CrossA1) % GLOBAL_ALIGNMENT == 0); + Vector3* newC2CrossA1 = reinterpret_cast(MemoryAllocator::alignAddress(newB2CrossA1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newC2CrossA1) % GLOBAL_ALIGNMENT == 0); + decimal* newImpulseLowerLimit = reinterpret_cast(MemoryAllocator::alignAddress(newC2CrossA1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseLowerLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newImpulseUpperLimit = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseLowerLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseUpperLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newImpulseMotor = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseUpperLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseMotor) % GLOBAL_ALIGNMENT == 0); + decimal* newInverseMassMatrixLimitMotor = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseMotor + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixLimitMotor) % GLOBAL_ALIGNMENT == 0); + decimal* newInverseMassMatrixMotor = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixLimitMotor + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixMotor) % GLOBAL_ALIGNMENT == 0); + decimal* newBLowerLimit = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixMotor + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBLowerLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newBUpperLimit = reinterpret_cast(MemoryAllocator::alignAddress(newBLowerLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBUpperLimit) % GLOBAL_ALIGNMENT == 0); + bool* newIsLimitEnabled = reinterpret_cast(MemoryAllocator::alignAddress(newBUpperLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsLimitEnabled) % GLOBAL_ALIGNMENT == 0); + bool* newIsMotorEnabled = reinterpret_cast(MemoryAllocator::alignAddress(newIsLimitEnabled + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsMotorEnabled) % GLOBAL_ALIGNMENT == 0); + decimal* newLowerLimit = reinterpret_cast(MemoryAllocator::alignAddress(newIsMotorEnabled + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLowerLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newUpperLimit = reinterpret_cast(MemoryAllocator::alignAddress(newLowerLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newUpperLimit) % GLOBAL_ALIGNMENT == 0); + bool* newIsLowerLimitViolated = reinterpret_cast(MemoryAllocator::alignAddress(newUpperLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsLowerLimitViolated) % GLOBAL_ALIGNMENT == 0); + bool* newIsUpperLimitViolated = reinterpret_cast(MemoryAllocator::alignAddress(newIsLowerLimitViolated + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsUpperLimitViolated) % GLOBAL_ALIGNMENT == 0); + decimal* newMotorSpeed = reinterpret_cast(MemoryAllocator::alignAddress(newIsUpperLimitViolated + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newMotorSpeed) % GLOBAL_ALIGNMENT == 0); + decimal* newMaxMotorTorque = reinterpret_cast(MemoryAllocator::alignAddress(newMotorSpeed + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newMaxMotorTorque) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newMaxMotorTorque + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -182,10 +219,10 @@ void HingeJointComponents::allocate(uint32 nbComponentsToAllocate) { } // Add a component -void HingeJointComponents::addComponent(Entity jointEntity, bool isSleeping, const HingeJointComponent& component) { +void HingeJointComponents::addComponent(Entity jointEntity, bool isDisabled, const HingeJointComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mJointEntities + index) Entity(jointEntity); diff --git a/ext/reactphysics3d/src/components/JointComponents.cpp b/ext/reactphysics3d/src/components/JointComponents.cpp index 12123c51a..571d5671a 100644 --- a/ext/reactphysics3d/src/components/JointComponents.cpp +++ b/ext/reactphysics3d/src/components/JointComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -35,10 +35,8 @@ using namespace reactphysics3d; JointComponents::JointComponents(MemoryAllocator& allocator) :Components(allocator, sizeof(Entity) + sizeof(Entity) + sizeof(Entity) + sizeof(Joint*) + sizeof(JointType) + sizeof(JointsPositionCorrectionTechnique) + sizeof(bool) + - sizeof(bool)) { + sizeof(bool), 8 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -46,22 +44,34 @@ void JointComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newJointsEntities = static_cast(newBuffer); - Entity* newBody1Entities = reinterpret_cast(newJointsEntities + nbComponentsToAllocate); - Entity* newBody2Entities = reinterpret_cast(newBody1Entities + nbComponentsToAllocate); - Joint** newJoints = reinterpret_cast(newBody2Entities + nbComponentsToAllocate); - JointType* newTypes = reinterpret_cast(newJoints + nbComponentsToAllocate); - JointsPositionCorrectionTechnique* newPositionCorrectionTechniques = reinterpret_cast(newTypes + nbComponentsToAllocate); - bool* newIsCollisionEnabled = reinterpret_cast(newPositionCorrectionTechniques + nbComponentsToAllocate); - bool* newIsAlreadyInIsland = reinterpret_cast(newIsCollisionEnabled + nbComponentsToAllocate); + Entity* newBody1Entities = reinterpret_cast(MemoryAllocator::alignAddress(newJointsEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBody1Entities) % GLOBAL_ALIGNMENT == 0); + Entity* newBody2Entities = reinterpret_cast(MemoryAllocator::alignAddress(newBody1Entities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBody2Entities) % GLOBAL_ALIGNMENT == 0); + Joint** newJoints = reinterpret_cast(MemoryAllocator::alignAddress(newBody2Entities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newJoints) % GLOBAL_ALIGNMENT == 0); + JointType* newTypes = reinterpret_cast(MemoryAllocator::alignAddress(newJoints + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newTypes) % GLOBAL_ALIGNMENT == 0); + JointsPositionCorrectionTechnique* newPositionCorrectionTechniques = reinterpret_cast(MemoryAllocator::alignAddress(newTypes + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newPositionCorrectionTechniques) % GLOBAL_ALIGNMENT == 0); + bool* newIsCollisionEnabled = reinterpret_cast(MemoryAllocator::alignAddress(newPositionCorrectionTechniques + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsCollisionEnabled) % GLOBAL_ALIGNMENT == 0); + bool* newIsAlreadyInIsland = reinterpret_cast(MemoryAllocator::alignAddress(newIsCollisionEnabled + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsAlreadyInIsland) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newIsAlreadyInIsland + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -93,10 +103,10 @@ void JointComponents::allocate(uint32 nbComponentsToAllocate) { } // Add a component -void JointComponents::addComponent(Entity jointEntity, bool isSleeping, const JointComponent& component) { +void JointComponents::addComponent(Entity jointEntity, bool isDisabled, const JointComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mJointEntities + index) Entity(jointEntity); diff --git a/ext/reactphysics3d/src/components/RigidBodyComponents.cpp b/ext/reactphysics3d/src/components/RigidBodyComponents.cpp index 3edcefe9e..6b5a065a5 100644 --- a/ext/reactphysics3d/src/components/RigidBodyComponents.cpp +++ b/ext/reactphysics3d/src/components/RigidBodyComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -44,10 +44,8 @@ RigidBodyComponents::RigidBodyComponents(MemoryAllocator& allocator) sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Quaternion) + sizeof(Vector3) + sizeof(Vector3) + sizeof(bool) + sizeof(bool) + sizeof(Array) + sizeof(Array) + - sizeof(Vector3) + sizeof(Vector3)) { + sizeof(Vector3) + sizeof(Vector3), 31 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -55,45 +53,80 @@ void RigidBodyComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newBodiesEntities = static_cast(newBuffer); - RigidBody** newBodies = reinterpret_cast(newBodiesEntities + nbComponentsToAllocate); - bool* newIsAllowedToSleep = reinterpret_cast(newBodies + nbComponentsToAllocate); - bool* newIsSleeping = reinterpret_cast(newIsAllowedToSleep + nbComponentsToAllocate); - decimal* newSleepTimes = reinterpret_cast(newIsSleeping + nbComponentsToAllocate); - BodyType* newBodyTypes = reinterpret_cast(newSleepTimes + nbComponentsToAllocate); - Vector3* newLinearVelocities = reinterpret_cast(newBodyTypes + nbComponentsToAllocate); - Vector3* newAngularVelocities = reinterpret_cast(newLinearVelocities + nbComponentsToAllocate); - Vector3* newExternalForces = reinterpret_cast(newAngularVelocities + nbComponentsToAllocate); - Vector3* newExternalTorques = reinterpret_cast(newExternalForces + nbComponentsToAllocate); - decimal* newLinearDampings = reinterpret_cast(newExternalTorques + nbComponentsToAllocate); - decimal* newAngularDampings = reinterpret_cast(newLinearDampings + nbComponentsToAllocate); - decimal* newMasses = reinterpret_cast(newAngularDampings + nbComponentsToAllocate); - decimal* newInverseMasses = reinterpret_cast(newMasses + nbComponentsToAllocate); - Vector3* newInertiaTensorLocal = reinterpret_cast(newInverseMasses + nbComponentsToAllocate); - Vector3* newInertiaTensorLocalInverses = reinterpret_cast(newInertiaTensorLocal + nbComponentsToAllocate); - Matrix3x3* newInertiaTensorWorldInverses = reinterpret_cast(newInertiaTensorLocalInverses + nbComponentsToAllocate); - Vector3* newConstrainedLinearVelocities = reinterpret_cast(newInertiaTensorWorldInverses + nbComponentsToAllocate); - Vector3* newConstrainedAngularVelocities = reinterpret_cast(newConstrainedLinearVelocities + nbComponentsToAllocate); - Vector3* newSplitLinearVelocities = reinterpret_cast(newConstrainedAngularVelocities + nbComponentsToAllocate); - Vector3* newSplitAngularVelocities = reinterpret_cast(newSplitLinearVelocities + nbComponentsToAllocate); - Vector3* newConstrainedPositions = reinterpret_cast(newSplitAngularVelocities + nbComponentsToAllocate); - Quaternion* newConstrainedOrientations = reinterpret_cast(newConstrainedPositions + nbComponentsToAllocate); - Vector3* newCentersOfMassLocal = reinterpret_cast(newConstrainedOrientations + nbComponentsToAllocate); - Vector3* newCentersOfMassWorld = reinterpret_cast(newCentersOfMassLocal + nbComponentsToAllocate); - bool* newIsGravityEnabled = reinterpret_cast(newCentersOfMassWorld + nbComponentsToAllocate); - bool* newIsAlreadyInIsland = reinterpret_cast(newIsGravityEnabled + nbComponentsToAllocate); - Array* newJoints = reinterpret_cast*>(newIsAlreadyInIsland + nbComponentsToAllocate); - Array* newContactPairs = reinterpret_cast*>(newJoints + nbComponentsToAllocate); - Vector3* newLinearLockAxisFactors = reinterpret_cast(newContactPairs + nbComponentsToAllocate); - Vector3* newAngularLockAxisFactors = reinterpret_cast(newLinearLockAxisFactors + nbComponentsToAllocate); + RigidBody** newBodies = reinterpret_cast(MemoryAllocator::alignAddress(newBodiesEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBodies) % GLOBAL_ALIGNMENT == 0); + bool* newIsAllowedToSleep = reinterpret_cast(MemoryAllocator::alignAddress(newBodies + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsAllowedToSleep) % GLOBAL_ALIGNMENT == 0); + bool* newIsSleeping = reinterpret_cast(MemoryAllocator::alignAddress(newIsAllowedToSleep + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsSleeping) % GLOBAL_ALIGNMENT == 0); + decimal* newSleepTimes = reinterpret_cast(MemoryAllocator::alignAddress(newIsSleeping + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newSleepTimes) % GLOBAL_ALIGNMENT == 0); + BodyType* newBodyTypes = reinterpret_cast(MemoryAllocator::alignAddress(newSleepTimes + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBodyTypes) % GLOBAL_ALIGNMENT == 0); + Vector3* newLinearVelocities = reinterpret_cast(MemoryAllocator::alignAddress(newBodyTypes + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLinearVelocities) % GLOBAL_ALIGNMENT == 0); + Vector3* newAngularVelocities = reinterpret_cast(MemoryAllocator::alignAddress(newLinearVelocities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newAngularVelocities) % GLOBAL_ALIGNMENT == 0); + Vector3* newExternalForces = reinterpret_cast(MemoryAllocator::alignAddress(newAngularVelocities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newExternalForces) % GLOBAL_ALIGNMENT == 0); + Vector3* newExternalTorques = reinterpret_cast(MemoryAllocator::alignAddress(newExternalForces + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newExternalTorques) % GLOBAL_ALIGNMENT == 0); + decimal* newLinearDampings = reinterpret_cast(MemoryAllocator::alignAddress(newExternalTorques + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLinearDampings) % GLOBAL_ALIGNMENT == 0); + decimal* newAngularDampings = reinterpret_cast(MemoryAllocator::alignAddress(newLinearDampings + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newAngularDampings) % GLOBAL_ALIGNMENT == 0); + decimal* newMasses = reinterpret_cast(MemoryAllocator::alignAddress(newAngularDampings + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newMasses) % GLOBAL_ALIGNMENT == 0); + decimal* newInverseMasses = reinterpret_cast(MemoryAllocator::alignAddress(newMasses + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMasses) % GLOBAL_ALIGNMENT == 0); + Vector3* newInertiaTensorLocal = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMasses + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInertiaTensorLocal) % GLOBAL_ALIGNMENT == 0); + Vector3* newInertiaTensorLocalInverses = reinterpret_cast(MemoryAllocator::alignAddress(newInertiaTensorLocal + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInertiaTensorLocal) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newInertiaTensorWorldInverses = reinterpret_cast(MemoryAllocator::alignAddress(newInertiaTensorLocalInverses + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInertiaTensorWorldInverses) % GLOBAL_ALIGNMENT == 0); + Vector3* newConstrainedLinearVelocities = reinterpret_cast(MemoryAllocator::alignAddress(newInertiaTensorWorldInverses + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newConstrainedLinearVelocities) % GLOBAL_ALIGNMENT == 0); + Vector3* newConstrainedAngularVelocities = reinterpret_cast(MemoryAllocator::alignAddress(newConstrainedLinearVelocities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newConstrainedAngularVelocities) % GLOBAL_ALIGNMENT == 0); + Vector3* newSplitLinearVelocities = reinterpret_cast(MemoryAllocator::alignAddress(newConstrainedAngularVelocities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newSplitLinearVelocities) % GLOBAL_ALIGNMENT == 0); + Vector3* newSplitAngularVelocities = reinterpret_cast(MemoryAllocator::alignAddress(newSplitLinearVelocities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newSplitAngularVelocities) % GLOBAL_ALIGNMENT == 0); + Vector3* newConstrainedPositions = reinterpret_cast(MemoryAllocator::alignAddress(newSplitAngularVelocities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newConstrainedPositions) % GLOBAL_ALIGNMENT == 0); + Quaternion* newConstrainedOrientations = reinterpret_cast(MemoryAllocator::alignAddress(newConstrainedPositions + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newConstrainedOrientations) % GLOBAL_ALIGNMENT == 0); + Vector3* newCentersOfMassLocal = reinterpret_cast(MemoryAllocator::alignAddress(newConstrainedOrientations + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newCentersOfMassLocal) % GLOBAL_ALIGNMENT == 0); + Vector3* newCentersOfMassWorld = reinterpret_cast(MemoryAllocator::alignAddress(newCentersOfMassLocal + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newCentersOfMassWorld) % GLOBAL_ALIGNMENT == 0); + bool* newIsGravityEnabled = reinterpret_cast(MemoryAllocator::alignAddress(newCentersOfMassWorld + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsGravityEnabled) % GLOBAL_ALIGNMENT == 0); + bool* newIsAlreadyInIsland = reinterpret_cast(MemoryAllocator::alignAddress(newIsGravityEnabled + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsAlreadyInIsland) % GLOBAL_ALIGNMENT == 0); + Array* newJoints = reinterpret_cast*>(MemoryAllocator::alignAddress(newIsAlreadyInIsland + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newJoints) % GLOBAL_ALIGNMENT == 0); + Array* newContactPairs = reinterpret_cast*>(MemoryAllocator::alignAddress(newJoints + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newContactPairs) % GLOBAL_ALIGNMENT == 0); + Vector3* newLinearLockAxisFactors = reinterpret_cast(MemoryAllocator::alignAddress(newContactPairs + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLinearLockAxisFactors) % GLOBAL_ALIGNMENT == 0); + Vector3* newAngularLockAxisFactors = reinterpret_cast(MemoryAllocator::alignAddress(newLinearLockAxisFactors + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newAngularLockAxisFactors) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newAngularLockAxisFactors + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -171,10 +204,10 @@ void RigidBodyComponents::allocate(uint32 nbComponentsToAllocate) { } // Add a component -void RigidBodyComponents::addComponent(Entity bodyEntity, bool isSleeping, const RigidBodyComponent& component) { +void RigidBodyComponents::addComponent(Entity bodyEntity, bool isDisabled, const RigidBodyComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mBodiesEntities + index) Entity(bodyEntity); diff --git a/ext/reactphysics3d/src/components/SliderJointComponents.cpp b/ext/reactphysics3d/src/components/SliderJointComponents.cpp index 4a6da7a01..9f8787f18 100644 --- a/ext/reactphysics3d/src/components/SliderJointComponents.cpp +++ b/ext/reactphysics3d/src/components/SliderJointComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -45,10 +45,8 @@ SliderJointComponents::SliderJointComponents(MemoryAllocator& allocator) sizeof(bool) + sizeof(bool) + sizeof(decimal) + sizeof(decimal) + sizeof(bool) + sizeof(bool) + sizeof(decimal) + sizeof(decimal) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + - sizeof(Vector3) + sizeof(Vector3)) { + sizeof(Vector3) + sizeof(Vector3), 40 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -56,54 +54,98 @@ void SliderJointComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newJointEntities = static_cast(newBuffer); - SliderJoint** newJoints = reinterpret_cast(newJointEntities + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody1 = reinterpret_cast(newJoints + nbComponentsToAllocate); - Vector3* newLocalAnchorPointBody2 = reinterpret_cast(newLocalAnchorPointBody1 + nbComponentsToAllocate); - Matrix3x3* newI1 = reinterpret_cast(newLocalAnchorPointBody2 + nbComponentsToAllocate); - Matrix3x3* newI2 = reinterpret_cast(newI1 + nbComponentsToAllocate); - Vector2* newImpulseTranslation = reinterpret_cast(newI2 + nbComponentsToAllocate); - Vector3* newImpulseRotation = reinterpret_cast(newImpulseTranslation + nbComponentsToAllocate); - Matrix2x2* newInverseMassMatrixTranslation = reinterpret_cast(newImpulseRotation + nbComponentsToAllocate); - Matrix3x3* newInverseMassMatrixRotation = reinterpret_cast(newInverseMassMatrixTranslation + nbComponentsToAllocate); - Vector2* newBiasTranslation = reinterpret_cast(newInverseMassMatrixRotation + nbComponentsToAllocate); - Vector3* newBiasRotation = reinterpret_cast(newBiasTranslation + nbComponentsToAllocate); - Quaternion* newInitOrientationDifferenceInv = reinterpret_cast(newBiasRotation + nbComponentsToAllocate); - Vector3* newSliderAxisBody1 = reinterpret_cast(newInitOrientationDifferenceInv + nbComponentsToAllocate); - Vector3* newSliderAxisWorld = reinterpret_cast(newSliderAxisBody1 + nbComponentsToAllocate); - Vector3* newR1 = reinterpret_cast(newSliderAxisWorld + nbComponentsToAllocate); - Vector3* newR2 = reinterpret_cast(newR1 + nbComponentsToAllocate); - Vector3* newN1 = reinterpret_cast(newR2 + nbComponentsToAllocate); - Vector3* newN2 = reinterpret_cast(newN1 + nbComponentsToAllocate); - decimal* newImpulseLowerLimit = reinterpret_cast(newN2 + nbComponentsToAllocate); - decimal* newImpulseUpperLimit = reinterpret_cast(newImpulseLowerLimit + nbComponentsToAllocate); - decimal* newImpulseMotor = reinterpret_cast(newImpulseUpperLimit + nbComponentsToAllocate); - decimal* newInverseMassMatrixLimit = reinterpret_cast(newImpulseMotor + nbComponentsToAllocate); - decimal* newInverseMassMatrixMotor = reinterpret_cast(newInverseMassMatrixLimit + nbComponentsToAllocate); - decimal* newBLowerLimit = reinterpret_cast(newInverseMassMatrixMotor + nbComponentsToAllocate); - decimal* newBUpperLimit = reinterpret_cast(newBLowerLimit + nbComponentsToAllocate); - bool* newIsLimitEnabled = reinterpret_cast(newBUpperLimit + nbComponentsToAllocate); - bool* newIsMotorEnabled = reinterpret_cast(newIsLimitEnabled + nbComponentsToAllocate); - decimal* newLowerLimit = reinterpret_cast(newIsMotorEnabled + nbComponentsToAllocate); - decimal* newUpperLimit = reinterpret_cast(newLowerLimit + nbComponentsToAllocate); - bool* newIsLowerLimitViolated = reinterpret_cast(newUpperLimit + nbComponentsToAllocate); - bool* newIsUpperLimitViolated = reinterpret_cast(newIsLowerLimitViolated + nbComponentsToAllocate); - decimal* newMotorSpeed = reinterpret_cast(newIsUpperLimitViolated + nbComponentsToAllocate); - decimal* newMaxMotorForce = reinterpret_cast(newMotorSpeed + nbComponentsToAllocate); - Vector3* newR2CrossN1 = reinterpret_cast(newMaxMotorForce + nbComponentsToAllocate); - Vector3* newR2CrossN2 = reinterpret_cast(newR2CrossN1 + nbComponentsToAllocate); - Vector3* newR2CrossSliderAxis = reinterpret_cast(newR2CrossN2 + nbComponentsToAllocate); - Vector3* newR1PlusUCrossN1 = reinterpret_cast(newR2CrossSliderAxis + nbComponentsToAllocate); - Vector3* newR1PlusUCrossN2 = reinterpret_cast(newR1PlusUCrossN1 + nbComponentsToAllocate); - Vector3* newR1PlusUCrossSliderAxis = reinterpret_cast(newR1PlusUCrossN2 + nbComponentsToAllocate); + SliderJoint** newJoints = reinterpret_cast(MemoryAllocator::alignAddress(newJointEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newJoints) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody1 = reinterpret_cast(MemoryAllocator::alignAddress(newJoints + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody1) % GLOBAL_ALIGNMENT == 0); + Vector3* newLocalAnchorPointBody2 = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLocalAnchorPointBody2) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI1 = reinterpret_cast(MemoryAllocator::alignAddress(newLocalAnchorPointBody2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI1) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newI2 = reinterpret_cast(MemoryAllocator::alignAddress(newI1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newI2) % GLOBAL_ALIGNMENT == 0); + Vector2* newImpulseTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newI2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseTranslation) % GLOBAL_ALIGNMENT == 0); + Vector3* newImpulseRotation = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseRotation) % GLOBAL_ALIGNMENT == 0); + Matrix2x2* newInverseMassMatrixTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixTranslation) % GLOBAL_ALIGNMENT == 0); + Matrix3x3* newInverseMassMatrixRotation = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixRotation) % GLOBAL_ALIGNMENT == 0); + Vector2* newBiasTranslation = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBiasTranslation) % GLOBAL_ALIGNMENT == 0); + Vector3* newBiasRotation = reinterpret_cast(MemoryAllocator::alignAddress(newBiasTranslation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBiasRotation) % GLOBAL_ALIGNMENT == 0); + Quaternion* newInitOrientationDifferenceInv = reinterpret_cast(MemoryAllocator::alignAddress(newBiasRotation + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInitOrientationDifferenceInv) % GLOBAL_ALIGNMENT == 0); + Vector3* newSliderAxisBody1 = reinterpret_cast(MemoryAllocator::alignAddress(newInitOrientationDifferenceInv + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newSliderAxisBody1) % GLOBAL_ALIGNMENT == 0); + Vector3* newSliderAxisWorld = reinterpret_cast(MemoryAllocator::alignAddress(newSliderAxisBody1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newSliderAxisWorld) % GLOBAL_ALIGNMENT == 0); + Vector3* newR1 = reinterpret_cast(MemoryAllocator::alignAddress(newSliderAxisWorld + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR1) % GLOBAL_ALIGNMENT == 0); + Vector3* newR2 = reinterpret_cast(MemoryAllocator::alignAddress(newR1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR2) % GLOBAL_ALIGNMENT == 0); + Vector3* newN1 = reinterpret_cast(MemoryAllocator::alignAddress(newR2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newN1) % GLOBAL_ALIGNMENT == 0); + Vector3* newN2 = reinterpret_cast(MemoryAllocator::alignAddress(newN1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newN2) % GLOBAL_ALIGNMENT == 0); + decimal* newImpulseLowerLimit = reinterpret_cast(MemoryAllocator::alignAddress(newN2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseLowerLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newImpulseUpperLimit = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseLowerLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseUpperLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newImpulseMotor = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseUpperLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newImpulseMotor) % GLOBAL_ALIGNMENT == 0); + decimal* newInverseMassMatrixLimit = reinterpret_cast(MemoryAllocator::alignAddress(newImpulseMotor + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newInverseMassMatrixMotor = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newInverseMassMatrixMotor) % GLOBAL_ALIGNMENT == 0); + decimal* newBLowerLimit = reinterpret_cast(MemoryAllocator::alignAddress(newInverseMassMatrixMotor + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBLowerLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newBUpperLimit = reinterpret_cast(MemoryAllocator::alignAddress(newBLowerLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newBUpperLimit) % GLOBAL_ALIGNMENT == 0); + bool* newIsLimitEnabled = reinterpret_cast(MemoryAllocator::alignAddress(newBUpperLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsLimitEnabled) % GLOBAL_ALIGNMENT == 0); + bool* newIsMotorEnabled = reinterpret_cast(MemoryAllocator::alignAddress(newIsLimitEnabled + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsMotorEnabled) % GLOBAL_ALIGNMENT == 0); + decimal* newLowerLimit = reinterpret_cast(MemoryAllocator::alignAddress(newIsMotorEnabled + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newLowerLimit) % GLOBAL_ALIGNMENT == 0); + decimal* newUpperLimit = reinterpret_cast(MemoryAllocator::alignAddress(newLowerLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newUpperLimit) % GLOBAL_ALIGNMENT == 0); + bool* newIsLowerLimitViolated = reinterpret_cast(MemoryAllocator::alignAddress(newUpperLimit + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsLowerLimitViolated) % GLOBAL_ALIGNMENT == 0); + bool* newIsUpperLimitViolated = reinterpret_cast(MemoryAllocator::alignAddress(newIsLowerLimitViolated + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newIsUpperLimitViolated) % GLOBAL_ALIGNMENT == 0); + decimal* newMotorSpeed = reinterpret_cast(MemoryAllocator::alignAddress(newIsUpperLimitViolated + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newMotorSpeed) % GLOBAL_ALIGNMENT == 0); + decimal* newMaxMotorForce = reinterpret_cast(MemoryAllocator::alignAddress(newMotorSpeed + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newMaxMotorForce) % GLOBAL_ALIGNMENT == 0); + Vector3* newR2CrossN1 = reinterpret_cast(MemoryAllocator::alignAddress(newMaxMotorForce + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR2CrossN1) % GLOBAL_ALIGNMENT == 0); + Vector3* newR2CrossN2 = reinterpret_cast(MemoryAllocator::alignAddress(newR2CrossN1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR2CrossN2) % GLOBAL_ALIGNMENT == 0); + Vector3* newR2CrossSliderAxis = reinterpret_cast(MemoryAllocator::alignAddress(newR2CrossN2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR2CrossSliderAxis) % GLOBAL_ALIGNMENT == 0); + Vector3* newR1PlusUCrossN1 = reinterpret_cast(MemoryAllocator::alignAddress(newR2CrossSliderAxis + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR1PlusUCrossN1) % GLOBAL_ALIGNMENT == 0); + Vector3* newR1PlusUCrossN2 = reinterpret_cast(MemoryAllocator::alignAddress(newR1PlusUCrossN1 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR1PlusUCrossN2) % GLOBAL_ALIGNMENT == 0); + Vector3* newR1PlusUCrossSliderAxis = reinterpret_cast(MemoryAllocator::alignAddress(newR1PlusUCrossN2 + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newR1PlusUCrossSliderAxis) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newR1PlusUCrossSliderAxis + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -199,10 +241,10 @@ void SliderJointComponents::allocate(uint32 nbComponentsToAllocate) { } // Add a component -void SliderJointComponents::addComponent(Entity jointEntity, bool isSleeping, const SliderJointComponent& component) { +void SliderJointComponents::addComponent(Entity jointEntity, bool isDisabled, const SliderJointComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mJointEntities + index) Entity(jointEntity); diff --git a/ext/reactphysics3d/src/components/TransformComponents.cpp b/ext/reactphysics3d/src/components/TransformComponents.cpp index 832450228..eb59fa22c 100644 --- a/ext/reactphysics3d/src/components/TransformComponents.cpp +++ b/ext/reactphysics3d/src/components/TransformComponents.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -26,19 +26,17 @@ // Libraries #include #include +#include #include #include // We want to use the ReactPhysics3D namespace using namespace reactphysics3d; - // Constructor TransformComponents::TransformComponents(MemoryAllocator& allocator) - :Components(allocator, sizeof(Entity) + sizeof(Transform)) { + :Components(allocator, sizeof(Entity) + sizeof(Transform), 2 * GLOBAL_ALIGNMENT) { - // Allocate memory for the components data - allocate(INIT_NB_ALLOCATED_COMPONENTS); } // Allocate memory for a given number of components @@ -46,16 +44,23 @@ void TransformComponents::allocate(uint32 nbComponentsToAllocate) { assert(nbComponentsToAllocate > mNbAllocatedComponents); + // Make sure capacity is an integral multiple of alignment + nbComponentsToAllocate = std::ceil(nbComponentsToAllocate / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Size for the data of a single component (in bytes) - const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize; + const size_t totalSizeBytes = nbComponentsToAllocate * mComponentDataSize + mAlignmentMarginSize; // Allocate memory void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); assert(newBuffer != nullptr); + assert(reinterpret_cast(newBuffer) % GLOBAL_ALIGNMENT == 0); // New pointers to components data Entity* newEntities = static_cast(newBuffer); - Transform* newTransforms = reinterpret_cast(newEntities + nbComponentsToAllocate); + assert(reinterpret_cast(newEntities) % GLOBAL_ALIGNMENT == 0); + Transform* newTransforms = reinterpret_cast(MemoryAllocator::alignAddress(newEntities + nbComponentsToAllocate, GLOBAL_ALIGNMENT)); + assert(reinterpret_cast(newTransforms) % GLOBAL_ALIGNMENT == 0); + assert(reinterpret_cast(newTransforms + nbComponentsToAllocate) <= reinterpret_cast(newBuffer) + totalSizeBytes); // If there was already components before if (mNbComponents > 0) { @@ -75,10 +80,10 @@ void TransformComponents::allocate(uint32 nbComponentsToAllocate) { } // Add a component -void TransformComponents::addComponent(Entity bodyEntity, bool isSleeping, const TransformComponent& component) { +void TransformComponents::addComponent(Entity bodyEntity, bool isDisabled, const TransformComponent& component) { // Prepare to add new component (allocate memory if necessary and compute insertion index) - uint32 index = prepareAddComponent(isSleeping); + uint32 index = prepareAddComponent(isDisabled); // Insert the new component data new (mBodies + index) Entity(bodyEntity); diff --git a/ext/reactphysics3d/src/constraint/BallAndSocketJoint.cpp b/ext/reactphysics3d/src/constraint/BallAndSocketJoint.cpp index a62a067ba..1476b5660 100644 --- a/ext/reactphysics3d/src/constraint/BallAndSocketJoint.cpp +++ b/ext/reactphysics3d/src/constraint/BallAndSocketJoint.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/constraint/ContactPoint.cpp b/ext/reactphysics3d/src/constraint/ContactPoint.cpp index a9db9d3f0..e0043a3dd 100644 --- a/ext/reactphysics3d/src/constraint/ContactPoint.cpp +++ b/ext/reactphysics3d/src/constraint/ContactPoint.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -36,7 +36,8 @@ ContactPoint::ContactPoint(const ContactPointInfo* contactInfo, decimal persiste mPenetrationDepth(contactInfo->penetrationDepth), mLocalPointOnShape1(contactInfo->localPoint1), mLocalPointOnShape2(contactInfo->localPoint2), - mIsRestingContact(false), mIsObsolete(false), + mIsRestingContact(false), mPenetrationImpulse(0), mIsObsolete(false), + mNext(nullptr), mPrevious(nullptr), mPersistentContactDistanceThreshold(persistentContactDistanceThreshold) { assert(mPenetrationDepth > decimal(0.0)); @@ -52,6 +53,7 @@ ContactPoint::ContactPoint(const ContactPointInfo& contactInfo, decimal persiste mLocalPointOnShape1(contactInfo.localPoint1), mLocalPointOnShape2(contactInfo.localPoint2), mIsRestingContact(false), mPenetrationImpulse(0), mIsObsolete(false), + mNext(nullptr), mPrevious(nullptr), mPersistentContactDistanceThreshold(persistentContactDistanceThreshold) { assert(mPenetrationDepth > decimal(0.0)); diff --git a/ext/reactphysics3d/src/constraint/FixedJoint.cpp b/ext/reactphysics3d/src/constraint/FixedJoint.cpp index 308e81158..64cad7015 100644 --- a/ext/reactphysics3d/src/constraint/FixedJoint.cpp +++ b/ext/reactphysics3d/src/constraint/FixedJoint.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/constraint/HingeJoint.cpp b/ext/reactphysics3d/src/constraint/HingeJoint.cpp index 3aff49084..1743034a2 100644 --- a/ext/reactphysics3d/src/constraint/HingeJoint.cpp +++ b/ext/reactphysics3d/src/constraint/HingeJoint.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/constraint/Joint.cpp b/ext/reactphysics3d/src/constraint/Joint.cpp index e3278c470..8e06477ea 100644 --- a/ext/reactphysics3d/src/constraint/Joint.cpp +++ b/ext/reactphysics3d/src/constraint/Joint.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/constraint/SliderJoint.cpp b/ext/reactphysics3d/src/constraint/SliderJoint.cpp index 08dd262ea..59b106d54 100644 --- a/ext/reactphysics3d/src/constraint/SliderJoint.cpp +++ b/ext/reactphysics3d/src/constraint/SliderJoint.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -177,7 +177,7 @@ void SliderJoint::setMinTranslationLimit(decimal lowerLimit) { // Set the maximum translation limit /** - * @param lowerLimit The maximum translation limit of the joint (in meters) + * @param upperLimit The maximum translation limit of the joint (in meters) */ void SliderJoint::setMaxTranslationLimit(decimal upperLimit) { diff --git a/ext/reactphysics3d/src/engine/Entity.cpp b/ext/reactphysics3d/src/engine/Entity.cpp index b78911b4d..72e73ec60 100644 --- a/ext/reactphysics3d/src/engine/Entity.cpp +++ b/ext/reactphysics3d/src/engine/Entity.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/engine/EntityManager.cpp b/ext/reactphysics3d/src/engine/EntityManager.cpp index 18f7bc766..142d9f910 100644 --- a/ext/reactphysics3d/src/engine/EntityManager.cpp +++ b/ext/reactphysics3d/src/engine/EntityManager.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/engine/Island.cpp b/ext/reactphysics3d/src/engine/Island.cpp index c8d932200..104f7df7d 100644 --- a/ext/reactphysics3d/src/engine/Island.cpp +++ b/ext/reactphysics3d/src/engine/Island.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/engine/Material.cpp b/ext/reactphysics3d/src/engine/Material.cpp index f5550a1eb..dfd1b7e17 100644 --- a/ext/reactphysics3d/src/engine/Material.cpp +++ b/ext/reactphysics3d/src/engine/Material.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/engine/OverlappingPairs.cpp b/ext/reactphysics3d/src/engine/OverlappingPairs.cpp index 0633b5e29..4f94da2e1 100644 --- a/ext/reactphysics3d/src/engine/OverlappingPairs.cpp +++ b/ext/reactphysics3d/src/engine/OverlappingPairs.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -36,10 +36,11 @@ using namespace reactphysics3d; // Constructor OverlappingPairs::OverlappingPairs(MemoryManager& memoryManager, ColliderComponents& colliderComponents, - CollisionBodyComponents& collisionBodyComponents, RigidBodyComponents& rigidBodyComponents, Set &noCollisionPairs, CollisionDispatch &collisionDispatch) + BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, Set &noCollisionPairs, CollisionDispatch &collisionDispatch) : mPoolAllocator(memoryManager.getPoolAllocator()), mHeapAllocator(memoryManager.getHeapAllocator()), mConvexPairs(memoryManager.getHeapAllocator()), - mConcavePairs(memoryManager.getHeapAllocator()), mMapConvexPairIdToPairIndex(memoryManager.getHeapAllocator()), mMapConcavePairIdToPairIndex(memoryManager.getHeapAllocator()), - mColliderComponents(colliderComponents), mCollisionBodyComponents(collisionBodyComponents), + mConcavePairs(memoryManager.getHeapAllocator()), mDisabledConvexPairs(memoryManager.getHeapAllocator()), mDisabledConcavePairs(memoryManager.getHeapAllocator()), mMapConvexPairIdToPairIndex(memoryManager.getHeapAllocator()), mMapConcavePairIdToPairIndex(memoryManager.getHeapAllocator()), + mMapDisabledConvexPairIdToPairIndex(memoryManager.getHeapAllocator()), mMapDisabledConcavePairIdToPairIndex(memoryManager.getHeapAllocator()), + mColliderComponents(colliderComponents), mBodyComponents(bodyComponents), mRigidBodyComponents(rigidBodyComponents), mNoCollisionPairs(noCollisionPairs), mCollisionDispatch(collisionDispatch) { } @@ -50,90 +51,348 @@ OverlappingPairs::~OverlappingPairs() { // Destroy the convex pairs while (mConvexPairs.size() > 0) { - removePair(mConvexPairs.size() - 1, true); + removeConvexPairWithIndex(mConvexPairs.size() - 1, true); } // Destroy the concave pairs while (mConcavePairs.size() > 0) { - removePair(mConcavePairs.size() - 1, false); + removeConcavePairWithIndex(mConcavePairs.size() - 1, true); + } + + // Destroy the disabled convex pairs + while (mDisabledConvexPairs.size() > 0) { + + removeDisabledConvexPairWithIndex(mDisabledConvexPairs.size() - 1, true); + } + + // Destroy the disabled concave pairs + while (mDisabledConcavePairs.size() > 0) { + + removeDisabledConcavePairWithIndex(mDisabledConcavePairs.size() - 1, true); } } -// Remove a component at a given index +// Remove an overlapping pair void OverlappingPairs::removePair(uint64 pairId) { - assert(mMapConvexPairIdToPairIndex.containsKey(pairId) || mMapConcavePairIdToPairIndex.containsKey(pairId)); + RP3D_PROFILE("OverlappingPairs::removePair()", mProfiler); + + assert(mMapConvexPairIdToPairIndex.containsKey(pairId) || mMapConcavePairIdToPairIndex.containsKey(pairId) || + mMapDisabledConvexPairIdToPairIndex.containsKey(pairId) || mMapDisabledConcavePairIdToPairIndex.containsKey(pairId)); auto it = mMapConvexPairIdToPairIndex.find(pairId); if (it != mMapConvexPairIdToPairIndex.end()) { - removePair(it->second, true); + removeConvexPairWithIndex(it->second, true); + return; } - else { - removePair(mMapConcavePairIdToPairIndex[pairId], false); + + auto it2 = mMapConcavePairIdToPairIndex.find(pairId); + if (it2 != mMapConcavePairIdToPairIndex.end()) { + removeConcavePairWithIndex(it2->second, true); + return; + } + + auto it3 = mMapDisabledConvexPairIdToPairIndex.find(pairId); + if (it3 != mMapDisabledConvexPairIdToPairIndex.end()) { + removeDisabledConvexPairWithIndex(it3->second, true); + return; + } + + auto it4 = mMapDisabledConcavePairIdToPairIndex.find(pairId); + if (it4 != mMapDisabledConcavePairIdToPairIndex.end()) { + removeDisabledConcavePairWithIndex(it4->second, true); + return; } } -// Remove a component at a given index -void OverlappingPairs::removePair(uint64 pairIndex, bool isConvexVsConvex) { +// Remove an disabled convex overlapping pair +void OverlappingPairs::removeDisabledConvexPairWithIndex(uint64 pairIndex, bool removeFromColliders) { - RP3D_PROFILE("OverlappingPairs::removePair()", mProfiler); + RP3D_PROFILE("OverlappingPairs::removeDisabledConvexPairWithIndex()", mProfiler); - if (isConvexVsConvex) { + // Remove the involved overlapping pair from the two colliders + assert(mColliderComponents.getOverlappingPairs(mDisabledConvexPairs[pairIndex].collider1).find(mDisabledConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mDisabledConvexPairs[pairIndex].collider1).end()); + assert(mColliderComponents.getOverlappingPairs(mDisabledConvexPairs[pairIndex].collider2).find(mDisabledConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mDisabledConvexPairs[pairIndex].collider2).end()); + if (removeFromColliders) { + + mColliderComponents.getOverlappingPairs(mDisabledConvexPairs[pairIndex].collider1).remove(mDisabledConvexPairs[pairIndex].pairID); + mColliderComponents.getOverlappingPairs(mDisabledConvexPairs[pairIndex].collider2).remove(mDisabledConvexPairs[pairIndex].pairID); + } + + assert(mMapDisabledConvexPairIdToPairIndex[mDisabledConvexPairs[pairIndex].pairID] == pairIndex); + mMapDisabledConvexPairIdToPairIndex.remove(mDisabledConvexPairs[pairIndex].pairID); + + const uint64 nbDisabledPairs = mDisabledConvexPairs.size(); + + // Change the mapping between the pairId and the index in the disabled pairs array if we swap the last item with the one to remove + if (mDisabledConvexPairs.size() > 1 && pairIndex < (nbDisabledPairs - 1)) { + + mMapDisabledConvexPairIdToPairIndex[mDisabledConvexPairs[nbDisabledPairs - 1].pairID] = pairIndex; + } + + // We want to keep the arrays tightly packed. Therefore, when a pair is removed, + // we replace it with the last element of the array. + mDisabledConvexPairs.removeAtAndReplaceByLast(pairIndex); +} + +// Remove an disabled concave overlapping pair +void OverlappingPairs::removeDisabledConcavePairWithIndex(uint64 pairIndex, bool removeFromColliders) { + + RP3D_PROFILE("OverlappingPairs::removeDisabledConcavePairWithIndex()", mProfiler); + + // Remove the involved overlapping pair from the two colliders + assert(mColliderComponents.getOverlappingPairs(mDisabledConcavePairs[pairIndex].collider1).find(mDisabledConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mDisabledConcavePairs[pairIndex].collider1).end()); + assert(mColliderComponents.getOverlappingPairs(mDisabledConcavePairs[pairIndex].collider2).find(mDisabledConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mDisabledConcavePairs[pairIndex].collider2).end()); + if (removeFromColliders) { + + mColliderComponents.getOverlappingPairs(mDisabledConcavePairs[pairIndex].collider1).remove(mDisabledConcavePairs[pairIndex].pairID); + mColliderComponents.getOverlappingPairs(mDisabledConcavePairs[pairIndex].collider2).remove(mDisabledConcavePairs[pairIndex].pairID); + } + + assert(mMapDisabledConcavePairIdToPairIndex[mDisabledConcavePairs[pairIndex].pairID] == pairIndex); + mMapDisabledConcavePairIdToPairIndex.remove(mDisabledConcavePairs[pairIndex].pairID); + + const uint64 nbDisabledPairs = mDisabledConcavePairs.size(); + + // Change the mapping between the pairId and the index in the disabled pairs array if we swap the last item with the one to remove + if (mDisabledConcavePairs.size() > 1 && pairIndex < (nbDisabledPairs - 1)) { + + mMapDisabledConcavePairIdToPairIndex[mDisabledConcavePairs[nbDisabledPairs - 1].pairID] = pairIndex; + } + + // We want to keep the arrays tightly packed. Therefore, when a pair is removed, + // we replace it with the last element of the array. + mDisabledConcavePairs.removeAtAndReplaceByLast(pairIndex); +} - const uint64 nbConvexPairs = mConvexPairs.size(); +// Remove a convex pair at a given index +void OverlappingPairs::removeConvexPairWithIndex(uint64 pairIndex, bool removeFromColliders) { - assert(pairIndex < nbConvexPairs); + RP3D_PROFILE("OverlappingPairs::removeConvexPairWithIndex()", mProfiler); + + const uint64 nbConvexPairs = mConvexPairs.size(); + + assert(pairIndex < nbConvexPairs); + + assert(mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).find(mConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).end()); + assert(mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).find(mConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).end()); + + if (removeFromColliders) { // Remove the involved overlapping pair from the two colliders - assert(mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).find(mConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).end()); - assert(mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).find(mConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).end()); mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).remove(mConvexPairs[pairIndex].pairID); mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).remove(mConvexPairs[pairIndex].pairID); + } - assert(mMapConvexPairIdToPairIndex[mConvexPairs[pairIndex].pairID] == pairIndex); - mMapConvexPairIdToPairIndex.remove(mConvexPairs[pairIndex].pairID); + assert(mMapConvexPairIdToPairIndex[mConvexPairs[pairIndex].pairID] == pairIndex); + mMapConvexPairIdToPairIndex.remove(mConvexPairs[pairIndex].pairID); - // Change the mapping between the pairId and the index in the convex pairs array if we swap the last item with the one to remove - if (mConvexPairs.size() > 1 && pairIndex < (nbConvexPairs - 1)) { + // Change the mapping between the pairId and the index in the convex pairs array if we swap the last item with the one to remove + if (mConvexPairs.size() > 1 && pairIndex < (nbConvexPairs - 1)) { - mMapConvexPairIdToPairIndex[mConvexPairs[nbConvexPairs - 1].pairID] = pairIndex; - } - - // We want to keep the arrays tightly packed. Therefore, when a pair is removed, - // we replace it with the last element of the array. - mConvexPairs.removeAtAndReplaceByLast(pairIndex); + mMapConvexPairIdToPairIndex[mConvexPairs[nbConvexPairs - 1].pairID] = pairIndex; } - else { - const uint64 nbConcavePairs = mConcavePairs.size(); + // We want to keep the arrays tightly packed. Therefore, when a pair is removed, + // we replace it with the last element of the array. + mConvexPairs.removeAtAndReplaceByLast(pairIndex); +} + +// Remove a concave pair at a given index +void OverlappingPairs::removeConcavePairWithIndex(uint64 pairIndex, bool removeFromColliders) { + + RP3D_PROFILE("OverlappingPairs::removeConcavePairWithIndex()", mProfiler); + + const uint64 nbConcavePairs = mConcavePairs.size(); + + assert(pairIndex < nbConcavePairs); + + assert(mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).find(mConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).end()); + assert(mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).find(mConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).end()); - assert(pairIndex < nbConcavePairs); + if (removeFromColliders) { - // Remove the involved overlapping pair to the two colliders - assert(mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).find(mConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).end()); - assert(mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).find(mConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).end()); + // Remove the involved overlapping pair from the two colliders mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).remove(mConcavePairs[pairIndex].pairID); mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).remove(mConcavePairs[pairIndex].pairID); + } + + assert(mMapConcavePairIdToPairIndex[mConcavePairs[pairIndex].pairID] == pairIndex); + mMapConcavePairIdToPairIndex.remove(mConcavePairs[pairIndex].pairID); + + // Destroy all the LastFrameCollisionInfo objects + mConcavePairs[pairIndex].destroyLastFrameCollisionInfos(); + + // Change the mapping between the pairId and the index in the convex pairs array if we swap the last item with the one to remove + if (mConcavePairs.size() > 1 && pairIndex < (nbConcavePairs - 1)) { - assert(mMapConcavePairIdToPairIndex[mConcavePairs[pairIndex].pairID] == pairIndex); - mMapConcavePairIdToPairIndex.remove(mConcavePairs[pairIndex].pairID); + mMapConcavePairIdToPairIndex[mConcavePairs[nbConcavePairs - 1].pairID] = pairIndex; + } + + // We want to keep the arrays tightly packed. Therefore, when a pair is removed, + // we replace it with the last element of the array. + mConcavePairs.removeAtAndReplaceByLast(pairIndex); +} + +// Enable an overlapping pair (because at least one body of the pair is awaken or not static anymore) +void OverlappingPairs::enablePair(uint64 pairId) { - // Destroy all the LastFrameCollisionInfo objects - mConcavePairs[pairIndex].destroyLastFrameCollisionInfos(); + RP3D_PROFILE("OverlappingPairs::enablePair()", mProfiler); - // Change the mapping between the pairId and the index in the convex pairs array if we swap the last item with the one to remove - if (mConcavePairs.size() > 1 && pairIndex < (nbConcavePairs - 1)) { + assert(isPairDisabled(pairId)); + assert(mMapConvexPairIdToPairIndex.find(pairId) == mMapConvexPairIdToPairIndex.end() && + mMapConcavePairIdToPairIndex.find(pairId) == mMapConcavePairIdToPairIndex.end()); + + // Get the existing overlapping pair from the convex/concave array + auto it = mMapDisabledConvexPairIdToPairIndex.find(pairId); + if (it != mMapDisabledConvexPairIdToPairIndex.end()) { + + // Enable the pair + enableConvexPairWithIndex(it->second); + } + else { + it = mMapDisabledConcavePairIdToPairIndex.find(pairId); + if (it != mMapDisabledConcavePairIdToPairIndex.end()) { - mMapConcavePairIdToPairIndex[mConcavePairs[nbConcavePairs - 1].pairID] = pairIndex; + // Enable the pair + enableConcavePairWithIndex(it->second); } + else { + // Should not happen + assert(false); + return; + } + } +} + +// Disable an overlapping pair (because both bodies of the pair are disabled) +void OverlappingPairs::disablePair(uint64 pairId) { - // We want to keep the arrays tightly packed. Therefore, when a pair is removed, - // we replace it with the last element of the array. - mConcavePairs.removeAtAndReplaceByLast(pairIndex); + RP3D_PROFILE("OverlappingPairs::disablePair()", mProfiler); + + assert(!isPairDisabled(pairId)); + assert(mMapConvexPairIdToPairIndex.find(pairId) != mMapConvexPairIdToPairIndex.end() || + mMapConcavePairIdToPairIndex.find(pairId) != mMapConcavePairIdToPairIndex.end()); + + // Get the existing overlapping pair from the convex/concave array + auto it = mMapConvexPairIdToPairIndex.find(pairId); + if (it != mMapConvexPairIdToPairIndex.end()) { + + // Disable the pair + disableConvexPairWithIndex(it->second); + } + else { + it = mMapConcavePairIdToPairIndex.find(pairId); + if (it != mMapConcavePairIdToPairIndex.end()) { + + // Disable the pair + disableConcavePairWithIndex(it->second); + } + else { + // Should not happen + assert(false); + return; + } } } +// Enable a convex overlapping pair +void OverlappingPairs::enableConvexPairWithIndex(uint64 pairIndex) { + + RP3D_PROFILE("OverlappingPairs::enableConvexPairWithIndex()", mProfiler); + + ConvexOverlappingPair* pair = &(mDisabledConvexPairs[static_cast(pairIndex)]); + + assert(!pair->isEnabled); + + const uint64 newPairIndex = mConvexPairs.size(); + + // Map the entity with the new pair lookup index + mMapConvexPairIdToPairIndex.add(Pair(pair->pairID, newPairIndex)); + + // Create a new pair to be added into the array of pairs + mConvexPairs.emplace(pair->pairID, pair->broadPhaseId1, pair->broadPhaseId2, pair->collider1, pair->collider2, + pair->narrowPhaseAlgorithmType, true); + mConvexPairs[newPairIndex].collidingInCurrentFrame = pair->collidingInCurrentFrame; + mConvexPairs[newPairIndex].collidingInPreviousFrame = pair->collidingInPreviousFrame; + + // Remove the previous overlapping pair from the convex array + removeDisabledConvexPairWithIndex(pairIndex, false); +} + +// Disable a convex overlapping pair (because both bodies of the pair are disabled) +void OverlappingPairs::disableConvexPairWithIndex(uint64 pairIndex) { + + RP3D_PROFILE("OverlappingPairs::disableConvexPairWithIndex()", mProfiler); + + ConvexOverlappingPair* pair = &(mConvexPairs[static_cast(pairIndex)]); + + assert(pair->isEnabled); + + const uint64 newPairIndex = mDisabledConvexPairs.size(); + + // Map the entity with the new pair lookup index + mMapDisabledConvexPairIdToPairIndex.add(Pair(pair->pairID, newPairIndex)); + + // Create a new pair to be added into the array of disable pairs + mDisabledConvexPairs.emplace(pair->pairID, pair->broadPhaseId1, pair->broadPhaseId2, pair->collider1, pair->collider2, + pair->narrowPhaseAlgorithmType, false); + mDisabledConvexPairs[newPairIndex].collidingInCurrentFrame = pair->collidingInCurrentFrame; + mDisabledConvexPairs[newPairIndex].collidingInPreviousFrame = pair->collidingInPreviousFrame; + + // Remove the previous overlapping pair from the convex array + removeConvexPairWithIndex(pairIndex, false); +} + +// Enable a concave overlapping pair +void OverlappingPairs::enableConcavePairWithIndex(uint64 pairIndex) { + + RP3D_PROFILE("OverlappingPairs::enableConcavePairWithIndex()", mProfiler); + + ConcaveOverlappingPair* pair = &(mDisabledConcavePairs[static_cast(pairIndex)]); + + assert(!pair->isEnabled); + + const uint64 newPairIndex = mConcavePairs.size(); + + // Map the entity with the new pair lookup index + mMapConcavePairIdToPairIndex.add(Pair(pair->pairID, newPairIndex)); + + // Create a new pair to be added into the array of disable pairs + mConcavePairs.emplace(pair->pairID, pair->broadPhaseId1, pair->broadPhaseId2, pair->collider1, pair->collider2, + pair->narrowPhaseAlgorithmType, pair->isShape1Convex, mPoolAllocator, mHeapAllocator, true); + mConcavePairs[newPairIndex].collidingInCurrentFrame = pair->collidingInCurrentFrame; + mConcavePairs[newPairIndex].collidingInPreviousFrame = pair->collidingInPreviousFrame; + + // Remove the previous overlapping pair from the concave array + removeDisabledConcavePairWithIndex(pairIndex, false); +} + +// Disable a concave overlapping pair (because both bodies of the pair are disabled) +void OverlappingPairs::disableConcavePairWithIndex(uint64 pairIndex) { + + RP3D_PROFILE("OverlappingPairs::disableConcavePairWithIndex()", mProfiler); + + ConcaveOverlappingPair* pair = &(mConcavePairs[static_cast(pairIndex)]); + + assert(pair->isEnabled); + + const uint64 newPairIndex = mDisabledConcavePairs.size(); + + // Map the entity with the new pair lookup index + mMapDisabledConcavePairIdToPairIndex.add(Pair(pair->pairID, newPairIndex)); + + // Create a new pair to be added into the array of disable pairs + mDisabledConcavePairs.emplace(pair->pairID, pair->broadPhaseId1, pair->broadPhaseId2, pair->collider1, pair->collider2, + pair->narrowPhaseAlgorithmType, pair->isShape1Convex, mPoolAllocator, mHeapAllocator, false, false); + mDisabledConcavePairs[newPairIndex].collidingInCurrentFrame = pair->collidingInCurrentFrame; + mDisabledConcavePairs[newPairIndex].collidingInPreviousFrame = pair->collidingInPreviousFrame; + + // Remove the previous overlapping pair from the concave array + removeConcavePairWithIndex(pairIndex, false); +} + // Add an overlapping pair uint64 OverlappingPairs::addPair(uint32 collider1Index, uint32 collider2Index, bool isConvexVsConvex) { @@ -163,7 +422,7 @@ uint64 OverlappingPairs::addPair(uint32 collider1Index, uint32 collider2Index, b mMapConvexPairIdToPairIndex.add(Pair(pairId, mConvexPairs.size())); // Create and add a new convex pair - mConvexPairs.emplace(pairId, broadPhase1Id, broadPhase2Id, collider1Entity, collider2Entity, algorithmType); + mConvexPairs.emplace(pairId, broadPhase1Id, broadPhase2Id, collider1Entity, collider2Entity, algorithmType, true); } else { @@ -177,7 +436,7 @@ uint64 OverlappingPairs::addPair(uint32 collider1Index, uint32 collider2Index, b // Create and add a new concave pair mConcavePairs.emplace(pairId, broadPhase1Id, broadPhase2Id, collider1Entity, collider2Entity, algorithmType, - isShape1Convex, mPoolAllocator, mHeapAllocator); + isShape1Convex, mPoolAllocator, mHeapAllocator, true); } // Add the involved overlapping pair to the two colliders diff --git a/ext/reactphysics3d/src/engine/PhysicsCommon.cpp b/ext/reactphysics3d/src/engine/PhysicsCommon.cpp index 525d8d04a..fa536d698 100644 --- a/ext/reactphysics3d/src/engine/PhysicsCommon.cpp +++ b/ext/reactphysics3d/src/engine/PhysicsCommon.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -25,6 +25,9 @@ // Libraries #include +#include +#include +#include using namespace reactphysics3d; @@ -40,8 +43,8 @@ PhysicsCommon::PhysicsCommon(MemoryAllocator* baseMemoryAllocator) mPhysicsWorlds(mMemoryManager.getHeapAllocator()), mSphereShapes(mMemoryManager.getHeapAllocator()), mBoxShapes(mMemoryManager.getHeapAllocator()), mCapsuleShapes(mMemoryManager.getHeapAllocator()), mConvexMeshShapes(mMemoryManager.getHeapAllocator()), mConcaveMeshShapes(mMemoryManager.getHeapAllocator()), - mHeightFieldShapes(mMemoryManager.getHeapAllocator()), mPolyhedronMeshes(mMemoryManager.getHeapAllocator()), - mTriangleMeshes(mMemoryManager.getHeapAllocator()), + mHeightFieldShapes(mMemoryManager.getHeapAllocator()), mConvexMeshes(mMemoryManager.getHeapAllocator()), + mTriangleMeshes(mMemoryManager.getHeapAllocator()), mHeightFields(mMemoryManager.getHeapAllocator()), mProfilers(mMemoryManager.getHeapAllocator()), mDefaultLoggers(mMemoryManager.getHeapAllocator()), mBoxShapeHalfEdgeStructure(mMemoryManager.getHeapAllocator(), 6, 8, 24), mTriangleShapeHalfEdgeStructure(mMemoryManager.getHeapAllocator(), 2, 3, 6) { @@ -102,7 +105,7 @@ void PhysicsCommon::initBoxShapeHalfEdgeStructure() { mBoxShapeHalfEdgeStructure.addFace(face4); mBoxShapeHalfEdgeStructure.addFace(face5); - mBoxShapeHalfEdgeStructure.init(); + mBoxShapeHalfEdgeStructure.computeHalfEdges(); } // Initialize the static half-edge structure of a TriangleShape @@ -124,7 +127,7 @@ void PhysicsCommon::initTriangleShapeHalfEdgeStructure() { mTriangleShapeHalfEdgeStructure.addFace(face0); mTriangleShapeHalfEdgeStructure.addFace(face1); - mTriangleShapeHalfEdgeStructure.init(); + mTriangleShapeHalfEdgeStructure.computeHalfEdges(); } // Destroy and release everything that has been allocated @@ -172,11 +175,11 @@ void PhysicsCommon::release() { } mConcaveMeshShapes.clear(); - // Destroy the polyhedron mesh - for (auto it = mPolyhedronMeshes.begin(); it != mPolyhedronMeshes.end(); ++it) { - deletePolyhedronMesh(*it); + // Destroy the convex mesh + for (auto it = mConvexMeshes.begin(); it != mConvexMeshes.end(); ++it) { + deleteConvexMesh(*it); } - mPolyhedronMeshes.clear(); + mConvexMeshes.clear(); // Destroy the triangle mesh for (auto it = mTriangleMeshes.begin(); it != mTriangleMeshes.end(); ++it) { @@ -184,6 +187,12 @@ void PhysicsCommon::release() { } mTriangleMeshes.clear(); + // Destroy the height-field mesh + for (auto it = mHeightFields.begin(); it != mHeightFields.end(); ++it) { + deleteHeightField(*it); + } + mHeightFields.clear(); + // Destroy the default loggers for (auto it = mDefaultLoggers.begin(); it != mDefaultLoggers.end(); ++it) { deleteDefaultLogger(*it); @@ -414,13 +423,13 @@ void PhysicsCommon::deleteCapsuleShape(CapsuleShape* capsuleShape) { // Create and return a convex mesh shape /** - * @param polyhedronMesh A pointer to the polyhedron mesh used to create the convex shape - * @param scaling Scaling factor to scale the polyhedron mesh if necessary + * @param convexMesh A pointer to the convex mesh for this shape + * @param scaling Scaling factor to scale the convex mesh if necessary * @return A pointer to the created convex mesh shape */ -ConvexMeshShape* PhysicsCommon::createConvexMeshShape(PolyhedronMesh* polyhedronMesh, const Vector3& scaling) { +ConvexMeshShape* PhysicsCommon::createConvexMeshShape(ConvexMesh* convexMesh, const Vector3& scaling) { - ConvexMeshShape* shape = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(ConvexMeshShape))) ConvexMeshShape(polyhedronMesh, mMemoryManager.getHeapAllocator(), scaling); + ConvexMeshShape* shape = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(ConvexMeshShape))) ConvexMeshShape(convexMesh, mMemoryManager.getHeapAllocator(), scaling); mConvexMeshShapes.add(shape); @@ -458,24 +467,51 @@ void PhysicsCommon::deleteConvexMeshShape(ConvexMeshShape* convexMeshShape) { mMemoryManager.release(MemoryManager::AllocationType::Pool, convexMeshShape, sizeof(ConvexMeshShape)); } -// Create and return a height-field shape +// Create and return a height-field /** - * @param nbGridColumns Number of columns in the grid of the height field - * @param nbGridRows Number of rows in the grid of the height field - * @param minHeight Minimum height value of the height field - * @param maxHeight Maximum height value of the height field - * @param heightFieldData Pointer to the first height value data (note that values are shared and not copied) + * @param nbGridColumns Number of columns in the grid of the height field (along the local x axis) + * @param nbGridRows Number of rows in the grid of the height field (along the local z axis) + * @param heightFieldData Pointer to the first height value data (note that values are copied into the heigh-field) * @param dataType Data type for the height values (int, float, double) - * @param upAxis Integer representing the up axis direction (0 for x, 1 for y and 2 for z) - * @param integerHeightScale Scaling factor used to scale the height values (only when height values type is integer) + * @param[out] messages A reference to the array where the messages (warnings, errors, ...) will be stored + * @param integerHeightScale Scaling factor for the height values of the height field + * @return A pointer to the created height-field + */ +HeightField* PhysicsCommon::createHeightField(int nbGridColumns, int nbGridRows, + const void* heightFieldData, + HeightField::HeightDataType dataType, + std::vector& messages, + decimal integerHeightScale) { + + // Create the height-field + HeightField* heightField = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(TriangleMesh))) HeightField(mMemoryManager.getHeapAllocator(), mTriangleShapeHalfEdgeStructure); + + // Initialize the height-field + bool isValid = heightField->init(nbGridColumns, nbGridRows, heightFieldData, dataType, messages, + integerHeightScale); + + if (!isValid) { + + heightField->~HeightField(); + mMemoryManager.release(MemoryManager::AllocationType::Pool, heightField, sizeof(HeightField)); + + return nullptr; + } + + mHeightFields.add(heightField); + + return heightField; +} + +// Create and return a height-field collision shape +/** + * @param heightField A pointer to a HeightField object + * @param scaling Scaling vector for the height field * @return A pointer to the created height field shape */ -HeightFieldShape* PhysicsCommon::createHeightFieldShape(int nbGridColumns, int nbGridRows, decimal minHeight, decimal maxHeight, - const void* heightFieldData, HeightFieldShape::HeightDataType dataType, - int upAxis, decimal integerHeightScale, const Vector3& scaling) { +HeightFieldShape* PhysicsCommon::createHeightFieldShape(HeightField* heightField, const Vector3& scaling) { - HeightFieldShape* shape = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(HeightFieldShape))) HeightFieldShape(nbGridColumns, nbGridRows, minHeight, maxHeight, - heightFieldData, dataType, mMemoryManager.getHeapAllocator(), mTriangleShapeHalfEdgeStructure, upAxis, integerHeightScale, scaling); + HeightFieldShape* shape = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(HeightFieldShape))) HeightFieldShape(heightField, mMemoryManager.getHeapAllocator(), scaling); mHeightFieldShapes.add(shape); @@ -560,57 +596,126 @@ void PhysicsCommon::deleteConcaveMeshShape(ConcaveMeshShape* concaveMeshShape) { mMemoryManager.release(MemoryManager::AllocationType::Pool, concaveMeshShape, sizeof(ConcaveMeshShape)); } -// Create a polyhedron mesh +// Create a convex mesh from a PolygonVertexArray describing vertices and faces +/// The data (vertices, faces indices, ...) are copied from the PolygonVertexArray into the +/// created ConvexMesh. /** - * @param polygonVertexArray A pointer to the polygon vertex array to use to create the polyhedron mesh - * @return A pointer to the created polyhedron mesh or nullptr if the mesh is not valid + * @param polygonVertexArray A pointer to the polygon vertex array to use to create the convex mesh + * @param messages A reference to a vector of messages. This vector might contains errors that occured during the creation + * @return A pointer to the created ConvexMesh instance or nullptr if errors occured during the creation */ -PolyhedronMesh* PhysicsCommon::createPolyhedronMesh(PolygonVertexArray* polygonVertexArray) { +ConvexMesh* PhysicsCommon::createConvexMesh(const PolygonVertexArray& polygonVertexArray, std::vector& messages) { - // Create the polyhedron mesh - PolyhedronMesh* mesh = PolyhedronMesh::create(polygonVertexArray, mMemoryManager.getPoolAllocator(), mMemoryManager.getHeapAllocator()); + MemoryAllocator& allocator = mMemoryManager.getHeapAllocator(); - // If the mesh is valid - if (mesh != nullptr) { + // Create the convex mesh + ConvexMesh* mesh = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(ConvexMesh))) ConvexMesh(allocator); + + // Create the half-edge structure of the mesh + bool isValid = mesh->init(polygonVertexArray, messages); - mPolyhedronMeshes.add(mesh); + // If the mesh is not valid + if (!isValid) { + mesh->~ConvexMesh(); + mMemoryManager.release(MemoryManager::AllocationType::Pool, mesh, sizeof(ConvexMesh)); + return nullptr; } + // If the mesh is valid + + mConvexMeshes.add(mesh); + return mesh; } -// Destroy a polyhedron mesh +// Create a convex mesh from an array of vertices (automatically computing the convex hull using QuickHull) +/// The data (vertices) are copied from the VertexArray into the created ConvexMesh. /** - * @param polyhedronMesh A pointer to the polyhedron mesh to destroy + * @param vertexArray A reference to the vertex object describing the vertices used to compute the convex hull + * @param messages A reference to the array of messages with errors that might have happened during convex mesh creation + * @return A pointer to the created ConvexMesh instance or nullptr if errors occured during the creation */ -void PhysicsCommon::destroyPolyhedronMesh(PolyhedronMesh* polyhedronMesh) { +ConvexMesh* PhysicsCommon::createConvexMesh(const VertexArray& vertexArray, std::vector& messages) { + + MemoryAllocator& allocator = mMemoryManager.getHeapAllocator(); - deletePolyhedronMesh(polyhedronMesh); + PolygonVertexArray outPolygonVertexArray; + Array vertices(allocator); + Array indices(allocator); + Array faces(allocator); + + // Use the Quick-Hull algorithm to compute the convex hull and return a PolygonVertexArray + bool isValid = QuickHull::computeConvexHull(vertexArray, outPolygonVertexArray, vertices, indices, faces, allocator, messages); + if (!isValid) { + return nullptr; + } + + // Create the convex mesh + + ConvexMesh* mesh = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(ConvexMesh))) ConvexMesh(allocator); + assert(mesh != nullptr); + + // Create the half-edge structure of the mesh + isValid &= mesh->init(outPolygonVertexArray, messages); + + // If the mesh is not valid + if (!isValid) { + mesh->~ConvexMesh(); + mMemoryManager.release(MemoryManager::AllocationType::Pool, mesh, sizeof(ConvexMesh)); + return nullptr; + } - mPolyhedronMeshes.remove(polyhedronMesh); + // If the mesh is valid + + mConvexMeshes.add(mesh); + + return mesh; +} + +// Destroy a convex mesh +/** + * @param convexMesh A pointer to the convex mesh to destroy + */ +void PhysicsCommon::destroyConvexMesh(ConvexMesh* convexMesh) { + + deleteConvexMesh(convexMesh); + mConvexMeshes.remove(convexMesh); } -// Delete a polyhedron mesh +// Delete a convex mesh /** - * @param polyhedronMesh A pointer to the polyhedron mesh to destroy + * @param convexMesh A pointer to the convex mesh to destroy */ -void PhysicsCommon::deletePolyhedronMesh(PolyhedronMesh* polyhedronMesh) { +void PhysicsCommon::deleteConvexMesh(ConvexMesh* convexMesh) { // Call the destructor of the shape - polyhedronMesh->~PolyhedronMesh(); + convexMesh->~ConvexMesh(); // Release allocated memory - mMemoryManager.release(MemoryManager::AllocationType::Pool, polyhedronMesh, sizeof(PolyhedronMesh)); + mMemoryManager.release(MemoryManager::AllocationType::Pool, convexMesh, sizeof(ConvexMesh)); } -// Create a triangle mesh +// Create a triangle mesh from a TriangleVertexArray +/// The data (vertices, faces indices) are copied from the TriangleVertexArray into the created ConvexMesh. /** + * @param triangleVertexArray A reference to the input TriangleVertexArray + * @param messages A reference to the array to stored the messages (warnings, erros, ...) * @return A pointer to the created triangle mesh */ -TriangleMesh* PhysicsCommon::createTriangleMesh() { +TriangleMesh* PhysicsCommon::createTriangleMesh(const TriangleVertexArray& triangleVertexArray, std::vector& messages) { TriangleMesh* mesh = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(TriangleMesh))) TriangleMesh(mMemoryManager.getHeapAllocator()); + bool isValid = mesh->init(triangleVertexArray, messages); + + if (!isValid) { + + mesh->~TriangleMesh(); + mMemoryManager.release(MemoryManager::AllocationType::Pool, mesh, sizeof(TriangleMesh)); + + return nullptr; + } + mTriangleMeshes.add(mesh); return mesh; @@ -618,7 +723,7 @@ TriangleMesh* PhysicsCommon::createTriangleMesh() { // Destroy a triangle mesh /** - * @param A pointer to the triangle mesh to destroy + * @param triangleMesh A pointer to the triangle mesh to destroy */ void PhysicsCommon::destroyTriangleMesh(TriangleMesh* triangleMesh) { @@ -627,9 +732,20 @@ void PhysicsCommon::destroyTriangleMesh(TriangleMesh* triangleMesh) { mTriangleMeshes.remove(triangleMesh); } +// Destroy a height-field +/** + * @param heightField A pointer to the height field to destroy + */ +void PhysicsCommon::destroyHeightField(HeightField* heightField) { + + deleteHeightField(heightField); + + mHeightFields.remove(heightField); +} + // Delete a triangle mesh /** - * @param A pointer to the triangle mesh to destroy + * @param triangleMesh A pointer to the triangle mesh to destroy */ void PhysicsCommon::deleteTriangleMesh(TriangleMesh* triangleMesh) { @@ -640,6 +756,19 @@ void PhysicsCommon::deleteTriangleMesh(TriangleMesh* triangleMesh) { mMemoryManager.release(MemoryManager::AllocationType::Pool, triangleMesh, sizeof(TriangleMesh)); } +// Delete a height-field +/** + * @param heightField A pointer to the height-field to destroy + */ +void PhysicsCommon::deleteHeightField(HeightField* heightField) { + + // Call the destructor of the shape + heightField->~HeightField(); + + // Release allocated memory + mMemoryManager.release(MemoryManager::AllocationType::Pool, heightField, sizeof(HeightField)); +} + // Create and return a new logger /** * @return A pointer to the created default logger @@ -655,7 +784,7 @@ DefaultLogger* PhysicsCommon::createDefaultLogger() { // Destroy a logger /** - * @param A pointer to the default logger to destroy + * @param logger A pointer to the default logger to destroy */ void PhysicsCommon::destroyDefaultLogger(DefaultLogger* logger) { @@ -666,7 +795,7 @@ void PhysicsCommon::destroyDefaultLogger(DefaultLogger* logger) { // Delete a logger /** - * @param A pointer to the default logger to destroy + * @param logger A pointer to the default logger to destroy */ void PhysicsCommon::deleteDefaultLogger(DefaultLogger* logger) { diff --git a/ext/reactphysics3d/src/engine/PhysicsWorld.cpp b/ext/reactphysics3d/src/engine/PhysicsWorld.cpp index c442452ef..80a425c56 100644 --- a/ext/reactphysics3d/src/engine/PhysicsWorld.cpp +++ b/ext/reactphysics3d/src/engine/PhysicsWorld.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -35,6 +35,7 @@ #include #include #include +#include // Namespaces using namespace reactphysics3d; @@ -57,24 +58,24 @@ PhysicsWorld::PhysicsWorld(MemoryManager& memoryManager, PhysicsCommon& physicsC Profiler* /*profiler*/) #endif : mMemoryManager(memoryManager), mConfig(worldSettings), mEntityManager(mMemoryManager.getHeapAllocator()), mDebugRenderer(mMemoryManager.getHeapAllocator()), - mCollisionBodyComponents(mMemoryManager.getHeapAllocator()), mRigidBodyComponents(mMemoryManager.getHeapAllocator()), + mIsDebugRenderingEnabled(false), mIsGravityEnabled(true), mBodyComponents(mMemoryManager.getHeapAllocator()), mRigidBodyComponents(mMemoryManager.getHeapAllocator()), mTransformComponents(mMemoryManager.getHeapAllocator()), mCollidersComponents(mMemoryManager.getHeapAllocator()), mJointsComponents(mMemoryManager.getHeapAllocator()), mBallAndSocketJointsComponents(mMemoryManager.getHeapAllocator()), mFixedJointsComponents(mMemoryManager.getHeapAllocator()), mHingeJointsComponents(mMemoryManager.getHeapAllocator()), - mSliderJointsComponents(mMemoryManager.getHeapAllocator()), mCollisionDetection(this, mCollidersComponents, mTransformComponents, mCollisionBodyComponents, mRigidBodyComponents, + mSliderJointsComponents(mMemoryManager.getHeapAllocator()), mCollisionDetection(this, mCollidersComponents, mTransformComponents, mBodyComponents, mRigidBodyComponents, mMemoryManager, physicsCommon.mTriangleShapeHalfEdgeStructure), mCollisionBodies(mMemoryManager.getHeapAllocator()), mEventListener(nullptr), mName(worldSettings.worldName), mIslands(mMemoryManager.getSingleFrameAllocator()), mProcessContactPairsOrderIslands(mMemoryManager.getSingleFrameAllocator()), - mContactSolverSystem(mMemoryManager, *this, mIslands, mCollisionBodyComponents, mRigidBodyComponents, + mContactSolverSystem(mMemoryManager, *this, mIslands, mBodyComponents, mRigidBodyComponents, mCollidersComponents, mConfig.restitutionVelocityThreshold), mConstraintSolverSystem(*this, mIslands, mRigidBodyComponents, mTransformComponents, mJointsComponents, mBallAndSocketJointsComponents, mFixedJointsComponents, mHingeJointsComponents, mSliderJointsComponents), - mDynamicsSystem(*this, mCollisionBodyComponents, mRigidBodyComponents, mTransformComponents, mCollidersComponents, mIsGravityEnabled, mConfig.gravity), + mDynamicsSystem(*this, mBodyComponents, mRigidBodyComponents, mTransformComponents, mCollidersComponents, mIsGravityEnabled, mConfig.gravity), mNbVelocitySolverIterations(mConfig.defaultVelocitySolverNbIterations), mNbPositionSolverIterations(mConfig.defaultPositionSolverNbIterations), mIsSleepingEnabled(mConfig.isSleepingEnabled), mRigidBodies(mMemoryManager.getPoolAllocator()), - mIsGravityEnabled(true), mSleepLinearVelocity(mConfig.defaultSleepLinearVelocity), + mSleepLinearVelocity(mConfig.defaultSleepLinearVelocity), mSleepAngularVelocity(mConfig.defaultSleepAngularVelocity), mTimeBeforeSleep(mConfig.defaultTimeBeforeSleep) { // Automatically generate a name for the world @@ -106,6 +107,16 @@ PhysicsWorld::PhysicsWorld(MemoryManager& memoryManager, PhysicsCommon& physicsC mNbWorlds++; + mTransformComponents.init(); + mCollidersComponents.init(); + mBodyComponents.init(); + mRigidBodyComponents.init(); + mJointsComponents.init(); + mBallAndSocketJointsComponents.init(); + mFixedJointsComponents.init(); + mSliderJointsComponents.init(); + mHingeJointsComponents.init(); + RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Physics world " + mName + " has been created", __FILE__, __LINE__); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, @@ -122,13 +133,6 @@ PhysicsWorld::~PhysicsWorld() { RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Physics world " + mName + " has been destroyed", __FILE__, __LINE__); - // Destroy all the collision bodies that have not been removed - uint32 i = static_cast(mCollisionBodies.size()); - while (i != 0) { - i--; - destroyCollisionBody(mCollisionBodies[i]); - } - #ifdef IS_RP3D_PROFILING_ENABLED @@ -143,7 +147,7 @@ PhysicsWorld::~PhysicsWorld() { } // Destroy all the rigid bodies that have not been removed - i = static_cast(mRigidBodies.size()); + uint32 i = static_cast(mRigidBodies.size()); while (i != 0) { i--; destroyRigidBody(mRigidBodies[i]); @@ -152,7 +156,7 @@ PhysicsWorld::~PhysicsWorld() { assert(mJointsComponents.getNbComponents() == 0); assert(mRigidBodies.size() == 0); assert(mCollisionBodies.size() == 0); - assert(mCollisionBodyComponents.getNbComponents() == 0); + assert(mBodyComponents.getNbComponents() == 0); assert(mTransformComponents.getNbComponents() == 0); assert(mCollidersComponents.getNbComponents() == 0); @@ -160,86 +164,13 @@ PhysicsWorld::~PhysicsWorld() { "Physics World: Physics world " + mName + " has been destroyed", __FILE__, __LINE__); } -// Create a collision body and add it to the world -/** - * @param transform Transformation mapping the local-space of the body to world-space - * @return A pointer to the body that has been created in the world - */ -CollisionBody* PhysicsWorld::createCollisionBody(const Transform& transform) { - - // Create a new entity for the body - Entity entity = mEntityManager.createEntity(); - - // Check that the transform is valid - if (!transform.isValid()) { - RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::Body, - "Error when creating a collision body: the init transform is not valid", __FILE__, __LINE__); - } - assert(transform.isValid()); - - mTransformComponents.addComponent(entity, false, TransformComponents::TransformComponent(transform)); - - // Create the collision body - CollisionBody* collisionBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, - sizeof(CollisionBody))) - CollisionBody(*this, entity); - - assert(collisionBody != nullptr); - - // Add the components - CollisionBodyComponents::CollisionBodyComponent bodyComponent(collisionBody); - mCollisionBodyComponents.addComponent(entity, false, bodyComponent); - - // Add the collision body to the world - mCollisionBodies.add(collisionBody); - -#ifdef IS_RP3D_PROFILING_ENABLED - - - collisionBody->setProfiler(mProfiler); - -#endif - - RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, - "Body " + std::to_string(entity.id) + ": New collision body created", __FILE__, __LINE__); - - // Return the pointer to the rigid body - return collisionBody; -} - -// Destroy a collision body -/** - * @param collisionBody Pointer to the body to destroy - */ -void PhysicsWorld::destroyCollisionBody(CollisionBody* collisionBody) { - - RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, - "Body " + std::to_string(collisionBody->getEntity().id) + ": collision body destroyed", __FILE__, __LINE__); - - // Remove all the collision shapes of the body - collisionBody->removeAllColliders(); - - mCollisionBodyComponents.removeComponent(collisionBody->getEntity()); - mTransformComponents.removeComponent(collisionBody->getEntity()); - mEntityManager.destroyEntity(collisionBody->getEntity()); - - // Call the destructor of the collision body - collisionBody->~CollisionBody(); - - // Remove the collision body from the array of bodies - mCollisionBodies.remove(collisionBody); - - // Free the object from the memory allocator - mMemoryManager.release(MemoryManager::AllocationType::Pool, collisionBody, sizeof(CollisionBody)); -} - // Notify the world if a body is disabled (sleeping) or not void PhysicsWorld::setBodyDisabled(Entity bodyEntity, bool isDisabled) { - if (isDisabled == mCollisionBodyComponents.getIsEntityDisabled(bodyEntity)) return; + if (isDisabled == mBodyComponents.getIsEntityDisabled(bodyEntity)) return; // Notify all the components - mCollisionBodyComponents.setIsEntityDisabled(bodyEntity, isDisabled); + mBodyComponents.setIsEntityDisabled(bodyEntity, isDisabled); mTransformComponents.setIsEntityDisabled(bodyEntity, isDisabled); assert(mRigidBodyComponents.hasComponent(bodyEntity)); @@ -247,10 +178,9 @@ void PhysicsWorld::setBodyDisabled(Entity bodyEntity, bool isDisabled) { mRigidBodyComponents.setIsEntityDisabled(bodyEntity, isDisabled); // For each collider of the body - const Array& collidersEntities = mCollisionBodyComponents.getColliders(bodyEntity); + const Array& collidersEntities = mBodyComponents.getColliders(bodyEntity); const uint32 nbColliderEntities = static_cast(collidersEntities.size()); for (uint32 i=0; i < nbColliderEntities; i++) { - mCollidersComponents.setIsEntityDisabled(collidersEntities[i], isDisabled); } } @@ -284,7 +214,7 @@ void PhysicsWorld::setJointDisabled(Entity jointEntity, bool isDisabled) { * @param body2 Pointer to a second body * @return True if the two bodies overlap */ -bool PhysicsWorld::testOverlap(CollisionBody* body1, CollisionBody* body2) { +bool PhysicsWorld::testOverlap(Body* body1, Body* body2) { return mCollisionDetection.testOverlap(body1, body2); } @@ -450,7 +380,7 @@ void PhysicsWorld::enableDisableJoints() { Entity body2 = mJointsComponents.mBody2Entities[jointEntityIndex]; // If both bodies of the joint are disabled - if (mCollisionBodyComponents.getIsEntityDisabled(body1) || mCollisionBodyComponents.getIsEntityDisabled(body2)) { + if (mBodyComponents.getIsEntityDisabled(body1) && mBodyComponents.getIsEntityDisabled(body2)) { // Disable the joint setJointDisabled(jointsEntites[i], true); @@ -487,8 +417,8 @@ RigidBody* PhysicsWorld::createRigidBody(const Transform& transform) { sizeof(RigidBody))) RigidBody(*this, entity); assert(rigidBody != nullptr); - CollisionBodyComponents::CollisionBodyComponent bodyComponent(rigidBody); - mCollisionBodyComponents.addComponent(entity, false, bodyComponent); + BodyComponents::BodyComponent bodyComponent(rigidBody); + mBodyComponents.addComponent(entity, false, bodyComponent); RigidBodyComponents::RigidBodyComponent rigidBodyComponent(rigidBody, BodyType::DYNAMIC, transform.getPosition()); mRigidBodyComponents.addComponent(entity, false, rigidBodyComponent); @@ -530,7 +460,7 @@ void PhysicsWorld::destroyRigidBody(RigidBody* rigidBody) { } // Destroy the corresponding entity and its components - mCollisionBodyComponents.removeComponent(rigidBody->getEntity()); + mBodyComponents.removeComponent(rigidBody->getEntity()); mRigidBodyComponents.removeComponent(rigidBody->getEntity()); mTransformComponents.removeComponent(rigidBody->getEntity()); mEntityManager.destroyEntity(rigidBody->getEntity()); @@ -835,7 +765,7 @@ void PhysicsWorld::createIslands() { } // If the body is involved in contacts with other bodies - // For each contact pair in which the current body is involded + // For each contact pair in which the current body is involved const uint32 nbBodyContactPairs = static_cast(mRigidBodyComponents.mContactPairs[bodyToVisitIndex].size()); for (uint32 p=0; p < nbBodyContactPairs; p++) { @@ -845,12 +775,29 @@ void PhysicsWorld::createIslands() { // Check if the current contact pair has already been added into an island if (pair.isAlreadyInIsland) continue; + pair.isAlreadyInIsland = true; + + const bool isCollider1SimulationCollider = mCollidersComponents.getIsSimulationCollider(pair.collider1Entity); + const bool isCollider2SimulationCollider = mCollidersComponents.getIsSimulationCollider(pair.collider2Entity); + + // Check that both colliders are simulation collider + if (!isCollider1SimulationCollider || !isCollider2SimulationCollider) { + continue; + } + const Entity otherBodyEntity = pair.body1Entity == bodyToVisitEntity ? pair.body2Entity : pair.body1Entity; - // If the colliding body is a RigidBody (and not a CollisionBody) and is not a trigger + assert(mCollidersComponents.getIsSimulationCollider(pair.collider1Entity)); + assert(mCollidersComponents.getIsSimulationCollider(pair.collider2Entity)); + assert(!mCollidersComponents.getIsTrigger(pair.collider1Entity)); + assert(!mCollidersComponents.getIsTrigger(pair.collider2Entity)); + uint32 otherBodyIndex; - if (mRigidBodyComponents.hasComponentGetIndex(otherBodyEntity, otherBodyIndex) - && !mCollidersComponents.getIsTrigger(pair.collider1Entity) && !mCollidersComponents.getIsTrigger(pair.collider2Entity)) { + const bool isFound = mRigidBodyComponents.hasComponentGetIndex(otherBodyEntity, otherBodyIndex); + assert(isFound); + + // If the body is a simulation collider + if (mBodyComponents.getHasSimulationCollider(otherBodyEntity)) { mProcessContactPairsOrderIslands.add(contactPairIndex); @@ -859,7 +806,6 @@ void PhysicsWorld::createIslands() { // Add the contact manifold into the island mIslands.nbContactManifolds[islandIndex] += pair.nbPotentialContactManifolds; - pair.isAlreadyInIsland = true; // Check if the other body has already been added to the island if (mRigidBodyComponents.mIsAlreadyInIsland[otherBodyIndex]) continue; @@ -868,11 +814,6 @@ void PhysicsWorld::createIslands() { bodyEntitiesToVisit.push(otherBodyEntity); mRigidBodyComponents.mIsAlreadyInIsland[otherBodyIndex] = true; } - else { - - // Add the contact pair index in the array of contact pairs that won't be part of islands - pair.isAlreadyInIsland = true; - } } // For each joint in which the current body is involved @@ -915,10 +856,12 @@ void PhysicsWorld::createIslands() { staticBodiesAddedToIsland.clear(); } - // Clear the associated contacts pairs of rigid bodies - const uint32 nbRigidBodyEnabledComponents = mRigidBodyComponents.getNbEnabledComponents(); - for (uint32 b=0; b < nbRigidBodyEnabledComponents; b++) { - mRigidBodyComponents.mContactPairs[b].clear(); + // Clear the contacts pairs that have been associated to rigid bodies + for (uint32 i=0; i < mCollisionDetection.mCurrentContactPairs->size(); i++) { + const ContactPair& pair = (*mCollisionDetection.mCurrentContactPairs)[i]; + + mRigidBodyComponents.removeAllContacPairs(pair.body1Entity); + mRigidBodyComponents.removeAllContacPairs(pair.body2Entity); } } @@ -1085,42 +1028,6 @@ void PhysicsWorld::setIsGravityEnabled(bool isGravityEnabled) { "Physics World: isGravityEnabled= " + (isGravityEnabled ? std::string("true") : std::string("false")), __FILE__, __LINE__); } -// Return a constant pointer to a given CollisionBody of the world -/** - * @param index Index of a CollisionBody in the world - * @return Constant pointer to a given CollisionBody - */ -const CollisionBody* PhysicsWorld::getCollisionBody(uint32 index) const { - - if (index >= getNbCollisionBodies()) { - - RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::World, - "Error when getting collision body: index is out of bounds", __FILE__, __LINE__); - } - - assert(index < mCollisionBodies.size()); - - return mCollisionBodies[index]; -} - -// Return a pointer to a given CollisionBody of the world -/** - * @param index Index of a CollisionBody in the world - * @return Pointer to a given CollisionBody - */ -CollisionBody* PhysicsWorld::getCollisionBody(uint32 index) { - - if (index >= getNbCollisionBodies()) { - - RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::World, - "Error when getting collision body: index is out of bounds", __FILE__, __LINE__); - } - - assert(index < mCollisionBodies.size()); - - return mCollisionBodies[index]; -} - // Return a constant pointer to a given RigidBody of the world /** * @param index Index of a RigidBody in the world diff --git a/ext/reactphysics3d/src/mathematics/Matrix2x2.cpp b/ext/reactphysics3d/src/mathematics/Matrix2x2.cpp index d17ebf63f..9dd03e5c8 100644 --- a/ext/reactphysics3d/src/mathematics/Matrix2x2.cpp +++ b/ext/reactphysics3d/src/mathematics/Matrix2x2.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/mathematics/Matrix3x3.cpp b/ext/reactphysics3d/src/mathematics/Matrix3x3.cpp index e6923e58f..01ca9b0ff 100644 --- a/ext/reactphysics3d/src/mathematics/Matrix3x3.cpp +++ b/ext/reactphysics3d/src/mathematics/Matrix3x3.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/mathematics/Quaternion.cpp b/ext/reactphysics3d/src/mathematics/Quaternion.cpp index 4e7fd6500..62b8bea40 100644 --- a/ext/reactphysics3d/src/mathematics/Quaternion.cpp +++ b/ext/reactphysics3d/src/mathematics/Quaternion.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/mathematics/Transform.cpp b/ext/reactphysics3d/src/mathematics/Transform.cpp index 191aeadef..beecadf3e 100644 --- a/ext/reactphysics3d/src/mathematics/Transform.cpp +++ b/ext/reactphysics3d/src/mathematics/Transform.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/mathematics/Vector2.cpp b/ext/reactphysics3d/src/mathematics/Vector2.cpp index b8be9a833..540c26a6f 100644 --- a/ext/reactphysics3d/src/mathematics/Vector2.cpp +++ b/ext/reactphysics3d/src/mathematics/Vector2.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/mathematics/Vector3.cpp b/ext/reactphysics3d/src/mathematics/Vector3.cpp index 42138def4..6a8fc37f5 100644 --- a/ext/reactphysics3d/src/mathematics/Vector3.cpp +++ b/ext/reactphysics3d/src/mathematics/Vector3.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/memory/HeapAllocator.cpp b/ext/reactphysics3d/src/memory/HeapAllocator.cpp index 85f063375..8f317ebcc 100644 --- a/ext/reactphysics3d/src/memory/HeapAllocator.cpp +++ b/ext/reactphysics3d/src/memory/HeapAllocator.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -36,7 +36,7 @@ size_t HeapAllocator::INIT_ALLOCATED_SIZE = 5 * 1048576; // 5 Mb // Constructor HeapAllocator::HeapAllocator(MemoryAllocator& baseAllocator, size_t initAllocatedMemory) - : mBaseAllocator(baseAllocator), mAllocatedMemory(0), mMemoryUnits(nullptr), mCachedFreeUnit(nullptr) { + : mBaseAllocator(baseAllocator), mAllocatedMemory(0), mMemoryUnits(nullptr), mFreeUnits(nullptr) { #ifndef NDEBUG mNbTimesAllocateMethodCalled = 0; @@ -55,18 +55,19 @@ HeapAllocator::~HeapAllocator() { #endif // Release the memory allocated for memory unit - MemoryUnitHeader* unit = mMemoryUnits; + MemoryUnitHeader* unit = mFreeUnits; while (unit != nullptr) { assert(!unit->isAllocated); - MemoryUnitHeader* nextUnit = unit->nextUnit; + MemoryUnitHeader* nextUnit = unit->nextFreeUnit; const size_t unitSize = unit->size; // Destroy the unit unit->~MemoryUnitHeader(); - mBaseAllocator.release(static_cast(unit), unitSize + sizeof(MemoryUnitHeader)); + const size_t sizeHeader = std::ceil(sizeof(MemoryUnitHeader) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + mBaseAllocator.release(static_cast(unit), unitSize + sizeHeader); unit = nextUnit; } @@ -76,23 +77,24 @@ HeapAllocator::~HeapAllocator() { /// left over space. The second unit is put into the free memory units void HeapAllocator::splitMemoryUnit(MemoryUnitHeader* unit, size_t size) { - assert(size <= unit->size); assert(!unit->isAllocated); - // Split the free memory unit in two memory units, one with the requested memory size - // and a second one with the left over space + // If the size of the unit is large enough to be slit if (size + sizeof(MemoryUnitHeader) < unit->size) { - assert(unit->size - size > 0); - // Create a new memory unit with left over space unsigned char* newUnitLocation = (reinterpret_cast(unit)) + sizeof(MemoryUnitHeader) + size; - MemoryUnitHeader* newUnit = new (static_cast(newUnitLocation)) MemoryUnitHeader(unit->size - sizeof(MemoryUnitHeader) - size, unit, unit->nextUnit, unit->isNextContiguousMemory); + MemoryUnitHeader* newUnit = new (static_cast(newUnitLocation)) MemoryUnitHeader(unit->size - sizeof(MemoryUnitHeader) - size, unit, unit->nextUnit, unit, unit->nextFreeUnit, unit->isNextContiguousMemory); assert(newUnit->nextUnit != newUnit); unit->nextUnit = newUnit; + unit->nextFreeUnit = newUnit; if (newUnit->nextUnit != nullptr) { newUnit->nextUnit->previousUnit = newUnit; } + if (newUnit->nextFreeUnit != nullptr) { + newUnit->nextFreeUnit->previousFreeUnit = newUnit; + } + assert(unit->nextUnit != unit); unit->isNextContiguousMemory = true; unit->size = size; @@ -100,9 +102,19 @@ void HeapAllocator::splitMemoryUnit(MemoryUnitHeader* unit, size_t size) { assert(unit->previousUnit == nullptr || unit->previousUnit->nextUnit == unit); assert(unit->nextUnit == nullptr || unit->nextUnit->previousUnit == unit); + assert(unit->previousFreeUnit == nullptr || unit->previousFreeUnit->nextFreeUnit == unit); + assert(unit->nextFreeUnit == nullptr || unit->nextFreeUnit->previousFreeUnit == unit); + assert(newUnit->previousUnit == nullptr || newUnit->previousUnit->nextUnit == newUnit); assert(newUnit->nextUnit == nullptr || newUnit->nextUnit->previousUnit == newUnit); - } + + assert(newUnit->previousFreeUnit->nextFreeUnit == newUnit); + assert(newUnit->nextFreeUnit == nullptr || newUnit->nextFreeUnit->previousFreeUnit == newUnit); + + assert(unit->nextFreeUnit == newUnit); + assert(newUnit->previousFreeUnit == unit); + assert(!newUnit->isAllocated); + } } // Allocate memory of a given size (in bytes) and return a pointer to the @@ -117,65 +129,86 @@ void* HeapAllocator::allocate(size_t size) { // We cannot allocate zero bytes if (size == 0) return nullptr; + // Allocate a little bit more memory to make sure we can return an aligned address + const size_t totalSize = size + GLOBAL_ALIGNMENT; + #ifndef NDEBUG mNbTimesAllocateMethodCalled++; #endif - MemoryUnitHeader* currentUnit = mMemoryUnits; - assert(mMemoryUnits->previousUnit == nullptr); - - // If there is a cached free memory unit - if (mCachedFreeUnit != nullptr) { - assert(!mCachedFreeUnit->isAllocated); - - // If the cached free memory unit matches the request - if (size <= mCachedFreeUnit->size) { - currentUnit = mCachedFreeUnit; - mCachedFreeUnit = nullptr; - } - } + MemoryUnitHeader* currentUnit = mFreeUnits; - // For each memory unit + // For each free memory unit while (currentUnit != nullptr) { - // If we have found a free memory unit with size large enough for the allocation request - if (!currentUnit->isAllocated && size <= currentUnit->size) { + assert(!currentUnit->isAllocated); - // Split the free memory unit in two memory units, one with the requested memory size - // and a second one with the left over space - splitMemoryUnit(currentUnit, size); + // If we have found a free memory unit with size large enough for the allocation request + if (totalSize <= currentUnit->size) { break; } - currentUnit = currentUnit->nextUnit; + assert(currentUnit->nextFreeUnit == nullptr || currentUnit->nextFreeUnit->previousFreeUnit == currentUnit); + + currentUnit = currentUnit->nextFreeUnit; } - // If we have not found a large enough memory unit we need to allocate more memory + // If we have not found a large enough free memory unit if (currentUnit == nullptr) { - reserve((mAllocatedMemory + size) * 2); + // We need to allocate more memory + reserve((mAllocatedMemory + totalSize) * 2); - assert(mCachedFreeUnit != nullptr); - assert(!mCachedFreeUnit->isAllocated); + assert(mFreeUnits != nullptr); // The cached free memory unit is large enough at this point - currentUnit = mCachedFreeUnit; + currentUnit = mFreeUnits; + } - assert(currentUnit->size >= size); + // Split the free memory unit in two memory units, one with the requested memory size + // and a second one with the left over space + splitMemoryUnit(currentUnit, totalSize); - splitMemoryUnit(currentUnit, size); - } + assert(currentUnit->size >= totalSize); + assert(!currentUnit->isAllocated); currentUnit->isAllocated = true; - // Cache the next memory unit if it is not allocated - if (currentUnit->nextUnit != nullptr && !currentUnit->nextUnit->isAllocated) { - mCachedFreeUnit = currentUnit->nextUnit; - } + removeFromFreeUnits(currentUnit); // Return a pointer to the memory area inside the unit - return static_cast(reinterpret_cast(currentUnit) + sizeof(MemoryUnitHeader)); + void* allocatedMemory = static_cast(reinterpret_cast(currentUnit) + sizeof(MemoryUnitHeader)); + + // Offset the allocated address such that it is properly aligned + allocatedMemory = computeAlignedAddress(allocatedMemory); + + // Check that allocated memory is 16-bytes aligned + assert(reinterpret_cast(allocatedMemory) % GLOBAL_ALIGNMENT == 0); + + return allocatedMemory; +} + +// Return the next aligned memory address +void* HeapAllocator::computeAlignedAddress(void* unalignedAddress) { + + // Take care of alignment to make sure that we always return an address to the + // enforce the global alignment of the library + + // Compute the aligned address + ptrdiff_t alignmentOffset; + void* alignedPointer = alignAddress(unalignedAddress, GLOBAL_ALIGNMENT, alignmentOffset); + + uintptr_t alignedAddress = reinterpret_cast(alignedPointer); + + // Store the adjustment in the byte immediately preceding the adjusted address. + // This way we can find again the original allocated memory address returned by malloc + // when this memory unit is released. + assert(alignmentOffset <= GLOBAL_ALIGNMENT); + uint8* pAlignedMemory = reinterpret_cast(alignedAddress); + pAlignedMemory[-1] = static_cast(alignmentOffset); + + return alignedPointer; } // Release previously allocated memory. @@ -193,9 +226,19 @@ void HeapAllocator::release(void* pointer, size_t size) { mNbTimesAllocateMethodCalled--; #endif - unsigned char* unitLocation = static_cast(pointer) - sizeof(MemoryUnitHeader); + // Read the alignment offset in order to compute the initial allocated + // raw address (instead of the aligned address) + const uint8* pAlignedMemory = reinterpret_cast(pointer); + const uintptr_t alignedAddress = reinterpret_cast(pAlignedMemory); + const ptrdiff_t alignmentOffset = static_cast(pAlignedMemory[-1]); + const uintptr_t initialAddress = alignedAddress - alignmentOffset; + void* pInitialAddress = reinterpret_cast(initialAddress); + + unsigned char* unitLocation = static_cast(pInitialAddress) - sizeof(MemoryUnitHeader); MemoryUnitHeader* unit = reinterpret_cast(unitLocation); assert(unit->isAllocated); + assert(unit->nextFreeUnit == nullptr); + assert(unit->previousFreeUnit == nullptr); unit->isAllocated = false; MemoryUnitHeader* currentUnit = unit; @@ -203,6 +246,8 @@ void HeapAllocator::release(void* pointer, size_t size) { // If the previous unit is not allocated and memory is contiguous to the current unit if (unit->previousUnit != nullptr && !unit->previousUnit->isAllocated && unit->previousUnit->isNextContiguousMemory) { + removeFromFreeUnits(unit->previousUnit); + currentUnit = unit->previousUnit; // Merge the two contiguous memory units @@ -212,11 +257,40 @@ void HeapAllocator::release(void* pointer, size_t size) { // If the next unit is not allocated and memory is contiguous to the current unit if (currentUnit->nextUnit != nullptr && !currentUnit->nextUnit->isAllocated && currentUnit->isNextContiguousMemory) { + removeFromFreeUnits(unit->nextUnit); + // Merge the two contiguous memory units mergeUnits(currentUnit, currentUnit->nextUnit); } - mCachedFreeUnit = currentUnit; + addToFreeUnits(currentUnit); +} + +// Add the unit from the linked-list of free units +void HeapAllocator::addToFreeUnits(MemoryUnitHeader* unit) { + + if (mFreeUnits != nullptr) { + assert(mFreeUnits->previousFreeUnit == nullptr); + mFreeUnits->previousFreeUnit = unit; + } + unit->nextFreeUnit = mFreeUnits; + mFreeUnits = unit; +} + +// Remove the unit from the linked-list of free units +void HeapAllocator::removeFromFreeUnits(MemoryUnitHeader* unit) { + + if (unit->previousFreeUnit != nullptr) { + unit->previousFreeUnit->nextFreeUnit = unit->nextFreeUnit; + } + if (unit->nextFreeUnit != nullptr) { + unit->nextFreeUnit->previousFreeUnit = unit->previousFreeUnit; + } + if (unit == mFreeUnits) { + mFreeUnits = unit->nextFreeUnit; + } + unit->nextFreeUnit = nullptr; + unit->previousFreeUnit = nullptr; } // Merge two contiguous memory units that are not allocated. @@ -247,12 +321,23 @@ void HeapAllocator::mergeUnits(MemoryUnitHeader* unit1, MemoryUnitHeader* unit2) // Reserve more memory for the allocator void HeapAllocator::reserve(size_t sizeToAllocate) { + const size_t sizeHeader = std::ceil(sizeof(MemoryUnitHeader) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Allocate memory - void* memory = mBaseAllocator.allocate(sizeToAllocate + sizeof(MemoryUnitHeader)); + void* memory = mBaseAllocator.allocate(sizeToAllocate + sizeHeader); assert(memory != nullptr); + // Check that allocated memory is 16-bytes aligned + assert(reinterpret_cast(memory) % GLOBAL_ALIGNMENT == 0); + // Create a new memory unit for the allocated memory - MemoryUnitHeader* memoryUnit = new (memory) MemoryUnitHeader(sizeToAllocate, nullptr, mMemoryUnits, false); + MemoryUnitHeader* memoryUnit = new (memory) MemoryUnitHeader(sizeToAllocate, nullptr, mMemoryUnits, nullptr, mFreeUnits, false); + + if (mFreeUnits != nullptr) { + + assert(mFreeUnits->previousFreeUnit == nullptr); + mFreeUnits->previousFreeUnit = memoryUnit; + } if (mMemoryUnits != nullptr) { mMemoryUnits->previousUnit = memoryUnit; @@ -260,8 +345,7 @@ void HeapAllocator::reserve(size_t sizeToAllocate) { // Add the memory unit at the beginning of the linked-list of memory units mMemoryUnits = memoryUnit; - - mCachedFreeUnit = mMemoryUnits; + mFreeUnits = mMemoryUnits; mAllocatedMemory += sizeToAllocate; } diff --git a/ext/reactphysics3d/src/memory/MemoryAllocator.cpp b/ext/reactphysics3d/src/memory/MemoryAllocator.cpp new file mode 100644 index 000000000..cf1e0b391 --- /dev/null +++ b/ext/reactphysics3d/src/memory/MemoryAllocator.cpp @@ -0,0 +1,71 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include +#include + +using namespace reactphysics3d; + +/// Given a pointer to memory, this method returns the next aligned address +/** +* @param pointer Pointer to a memory location +* @param alignment Desired alignment +* @return Pointer to the next aligned memory location +*/ +void* MemoryAllocator::alignAddress(void* pointer, uint8 alignment) { + + ptrdiff_t alignmentOffset; + return alignAddress(pointer, alignment, alignmentOffset); +} + +/// Given a pointer to memory, this method returns the next aligned address and also output the alignment offset +/** +* @param pointer Pointer to a memory location +* @param alignment Desired alignment +* @param outAlignmentOffset Output alignment offset needed to align the initial pointer +* @return Pointer to the next aligned memory location +*/ +void* MemoryAllocator::alignAddress(void* pointer, uint8 alignment, ptrdiff_t& outAlignmentOffset) { + + // Take care of alignment to make sure that we always return an address to the + // enforce the global alignment of the library + const uintptr_t currentAdress = reinterpret_cast(pointer); + + // Calculate the adjustment by masking off the lower bits of the address, to determine how "misaligned" it is. + const size_t mask = alignment - 1; + const uintptr_t misalignment = currentAdress & mask; + outAlignmentOffset = alignment - misalignment; + assert(outAlignmentOffset <= alignment); + + // Compute the aligned address + const uintptr_t alignedAdress = currentAdress + outAlignmentOffset; + void* alignedPointer = reinterpret_cast(alignedAdress); + + // Check that allocated memory is correctly aligned + assert(reinterpret_cast(alignedPointer) % alignment == 0); + + return alignedPointer; +} diff --git a/ext/reactphysics3d/src/memory/MemoryManager.cpp b/ext/reactphysics3d/src/memory/MemoryManager.cpp index 55edd19e0..8b67b8611 100644 --- a/ext/reactphysics3d/src/memory/MemoryManager.cpp +++ b/ext/reactphysics3d/src/memory/MemoryManager.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/memory/PoolAllocator.cpp b/ext/reactphysics3d/src/memory/PoolAllocator.cpp index c8fcc6806..df04b8eb8 100644 --- a/ext/reactphysics3d/src/memory/PoolAllocator.cpp +++ b/ext/reactphysics3d/src/memory/PoolAllocator.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -57,7 +57,7 @@ PoolAllocator::PoolAllocator(MemoryAllocator& baseAllocator) : mBaseAllocator(ba // Initialize the array that contains the sizes of the memory units that will // be allocated in each different heap for (uint i=0; i < NB_HEAPS; i++) { - mUnitSizes[i] = (i+1) * 8; + mUnitSizes[i] = (i+1) * MIN_UNIT_SIZE; } // Initialize the lookup table that maps the size to allocated to the @@ -116,7 +116,12 @@ void* PoolAllocator::allocate(size_t size) { if (size > MAX_UNIT_SIZE) { // Allocate memory using default allocation - return mBaseAllocator.allocate(size); + void* allocatedMemory = mBaseAllocator.allocate(size); + + // Check that allocated memory is 16-bytes aligned + assert(reinterpret_cast(allocatedMemory) % GLOBAL_ALIGNMENT == 0); + + return allocatedMemory; } // Get the index of the heap that will take care of the allocation request @@ -129,7 +134,13 @@ void* PoolAllocator::allocate(size_t size) { // Return a pointer to the memory unit MemoryUnit* unit = mFreeMemoryUnits[indexHeap]; mFreeMemoryUnits[indexHeap] = unit->nextUnit; - return unit; + + void* allocatedMemory = static_cast(unit); + + // Check that allocated memory is 16-bytes aligned + assert(reinterpret_cast(allocatedMemory) % GLOBAL_ALIGNMENT == 0); + + return allocatedMemory; } else { // If there is no more free memory units in the corresponding heap @@ -170,8 +181,13 @@ void* PoolAllocator::allocate(size_t size) { mFreeMemoryUnits[indexHeap] = newBlock->memoryUnits->nextUnit; mNbCurrentMemoryBlocks++; + void* allocatedMemory = newBlock->memoryUnits; + + // Check that allocated memory is 16-bytes aligned + assert(reinterpret_cast(allocatedMemory) % GLOBAL_ALIGNMENT == 0); + // Return the pointer to the first memory unit of the new allocated block - return newBlock->memoryUnits; + return allocatedMemory; } } diff --git a/ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp b/ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp index 8cc1970ae..00ff8e64f 100644 --- a/ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp +++ b/ext/reactphysics3d/src/memory/SingleFrameAllocator.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -34,11 +34,17 @@ using namespace reactphysics3d; // Constructor SingleFrameAllocator::SingleFrameAllocator(MemoryAllocator& baseAllocator) : mBaseAllocator(baseAllocator), mTotalSizeBytes(INIT_SINGLE_FRAME_ALLOCATOR_NB_BYTES), - mCurrentOffset(0), mNbFramesTooMuchAllocated(0), mNeedToAllocatedMore(false) { + mCurrentOffset(0), mNeedToAllocatedMore(false) { // Allocate a whole block of memory at the beginning - mMemoryBufferStart = static_cast(mBaseAllocator.allocate(mTotalSizeBytes)); - assert(mMemoryBufferStart != nullptr); + void* allocatedMemory = mBaseAllocator.allocate(mTotalSizeBytes); + + assert(allocatedMemory != nullptr); + + // Check that allocated memory is 16-bytes aligned + assert(reinterpret_cast(allocatedMemory) % GLOBAL_ALIGNMENT == 0); + + mMemoryBufferStart = static_cast(allocatedMemory); } // Destructor @@ -48,29 +54,37 @@ SingleFrameAllocator::~SingleFrameAllocator() { mBaseAllocator.release(mMemoryBufferStart, mTotalSizeBytes); } - // Allocate memory of a given size (in bytes) and return a pointer to the -// allocated memory. +// allocated memory. Allocated memory must be 16-bytes aligned. void* SingleFrameAllocator::allocate(size_t size) { // Lock the method with a mutex std::lock_guard lock(mMutex); + // Allocate a little bit more memory to make sure we can return an aligned address + const size_t totalSize = size + GLOBAL_ALIGNMENT; + // Check that there is enough remaining memory in the buffer - if (mCurrentOffset + size > mTotalSizeBytes) { + if (mCurrentOffset + totalSize > mTotalSizeBytes) { // We need to allocate more memory next time reset() is called - mNeedToAllocatedMore = true; + mNeedToAllocatedMore = true; - // Return default memory allocation - return mBaseAllocator.allocate(size); + // Return default memory allocation + return mBaseAllocator.allocate(size); } // Next available memory location void* nextAvailableMemory = mMemoryBufferStart + mCurrentOffset; + // Compute the next aligned memory address + nextAvailableMemory = alignAddress(nextAvailableMemory, GLOBAL_ALIGNMENT); + // Increment the offset - mCurrentOffset += size; + mCurrentOffset += totalSize; + + // Check that allocated memory is 16-bytes aligned + assert(reinterpret_cast(nextAvailableMemory) % GLOBAL_ALIGNMENT == 0); // Return the next available memory location return nextAvailableMemory; @@ -97,31 +111,6 @@ void SingleFrameAllocator::reset() { // Lock the method with a mutex std::lock_guard lock(mMutex); - // If too much memory is allocated - if (mCurrentOffset < mTotalSizeBytes / 2) { - - mNbFramesTooMuchAllocated++; - - if (mNbFramesTooMuchAllocated > NB_FRAMES_UNTIL_SHRINK) { - - // Release the memory allocated at the beginning - mBaseAllocator.release(mMemoryBufferStart, mTotalSizeBytes); - - // Divide the total memory to allocate by two - mTotalSizeBytes /= 2; - if (mTotalSizeBytes == 0) mTotalSizeBytes = 1; - - // Allocate a whole block of memory at the beginning - mMemoryBufferStart = static_cast(mBaseAllocator.allocate(mTotalSizeBytes)); - assert(mMemoryBufferStart != nullptr); - - mNbFramesTooMuchAllocated = 0; - } - } - else { - mNbFramesTooMuchAllocated = 0; - } - // If we need to allocate more memory if (mNeedToAllocatedMore) { @@ -136,7 +125,6 @@ void SingleFrameAllocator::reset() { assert(mMemoryBufferStart != nullptr); mNeedToAllocatedMore = false; - mNbFramesTooMuchAllocated = 0; } // Reset the current offset at the beginning of the block diff --git a/ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp b/ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp index 7fa3c06f4..560d1ff1d 100644 --- a/ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp +++ b/ext/reactphysics3d/src/systems/BroadPhaseSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -73,10 +73,6 @@ void BroadPhaseSystem::raycast(const Ray& ray, RaycastTest& raycastTest, unsigne BroadPhaseRaycastCallback broadPhaseRaycastCallback(mDynamicAABBTree, raycastWithCategoryMaskBits, raycastTest); - // Compute the inverse ray direction - const Vector3 rayDirection = ray.point2 - ray.point1; - const Vector3 rayDirectionInverse(decimal(1.0) / rayDirection.x, decimal(1.0) / rayDirection.y, decimal(1.0) / rayDirection.z); - mDynamicAABBTree.raycast(ray, broadPhaseRaycastCallback); } @@ -119,7 +115,7 @@ void BroadPhaseSystem::updateCollider(Entity colliderEntity) { assert(mCollidersComponents.mMapEntityToComponentIndex.containsKey(colliderEntity)); // Get the index of the collider component in the array - uint32 index = mCollidersComponents.mMapEntityToComponentIndex[colliderEntity]; + const uint32 index = mCollidersComponents.mMapEntityToComponentIndex[colliderEntity]; // Update the collider component updateCollidersComponents(index, 1); @@ -164,11 +160,6 @@ void BroadPhaseSystem::updateCollidersComponents(uint32 startIndex, uint32 nbIte assert(startIndex < mCollidersComponents.getNbComponents()); assert(startIndex + nbItems <= mCollidersComponents.getNbComponents()); - // Make sure we do not update disabled components - startIndex = std::min(startIndex, mCollidersComponents.getNbEnabledComponents()); - uint32 endIndex = std::min(startIndex + nbItems, mCollidersComponents.getNbEnabledComponents()); - nbItems = endIndex - startIndex; - // For each collider component to update for (uint32 i = startIndex; i < startIndex + nbItems; i++) { @@ -179,8 +170,7 @@ void BroadPhaseSystem::updateCollidersComponents(uint32 startIndex, uint32 nbIte const Transform& transform = mTransformsComponents.getTransform(bodyEntity); // Recompute the world-space AABB of the collision shape - AABB aabb; - mCollidersComponents.mCollisionShapes[i]->computeAABB(aabb, transform * mCollidersComponents.mLocalToBodyTransforms[i]); + const AABB aabb = mCollidersComponents.mCollisionShapes[i]->computeTransformedAABB(transform * mCollidersComponents.mLocalToBodyTransforms[i]); // If the size of the collision shape has been changed by the user, // we need to reset the broad-phase AABB to its new size @@ -238,8 +228,8 @@ decimal BroadPhaseRaycastCallback::raycastBroadPhaseShape(int32 nodeId, const Ra // Get the collider from the node Collider* collider = static_cast(mDynamicAABBTree.getNodeDataPointer(nodeId)); - // Check if the raycast filtering mask allows raycast against this shape - if ((mRaycastWithCategoryMaskBits & collider->getCollisionCategoryBits()) != 0) { + // Check if the raycast filtering mask allows raycast against this shape and if world query is enabled for this collider + if ((mRaycastWithCategoryMaskBits & collider->getCollisionCategoryBits()) != 0 && collider->getIsWorldQueryCollider()) { // Ask the collision detection to perform a ray cast test against // the collider of this node because the ray is overlapping diff --git a/ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp b/ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp index fa7ef6b0d..d2a3b8330 100644 --- a/ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp +++ b/ext/reactphysics3d/src/systems/CollisionDetectionSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -48,14 +48,17 @@ using namespace reactphysics3d; using namespace std; +// TriangleShape allocated size +const size_t CollisionDetectionSystem::mTriangleShapeAllocatedSize = std::ceil(sizeof(TriangleShape) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + // Constructor CollisionDetectionSystem::CollisionDetectionSystem(PhysicsWorld* world, ColliderComponents& collidersComponents, TransformComponents& transformComponents, - CollisionBodyComponents& collisionBodyComponents, RigidBodyComponents& rigidBodyComponents, + BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, MemoryManager& memoryManager, HalfEdgeStructure& triangleHalfEdgeStructure) : mMemoryManager(memoryManager), mCollidersComponents(collidersComponents), mRigidBodyComponents(rigidBodyComponents), mCollisionDispatch(mMemoryManager.getPoolAllocator()), mWorld(world), mNoCollisionPairs(mMemoryManager.getPoolAllocator()), - mOverlappingPairs(mMemoryManager, mCollidersComponents, collisionBodyComponents, rigidBodyComponents, + mOverlappingPairs(mMemoryManager, mCollidersComponents, bodyComponents, rigidBodyComponents, mNoCollisionPairs, mCollisionDispatch), mBroadPhaseOverlappingNodes(mMemoryManager.getHeapAllocator(), 32), mBroadPhaseSystem(*this, mCollidersComponents, transformComponents, rigidBodyComponents), @@ -67,7 +70,7 @@ CollisionDetectionSystem::CollisionDetectionSystem(PhysicsWorld* world, Collider mContactManifolds1(mMemoryManager.getPoolAllocator()), mContactManifolds2(mMemoryManager.getPoolAllocator()), mPreviousContactManifolds(&mContactManifolds1), mCurrentContactManifolds(&mContactManifolds2), mContactPoints1(mMemoryManager.getPoolAllocator()), mContactPoints2(mMemoryManager.getPoolAllocator()), - mPreviousContactPoints(&mContactPoints1), mCurrentContactPoints(&mContactPoints2), mCollisionBodyContactPairsIndices(mMemoryManager.getSingleFrameAllocator()), + mPreviousContactPoints(&mContactPoints1), mCurrentContactPoints(&mContactPoints2), mNbPreviousPotentialContactManifolds(0), mNbPreviousPotentialContactPoints(0), mTriangleHalfEdgeStructure(triangleHalfEdgeStructure) { #ifdef IS_RP3D_PROFILING_ENABLED @@ -90,7 +93,7 @@ void CollisionDetectionSystem::computeCollisionDetection() { computeBroadPhase(); // Compute the middle-phase collision detection - computeMiddlePhase(mNarrowPhaseInput, true); + computeMiddlePhase(mNarrowPhaseInput, true, false); // Compute the narrow-phase collision detection computeNarrowPhase(); @@ -136,14 +139,7 @@ void CollisionDetectionSystem::removeNonOverlappingPairs() { } else { - // If the two colliders of the pair were colliding in the previous frame - if (overlappingPair.collidingInPreviousFrame) { - - // Create a new lost contact pair - addLostContactPair(overlappingPair); - } - - mOverlappingPairs.removePair(i, true); + removeConvexOverlappingPairWithIndex(i); i--; } } @@ -163,20 +159,63 @@ void CollisionDetectionSystem::removeNonOverlappingPairs() { } else { - // If the two colliders of the pair were colliding in the previous frame - if (overlappingPair.collidingInPreviousFrame) { - - // Create a new lost contact pair - addLostContactPair(overlappingPair); - } - - mOverlappingPairs.removePair(i, false); + removeConcaveOverlappingPairWithIndex(i); i--; } } } } +// Disable an overlapping pair (because both bodies of the pair are disabled) +void CollisionDetectionSystem::disableOverlappingPair(uint64 pairId) { + mOverlappingPairs.disablePair(pairId); +} + +// Remove an overlapping pair with an index +void CollisionDetectionSystem::removeOverlappingPair(uint64 pairId, bool notifyLostContact) { + + OverlappingPairs::OverlappingPair* pair = mOverlappingPairs.getOverlappingPair(pairId); + + // If the two colliders of the pair were colliding in the previous frame + if (pair->collidingInPreviousFrame && notifyLostContact) { + + // Create a new lost contact pair + addLostContactPair(*pair); + } + + mOverlappingPairs.removePair(pairId); +} + +// Remove a convex overlapping pair with an index +void CollisionDetectionSystem::removeConvexOverlappingPairWithIndex(uint64 pairIndex) { + + OverlappingPairs::OverlappingPair& pair = mOverlappingPairs.mConvexPairs[pairIndex]; + + // If the two colliders of the pair were colliding in the previous frame + if (pair.collidingInPreviousFrame) { + + // Create a new lost contact pair + addLostContactPair(pair); + } + + mOverlappingPairs.removeConvexPairWithIndex(pairIndex, true); +} + +// Remove a concave overlapping pair with an index +void CollisionDetectionSystem::removeConcaveOverlappingPairWithIndex(uint64 pairIndex) { + + OverlappingPairs::OverlappingPair& pair = mOverlappingPairs.mConcavePairs[pairIndex]; + + // If the two colliders of the pair were colliding in the previous frame + if (pair.collidingInPreviousFrame) { + + // Create a new lost contact pair + addLostContactPair(pair); + } + + mOverlappingPairs.removeConcavePairWithIndex(pairIndex, true); +} + // Add a lost contact pair (pair of colliders that are not in contact anymore) void CollisionDetectionSystem::addLostContactPair(OverlappingPairs::OverlappingPair& overlappingPair) { @@ -202,7 +241,7 @@ void CollisionDetectionSystem::addNoCollisionPair(Entity body1Entity, Entity bod // If there already are OverlappingPairs involved, they should be removed; Or they will remain in collision state Array toBeRemoved(mMemoryManager.getPoolAllocator()); - const Array& colliderEntities = mWorld->mCollisionBodyComponents.getColliders(body1Entity); + const Array& colliderEntities = mWorld->mBodyComponents.getColliders(body1Entity); for (uint32 i = 0; i < colliderEntities.size(); ++i) { // Get the currently overlapping pairs for colliders of body1 @@ -223,7 +262,7 @@ void CollisionDetectionSystem::addNoCollisionPair(Entity body1Entity, Entity bod // Remove the overlapping pairs that needs to be removed for (uint32 i = 0; i < toBeRemoved.size(); ++i) { - mOverlappingPairs.removePair(toBeRemoved[i]); + removeOverlappingPair(toBeRemoved[i], true); } } @@ -255,66 +294,47 @@ void CollisionDetectionSystem::updateOverlappingPairs(const ArraygetCollisionShape()->isConvex(); - const bool isShape2Convex = shape2->getCollisionShape()->isConvex(); - if (isShape1Convex || isShape2Convex) { + // Check that at least one collision shape is convex + const bool isShape1Convex = shape1->getCollisionShape()->isConvex(); + const bool isShape2Convex = shape2->getCollisionShape()->isConvex(); + if (isShape1Convex || isShape2Convex) { - // Add the new overlapping pair - mOverlappingPairs.addPair(collider1Index, collider2Index, isShape1Convex && isShape2Convex); - } + // Add the new overlapping pair + mOverlappingPairs.addPair(collider1Index, collider2Index, isShape1Convex && isShape2Convex); } } - else { + } + else { - // We do not need to test the pair for overlap because it has just been reported that they still overlap - overlappingPair->needToTestOverlap = false; - } + // We do not need to test the pair for overlap because it has just been reported that they still overlap + overlappingPair->needToTestOverlap = false; } } } @@ -323,7 +343,7 @@ void CollisionDetectionSystem::updateOverlappingPairs(const ArrayisConvex()); assert(!concaveShape->isConvex()); - assert(overlappingPair.narrowPhaseAlgorithmType != NarrowPhaseAlgorithmType::None); + assert(overlappingPair.narrowPhaseAlgorithmType != NarrowPhaseAlgorithmType::NoCollisionTest); // Compute the convex shape AABB in the local-space of the concave shape - AABB aabb; - convexShape->computeAABB(aabb, convexToConcaveTransform); + const AABB aabb = convexShape->computeTransformedAABB(convexToConcaveTransform); // Compute the concave shape triangles that are overlapping with the convex mesh AABB Array triangleVertices(allocator, 64); @@ -499,8 +570,8 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(OverlappingPair const bool isCollider2Trigger = mCollidersComponents.mIsTrigger[collider2Index]; reportContacts = reportContacts && !isCollider1Trigger && !isCollider2Trigger; - CollisionShape* shape1; - CollisionShape* shape2; + CollisionShape* shape1 = nullptr; + CollisionShape* shape2 = nullptr; if (overlappingPair.isShape1Convex) { shape1 = convexShape; @@ -515,7 +586,7 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(OverlappingPair // Create a triangle collision shape (the allocated memory for the TriangleShape will be released in the // destructor of the corresponding NarrowPhaseInfo. - TriangleShape* triangleShape = new (allocator.allocate(sizeof(TriangleShape))) + TriangleShape* triangleShape = new (allocator.allocate(mTriangleShapeAllocatedSize)) TriangleShape(&(triangleVertices[i * 3]), &(triangleVerticesNormals[i * 3]), shapeIds[i], mTriangleHalfEdgeStructure, allocator); #ifdef IS_RP3D_PROFILING_ENABLED @@ -654,23 +725,9 @@ void CollisionDetectionSystem::addContactPairsToBodies() { ContactPair& contactPair = (*mCurrentContactPairs)[p]; - const bool isBody1Rigid = mRigidBodyComponents.hasComponent(contactPair.body1Entity); - const bool isBody2Rigid = mRigidBodyComponents.hasComponent(contactPair.body2Entity); - // Add the associated contact pair to both bodies of the pair (used to create islands later) - if (isBody1Rigid) { - mRigidBodyComponents.addContacPair(contactPair.body1Entity, p); - } - if (isBody2Rigid) { - mRigidBodyComponents.addContacPair(contactPair.body2Entity, p); - } - - // If at least of body is a CollisionBody - if (!isBody1Rigid || !isBody2Rigid) { - - // Add the pair index to the array of pairs with CollisionBody - mCollisionBodyContactPairsIndices.add(p); - } + mRigidBodyComponents.addContacPair(contactPair.body1Entity, p); + mRigidBodyComponents.addContacPair(contactPair.body2Entity, p); } } @@ -853,13 +910,6 @@ void CollisionDetectionSystem::createContacts() { mCurrentContactManifolds->reserve(mCurrentContactPairs->size()); mCurrentContactPoints->reserve(mCurrentContactManifolds->size()); - // We go through all the contact pairs and add the pairs with a least a CollisionBody at the end of the - // mProcessContactPairsOrderIslands array because those pairs have not been added during the islands - // creation (only the pairs with two RigidBodies are added during island creation) - mWorld->mProcessContactPairsOrderIslands.addRange(mCollisionBodyContactPairsIndices); - - assert(mWorld->mProcessContactPairsOrderIslands.size() == (*mCurrentContactPairs).size()); - // Process the contact pairs in the order defined by the islands such that the contact manifolds and // contact points of a given island are packed together in the array of manifolds and contact points const uint32 nbContactPairsToProcess = static_cast(mWorld->mProcessContactPairsOrderIslands.size()); @@ -877,6 +927,7 @@ void CollisionDetectionSystem::createContacts() { for (uint32 m=0; m < contactPair.nbPotentialContactManifolds; m++) { ContactManifoldInfo& potentialManifold = mPotentialContactManifolds[contactPair.potentialContactManifoldsIndices[m]]; + assert(potentialManifold.nbPotentialContactPoints > 0); // Start index and number of contact points for this manifold const uint32 contactPointsIndex = static_cast(mCurrentContactPoints->size()); @@ -920,14 +971,14 @@ void CollisionDetectionSystem::createContacts() { // Compute the map from contact pairs ids to contact pair for the next frame computeMapPreviousContactPairs(); - mCollisionBodyContactPairsIndices.clear(true); - mNarrowPhaseInput.clear(); } // Compute the lost contact pairs (contact pairs in contact in the previous frame but not in the current one) void CollisionDetectionSystem::computeLostContactPairs() { + RP3D_PROFILE("CollisionDetectionSystem::computeLostContactPairs()", mProfiler); + // For each convex pair const uint32 nbConvexPairs = static_cast(mOverlappingPairs.mConvexPairs.size()); for (uint32 i=0; i < nbConvexPairs; i++) { @@ -935,12 +986,11 @@ void CollisionDetectionSystem::computeLostContactPairs() { // If the two colliders of the pair were colliding in the previous frame but not in the current one if (mOverlappingPairs.mConvexPairs[i].collidingInPreviousFrame && !mOverlappingPairs.mConvexPairs[i].collidingInCurrentFrame) { - // If both bodies still exist - if (mCollidersComponents.hasComponent(mOverlappingPairs.mConvexPairs[i].collider1) && mCollidersComponents.hasComponent(mOverlappingPairs.mConvexPairs[i].collider2)) { + assert(mCollidersComponents.hasComponent(mOverlappingPairs.mConvexPairs[i].collider1)); + assert(mCollidersComponents.hasComponent(mOverlappingPairs.mConvexPairs[i].collider2)); - // Create a lost contact pair - addLostContactPair(mOverlappingPairs.mConvexPairs[i]); - } + // Create a lost contact pair + addLostContactPair(mOverlappingPairs.mConvexPairs[i]); } } @@ -951,12 +1001,11 @@ void CollisionDetectionSystem::computeLostContactPairs() { // If the two colliders of the pair were colliding in the previous frame but not in the current one if (mOverlappingPairs.mConcavePairs[i].collidingInPreviousFrame && !mOverlappingPairs.mConcavePairs[i].collidingInCurrentFrame) { - // If both bodies still exist - if (mCollidersComponents.hasComponent(mOverlappingPairs.mConcavePairs[i].collider1) && mCollidersComponents.hasComponent(mOverlappingPairs.mConcavePairs[i].collider2)) { + assert(mCollidersComponents.hasComponent(mOverlappingPairs.mConcavePairs[i].collider1)); + assert(mCollidersComponents.hasComponent(mOverlappingPairs.mConcavePairs[i].collider2)); - // Create a lost contact pair - addLostContactPair(mOverlappingPairs.mConcavePairs[i]); - } + // Create a lost contact pair + addLostContactPair(mOverlappingPairs.mConcavePairs[i]); } } } @@ -988,6 +1037,7 @@ void CollisionDetectionSystem::createSnapshotContacts(Array& contac for (uint32 m=0; m < contactPair.nbPotentialContactManifolds; m++) { ContactManifoldInfo& potentialManifold = potentialContactManifolds[contactPair.potentialContactManifoldsIndices[m]]; + assert(potentialManifold.nbPotentialContactPoints > 0); // Start index and number of contact points for this manifold const uint32 contactPointsIndex = static_cast(contactPoints.size()); @@ -1123,8 +1173,7 @@ void CollisionDetectionSystem::removeCollider(Collider* collider) { // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds array of the two bodies involved - // Remove the overlapping pair - mOverlappingPairs.removePair(overlappingPairs[0]); + removeOverlappingPair(overlappingPairs[0], false); } mMapBroadPhaseIdToColliderEntity.remove(colliderBroadPhaseId); @@ -1180,6 +1229,7 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na overlappingPair->collidingInCurrentFrame = true; + const Entity collider1Entity = narrowPhaseInfoBatch.narrowPhaseInfos[i].colliderEntity1; const Entity collider2Entity = narrowPhaseInfoBatch.narrowPhaseInfos[i].colliderEntity2; @@ -1189,15 +1239,16 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na const Entity body1Entity = mCollidersComponents.mBodiesEntities[collider1Index]; const Entity body2Entity = mCollidersComponents.mBodiesEntities[collider2Index]; + const bool isTrigger = mCollidersComponents.mIsTrigger[collider1Index] || mCollidersComponents.mIsTrigger[collider2Index]; + assert((isTrigger && narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints == 0) || + (!isTrigger && narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints > 0)); + // If we have a convex vs convex collision (if we consider the base collision shapes of the colliders) - if (mCollidersComponents.mCollisionShapes[collider1Index]->isConvex() && mCollidersComponents.mCollisionShapes[collider2Index]->isConvex()) { + if (mCollidersComponents.mCollisionShapes[collider1Index]->isConvex() && + mCollidersComponents.mCollisionShapes[collider2Index]->isConvex()) { // Create a new ContactPair - const bool isTrigger = mCollidersComponents.mIsTrigger[collider1Index] || mCollidersComponents.mIsTrigger[collider2Index]; - - assert(!mWorld->mCollisionBodyComponents.getIsEntityDisabled(body1Entity) || !mWorld->mCollisionBodyComponents.getIsEntityDisabled(body2Entity)); - const uint32 newContactPairIndex = static_cast(contactPairs->size()); contactPairs->emplace(pairId, body1Entity, body2Entity, collider1Entity, collider2Entity, @@ -1205,33 +1256,38 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na ContactPair* pairContact = &((*contactPairs)[newContactPairIndex]); - // Create a new potential contact manifold for the overlapping pair - uint32 contactManifoldIndex = static_cast(potentialContactManifolds.size()); - potentialContactManifolds.emplace(pairId); - ContactManifoldInfo& contactManifoldInfo = potentialContactManifolds[contactManifoldIndex]; + // If there are contact points (not the case with collision with a trigger) + if (narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints > 0) { - const uint32 contactPointIndexStart = static_cast(potentialContactPoints.size()); + // Create a new potential contact manifold for the overlapping pair + uint32 contactManifoldIndex = static_cast(potentialContactManifolds.size()); + potentialContactManifolds.emplace(pairId); + ContactManifoldInfo& contactManifoldInfo = potentialContactManifolds[contactManifoldIndex]; - // Add the potential contacts - for (uint32 j=0; j < narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints; j++) { + const uint32 contactPointIndexStart = static_cast(potentialContactPoints.size()); - if (contactManifoldInfo.nbPotentialContactPoints < NB_MAX_CONTACT_POINTS_IN_POTENTIAL_MANIFOLD) { + // Add the potential contacts + for (uint32 j=0; j < narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints; j++) { - // Add the contact point to the manifold - contactManifoldInfo.potentialContactPointsIndices[contactManifoldInfo.nbPotentialContactPoints] = contactPointIndexStart + j; - contactManifoldInfo.nbPotentialContactPoints++; + if (contactManifoldInfo.nbPotentialContactPoints < NB_MAX_CONTACT_POINTS_IN_POTENTIAL_MANIFOLD) { - // Add the contact point to the array of potential contact points - const ContactPointInfo& contactPoint = narrowPhaseInfoBatch.narrowPhaseInfos[i].contactPoints[j]; + // Add the contact point to the manifold + contactManifoldInfo.potentialContactPointsIndices[contactManifoldInfo.nbPotentialContactPoints] = contactPointIndexStart + j; + contactManifoldInfo.nbPotentialContactPoints++; - potentialContactPoints.add(contactPoint); + // Add the contact point to the array of potential contact points + const ContactPointInfo& contactPoint = narrowPhaseInfoBatch.narrowPhaseInfos[i].contactPoints[j]; + + potentialContactPoints.add(contactPoint); + } } - } - // Add the contact manifold to the overlapping pair contact - assert(pairId == contactManifoldInfo.pairId); - pairContact->potentialContactManifoldsIndices[0] = contactManifoldIndex; - pairContact->nbPotentialContactManifolds = 1; + // Add the contact manifold to the overlapping pair contact + assert(potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints > 0); + assert(pairId == contactManifoldInfo.pairId); + pairContact->potentialContactManifoldsIndices[0] = contactManifoldIndex; + pairContact->nbPotentialContactManifolds = 1; + } } else { @@ -1242,16 +1298,11 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na // Create a new ContactPair - const bool isTrigger = mCollidersComponents.mIsTrigger[collider1Index] || mCollidersComponents.mIsTrigger[collider2Index]; - - assert(!mWorld->mCollisionBodyComponents.getIsEntityDisabled(body1Entity) || !mWorld->mCollisionBodyComponents.getIsEntityDisabled(body2Entity)); - const uint32 newContactPairIndex = static_cast(contactPairs->size()); contactPairs->emplace(pairId, body1Entity, body2Entity, collider1Entity, collider2Entity, newContactPairIndex, overlappingPair->collidingInPreviousFrame , isTrigger); pairContact = &((*contactPairs)[newContactPairIndex]); mapPairIdToContactPairIndex.add(Pair(pairId, newContactPairIndex)); - } else { // If a ContactPair already exists for this overlapping pair, we use this one @@ -1263,66 +1314,74 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na assert(pairContact != nullptr); - // Add the potential contacts - for (uint32 j=0; j < narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints; j++) { + // If there are contact points (not the case with collision with a trigger) + if (narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints > 0) { - const ContactPointInfo& contactPoint = narrowPhaseInfoBatch.narrowPhaseInfos[i].contactPoints[j]; + // Add the potential contacts + for (uint32 j=0; j < narrowPhaseInfoBatch.narrowPhaseInfos[i].nbContactPoints; j++) { - // Add the contact point to the array of potential contact points - const uint32 contactPointIndex = static_cast(potentialContactPoints.size()); + const ContactPointInfo& contactPoint = narrowPhaseInfoBatch.narrowPhaseInfos[i].contactPoints[j]; - potentialContactPoints.add(contactPoint); + // Add the contact point to the array of potential contact points + const uint32 contactPointIndex = static_cast(potentialContactPoints.size()); + + potentialContactPoints.add(contactPoint); - bool similarManifoldFound = false; + bool similarManifoldFound = false; - // For each contact manifold of the overlapping pair - for (uint32 m=0; m < pairContact->nbPotentialContactManifolds; m++) { + // For each contact manifold of the overlapping pair + for (uint32 m=0; m < pairContact->nbPotentialContactManifolds; m++) { - uint32 contactManifoldIndex = pairContact->potentialContactManifoldsIndices[m]; + assert(m < pairContact->nbPotentialContactManifolds); - if (potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints < NB_MAX_CONTACT_POINTS_IN_POTENTIAL_MANIFOLD) { + uint32 contactManifoldIndex = pairContact->potentialContactManifoldsIndices[m]; - // Get the first contact point of the current manifold assert(potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints > 0); - const uint manifoldContactPointIndex = potentialContactManifolds[contactManifoldIndex].potentialContactPointsIndices[0]; - const ContactPointInfo& manifoldContactPoint = potentialContactPoints[manifoldContactPointIndex]; - // If we have found a corresponding manifold for the new contact point - // (a manifold with a similar contact normal direction) - if (manifoldContactPoint.normal.dot(contactPoint.normal) >= mWorld->mConfig.cosAngleSimilarContactManifold) { + if (potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints < NB_MAX_CONTACT_POINTS_IN_POTENTIAL_MANIFOLD) { - // Add the contact point to the manifold - potentialContactManifolds[contactManifoldIndex].potentialContactPointsIndices[potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints] = contactPointIndex; - potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints++; + // Get the first contact point of the current manifold + const uint manifoldContactPointIndex = potentialContactManifolds[contactManifoldIndex].potentialContactPointsIndices[0]; + const ContactPointInfo& manifoldContactPoint = potentialContactPoints[manifoldContactPointIndex]; - similarManifoldFound = true; + // If we have found a corresponding manifold for the new contact point + // (a manifold with a similar contact normal direction) + if (manifoldContactPoint.normal.dot(contactPoint.normal) >= mWorld->mConfig.cosAngleSimilarContactManifold) { - break; - } - } - } + // Add the contact point to the manifold + potentialContactManifolds[contactManifoldIndex].potentialContactPointsIndices[potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints] = contactPointIndex; + potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints++; - // If we have not found a manifold with a similar contact normal for the contact point - if (!similarManifoldFound && pairContact->nbPotentialContactManifolds < NB_MAX_POTENTIAL_CONTACT_MANIFOLDS) { + similarManifoldFound = true; - // Create a new potential contact manifold for the overlapping pair - uint32 contactManifoldIndex = static_cast(potentialContactManifolds.size()); - potentialContactManifolds.emplace(pairId); - ContactManifoldInfo& contactManifoldInfo = potentialContactManifolds[contactManifoldIndex]; + break; + } + } + } - // Add the contact point to the manifold - contactManifoldInfo.potentialContactPointsIndices[0] = contactPointIndex; - contactManifoldInfo.nbPotentialContactPoints = 1; + // If we have not found a manifold with a similar contact normal for the contact point + if (!similarManifoldFound && pairContact->nbPotentialContactManifolds < NB_MAX_POTENTIAL_CONTACT_MANIFOLDS) { - assert(pairContact != nullptr); + // Create a new potential contact manifold for the overlapping pair + uint32 contactManifoldIndex = static_cast(potentialContactManifolds.size()); + potentialContactManifolds.emplace(pairId); + ContactManifoldInfo& contactManifoldInfo = potentialContactManifolds[contactManifoldIndex]; - // Add the contact manifold to the overlapping pair contact - assert(pairContact->pairId == contactManifoldInfo.pairId); - pairContact->potentialContactManifoldsIndices[pairContact->nbPotentialContactManifolds] = contactManifoldIndex; - pairContact->nbPotentialContactManifolds++; - } + // Add the contact point to the manifold + contactManifoldInfo.potentialContactPointsIndices[0] = contactPointIndex; + contactManifoldInfo.nbPotentialContactPoints = 1; + + assert(pairContact != nullptr); - assert(pairContact->nbPotentialContactManifolds > 0); + // Add the contact manifold to the overlapping pair contact + assert(potentialContactManifolds[contactManifoldIndex].nbPotentialContactPoints > 0); + assert(pairContact->pairId == contactManifoldInfo.pairId); + pairContact->potentialContactManifoldsIndices[pairContact->nbPotentialContactManifolds] = contactManifoldIndex; + pairContact->nbPotentialContactManifolds++; + } + + assert(pairContact->nbPotentialContactManifolds > 0); + } } } @@ -1353,6 +1412,7 @@ void CollisionDetectionSystem::reducePotentialContactManifolds(Array 0); // Get the largest contact point penetration depth of the manifold const decimal depth = computePotentialManifoldLargestContactDepth(manifold, potentialContactPoints); @@ -1378,6 +1438,7 @@ void CollisionDetectionSystem::reducePotentialContactManifolds(Array 0); // If there are two many contact points in the manifold if (manifold.nbPotentialContactPoints > MAX_CONTACT_POINTS_IN_MANIFOLD) { @@ -1614,6 +1675,8 @@ void CollisionDetectionSystem::removeDuplicatedContactPointsInManifold(ContactMa RP3D_PROFILE("CollisionDetectionSystem::removeDuplicatedContactPointsInManifold()", mProfiler); + assert(manifold.nbPotentialContactPoints > 0); + const decimal distThresholdSqr = SAME_CONTACT_POINT_DISTANCE_THRESHOLD * SAME_CONTACT_POINT_DISTANCE_THRESHOLD; // For each contact point of the manifold @@ -1637,6 +1700,8 @@ void CollisionDetectionSystem::removeDuplicatedContactPointsInManifold(ContactMa } } } + + assert(manifold.nbPotentialContactPoints > 0); } // Report contacts and triggers @@ -1707,7 +1772,7 @@ void CollisionDetectionSystem::reportDebugRenderingContacts(Array* } // Return true if two bodies overlap (collide) -bool CollisionDetectionSystem::testOverlap(CollisionBody* body1, CollisionBody* body2) { +bool CollisionDetectionSystem::testOverlap(Body* body1, Body* body2) { NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator(), mOverlappingPairs); @@ -1740,14 +1805,14 @@ void CollisionDetectionSystem::testOverlap(OverlapCallback& callback) { computeBroadPhase(); // Compute the middle-phase collision detection - computeMiddlePhase(narrowPhaseInput, false); + computeMiddlePhase(narrowPhaseInput, false, true); // Compute the narrow-phase collision detection and report overlapping shapes computeNarrowPhaseOverlapSnapshot(narrowPhaseInput, &callback); } // Report all the bodies that overlap (collide) with the body in parameter -void CollisionDetectionSystem::testOverlap(CollisionBody* body, OverlapCallback& callback) { +void CollisionDetectionSystem::testOverlap(Body* body, OverlapCallback& callback) { NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator(), mOverlappingPairs); @@ -1770,7 +1835,7 @@ void CollisionDetectionSystem::testOverlap(CollisionBody* body, OverlapCallback& } // Test collision and report contacts between two bodies. -void CollisionDetectionSystem::testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback) { +void CollisionDetectionSystem::testCollision(Body* body1, Body* body2, CollisionCallback& callback) { NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator(), mOverlappingPairs); @@ -1793,7 +1858,7 @@ void CollisionDetectionSystem::testCollision(CollisionBody* body1, CollisionBody } // Test collision and report all the contacts involving the body in parameter -void CollisionDetectionSystem::testCollision(CollisionBody* body, CollisionCallback& callback) { +void CollisionDetectionSystem::testCollision(Body* body, CollisionCallback& callback) { NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator(), mOverlappingPairs); @@ -1824,7 +1889,7 @@ void CollisionDetectionSystem::testCollision(CollisionCallback& callback) { computeBroadPhase(); // Compute the middle-phase collision detection - computeMiddlePhase(narrowPhaseInput, true); + computeMiddlePhase(narrowPhaseInput, true, true); // Compute the narrow-phase collision detection and report contacts computeNarrowPhaseCollisionSnapshot(narrowPhaseInput, callback); diff --git a/ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp b/ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp index 96731f8f8..54c488fc5 100644 --- a/ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp +++ b/ext/reactphysics3d/src/systems/ConstraintSolverSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -40,7 +40,7 @@ ConstraintSolverSystem::ConstraintSolverSystem(PhysicsWorld& world, Islands& isl FixedJointComponents& fixedJointComponents, HingeJointComponents& hingeJointComponents, SliderJointComponents& sliderJointComponents) - : mIsWarmStartingActive(true), mIslands(islands), + : mTimeStep(-1), mIsWarmStartingActive(true), mIslands(islands), mConstraintSolverData(rigidBodyComponents, jointComponents), mSolveBallAndSocketJointSystem(world, rigidBodyComponents, transformComponents, jointComponents, ballAndSocketJointComponents), mSolveFixedJointSystem(world, rigidBodyComponents, transformComponents, jointComponents, fixedJointComponents), diff --git a/ext/reactphysics3d/src/systems/ContactSolverSystem.cpp b/ext/reactphysics3d/src/systems/ContactSolverSystem.cpp index 1cfba2901..a7ab18774 100644 --- a/ext/reactphysics3d/src/systems/ContactSolverSystem.cpp +++ b/ext/reactphysics3d/src/systems/ContactSolverSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include @@ -46,10 +46,11 @@ const decimal ContactSolverSystem::SLOP = decimal(0.01); // Constructor ContactSolverSystem::ContactSolverSystem(MemoryManager& memoryManager, PhysicsWorld& world, Islands& islands, - CollisionBodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, + BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, ColliderComponents& colliderComponents, decimal& restitutionVelocityThreshold) - :mMemoryManager(memoryManager), mWorld(world), mRestitutionVelocityThreshold(restitutionVelocityThreshold), + :mMemoryManager(memoryManager), mWorld(world), mTimeStep(-1), mRestitutionVelocityThreshold(restitutionVelocityThreshold), mContactConstraints(nullptr), mContactPoints(nullptr), + mNbContactPoints(0), mNbContactManifolds(0), mIslands(islands), mAllContactManifolds(nullptr), mAllContactPoints(nullptr), mBodyComponents(bodyComponents), mRigidBodyComponents(rigidBodyComponents), mColliderComponents(colliderComponents), mIsSplitImpulseActive(true) { @@ -130,10 +131,6 @@ void ContactSolverSystem::initializeForIsland(uint32 islandIndex) { const uint32 rigidBodyIndex1 = mRigidBodyComponents.getEntityIndex(externalManifold.bodyEntity1); const uint32 rigidBodyIndex2 = mRigidBodyComponents.getEntityIndex(externalManifold.bodyEntity2); - // Get the two bodies of the contact - assert(!mBodyComponents.getIsEntityDisabled(externalManifold.bodyEntity1)); - assert(!mBodyComponents.getIsEntityDisabled(externalManifold.bodyEntity2)); - const uint32 collider1Index = mColliderComponents.getEntityIndex(externalManifold.colliderEntity1); const uint32 collider2Index = mColliderComponents.getEntityIndex(externalManifold.colliderEntity2); diff --git a/ext/reactphysics3d/src/systems/DynamicsSystem.cpp b/ext/reactphysics3d/src/systems/DynamicsSystem.cpp index e222766ef..5facbb89d 100644 --- a/ext/reactphysics3d/src/systems/DynamicsSystem.cpp +++ b/ext/reactphysics3d/src/systems/DynamicsSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -31,9 +31,9 @@ using namespace reactphysics3d; // Constructor -DynamicsSystem::DynamicsSystem(PhysicsWorld& world, CollisionBodyComponents& collisionBodyComponents, RigidBodyComponents& rigidBodyComponents, +DynamicsSystem::DynamicsSystem(PhysicsWorld& world, BodyComponents& bodyComponents, RigidBodyComponents& rigidBodyComponents, TransformComponents& transformComponents, ColliderComponents& colliderComponents, bool& isGravityEnabled, Vector3& gravity) - :mWorld(world), mCollisionBodyComponents(collisionBodyComponents), mRigidBodyComponents(rigidBodyComponents), mTransformComponents(transformComponents), mColliderComponents(colliderComponents), + :mWorld(world), mBodyComponents(bodyComponents), mRigidBodyComponents(rigidBodyComponents), mTransformComponents(transformComponents), mColliderComponents(colliderComponents), mIsGravityEnabled(isGravityEnabled), mGravity(gravity) { } diff --git a/ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp b/ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp index 8d092138f..70c851d6f 100644 --- a/ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp +++ b/ext/reactphysics3d/src/systems/SolveBallAndSocketJointSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -64,8 +64,7 @@ void SolveBallAndSocketJointSystem::initBeforeSolve() { const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity); const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity); - assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity)); - assert(!mRigidBodyComponents.getIsEntityDisabled(body2Entity)); + assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity) || !mRigidBodyComponents.getIsEntityDisabled(body2Entity)); // Get the inertia tensor of bodies mBallAndSocketJointComponents.mI1[i] = mRigidBodyComponents.mInverseInertiaTensorsWorld[componentIndexBody1]; diff --git a/ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp b/ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp index 0bea9ce55..048da03d3 100644 --- a/ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp +++ b/ext/reactphysics3d/src/systems/SolveFixedJointSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -63,8 +63,7 @@ void SolveFixedJointSystem::initBeforeSolve() { const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity); const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity); - assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity)); - assert(!mRigidBodyComponents.getIsEntityDisabled(body2Entity)); + assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity) || !mRigidBodyComponents.getIsEntityDisabled(body2Entity)); // Get the inertia tensor of bodies mFixedJointComponents.mI1[i] = mRigidBodyComponents.mInverseInertiaTensorsWorld[componentIndexBody1]; diff --git a/ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp b/ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp index d1865c732..f96e28abf 100644 --- a/ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp +++ b/ext/reactphysics3d/src/systems/SolveHingeJointSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -63,8 +63,7 @@ void SolveHingeJointSystem::initBeforeSolve() { const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity); const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity); - assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity)); - assert(!mRigidBodyComponents.getIsEntityDisabled(body2Entity)); + assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity) || !mRigidBodyComponents.getIsEntityDisabled(body2Entity)); // Get the inertia tensor of bodies mHingeJointComponents.mI1[i] = mRigidBodyComponents.mInverseInertiaTensorsWorld[componentIndexBody1]; diff --git a/ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp b/ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp index 482773d70..3f8a721f6 100644 --- a/ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp +++ b/ext/reactphysics3d/src/systems/SolveSliderJointSystem.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -63,8 +63,7 @@ void SolveSliderJointSystem::initBeforeSolve() { const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity); const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity); - assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity)); - assert(!mRigidBodyComponents.getIsEntityDisabled(body2Entity)); + assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity) || !mRigidBodyComponents.getIsEntityDisabled(body2Entity)); // Get the inertia tensor of bodies mSliderJointComponents.mI1[i] = mRigidBodyComponents.mInverseInertiaTensorsWorld[componentIndexBody1]; diff --git a/ext/reactphysics3d/src/utils/DebugRenderer.cpp b/ext/reactphysics3d/src/utils/DebugRenderer.cpp index 4e8e86675..cc329003c 100644 --- a/ext/reactphysics3d/src/utils/DebugRenderer.cpp +++ b/ext/reactphysics3d/src/utils/DebugRenderer.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -42,13 +42,15 @@ using namespace reactphysics3d; // Constructor DebugRenderer::DebugRenderer(MemoryAllocator& allocator) :mAllocator(allocator), mLines(allocator), mTriangles(allocator), mDisplayedDebugItems(0), mMapDebugItemWithColor(allocator), - mContactPointSphereRadius(DEFAULT_CONTACT_POINT_SPHERE_RADIUS), mContactNormalLength(DEFAULT_CONTACT_NORMAL_LENGTH) { + mContactPointSphereRadius(DEFAULT_CONTACT_POINT_SPHERE_RADIUS), mContactNormalLength(DEFAULT_CONTACT_NORMAL_LENGTH), + mCollisionShapeNormalLength(DEFAULT_COLLISION_SHAPE_NORMAL_LENGTH) { mMapDebugItemWithColor.add(Pair(DebugItem::COLLIDER_AABB, static_cast(DebugColor::MAGENTA))); mMapDebugItemWithColor.add(Pair(DebugItem::COLLIDER_BROADPHASE_AABB, static_cast(DebugColor::YELLOW))); mMapDebugItemWithColor.add(Pair(DebugItem::COLLISION_SHAPE, static_cast(DebugColor::GREEN))); mMapDebugItemWithColor.add(Pair(DebugItem::CONTACT_POINT, static_cast(DebugColor::RED))); mMapDebugItemWithColor.add(Pair(DebugItem::CONTACT_NORMAL, static_cast(DebugColor::WHITE))); + mMapDebugItemWithColor.add(Pair(DebugItem::COLLISION_SHAPE_NORMAL, static_cast(DebugColor::BLUE))); } // Destructor @@ -89,8 +91,12 @@ void DebugRenderer::drawAABB(const AABB& aabb, uint32 color) { } // Draw a box -void DebugRenderer::drawBox(const Transform& transform, const Vector3& halfExtents, uint32 color) { +void DebugRenderer::drawBox(const Transform& transform, const BoxShape* boxShape, uint32 colorShape, uint32 colorShapeNormals) { + const bool drawShape = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE); + const bool drawNormal = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE_NORMAL); + + Vector3 halfExtents = boxShape->getHalfExtents(); Vector3 vertices[8]; // Vertices @@ -103,19 +109,37 @@ void DebugRenderer::drawBox(const Transform& transform, const Vector3& halfExten vertices[6] = transform * Vector3(halfExtents.x, halfExtents.y, -halfExtents.z); vertices[7] = transform * Vector3(-halfExtents.x, halfExtents.y, -halfExtents.z); - // Triangle faces - mTriangles.add(DebugTriangle(vertices[0], vertices[1], vertices[5], color)); - mTriangles.add(DebugTriangle(vertices[0], vertices[5], vertices[4], color)); - mTriangles.add(DebugTriangle(vertices[1], vertices[2], vertices[6], color)); - mTriangles.add(DebugTriangle(vertices[1], vertices[6], vertices[5], color)); - mTriangles.add(DebugTriangle(vertices[2], vertices[3], vertices[6], color)); - mTriangles.add(DebugTriangle(vertices[3], vertices[7], vertices[6], color)); - mTriangles.add(DebugTriangle(vertices[0], vertices[7], vertices[3], color)); - mTriangles.add(DebugTriangle(vertices[0], vertices[4], vertices[7], color)); - mTriangles.add(DebugTriangle(vertices[0], vertices[2], vertices[1], color)); - mTriangles.add(DebugTriangle(vertices[0], vertices[3], vertices[2], color)); - mTriangles.add(DebugTriangle(vertices[5], vertices[6], vertices[4], color)); - mTriangles.add(DebugTriangle(vertices[4], vertices[6], vertices[7], color)); + if (drawShape) { + + // Triangle faces + mTriangles.add(DebugTriangle(vertices[0], vertices[1], vertices[5], colorShape)); + mTriangles.add(DebugTriangle(vertices[0], vertices[5], vertices[4], colorShape)); + mTriangles.add(DebugTriangle(vertices[1], vertices[2], vertices[6], colorShape)); + mTriangles.add(DebugTriangle(vertices[1], vertices[6], vertices[5], colorShape)); + mTriangles.add(DebugTriangle(vertices[2], vertices[3], vertices[6], colorShape)); + mTriangles.add(DebugTriangle(vertices[3], vertices[7], vertices[6], colorShape)); + mTriangles.add(DebugTriangle(vertices[0], vertices[7], vertices[3], colorShape)); + mTriangles.add(DebugTriangle(vertices[0], vertices[4], vertices[7], colorShape)); + mTriangles.add(DebugTriangle(vertices[0], vertices[2], vertices[1], colorShape)); + mTriangles.add(DebugTriangle(vertices[0], vertices[3], vertices[2], colorShape)); + mTriangles.add(DebugTriangle(vertices[5], vertices[6], vertices[4], colorShape)); + mTriangles.add(DebugTriangle(vertices[4], vertices[6], vertices[7], colorShape)); + } + + if (drawNormal) { + + // Face normals + for (int f=0; f < 6; f++) { + + const HalfEdgeStructure::Face& face = boxShape->getFace(f); + const Vector3 facePoint = 0.25 * (boxShape->getVertexPosition(face.faceVertices[0]) + boxShape->getVertexPosition(face.faceVertices[1]) + + boxShape->getVertexPosition(face.faceVertices[2]) + boxShape->getVertexPosition(face.faceVertices[3])); + const Vector3 normalPoint = facePoint + mCollisionShapeNormalLength * boxShape->getFaceNormal(f); + + mLines.add(DebugLine(transform * facePoint, transform * normalPoint, colorShapeNormals)); + } + } + } /// Draw a sphere @@ -272,14 +296,20 @@ void DebugRenderer::drawCapsule(const Transform& transform, decimal radius, deci } // Draw a convex mesh -void DebugRenderer::drawConvexMesh(const Transform& transform, const ConvexMeshShape* convexMesh, uint32 color) { +void DebugRenderer::drawConvexMesh(const Transform& transform, const ConvexMeshShape* convexMesh, + uint32 colorShape, uint32 colorShapeNormals) { - // For each face of the convex mesh + const bool drawShape = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE); + const bool drawNormal = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE_NORMAL); + + // For each face of the convex mesh for (uint32 f = 0; f < convexMesh->getNbFaces(); f++) { const HalfEdgeStructure::Face& face = convexMesh->getFace(f); assert(face.faceVertices.size() >= 3); + Vector3 centroid; + // Perform a fan triangulation of the convex polygon face const uint32 nbFaceVertices = static_cast(face.faceVertices.size()); for (uint32 v = 2; v < nbFaceVertices; v++) { @@ -296,38 +326,71 @@ void DebugRenderer::drawConvexMesh(const Transform& transform, const ConvexMeshS v2 = transform * v2; v3 = transform * v3; - mTriangles.add(DebugTriangle(v1, v2, v3, color)); + if (v == 2) { + centroid += v1 + v2; + } + + centroid += v3; + + if (drawShape) { + mTriangles.add(DebugTriangle(v1, v2, v3, colorShape)); + } } - } + + if (drawNormal) { + + centroid /= nbFaceVertices; + const Vector3 normalPoint = centroid + transform.getOrientation() * convexMesh->getFaceNormal(f) * mCollisionShapeNormalLength; + mLines.add(DebugLine(centroid, normalPoint, colorShapeNormals)); + } + } } // Draw a concave mesh shape -void DebugRenderer::drawConcaveMeshShape(const Transform& transform, const ConcaveMeshShape* concaveMeshShape, uint32 color) { +void DebugRenderer::drawConcaveMeshShape(const Transform& transform, const ConcaveMeshShape* concaveMeshShape, + uint32 colorShape, uint32 colorShapeNormals) { - // For each sub-part of the mesh - for (uint32 p = 0; p < concaveMeshShape->getNbSubparts(); p++) { + const bool drawShape = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE); + const bool drawNormal = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE_NORMAL); - // For each triangle of the sub-part - for (uint32 t = 0; t < concaveMeshShape->getNbTriangles(p); t++) { - - Vector3 triangleVertices[3]; - concaveMeshShape->getTriangleVertices(p, t, triangleVertices); + if (drawShape) { - triangleVertices[0] = transform * triangleVertices[0]; - triangleVertices[1] = transform * triangleVertices[1]; - triangleVertices[2] = transform * triangleVertices[2]; + // For each triangle of the mesh + for (uint32 t = 0; t < concaveMeshShape->getNbTriangles(); t++) { - mTriangles.add(DebugTriangle(triangleVertices[0], triangleVertices[1], triangleVertices[2], color)); - } - } + Vector3 v1, v2, v3; + concaveMeshShape->getTriangleVertices(t, v1, v2, v3); + + v1 = transform * v1; + v2 = transform * v2; + v3 = transform * v3; + + mTriangles.add(DebugTriangle(v1, v2, v3, colorShape)); + } + } + + if (drawNormal) { + + // For each vertex of the mesh + for (uint32 v = 0; v < concaveMeshShape->getNbVertices(); v++) { + + const Vector3& vertex = transform * concaveMeshShape->getVertex(v); + const Vector3 normalPoint = vertex + transform.getOrientation() * concaveMeshShape->getVertexNormal(v) * mCollisionShapeNormalLength; + mLines.add(DebugLine(vertex, normalPoint, colorShapeNormals)); + } + } } // Draw a height field shape -void DebugRenderer::drawHeightFieldShape(const Transform& transform, const HeightFieldShape* heightFieldShape, uint32 color) { +void DebugRenderer::drawHeightFieldShape(const Transform& transform, const HeightFieldShape* heightFieldShape, + uint32 colorShape, uint32 colorShapeNormals) { + + const bool drawShape = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE); + const bool drawNormal = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE_NORMAL); // For each sub-grid points (except the last ones one each dimension) - for (int i = 0; i < heightFieldShape->getNbColumns() - 1; i++) { - for (int j = 0; j < heightFieldShape->getNbRows() - 1; j++) { + for (uint32 i = 0; i < heightFieldShape->getHeightField()->getNbColumns() - 1; i++) { + for (uint32 j = 0; j < heightFieldShape->getHeightField()->getNbRows() - 1; j++) { // Compute the four point of the current quad Vector3 p1 = heightFieldShape->getVertexAt(i, j); @@ -340,51 +403,72 @@ void DebugRenderer::drawHeightFieldShape(const Transform& transform, const Heigh p3 = transform * p3; p4 = transform * p4; - mTriangles.add(DebugTriangle(p1, p2, p3, color)); - mTriangles.add(DebugTriangle(p3, p2, p4, color)); + if (drawShape) { + + mTriangles.add(DebugTriangle(p1, p2, p3, colorShape)); + mTriangles.add(DebugTriangle(p3, p2, p4, colorShape)); + } + + if (drawNormal) { + + // Compute the triangle normal + Vector3 triangle1Normal = (p2 - p1).cross(p3 - p1).getUnit(); + + const Vector3 normalPoint1 = p1 + triangle1Normal * mCollisionShapeNormalLength; + const Vector3 normalPoint2 = p2 + triangle1Normal * mCollisionShapeNormalLength; + const Vector3 normalPoint3 = p3 + triangle1Normal * mCollisionShapeNormalLength; + const Vector3 normalPoint4 = p4 + triangle1Normal * mCollisionShapeNormalLength; + mLines.add(DebugLine(p1, normalPoint1, colorShapeNormals)); + mLines.add(DebugLine(p2, normalPoint2, colorShapeNormals)); + mLines.add(DebugLine(p3, normalPoint3, colorShapeNormals)); + mLines.add(DebugLine(p4, normalPoint4, colorShapeNormals)); + } } } } // Draw the collision shape of a collider -void DebugRenderer::drawCollisionShapeOfCollider(const Collider* collider, uint32 color) { +void DebugRenderer::drawCollisionShapeOfCollider(const Collider* collider) { + uint32 colorShape = mMapDebugItemWithColor[DebugItem::COLLISION_SHAPE]; + uint32 colorShapeNormals = mMapDebugItemWithColor[DebugItem::COLLISION_SHAPE_NORMAL]; + switch (collider->getCollisionShape()->getName()) { case CollisionShapeName::BOX: { const BoxShape* boxShape = static_cast(collider->getCollisionShape()); - drawBox(collider->getLocalToWorldTransform(), boxShape->getHalfExtents(), color); + drawBox(collider->getLocalToWorldTransform(), boxShape, colorShape, colorShapeNormals); break; } case CollisionShapeName::SPHERE: { const SphereShape* sphereShape = static_cast(collider->getCollisionShape()); - drawSphere(collider->getLocalToWorldTransform().getPosition(), sphereShape->getRadius(), color); + drawSphere(collider->getLocalToWorldTransform().getPosition(), sphereShape->getRadius(), colorShape); break; } case CollisionShapeName::CAPSULE: { const CapsuleShape* capsuleShape = static_cast(collider->getCollisionShape()); - drawCapsule(collider->getLocalToWorldTransform(), capsuleShape->getRadius(), capsuleShape->getHeight(), color); + drawCapsule(collider->getLocalToWorldTransform(), capsuleShape->getRadius(), capsuleShape->getHeight(), colorShape); break; } case CollisionShapeName::CONVEX_MESH: { - const ConvexMeshShape* convexMeshShape = static_cast(collider->getCollisionShape()); - drawConvexMesh(collider->getLocalToWorldTransform(), convexMeshShape, color); + const ConvexMeshShape* convexMeshShape = static_cast(collider->getCollisionShape()); + drawConvexMesh(collider->getLocalToWorldTransform(), convexMeshShape, colorShape, colorShapeNormals); break; } case CollisionShapeName::TRIANGLE_MESH: { const ConcaveMeshShape* concaveMeshShape = static_cast(collider->getCollisionShape()); - drawConcaveMeshShape(collider->getLocalToWorldTransform(), concaveMeshShape, color); + drawConcaveMeshShape(collider->getLocalToWorldTransform(), concaveMeshShape, colorShape, colorShapeNormals); break; } case CollisionShapeName::HEIGHTFIELD: { const HeightFieldShape* heighFieldShape = static_cast(collider->getCollisionShape()); - drawHeightFieldShape(collider->getLocalToWorldTransform(), heighFieldShape, color); + drawHeightFieldShape(collider->getLocalToWorldTransform(), heighFieldShape, colorShape, colorShapeNormals); break; } default: @@ -400,17 +484,17 @@ void DebugRenderer::computeDebugRenderingPrimitives(const PhysicsWorld& world) { const bool drawColliderAABB = getIsDebugItemDisplayed(DebugItem::COLLIDER_AABB); const bool drawColliderBroadphaseAABB = getIsDebugItemDisplayed(DebugItem::COLLIDER_BROADPHASE_AABB); const bool drawCollisionShape = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE); - - const uint32 nbCollisionBodies = world.getNbCollisionBodies(); + const bool drawCollisionShapeNormals = getIsDebugItemDisplayed(DebugItem::COLLISION_SHAPE_NORMAL); + const uint32 nbRigidBodies = world.getNbRigidBodies(); // For each body of the world - for (uint32 b = 0; b < nbCollisionBodies + nbRigidBodies; b++) { + for (uint32 b = 0; b < nbRigidBodies; b++) { // Get a body - const CollisionBody* body = b < nbCollisionBodies ? world.getCollisionBody(b) : world.getRigidBody(b - nbCollisionBodies); + const Body* body = world.getRigidBody(b); - if (body->isActive()) { + if (body->isActive() && body->isDebugEnabled()) { // For each collider of the body for (uint32 c = 0; c < body->getNbColliders(); c++) { @@ -433,9 +517,9 @@ void DebugRenderer::computeDebugRenderingPrimitives(const PhysicsWorld& world) { } // If we need to draw the collision shape - if (drawCollisionShape) { + if (drawCollisionShape || drawCollisionShapeNormals) { - drawCollisionShapeOfCollider(collider, mMapDebugItemWithColor[DebugItem::COLLISION_SHAPE]); + drawCollisionShapeOfCollider(collider); } } } diff --git a/ext/reactphysics3d/src/utils/DefaultLogger.cpp b/ext/reactphysics3d/src/utils/DefaultLogger.cpp index aff4f1629..1cdc57ee4 100644 --- a/ext/reactphysics3d/src/utils/DefaultLogger.cpp +++ b/ext/reactphysics3d/src/utils/DefaultLogger.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -64,14 +64,20 @@ DefaultLogger::Formatter* DefaultLogger::getFormatter(Format format) const { // Add a log file destination to the logger void DefaultLogger::addFileDestination(const std::string& filePath, uint logLevelFlag, Format format) { - FileDestination* destination = new (mAllocator.allocate(sizeof(FileDestination))) FileDestination(filePath, logLevelFlag, getFormatter(format)); + // Make sure capacity is an integral multiple of alignment + const size_t allocatedSize = std::ceil(sizeof(FileDestination) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + + FileDestination* destination = new (mAllocator.allocate(allocatedSize)) FileDestination(filePath, logLevelFlag, getFormatter(format)); mDestinations.add(destination); } /// Add a stream destination to the logger void DefaultLogger::addStreamDestination(std::ostream& outputStream, uint logLevelFlag, Format format) { - StreamDestination* destination = new (mAllocator.allocate(sizeof(StreamDestination))) StreamDestination(outputStream, logLevelFlag, getFormatter(format)); + // Make sure capacity is an integral multiple of alignment + const size_t allocatedSize = std::ceil(sizeof(StreamDestination) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + + StreamDestination* destination = new (mAllocator.allocate(allocatedSize)) StreamDestination(outputStream, logLevelFlag, getFormatter(format)); mDestinations.add(destination); } @@ -85,6 +91,9 @@ void DefaultLogger::removeAllDestinations() { mDestinations[i]->~Destination(); + // Make sure capacity is an integral multiple of alignment + size = std::ceil(size / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + mAllocator.release(mDestinations[i], size); } diff --git a/ext/reactphysics3d/src/utils/Profiler.cpp b/ext/reactphysics3d/src/utils/Profiler.cpp index 7ef3e7ec5..6124327b7 100644 --- a/ext/reactphysics3d/src/utils/Profiler.cpp +++ b/ext/reactphysics3d/src/utils/Profiler.cpp @@ -1,6 +1,6 @@ /******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2022 Daniel Chappuis * +* Copyright (c) 2010-2024 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * diff --git a/ext/reactphysics3d/src/utils/quickhull/QHHalfEdgeStructure.cpp b/ext/reactphysics3d/src/utils/quickhull/QHHalfEdgeStructure.cpp new file mode 100644 index 000000000..124f28d00 --- /dev/null +++ b/ext/reactphysics3d/src/utils/quickhull/QHHalfEdgeStructure.cpp @@ -0,0 +1,419 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include +#include +#include +#include + +using namespace reactphysics3d; + +// Make sure capacity is an integral multiple of alignment +const size_t QHHalfEdgeStructure::mVertexAllocatedSize = std::ceil(sizeof(Vertex) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; +const size_t QHHalfEdgeStructure::mEdgeAllocatedSize = std::ceil(sizeof(Edge) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; +const size_t QHHalfEdgeStructure::mFaceAllocatedSize = std::ceil(sizeof(Face) / float(GLOBAL_ALIGNMENT)) * GLOBAL_ALIGNMENT; + + +// Destructor +QHHalfEdgeStructure::~QHHalfEdgeStructure() { + + // Delete faces + Face* face = mFaces; + while (face != nullptr) { + + Face* nextFace = face->nextFace; + + face->~Face(); + mAllocator.release(face, mFaceAllocatedSize); + + face = nextFace; + } + + // Delete edges + Edge* edge = mHalfEdges; + while (edge != nullptr) { + + Edge* nextEdge = edge->nextEdge; + + edge->~Edge(); + mAllocator.release(edge, mEdgeAllocatedSize); + + edge = nextEdge; + } + + // Delete vertices + Vertex* vertex = mVertices; + while (vertex != nullptr) { + + Vertex* nextVertex = vertex->nextVertex; + + vertex->~Vertex(); + mAllocator.release(vertex, mVertexAllocatedSize); + + vertex = nextVertex; + } +} + +// Add a vertex +/** + * @param externalIndex Index of the vertex in the external user vertex data array + * @return A pointer to the new vertex + */ +QHHalfEdgeStructure::Vertex* QHHalfEdgeStructure::addVertex(uint32 externalIndex) { + + // Create a new vertex + Vertex* vertex = new (mAllocator.allocate(mVertexAllocatedSize)) Vertex(externalIndex, nullptr, mVertices); + + if (mVertices != nullptr) { + mVertices->previousVertex = vertex; + } + + mVertices = vertex; + + mNbVertices++; + + return vertex; +} + +// Add a face +/** + * @param faceVertices Array of the vertices in a face (ordered in CCW order as seen from outside + * the polyhedron). The indices are the internal indices of the vertices inside the HalfEdgeStructure. + * @param points Array with the points (coordinates) of the face vertices + * @param allocator Reference to a memory allocator + * @return A Pointer to the new face + */ +QHHalfEdgeStructure::Face* QHHalfEdgeStructure::addFace(const Array& faceVertices, const Array& points, + MemoryAllocator& allocator) { + + assert(faceVertices.size() >= 3); + + // Create a new face + Face* face = new (mAllocator.allocate(mFaceAllocatedSize)) Face(allocator); + + Edge* prevFaceEdge = nullptr; + Edge* firstFaceEdge = nullptr; + + // For each edge of the face + for (uint32 i=0; i < faceVertices.size(); i++) { + + Vertex* v1 = faceVertices[i]; + Vertex* v2 = faceVertices[i == faceVertices.size() - 1 ? 0 : i+1]; + + assert(!mMapVerticesToEdge.containsKey(EdgeVertices(v1, v2))); + + // Create an edge + Edge* edge = new (mAllocator.allocate(mEdgeAllocatedSize)) Edge(v1, v2, face); + edge->previousFaceEdge = prevFaceEdge; + + // Get twin edge (if any) + Edge* twinEdge = nullptr; + auto it = mMapVerticesToEdge.find(EdgeVertices(v2, v1)); + if (it != mMapVerticesToEdge.end()) { + twinEdge = it->second; + assert(twinEdge->twinEdge == nullptr); + twinEdge->twinEdge = edge; + } + edge->twinEdge = twinEdge; + + if (prevFaceEdge != nullptr) { + prevFaceEdge->nextFaceEdge = edge; + } + + if (i == 0) { + firstFaceEdge = edge; + } + + // Add the new edge before the corresponding twin edge in the linked-list of edges (or at the beginning if there is no twin edge) + addEdgeToLinkedListBefore(edge, twinEdge); + + mNbHalfEdges++; + + // Add the edge into the map + assert(!mMapVerticesToEdge.containsKey(EdgeVertices(v1, v2))); + mMapVerticesToEdge.add(Pair(EdgeVertices(v1, v2), edge)); + + assert(twinEdge != nullptr || mHalfEdges == edge); + assert(edge->face == face); + + prevFaceEdge = edge; + } + + prevFaceEdge->nextFaceEdge = firstFaceEdge; + firstFaceEdge->previousFaceEdge = prevFaceEdge; + + face->edge = firstFaceEdge; + + addFaceToLinkedList(face); + + mNbFaces++; + + // Compute the normal, area and centroid + face->recalculateFace(points); + + return face; +} + +// Add an edge before another one in the linked-list of edges +void QHHalfEdgeStructure::addEdgeToLinkedListBefore(Edge* newEdge, Edge* edge) { + + assert(newEdge != nullptr); + + // If we need to add the edge at the beginning instead + if (edge == nullptr) { + newEdge->nextEdge = mHalfEdges; + newEdge->previousEdge = nullptr; + if (mHalfEdges != nullptr) { + mHalfEdges->previousEdge = newEdge; + } + mHalfEdges = newEdge; + } + else { // If we need to add the newEdge before the edge + + if (edge->previousEdge != nullptr) { + assert(edge->previousEdge->nextEdge == edge); + edge->previousEdge->nextEdge = newEdge; + } + else { // If the new edge must be the first one in the linked-list + assert(mHalfEdges == edge); + mHalfEdges = newEdge; + } + + newEdge->previousEdge = edge->previousEdge; + edge->previousEdge = newEdge; + newEdge->nextEdge = edge; + } + + assert(edge == nullptr || edge->previousEdge == newEdge); + assert(mHalfEdges == newEdge || newEdge->previousEdge != nullptr); + assert(mHalfEdges == newEdge || newEdge->previousEdge->nextEdge == newEdge); + assert(newEdge->nextEdge == nullptr || newEdge->nextEdge->previousEdge == newEdge); +} + +// Remove an half-edge from the linked-list of half-edges +void QHHalfEdgeStructure::removeEdgeFromLinkedList(Edge* edge) { + + if (edge->nextEdge != nullptr) { + edge->nextEdge->previousEdge = edge->previousEdge; + } + if (edge->previousEdge != nullptr) { + edge->previousEdge->nextEdge = edge->nextEdge; + } + else { + assert(mHalfEdges == edge); + mHalfEdges = edge->nextEdge; + assert(mHalfEdges == nullptr || mHalfEdges->previousEdge == nullptr); + } +} + +// Add a face to the linked-list of faces +void QHHalfEdgeStructure::addFaceToLinkedList(Face* face) { + + if (mFaces != nullptr) { + mFaces->previousFace = face; + } + face->nextFace = mFaces; + + mFaces = face; +} + +// Remove a face from the linked-list of faces +void QHHalfEdgeStructure::removeFaceFromLinkedList(Face* face) { + + if (face->nextFace != nullptr) { + face->nextFace->previousFace = face->previousFace; + } + if (face->previousFace != nullptr) { + face->previousFace->nextFace = face->nextFace; + } + else { + assert(mFaces == face); + mFaces = face->nextFace; + } +} + +// Remove an half-edge +void QHHalfEdgeStructure::removeHalfEdge(Edge* edge) { + + assert(mMapVerticesToEdge.containsKey(EdgeVertices(edge->startVertex, edge->endVertex))); + mMapVerticesToEdge.remove(EdgeVertices(edge->startVertex, edge->endVertex)); + + if (edge->twinEdge != nullptr) { + edge->twinEdge->twinEdge = nullptr; + } + + removeEdgeFromLinkedList(edge); + + edge->~Edge(); + mAllocator.release(edge, mEdgeAllocatedSize); + + mNbHalfEdges--; +} + +// Remove a face and all its edges +void QHHalfEdgeStructure::removeFace(Face* face) { + + // Remove all half-edges of the face + Edge* firstEdge = face->edge; + Edge* edge = firstEdge; + do { + + Edge* nextFaceEdge = edge->nextFaceEdge; + + removeHalfEdge(edge); + + edge = nextFaceEdge; + + } while(edge != firstEdge); + + deleteFace(face); +} + +// Delete the face +void QHHalfEdgeStructure::deleteFace(Face* face) { + + // Remove the face + removeFaceFromLinkedList(face); + + face->~Face(); + mAllocator.release(face, mFaceAllocatedSize); + + mNbFaces--; +} + +// Remove a vertex +void QHHalfEdgeStructure::removeVertex(Vertex* vertex) { + + if (vertex->previousVertex != nullptr) { + vertex->previousVertex->nextVertex = vertex->nextVertex; + } + if (vertex->nextVertex != nullptr) { + vertex->nextVertex->previousVertex = vertex->previousVertex; + } + + if (mVertices == vertex) { + mVertices = vertex->nextVertex; + } + + vertex->~Vertex(); + mAllocator.release(vertex, mVertexAllocatedSize); + + mNbVertices--; +} + +// Return true if the half-edge structure is valid (for debugging purpose) +bool QHHalfEdgeStructure::isValid() const { + + bool isValid = true; + + // For each face + uint32 nbFaces = 0; + Face* face; + Face* previousFace; + for (face = mFaces, previousFace = nullptr; face != nullptr; previousFace = face, face = face->nextFace) { + + isValid &= face->edge != nullptr; + isValid &= (face->previousFace != nullptr || nbFaces == 0); + isValid &= (face->previousFace == nullptr || face->previousFace == previousFace); + isValid &= (previousFace == nullptr || previousFace->nextFace == face); + isValid &= face->isValid(); + isValid &= face->area > 0.00001; + nbFaces++; + }; + isValid &= nbFaces > 0 || mFaces == nullptr; + + // For each vertex + uint32 nbVertices = 0; + Vertex* vertex; + Vertex* previousVertex; + for (vertex = mVertices, previousVertex = nullptr; vertex != nullptr; previousVertex = vertex, vertex = vertex->nextVertex) { + + isValid &= (vertex->previousVertex != nullptr || nbVertices == 0); + isValid &= (vertex->previousVertex == nullptr || vertex->previousVertex == previousVertex); + isValid &= (previousVertex == nullptr || previousVertex->nextVertex == vertex); + nbVertices++; + }; + isValid &= nbVertices == mNbVertices; + isValid &= (nbVertices > 0 || mVertices == nullptr); + + // For each half-edge + uint32 nbEdges = 0; + Edge* edge; + Edge* previousEdge; + for (edge = mHalfEdges, previousEdge = nullptr; edge != nullptr; previousEdge = edge, edge = edge->nextEdge) { + + isValid &= (edge->previousEdge != nullptr || nbEdges == 0); + isValid &= (edge->previousEdge == nullptr || edge->previousEdge == previousEdge); + isValid &= (previousEdge == nullptr || previousEdge->nextEdge == edge); + isValid &= edge->isValid(); + nbEdges++; + }; + + isValid &= nbEdges == mNbHalfEdges; + isValid &= mNbHalfEdges % 2 == 0; + isValid &= (nbEdges > 0 || mHalfEdges == nullptr); + + return isValid; +} + +// Return a string representation of the half-edge structure +std::string QHHalfEdgeStructure::to_string() const { + + std::string faces = "Faces{"; + for (const Face* face = mFaces; face != nullptr; face = face->nextFace) { + + faces += "Face =" + face->verticesString(); + + if (face->nextFace != nullptr) { + faces += ","; + } + } + + faces += "}"; + + std::string edges = "Edges{"; + for (const Edge* edge = mHalfEdges; edge != nullptr; edge = edge->nextEdge) { + + edges += "Edge("; + + edges += std::to_string(edge->startVertex->externalIndex) + "," + std::to_string(edge->endVertex->externalIndex) + ")"; + + if (edge->nextEdge != nullptr) { + edges += ","; + } + } + + std::string vertices = "Vertices{"; + for (const Vertex* vertex = mVertices; vertex != nullptr; vertex = vertex->nextVertex) { + + vertices += std::to_string(vertex->externalIndex); + + if (vertex->nextVertex != nullptr) { + vertices += ","; + } + } + return "HalfEdgeStructure(" + faces + ",\n" + edges + ",\n" + vertices + ")"; +} diff --git a/ext/reactphysics3d/src/utils/quickhull/QuickHull.cpp b/ext/reactphysics3d/src/utils/quickhull/QuickHull.cpp new file mode 100644 index 000000000..f45a55fc2 --- /dev/null +++ b/ext/reactphysics3d/src/utils/quickhull/QuickHull.cpp @@ -0,0 +1,1076 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2024 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Namespace +using namespace reactphysics3d; + +// Compute the convex hull of a set of points and return the resulting convex mesh +bool QuickHull::computeConvexHull(const VertexArray& vertexArray, PolygonVertexArray& outPolygonVertexArray, + Array& outVertices, Array& outIndices, + Array& outFaces, MemoryAllocator& allocator, + std::vector& errors) { + + bool isValid = true; + + // Extract the points from the array + Array points(allocator); + extractPoints(vertexArray, points); + + // Remove the duplicated vertices from the points + removeDuplicatedVertices(points, allocator); + + // If there are less than four vertices in the vertex array + if (points.size() < 4) { + + errors.push_back(Message("The VertexArray must contain at least 4 vertices to create a convex mesh")); + return false; + } + + Array orphanPointsIndices(allocator, points.size()); + decimal maxAbsX = 0; + decimal maxAbsY = 0; + decimal maxAbsZ = 0; + for (uint32 i=0 ; i < points.size(); i++) { + orphanPointsIndices.add(i); + + decimal absX = std::abs(points[i].x); + decimal absY = std::abs(points[i].y); + decimal absZ = std::abs(points[i].z); + + if (absX > maxAbsX) { + maxAbsX = absX; + } + if (absY > maxAbsY) { + maxAbsY = absY; + } + if (absZ > maxAbsZ) { + maxAbsZ = absZ; + } + } + + // Compute the 'epsilon' value for this set of points + const decimal epsilon = 3 * (maxAbsX + maxAbsY + maxAbsZ) * MACHINE_EPSILON; + + QHHalfEdgeStructure convexHull(allocator); + + Array initialFaces(allocator); + + // Compute the initial convex hull + isValid &= computeInitialHull(points, convexHull, initialFaces, orphanPointsIndices, allocator, errors); + if (!isValid) { + return false; + } + + assert(convexHull.getNbVertices() == 4); + assert(convexHull.getNbFaces() == 4); + + assert(convexHull.getNbVertices() == 4); + + // Associate all the remaining points with the closest faces of the initial hull + Set deletedFaces(allocator); + associateOrphanPointsToNewFaces(orphanPointsIndices, initialFaces, points, epsilon, deletedFaces); + + // Get The next vertex candidate + uint32 nextVertexIndex; + QHHalfEdgeStructure::Face* nextFace; + findNextVertexCandidate(points, nextVertexIndex, convexHull, nextFace, epsilon); + while (nextVertexIndex != INVALID_VERTEX_INDEX) { + + assert(nextFace != nullptr); + + // Add the vertex to the hull + addVertexToHull(nextVertexIndex, nextFace, points, convexHull, epsilon, allocator); + + // Get the next vertex candidate + findNextVertexCandidate(points, nextVertexIndex, convexHull, nextFace, epsilon); + + assert(convexHull.isValid()); + } + + assert(convexHull.isValid()); + + // Compute the final PolygonVertexArray with the resulting convex hull mesh + computeFinalPolygonVertexArray(convexHull, points, outPolygonVertexArray, outVertices, outIndices, outFaces, allocator); + + return isValid; +} + +// Compute the final PolygonVertexArray from the convex hull half-edge structure +void QuickHull::computeFinalPolygonVertexArray(const QHHalfEdgeStructure& convexHull, + const Array& points, + PolygonVertexArray& outPolygonVertexArray, + Array& outVertices, Array& outIndices, + Array& outFaces, + MemoryAllocator& allocator) { + + assert(outVertices.size() == 0); + assert(outIndices.size() == 0); + assert(outFaces.size() == 0); + + Map mapOldVertexIndexToNew(allocator, convexHull.getNbVertices()); + + // For each face of the convex hull + for (const QHHalfEdgeStructure::Face* face = convexHull.getFaces(); face != nullptr; face = face->nextFace) { + + assert(face->area > 0.00001); + + PolygonVertexArray::PolygonFace polygonFace; + polygonFace.nbVertices = 0; + polygonFace.indexBase = outIndices.size(); + + // For each edge of the face + QHHalfEdgeStructure::Edge* firstFaceEdge = face->edge; + QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge; + do { + + assert(faceEdge != nullptr); + + const uint32 vOldIndex = faceEdge->startVertex->externalIndex; + uint32 vNewIndex = outVertices.size() / 3; + auto it = mapOldVertexIndexToNew.find(vOldIndex); + + // If the vertex is already in the new array of vertices + if (it != mapOldVertexIndexToNew.end()) { + vNewIndex = it->second; + } + else { + + // Add the vertex to the new array of vertices + mapOldVertexIndexToNew.add(Pair(vOldIndex, vNewIndex)); + outVertices.add(points[vOldIndex].x); + outVertices.add(points[vOldIndex].y); + outVertices.add(points[vOldIndex].z); + } + + // Add the new vertex index to the array of indices + outIndices.add(vNewIndex); + + polygonFace.nbVertices++; + + // Go to the next edge of the face + faceEdge = faceEdge->nextFaceEdge; + + } while(faceEdge != firstFaceEdge); + + outFaces.add(polygonFace); + } + + assert(convexHull.getNbVertices() == outVertices.size() / 3); + assert(convexHull.getNbFaces() == outFaces.size()); + + outPolygonVertexArray.init(outVertices.size() / 3, &(outVertices[0]), 3 * sizeof(float), + &(outIndices[0]), sizeof(unsigned int), + outFaces.size(), &(outFaces[0]), + PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE, + PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE); +} + +// Add a vertex to the current convex hull to expand it +void QuickHull::addVertexToHull(uint32 vertexIndex, + QHHalfEdgeStructure::Face* face, + Array& points, + QHHalfEdgeStructure& convexHull, + decimal epsilon, + MemoryAllocator& allocator) { + + Array horizonVertices(allocator); + Array visibleFaces(allocator); + Array orphanPointsIndices(allocator); + + // Find the horizon edges + findHorizon(points[vertexIndex], face, allocator, horizonVertices, visibleFaces, epsilon); + + assert(visibleFaces.size() >= 1); + assert(horizonVertices.size() >= 6); + + assert(horizonVertices[0]->externalIndex == horizonVertices[horizonVertices.size()-1]->externalIndex); + + // Delete all the faces visible from the vertex + deleteVisibleFaces(visibleFaces, convexHull, orphanPointsIndices, horizonVertices, allocator); + + // Build the new faces + Array newFaces(allocator); + buildNewFaces(vertexIndex, horizonVertices, convexHull, points, newFaces, allocator); + + // Store faces deleted during merging of concave faces + Set deletedFaces(allocator); + + // Merge concave faces (by giving priority to merging with large faces) + mergeLargeConcaveFaces(convexHull, newFaces, points, epsilon, deletedFaces); + + // Merge concave faces + mergeConcaveFaces(convexHull, newFaces, points, epsilon, deletedFaces); + + associateOrphanPointsToNewFaces(orphanPointsIndices, newFaces, points, epsilon, deletedFaces); +} + +// Build the new faces that contain the new vertex and the horizon edges +void QuickHull::buildNewFaces(uint32 newVertexIndex, + Array& horizonVertices, + QHHalfEdgeStructure& convexHull, + Array& points, + Array& newFaces, + MemoryAllocator& allocator) { + + // Add the new vertex to the convex hull + QHHalfEdgeStructure::Vertex* v = convexHull.addVertex(newVertexIndex); + + Array faceVertices(allocator, 8); + + // For each horizon edge + for (uint32 i=0; i < horizonVertices.size(); i += 2) { + + QHHalfEdgeStructure::Vertex* v2 = horizonVertices[i]; + QHHalfEdgeStructure::Vertex* v3 = horizonVertices[i+1]; + + // Create the vertices of a triangular face + faceVertices.add(v2); + faceVertices.add(v3); + faceVertices.add(v); + + // Create a new face + QHHalfEdgeStructure::Face* face = convexHull.addFace(faceVertices, points, allocator); + + newFaces.add(face); + + faceVertices.clear(); + } +} + +// Delete all the faces visible from the vertex to be added +void QuickHull::deleteVisibleFaces(const Array& visibleFaces, + QHHalfEdgeStructure& convexHull, + Array& orphanPoints, + const Array& horizonVertices, + MemoryAllocator& allocator) { + + const uint32 nbFaces = visibleFaces.size(); + + Set verticesToRemove(allocator); + + // For each visible face + for (uint32 i=0; i < nbFaces; i++) { + + // Add all the remaining points associated with this face to the array of orphan points + orphanPoints.addRange(visibleFaces[i]->conflictPoints); + + // For each vertex of the face to remove + const QHHalfEdgeStructure::Edge* firstFaceEdge = visibleFaces[i]->edge; + const QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge; + do { + + QHHalfEdgeStructure::Vertex* vertex = faceEdge->startVertex; + + // If the vertex is not part of the horizon + if (!testIsVertexInHorizon(vertex, horizonVertices)) { + + // Add the vertex to the set of vertices to be removed + verticesToRemove.add(vertex); + } + + faceEdge = faceEdge->nextFaceEdge; + + } while(faceEdge != firstFaceEdge); + + // Remove the face from the current convex hull + convexHull.removeFace(visibleFaces[i]); + } + + // Remove the vertices to be removed + for (auto it = verticesToRemove.begin(); it != verticesToRemove.end(); ++it) { + convexHull.removeVertex(*it); + } +} + +// Return true if the vertex is part of horizon edges +bool QuickHull::testIsVertexInHorizon(QHHalfEdgeStructure::Vertex* vertex, const Array& horizonVertices) { + + // For each edge of the horizon + for (uint32 i=0; i < horizonVertices.size(); i += 2) { + + if (horizonVertices[i] == vertex) return true; + } + + return false; +} + + +// Return true if a given edge is convex and false otherwise +bool QuickHull::testIsConvexEdge(const QHHalfEdgeStructure::Edge* edge, decimal epsilon) { + + // Get the two neighbor faces + assert(edge->twinEdge != nullptr); + QHHalfEdgeStructure::Face* face1 = edge->face; + QHHalfEdgeStructure::Face* face2 = edge->twinEdge->face; + + // We test if the center of face1 is below the face2 plane + if (computePointToPlaneDistance(face1->centroid, face2->normal, face2->centroid) >= -epsilon) return false; + + // We test if the center of face2 is below the face1 plane + if (computePointToPlaneDistance(face2->centroid, face1->normal, face1->centroid) >= -epsilon) return false; + + // If both tests are true, the edge is convex + return true; +} + +// Find the horizon (edges) forming the separation between the faces that are visible from the new vertex and the faces that are not visible +void QuickHull::findHorizon(const Vector3& vertex, QHHalfEdgeStructure::Face* face, + MemoryAllocator& allocator, + Array& outHorizonVertices, + Array& outVisibleFaces, decimal epsilon) { + + Stack facesToVisit(allocator); + Set visitedFaces(allocator); + + facesToVisit.push(CandidateFace(face, face->edge)); + + outVisibleFaces.add(face); + + // While there still are faces to visit + while (facesToVisit.size() > 0) { + + // Get the next face to process + CandidateFace& candidateFace = facesToVisit.top(); + + // Mark the current face as visited + visitedFaces.add(candidateFace.face); + + bool goToVisibleFace = false; + + // For each edge of the current face + do { + + // Get the current edge to cross of the current face + const QHHalfEdgeStructure::Edge* edge = candidateFace.currentEdge; + assert(edge->face == candidateFace.face); + + const QHHalfEdgeStructure::Edge* twinEdge = edge->twinEdge; + + // Get the next face + QHHalfEdgeStructure::Face* nextFace = twinEdge->face; + + // If the next face is not visited yet + if (!visitedFaces.contains(nextFace)) { + + // If the next face is visible from the vertex + if (nextFace->normal.dot(vertex - nextFace->centroid) > epsilon) { + + // Add the next face to the stack of faces to visit + facesToVisit.push(CandidateFace(nextFace, twinEdge->nextFaceEdge)); + + outVisibleFaces.add(nextFace); + + goToVisibleFace = true; + + // If the face is visible we move to visit this new visible face (Depth First Search) + break; + } + else { // We have found a face that is not visible from the vertex + + // We add the edge between current face and next face to the array of horizon edges + outHorizonVertices.add(candidateFace.currentEdge->startVertex); + outHorizonVertices.add(candidateFace.currentEdge->endVertex); + } + } + + // Move to the next edge + candidateFace.currentEdge = edge->nextFaceEdge; + + } while (candidateFace.currentEdge != candidateFace.startEdge); + + // If we have visited all the edges of the current face + if (!goToVisibleFace) { + + // Remove the current face from the stack of faces to visit + facesToVisit.pop(); + } + } +} + +// Iterate over all new faces and fix faces that are forming a concave or coplanar shape in order to always keep the hull convex by +// giving priority to large faces +void QuickHull::mergeLargeConcaveFaces(QHHalfEdgeStructure& convexHull, Array& newFaces, + const Array& points, decimal epsilon, Set& deletedFaces) { + + assert(newFaces.size() > 0); + + // For each new face + uint32 i = 0; + while(i < newFaces.size()) { + + QHHalfEdgeStructure::Face* face1 = newFaces[i]; + + // If the face has not been deleted during the process of merging the concave faces + if (!deletedFaces.contains(face1)) { + + QHHalfEdgeStructure::Edge* concaveEdge = nullptr; + + // For each edge of the new face + QHHalfEdgeStructure::Edge* firstFaceEdge = face1->edge; + QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge; + do { + + assert(faceEdge != nullptr); + + // Get the two neighbor faces + assert(faceEdge->twinEdge != nullptr); + QHHalfEdgeStructure::Face* face2 = faceEdge->twinEdge->face; + assert(!deletedFaces.contains(face2)); + + if (face1->area > face2->area) { + + // We test if the center of face2 is below the face1 plane (if edge is convex w.r.t face1) + if (computePointToPlaneDistance(face2->centroid, face1->normal, face1->centroid) < -epsilon) { + + // Move to the next edge of the face + faceEdge = faceEdge->nextFaceEdge; + + continue; + } + + // The two faces at this edge are forming a concave or coplanar shape + concaveEdge = faceEdge; + break; + } + + // We test if the center of face1 is below the face2 plane (if edge is convex w.r.t face2) + if (computePointToPlaneDistance(face1->centroid, face2->normal, face2->centroid) < -epsilon) { + + // Move to the next edge of the face + faceEdge = faceEdge->nextFaceEdge; + + continue; + } + + // The two faces at this edge are forming a concave or coplanar shape + concaveEdge = faceEdge; + break; + + } while(faceEdge != firstFaceEdge); + + // If we have found a concave or coplanar edge + if (concaveEdge != nullptr) { + + assert(concaveEdge->face == face1 || concaveEdge->twinEdge->face == face1); + + // Merge the two faces at this edge + mergeConcaveFacesAtEdge(concaveEdge, convexHull, points, deletedFaces); + + continue; + } + } + + i++; + } +} + +// Iterate over all new faces and fix faces that are forming a concave or coplanar shape in order to always keep the hull convex +void QuickHull::mergeConcaveFaces(QHHalfEdgeStructure& convexHull, Array& newFaces, + const Array& points, decimal epsilon, Set& deletedFaces) { + + assert(newFaces.size() > 0); + + // For each new face + uint32 i = 0; + while(i < newFaces.size()) { + + QHHalfEdgeStructure::Face* face = newFaces[i]; + + // If the face has not been deleted during the process of merging the concave faces + if (!deletedFaces.contains(face)) { + + QHHalfEdgeStructure::Edge* concaveEdge = nullptr; + + // For each edge of the new face + QHHalfEdgeStructure::Edge* firstFaceEdge = face->edge; + QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge; + do { + + assert(faceEdge != nullptr); + + // If the two faces at this edge are forming a convex shape + if (testIsConvexEdge(faceEdge, epsilon)) { + + // Move to the next edge of the face + faceEdge = faceEdge->nextFaceEdge; + + continue; + } + + // The two faces at this edge are forming a concave or coplanar shape + concaveEdge = faceEdge; + break; + + } while(faceEdge != firstFaceEdge); + + // If we have found a concave or coplanar edge + if (concaveEdge != nullptr) { + + assert(concaveEdge->face == face || concaveEdge->twinEdge->face == face); + + // Merge the two faces at this edge + mergeConcaveFacesAtEdge(concaveEdge, convexHull, points, deletedFaces); + + continue; + } + } + + i++; + } +} + +// Merge two faces that are concave at a given edge +void QuickHull::mergeConcaveFacesAtEdge(QHHalfEdgeStructure::Edge* edge, QHHalfEdgeStructure& convexHull, const Array& points, + Set& deletedFaces) { + + // We merge the face next to the 'twin edge' into the face next to 'edge' + + QHHalfEdgeStructure::Face* faceToRemove = edge->twinEdge->face; + QHHalfEdgeStructure::Face* faceToKeep = edge->face; + + assert(faceToRemove->isValid()); + assert(faceToKeep->isValid()); + + QHHalfEdgeStructure::Edge* edgeBefore = edge->previousFaceEdge; + QHHalfEdgeStructure::Edge* edgeAfter = edge->nextFaceEdge; + + // Make sure the face to keep does not reference the edge to be removed + faceToKeep->edge = edge->previousFaceEdge; + + // Make sure the edges of the face to delete reference the face to keep + QHHalfEdgeStructure::Edge* firstFaceEdge = edge->twinEdge; + QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge->nextFaceEdge; + while (faceEdge != firstFaceEdge) { + + assert(faceEdge != nullptr); + + faceEdge->face = faceToKeep; + + // Move to the next edge of the face + faceEdge = faceEdge->nextFaceEdge; + }; + + assert(faceToKeep->isValid()); + + // Fix the linked-list of face edges + edge->previousFaceEdge->nextFaceEdge = edge->twinEdge->nextFaceEdge; + edge->nextFaceEdge->previousFaceEdge = edge->twinEdge->previousFaceEdge; + edge->twinEdge->previousFaceEdge->nextFaceEdge = edge->nextFaceEdge; + edge->twinEdge->nextFaceEdge->previousFaceEdge = edge->previousFaceEdge; + + assert(faceToKeep->isValid()); + + // Move the remaining closest vertices of the face to remove to the face to keep + //std::cout << "Transfer remaining closests points: " << faceToRemove->remainingClosestPoints.size() << std::endl; + faceToKeep->conflictPoints.addRange(faceToRemove->conflictPoints); + + // Remove the face + deletedFaces.add(faceToRemove); + convexHull.deleteFace(faceToRemove); + + // Remove the edges + convexHull.removeHalfEdge(edge->twinEdge); + convexHull.removeHalfEdge(edge); + + assert(faceToKeep->isValid()); + + // Recalculate the face centroid and normal to better fit its vertices (using Newell method) + faceToKeep->recalculateFace(points); + + assert(faceToKeep->edge->face == faceToKeep); + assert(edgeBefore->nextFaceEdge->previousFaceEdge == edgeBefore); + assert(edgeAfter->previousFaceEdge->nextFaceEdge == edgeAfter); + assert(edgeBefore->nextFaceEdge->face == faceToKeep); + assert(edgeAfter->previousFaceEdge->face == faceToKeep); + + // Fix topological issues (if any) that might have been created during merge + fixTopologicalIssues(convexHull, faceToKeep, points, deletedFaces); + + assert(faceToKeep->isValid()); +} + +// Fix topological issues (if any) that might have been created during faces merge +void QuickHull::fixTopologicalIssues(QHHalfEdgeStructure& convexHull, QHHalfEdgeStructure::Face* face, const Array& points, + Set& deletedFaces) { + + // Here we want to make sure that each vertex of the convex hull has at least + // three adjacent faces + + QHHalfEdgeStructure::Edge* edgeError; + + // While we can find an edge with an error (a redundant vertex) in the face + do { + + edgeError = nullptr; + + // For each vertex of the face we check if the incoming and outgoing edge have + // the same face on the opposite side + QHHalfEdgeStructure::Edge* firstInEdge = face->edge; + QHHalfEdgeStructure::Edge* inEdge = firstInEdge; + do { + + assert(inEdge != nullptr); + + QHHalfEdgeStructure::Edge* outEdge = inEdge->nextFaceEdge; + + if (inEdge->twinEdge->face == outEdge->twinEdge->face) { + edgeError = inEdge; + break; + } + + // Move to the next edge of the face + inEdge = outEdge; + } + while (inEdge != firstInEdge); + + // If we have found an edge with error (redundant vertex) + if (edgeError != nullptr) { + fixTopologicalIssueAtEdge(convexHull, face, edgeError, points, deletedFaces); + } + + } while (edgeError != nullptr); +} + +// Fix topological issue at a given edge +void QuickHull::fixTopologicalIssueAtEdge(QHHalfEdgeStructure& convexHull, QHHalfEdgeStructure::Face* face, + QHHalfEdgeStructure::Edge* inEdge, const Array& points, + Set& deletedFaces) { + + assert(inEdge->face == face); + + // If the opposite face is a triangle + if (inEdge->twinEdge->face->isTriangle()) { + + QHHalfEdgeStructure::Edge* edgeBeforeTriangle = inEdge->previousFaceEdge; + QHHalfEdgeStructure::Edge* edgeAfterTriangle = inEdge->nextFaceEdge->nextFaceEdge; + + QHHalfEdgeStructure::Face* faceToRemove = inEdge->twinEdge->face; + QHHalfEdgeStructure::Face* faceToKeep = face; + + // Make sure the face to keep does not reference the edge to be removed + faceToKeep->edge = edgeBeforeTriangle; + + // Make sure the remaining edge of the face to delete reference the face to keep + inEdge->twinEdge->nextFaceEdge->face = faceToKeep; + + // Fix the linked-list of face edges + edgeBeforeTriangle->nextFaceEdge = inEdge->twinEdge->nextFaceEdge; + inEdge->twinEdge->nextFaceEdge->previousFaceEdge = edgeBeforeTriangle; + edgeAfterTriangle->previousFaceEdge = inEdge->twinEdge->nextFaceEdge; + inEdge->twinEdge->nextFaceEdge->nextFaceEdge = edgeAfterTriangle; + + // Move the remaining closest vertices of the face to remove to the face to keep + faceToKeep->conflictPoints.addRange(faceToRemove->conflictPoints); + + // Remove the face + convexHull.deleteFace(faceToRemove); + deletedFaces.add(faceToRemove); + + QHHalfEdgeStructure::Vertex* vertexToRemove = inEdge->endVertex; + + // Remove the edges + convexHull.removeHalfEdge(inEdge->nextFaceEdge->twinEdge); + convexHull.removeHalfEdge(inEdge->nextFaceEdge); + convexHull.removeHalfEdge(inEdge->twinEdge); + convexHull.removeHalfEdge(inEdge); + + // Remove the redundant vertex + convexHull.removeVertex(vertexToRemove); + + assert(edgeBeforeTriangle->nextFaceEdge->previousFaceEdge == edgeBeforeTriangle); + assert(edgeAfterTriangle->previousFaceEdge->nextFaceEdge == edgeAfterTriangle); + assert(edgeBeforeTriangle->nextFaceEdge->face == faceToKeep); + assert(faceToKeep->edge->face == faceToKeep); + assert(edgeBeforeTriangle->nextFaceEdge->twinEdge->twinEdge == edgeBeforeTriangle->nextFaceEdge); + + // Recalculate the face centroid and normal to better fit its vertices (using Newell method) + faceToKeep->recalculateFace(points); + + assert(faceToKeep->isValid()); + } + else { // If the opposite face is not a triangle + + QHHalfEdgeStructure::Vertex* vertexToRemove = inEdge->endVertex; + QHHalfEdgeStructure::Edge* outEdgeToRemove = inEdge->nextFaceEdge; + + inEdge->twinEdge->startVertex = outEdgeToRemove->endVertex; + outEdgeToRemove->twinEdge->previousFaceEdge->nextFaceEdge = inEdge->twinEdge; + inEdge->twinEdge->previousFaceEdge = outEdgeToRemove->twinEdge->previousFaceEdge; + + inEdge->endVertex = outEdgeToRemove->endVertex; + inEdge->nextFaceEdge = outEdgeToRemove->nextFaceEdge; + inEdge->nextFaceEdge->previousFaceEdge = inEdge; + + // Make sure both faces do not reference the outgoing edge we are about to remove + face->edge = inEdge; + inEdge->twinEdge->face->edge = inEdge->twinEdge; + + // Remove the outgoing edge + convexHull.removeHalfEdge(outEdgeToRemove->twinEdge); + convexHull.removeHalfEdge(outEdgeToRemove); + + // Remove the redundant vertex + convexHull.removeVertex(vertexToRemove); + + // Recalculate the face centroid and normal to better fit its vertices (using Newell method) + face->recalculateFace(points); + inEdge->twinEdge->face->recalculateFace(points); + } +} + +// Remove duplicated vertices in the input array of points +void QuickHull::removeDuplicatedVertices(Array& points, MemoryAllocator& allocator) { + + const decimal distanceEpsilon = 0.00001f; + Array pointsToKeep(allocator, points.size()); + + // Compute the points cloud center + Vector3 center(0, 0, 0); + for (uint32 i=0; i < points.size(); i++) { + center += points[i]; + } + center /= points.size(); + + // For each input point + for (uint32 i=0; i < points.size(); i++) { + + // For each point to keep + uint32 j; + for (j=0; j < pointsToKeep.size(); j++) { + + decimal dx = std::abs(pointsToKeep[j].x - points[i].x); + decimal dy = std::abs(pointsToKeep[j].y - points[i].y); + decimal dz = std::abs(pointsToKeep[j].z - points[i].z); + + // If the points are nearly the same + if (dx < distanceEpsilon && dy < distanceEpsilon && dz < distanceEpsilon) { + + // Between the two points, we keep the one that is the furthest away from the cloud points center + if ((points[i] - center).lengthSquare() > (pointsToKeep[j] - center).lengthSquare()) { + + pointsToKeep[j] = points[i]; + } + + break; + } + } + + // If the point is not already in the array of points to keep + if (j == pointsToKeep.size()) { + + // We add it + pointsToKeep.add(points[i]); + } + } + + points.clear(); + points.addRange(pointsToKeep); +} + +// Return the index of the next vertex candidate to be added to the hull +// This method returns INVALID_VERTEX_INDEX if there is no more vertex candidate +void QuickHull::findNextVertexCandidate(Array& points, uint32& outNextVertexIndex, + QHHalfEdgeStructure& convexHull, + QHHalfEdgeStructure::Face*& outNextFace, decimal epsilon) { + + decimal maxDistance = epsilon; + uint32 maxVertexI = INVALID_VERTEX_INDEX; + outNextFace = nullptr; + outNextVertexIndex = INVALID_VERTEX_INDEX; + + // For each face + for (auto face = convexHull.getFaces(); face != nullptr; face = face->nextFace) { + + // If the face has remaining candidates points + if (face->conflictPoints.size() > 0) { + + const Vector3& faceNormal = face->normal; + + // For each remaining candidate point of the face + for (uint32 i=0; i < face->conflictPoints.size(); i++) { + + uint32 vertexIndex = face->conflictPoints[i]; + + const decimal distance = (points[vertexIndex] - face->centroid).dot(faceNormal); + assert(distance > epsilon); + + if (distance > maxDistance) { + maxDistance = distance; + outNextVertexIndex = vertexIndex; + maxVertexI = i; + outNextFace = const_cast(face); + } + } + } + } + + // Remove the vertex from the array of remaining vertices for that face + if (outNextFace != nullptr) { + outNextFace->conflictPoints.removeAt(maxVertexI); + } +} + +// Take all the orphan points to the furthest faces among the new faces +void QuickHull::associateOrphanPointsToNewFaces(Array& orphanPointsIndices, Array& newFaces, + Array& points, decimal epsilon, Set& deletedFaces) { + + // For each candidate points of the old faces + for (uint32 i=0; i < orphanPointsIndices.size(); i++) { + + findFarthestFaceForVertex(orphanPointsIndices[i], newFaces, points, epsilon, deletedFaces); + } +} + +// Find the closest face for a given vertex and add this vertex to the conflict list for this face +void QuickHull::findFarthestFaceForVertex(uint32 vertexIndex, Array& faces, Array& points, decimal epsilon, + Set& deletedFaces) { + + decimal maxDistanceToFace = epsilon; + QHHalfEdgeStructure::Face* farthestFace = nullptr; + + // For each new face + for (uint32 f=0; f < faces.size(); f++) { + + QHHalfEdgeStructure::Face* face = faces[f]; + + // If the face was deleted during merging of concave faces + if (deletedFaces.contains(face)) continue; + + const decimal distanceToFace = face->normal.dot(points[vertexIndex] - face->centroid); + + // If the point is in front the face and with a larger distance from the face + if (distanceToFace > maxDistanceToFace) { + maxDistanceToFace = distanceToFace; + farthestFace = face; + } + } + + // If we have found a farthest face + if (farthestFace != nullptr) { + + // Add the vertex to the conflict list of the face + farthestFace->conflictPoints.add(vertexIndex); + } +} + +// Compute the initial tetrahedron convex hull +bool QuickHull::computeInitialHull(Array& points, QHHalfEdgeStructure& convexHull, + Array& initialFaces, + Array& orphanPointsIndices, + MemoryAllocator& allocator, std::vector& errors) { + + // Find the extreme points on each X, Y and Z axes + + uint32 extremePointsIndices[6] = {0, 0, 0, 0, 0, 0}; + + const uint32 nbPoints = points.size(); + for (uint32 i=0; i < nbPoints; i++) { + + if (points[i].x < points[extremePointsIndices[0]].x) { + extremePointsIndices[0] = i; + } + else if (points[i].x > points[extremePointsIndices[1]].x) { + extremePointsIndices[1] = i; + } + + if (points[i].y < points[extremePointsIndices[2]].y) { + extremePointsIndices[2] = i; + } + else if (points[i].y > points[extremePointsIndices[3]].y) { + extremePointsIndices[3] = i; + } + + if (points[i].z < points[extremePointsIndices[4]].z) { + extremePointsIndices[4] = i; + } + else if (points[i].z > points[extremePointsIndices[5]].z) { + extremePointsIndices[5] = i; + } + } + + // Find the two extreme points with largest distance + + decimal maxLargestDistSquare = 0; + uint32 iMax = 0; + for (uint32 i=0; i < 3; i++) { + const decimal distSquare = (points[extremePointsIndices[i*2]] - points[extremePointsIndices[i*2+1]]).lengthSquare(); + if (distSquare > maxLargestDistSquare) { + iMax = i; + maxLargestDistSquare = distSquare; + } + } + + if (maxLargestDistSquare < MACHINE_EPSILON) { + errors.push_back(Message("Error during initial hull creation in QuickHull: vertices too close to each other")); + return false; + } + + // The pair of points that have the largest distance between them + uint32 i1 = extremePointsIndices[iMax * 2]; + uint32 i2 = extremePointsIndices[iMax * 2 + 1]; + + // Find a third point that is the furthest from line (v1, v2) + uint32 i3 = 0; + maxLargestDistSquare = 0; + Vector3 lineDirection = points[i2] - points[i1]; + lineDirection.normalize(); + for (uint32 i=0; i < nbPoints; i++) { + + Vector3 vec = points[i] - points[i1]; + const decimal distSquare = vec.cross(lineDirection).lengthSquare(); + + if (distSquare > maxLargestDistSquare) { + i3 = i; + maxLargestDistSquare = distSquare; + } + } + + if (maxLargestDistSquare < MACHINE_EPSILON) { + errors.push_back(Message("Error during initial hull creation in QuickHull: vertices too close to each other")); + return false; + } + + // Find a fourth point that has the largest distance with the v1, v2, v3 plane + + uint32 i4 = 0; + maxLargestDistSquare = 0; + Vector3 planeNormal = (points[i3] - points[i1]).cross(points[i2] - points[i1]); + for (uint32 i=0; i < nbPoints; i++) { + + Vector3 vec = points[i] - points[i1]; + const decimal distSquare = std::abs(vec.dot(planeNormal)); + + if (distSquare > maxLargestDistSquare) { + i4 = i; + maxLargestDistSquare = distSquare; + } + } + + if (maxLargestDistSquare < MACHINE_EPSILON) { + errors.push_back(Message("Error during initial hull creation in QuickHull: vertices too close to each other")); + return false; + } + + assert(i1 != i2 && i1 != i3 && i1 != i4); + assert(i2 != i1 && i2 != i3 && i2 != i4); + assert(i3 != i1 && i3 != i2 && i3 != i4); + assert(i4 != i1 && i4 != i2 && i4 != i3); + + // Test in which side of the triangle face v1,v2,v3 is the point v4 + // to know the orientation of the tetrahedron + + QHHalfEdgeStructure::Vertex* v0 = convexHull.addVertex(i1); + QHHalfEdgeStructure::Vertex* v1 = convexHull.addVertex(i2); + QHHalfEdgeStructure::Vertex* v2 = convexHull.addVertex(i3); + QHHalfEdgeStructure::Vertex* v3 = convexHull.addVertex(i4); + + Array face1Vertices(allocator); + Array face2Vertices(allocator); + Array face3Vertices(allocator); + Array face4Vertices(allocator); + + const Vector3 v1V4 = points[i4] - points[i1]; + + QHHalfEdgeStructure::Face* face0 = nullptr; + QHHalfEdgeStructure::Face* face1 = nullptr; + QHHalfEdgeStructure::Face* face2 = nullptr; + QHHalfEdgeStructure::Face* face3 = nullptr; + + // Create the tetrahedron faces (according to the orientation of the tetrahedron) + if (planeNormal.dot(v1V4) < 0) { + + face1Vertices.add(v0); face1Vertices.add(v2); face1Vertices.add(v1); + face2Vertices.add(v1); face2Vertices.add(v2); face2Vertices.add(v3); + face3Vertices.add(v0); face3Vertices.add(v1); face3Vertices.add(v3); + face4Vertices.add(v0); face4Vertices.add(v3); face4Vertices.add(v2); + + // Add vertices for each face + face0 = convexHull.addFace(face1Vertices, points, allocator); + face1 = convexHull.addFace(face2Vertices, points, allocator); + face2 = convexHull.addFace(face3Vertices, points, allocator); + face3 = convexHull.addFace(face4Vertices, points, allocator); + } + else { + + face1Vertices.add(v0); face1Vertices.add(v1); face1Vertices.add(v2); + face2Vertices.add(v1); face2Vertices.add(v3); face2Vertices.add(v2); + face3Vertices.add(v0); face3Vertices.add(v3); face3Vertices.add(v1); + face4Vertices.add(v0); face4Vertices.add(v2); face4Vertices.add(v3); + + // Add vertices for each face + face0 = convexHull.addFace(face1Vertices, points, allocator); + face1 = convexHull.addFace(face2Vertices, points, allocator); + face2 = convexHull.addFace(face3Vertices, points, allocator); + face3 = convexHull.addFace(face4Vertices, points, allocator); + } + + initialFaces.add(face0); + initialFaces.add(face1); + initialFaces.add(face2); + initialFaces.add(face3); + + orphanPointsIndices.remove(v0->externalIndex); + orphanPointsIndices.remove(v1->externalIndex); + orphanPointsIndices.remove(v2->externalIndex); + orphanPointsIndices.remove(v3->externalIndex); + + return true; +} + +// Extract the points from the array +void QuickHull::extractPoints(const VertexArray& vertexArray, Array& outArray) { + + const unsigned char* pointsStartPointer = reinterpret_cast(vertexArray.getStart()); + + if (vertexArray.getDataType() == VertexArray::DataType::VERTEX_FLOAT_TYPE) { + for (uint32 p=0; p < vertexArray.getNbVertices(); p++) { + const float* points = (float*)(pointsStartPointer + p * vertexArray.getStride()); + outArray.add(Vector3(points[0], points[1], points[2])); + } + } + else if (vertexArray.getDataType() == VertexArray::DataType::VERTEX_DOUBLE_TYPE) { + for (uint32 p=0; p < vertexArray.getNbVertices(); p++) { + const double* points = (double*)(pointsStartPointer + p * vertexArray.getStride()); + outArray.add(Vector3(points[0], points[1], points[2])); + } + } + else { + assert(false); + } +} diff --git a/src/tdme/engine/SceneConnector.cpp b/src/tdme/engine/SceneConnector.cpp index 2df07747d..cd660a048 100644 --- a/src/tdme/engine/SceneConnector.cpp +++ b/src/tdme/engine/SceneConnector.cpp @@ -1011,8 +1011,6 @@ void SceneConnector::addScene(World* world, Scene* scene, bool enable, const Vec auto heightMap = make_unique( terrainHeightVectorVerticesPerX, terreinHeightVectorVerticesPerZ, - minHeight, - maxHeight, terrain->getHeightVector().data() ); // diff --git a/src/tdme/engine/physics/Body.cpp b/src/tdme/engine/physics/Body.cpp index ad846c4ca..2bf05c818 100644 --- a/src/tdme/engine/physics/Body.cpp +++ b/src/tdme/engine/physics/Body.cpp @@ -200,6 +200,7 @@ void Body::resetColliders(vector& colliders, vectorgetMaterial().setBounciness(restitution); collider->getMaterial().setFrictionCoefficient(friction); if (type == BODYTYPE_COLLISION_STATIC || type == BODYTYPE_COLLISION_DYNAMIC) collider->setIsTrigger(true); + // collider->getMaterial().setMassDensity(volumeBoundingVolume / volumeTotal); // diff --git a/src/tdme/engine/primitives/BoundingVolume.cpp b/src/tdme/engine/primitives/BoundingVolume.cpp index 6b883e5c8..2ecf9058c 100644 --- a/src/tdme/engine/primitives/BoundingVolume.cpp +++ b/src/tdme/engine/primitives/BoundingVolume.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include #include diff --git a/src/tdme/engine/primitives/ConvexMesh.cpp b/src/tdme/engine/primitives/ConvexMesh.cpp index 13ce3bdaa..de18eb772 100644 --- a/src/tdme/engine/primitives/ConvexMesh.cpp +++ b/src/tdme/engine/primitives/ConvexMesh.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -383,12 +384,12 @@ void ConvexMesh::setScale(const Vector3& scale) { void ConvexMesh::destroyCollisionShape() { if (collisionShape == nullptr) return; this->world->physicsCommon.destroyConvexMeshShape(static_cast(collisionShape)); - this->world->physicsCommon.destroyPolyhedronMesh(polyhedronMesh); + this->world->physicsCommon.destroyConvexMesh(convexMesh); polygonVertexArray = nullptr; verticesByteBuffer = nullptr; indicesByteBuffer = nullptr; collisionShape = nullptr; - polyhedronMesh = nullptr; + convexMesh = nullptr; world = nullptr; } @@ -412,21 +413,37 @@ void ConvexMesh::createCollisionShape(World* world) { reactphysics3d::PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE, reactphysics3d::PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE ); - polyhedronMesh = world->physicsCommon.createPolyhedronMesh(polygonVertexArray.get()); - if (polyhedronMesh == nullptr) { + vector messages; + convexMesh = world->physicsCommon.createConvexMesh(*polygonVertexArray.get(), messages); + // dump messages + for (const auto& message: messages) { + auto getMessageTypeText = [](const reactphysics3d::Message& message) -> const string { + switch (message.type) { + case reactphysics3d::Message::Type::Error: + return "ERROR"; + case reactphysics3d::Message::Type::Warning: + return "WARNING"; + case reactphysics3d::Message::Type::Information: + return "INFORMATION"; + } + }; + Console::printLine("ConvexMesh::createCollisionShape(): " + getMessageTypeText(message) + ": " + message.text); + } + // + if (convexMesh == nullptr) { throw ExceptionBase("Invalid polyhedron mesh"); } // create convex mesh shape - collisionShape = world->physicsCommon.createConvexMeshShape(polyhedronMesh); + collisionShape = world->physicsCommon.createConvexMeshShape(convexMesh); } catch (Exception& exception) { Console::printLine("ConvexMesh::createCollisionShape(): an error occurred: " + string(exception.what())); if (collisionShape != nullptr) { this->world->physicsCommon.destroyConvexMeshShape(static_cast(collisionShape)); collisionShape = nullptr; } - if (polyhedronMesh != nullptr) { - this->world->physicsCommon.destroyPolyhedronMesh(polyhedronMesh); - polyhedronMesh = nullptr; + if (convexMesh != nullptr) { + this->world->physicsCommon.destroyConvexMesh(convexMesh); + convexMesh = nullptr; } polygonVertexArray = nullptr; this->world = nullptr; diff --git a/src/tdme/engine/primitives/ConvexMesh.h b/src/tdme/engine/primitives/ConvexMesh.h index ef620fe4c..eea1607a4 100644 --- a/src/tdme/engine/primitives/ConvexMesh.h +++ b/src/tdme/engine/primitives/ConvexMesh.h @@ -3,8 +3,8 @@ #include #include +#include #include -#include #include #include @@ -42,7 +42,7 @@ class tdme::engine::primitives::ConvexMesh final vector facesVerticesCount; vector indices; - reactphysics3d::PolyhedronMesh* polyhedronMesh { nullptr }; + reactphysics3d::ConvexMesh* convexMesh { nullptr }; unique_ptr polygonVertexArray; unique_ptr verticesByteBuffer; unique_ptr indicesByteBuffer; diff --git a/src/tdme/engine/primitives/HeightMap.cpp b/src/tdme/engine/primitives/HeightMap.cpp index 79f5d5292..9657e9047 100644 --- a/src/tdme/engine/primitives/HeightMap.cpp +++ b/src/tdme/engine/primitives/HeightMap.cpp @@ -1,7 +1,11 @@ #include +#include + #include +#include #include +#include #include #include @@ -19,16 +23,12 @@ using tdme::utilities::Console; HeightMap::HeightMap( int columns, int rows, - float minHeight, - float maxHeight, float* heightValues, const Vector3& scale ) { this->scale.set(scale); this->columns = columns; this->rows = rows; - this->minHeight = minHeight; - this->maxHeight = maxHeight; this->heightValues = heightValues; } @@ -43,6 +43,7 @@ void HeightMap::setScale(const Vector3& scale) { void HeightMap::destroyCollisionShape() { if (collisionShape == nullptr) return; this->world->physicsCommon.destroyHeightFieldShape(static_cast(collisionShape)); + this->world->physicsCommon.destroyHeightField(heightField); collisionShape = nullptr; world = nullptr; } @@ -52,23 +53,36 @@ void HeightMap::createCollisionShape(World* world) { Console::printLine("HeightMap::createCollisionShape(): already attached to a world."); } this->world = world; - - // - collisionShape = world->physicsCommon.createHeightFieldShape( + vector messages; + heightField = world->physicsCommon.createHeightField( columns, rows, - minHeight, - maxHeight, heightValues, - reactphysics3d::HeightFieldShape::HeightDataType::HEIGHT_FLOAT_TYPE, - 1, - 1.0f, - reactphysics3d::Vector3(scale.getX(), scale.getY(), scale.getZ()) + reactphysics3d::HeightField::HeightDataType::HEIGHT_FLOAT_TYPE, + messages + ); + // dump messages + for (const auto& message: messages) { + auto getMessageTypeText = [](const reactphysics3d::Message& message) -> const string { + switch (message.type) { + case reactphysics3d::Message::Type::Error: + return "ERROR"; + case reactphysics3d::Message::Type::Warning: + return "WARNING"; + case reactphysics3d::Message::Type::Information: + return "INFORMATION"; + } + }; + Console::printLine("HeightMap::createCollisionShape(): " + getMessageTypeText(message) + ": " + message.text); + } + // + collisionShape = world->physicsCommon.createHeightFieldShape( + heightField ); } BoundingVolume* HeightMap::clone() const { - return new HeightMap(columns, rows, minHeight, maxHeight, heightValues); + return new HeightMap(columns, rows, heightValues); } diff --git a/src/tdme/engine/primitives/HeightMap.h b/src/tdme/engine/primitives/HeightMap.h index 03d4bc8e0..aee7d1f18 100644 --- a/src/tdme/engine/primitives/HeightMap.h +++ b/src/tdme/engine/primitives/HeightMap.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -20,9 +22,8 @@ class tdme::engine::primitives::HeightMap final private: int columns; int rows; - float minHeight; - float maxHeight; float* heightValues; + reactphysics3d::HeightField* heightField { nullptr }; // overridden methods void destroyCollisionShape() override; @@ -36,16 +37,12 @@ class tdme::engine::primitives::HeightMap final * Public constructor * @param colums columns * @param rows rows - * @param minHeight min height - * @param maxHeight max height * @param heightValues height values which will not be copied * @param scale scale */ HeightMap( int columns, int rows, - float minHeight, - float maxHeight, float* heightValues, const Vector3& scale = Vector3(1.0f, 1.0f, 1.0f) ); diff --git a/src/tdme/engine/primitives/TerrainMesh.cpp b/src/tdme/engine/primitives/TerrainMesh.cpp index 2af3dc3c5..1629c57fc 100644 --- a/src/tdme/engine/primitives/TerrainMesh.cpp +++ b/src/tdme/engine/primitives/TerrainMesh.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -104,8 +105,23 @@ void TerrainMesh::createCollisionShape(World* world) { ); // add the triangle vertex array to the triangle mesh - triangleMesh = world->physicsCommon.createTriangleMesh(); - triangleMesh->addSubpart(triangleVertexArray.get()); + vector messages; + triangleMesh = world->physicsCommon.createTriangleMesh(*triangleVertexArray.get(), messages); + + // dump messages + for (const auto& message: messages) { + auto getMessageTypeText = [](const reactphysics3d::Message& message) -> const string { + switch (message.type) { + case reactphysics3d::Message::Type::Error: + return "ERROR"; + case reactphysics3d::Message::Type::Warning: + return "WARNING"; + case reactphysics3d::Message::Type::Information: + return "INFORMATION"; + } + }; + Console::printLine("TerrainMesh::createCollisionShape(): " + getMessageTypeText(message) + ": " + message.text); + } // create the concave mesh shape collisionShape = world->physicsCommon.createConcaveMeshShape(triangleMesh);