diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index a685e3872..0acc65ce2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -8,7 +8,10 @@ on: - master - develop pull_request: - release: + workflow_call: + secrets: + codecov_token: + required: true # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: @@ -306,8 +309,8 @@ jobs: - name: Upload coverage to Codecov if: ${{ matrix.config.coverage }} - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 with: fail_ci_if_error: true + token: ${{ secrets.codecov_token }} verbose: false - diff --git a/.github/workflows/publish-documentation.yml b/.github/workflows/publish-documentation.yml new file mode 100644 index 000000000..8fffcc600 --- /dev/null +++ b/.github/workflows/publish-documentation.yml @@ -0,0 +1,49 @@ +# Build the Doxygen documentation (User manual and API reference) and publish it to www.reactphysics3d.com website +name: Publish Documentation + +# Controls when the action will run. Triggers the workflow on push +on: + push: + branches: + - master + +jobs: + documentation: + name: Build/Publish Documentation + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install cmake doxygen texlive-latex-extra ghostscript + cmake --version + doxygen --version + + - name: CMake Configure + shell: bash + run: | + mkdir build + cmake \ + -S . \ + -B build \ + -DRP3D_COMPILE_LIBRARY=False \ + -DRP3D_GENERATE_DOCUMENTATION=True \ + -DRP3D_COMPILE_TESTS=False \ + -DRP3D_COMPILE_TESTBED=False + + - name: Build Documentation + shell: bash + run: cmake --build build/ + + - name: Publish to FTP of website + uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + with: + server: ftp.cluster010.ovh.net + username: ${{ secrets.ftp_username }} + password: ${{ secrets.ftp_password }} + local-dir: ./build/documentation/html/ + server-dir: /wwwrp3d/documentation/ + dangerous-clean-slate: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..547f0614b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +# Build, run tests and create a new release using the CHANGELOG.md file as release notes +name: release + +# Controls when the action will run. Triggers the workflow on push with a release tag +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + # Reuse the workflow to build and test the library + build-and-test: + name: Build and Test + uses: ./.github/workflows/build-and-test.yml + secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} + + # Create a new release + create-release: + name: Create Release + runs-on: ubuntu-latest + needs: [build-and-test] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build Documentation + uses: docker://antonyurchenko/git-release:v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DRAFT_RELEASE: "false" + PRE_RELEASE: "false" + ALLOW_EMPTY_CHANGELOG: "false" + + + diff --git a/.gitmodules b/.gitmodules index c004e876a..d9af92601 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = testbed/extern/nanogui url = https://github.com/mitsuba-renderer/nanogui.git ignore = dirty +[submodule "documentation/doxygen-awesome-css"] + path = documentation/doxygen-awesome-css + url = https://github.com/jothepro/doxygen-awesome-css.git diff --git a/CHANGELOG.md b/CHANGELOG.md index cd1feb13f..88e922aa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,57 +1,68 @@ # Changelog -## Version 0.10.0 (March 10, 2024) +## [0.10.1] - 2024-06-23 -### Added +### 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 - - 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 +## [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 +- 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 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 +- 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 @@ -69,53 +80,53 @@ - 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) +- Issue with edge vs edge collision detection for BoxShape, ConvexMeshShape, ConcaveMeshShape and HeightFieldShape (SAT algorithm) - Compilation error on Clang 19 -## Version 0.9.0 (January 4, 2022) +## [0.9.0] - 2022-01-04 -### Added +### Changed - - 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 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) -### Changed +### Added - - 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 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 @@ -131,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 @@ -140,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/CMakeLists.txt b/CMakeLists.txt index e72ee2084..4282f7096 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,12 @@ # Minimum cmake version required cmake_minimum_required(VERSION 3.10) +# Extract ReactPhysics3D version number from VERSION file +file(READ VERSION RP3D_VERSION) +string(STRIP ${RP3D_VERSION} RP3D_VERSION) + # Project configuration -project(ReactPhysics3D VERSION 0.10.0 LANGUAGES CXX) +project(ReactPhysics3D VERSION "${RP3D_VERSION}" LANGUAGES CXX) # In order to install libraries into correct locations on all platforms. include(GNUInstallDirs) @@ -28,9 +32,11 @@ set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) enable_testing() # Options +option(RP3D_COMPILE_LIBRARY "Select this if you want to build the ReactPhysics3D library" ON) option(RP3D_COMPILE_TESTBED "Select this if you want to build the testbed application with demos" OFF) option(RP3D_COMPILE_TESTS "Select this if you want to build the unit tests" OFF) option(RP3D_PROFILING_ENABLED "Select this if you want to compile for performanace profiling" OFF) +option(RP3D_GENERATE_DOCUMENTATION "Select this if you want to generate the project documentation" OFF) option(RP3D_CODE_COVERAGE_ENABLED "Select this if you need to build for code coverage calculation" OFF) option(RP3D_DOUBLE_PRECISION_ENABLED "Select this if you want to compile using double precision floating values" OFF) @@ -42,227 +48,229 @@ if(RP3D_CODE_COVERAGE_ENABLED) # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") endif() -# Headers files -set (REACTPHYSICS3D_HEADERS - "include/reactphysics3d/configuration.h" - "include/reactphysics3d/decimal.h" - "include/reactphysics3d/reactphysics3d.h" - "include/reactphysics3d/body/Body.h" - "include/reactphysics3d/body/RigidBody.h" - "include/reactphysics3d/collision/ContactPointInfo.h" - "include/reactphysics3d/collision/ContactManifoldInfo.h" - "include/reactphysics3d/collision/ContactPair.h" - "include/reactphysics3d/collision/broadphase/DynamicAABBTree.h" - "include/reactphysics3d/collision/narrowphase/CollisionDispatch.h" - "include/reactphysics3d/collision/narrowphase/GJK/VoronoiSimplex.h" - "include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h" - "include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h" - "include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h" - "include/reactphysics3d/collision/shapes/AABB.h" - "include/reactphysics3d/collision/shapes/ConvexShape.h" - "include/reactphysics3d/collision/shapes/ConvexPolyhedronShape.h" - "include/reactphysics3d/collision/shapes/ConcaveShape.h" - "include/reactphysics3d/collision/shapes/BoxShape.h" - "include/reactphysics3d/collision/shapes/CapsuleShape.h" - "include/reactphysics3d/collision/shapes/CollisionShape.h" - "include/reactphysics3d/collision/shapes/ConvexMeshShape.h" - "include/reactphysics3d/collision/shapes/SphereShape.h" - "include/reactphysics3d/collision/shapes/TriangleShape.h" - "include/reactphysics3d/collision/shapes/ConcaveMeshShape.h" - "include/reactphysics3d/collision/shapes/HeightFieldShape.h" - "include/reactphysics3d/collision/RaycastInfo.h" - "include/reactphysics3d/collision/Collider.h" - "include/reactphysics3d/collision/TriangleVertexArray.h" - "include/reactphysics3d/collision/PolygonVertexArray.h" - "include/reactphysics3d/collision/VertexArray.h" - "include/reactphysics3d/collision/TriangleMesh.h" - "include/reactphysics3d/collision/HeightField.h" - "include/reactphysics3d/collision/ConvexMesh.h" - "include/reactphysics3d/collision/HalfEdgeStructure.h" - "include/reactphysics3d/collision/ContactManifold.h" - "include/reactphysics3d/constraint/BallAndSocketJoint.h" - "include/reactphysics3d/constraint/ContactPoint.h" - "include/reactphysics3d/constraint/FixedJoint.h" - "include/reactphysics3d/constraint/HingeJoint.h" - "include/reactphysics3d/constraint/Joint.h" - "include/reactphysics3d/constraint/SliderJoint.h" - "include/reactphysics3d/engine/Entity.h" - "include/reactphysics3d/engine/EntityManager.h" - "include/reactphysics3d/engine/PhysicsCommon.h" - "include/reactphysics3d/systems/ConstraintSolverSystem.h" - "include/reactphysics3d/systems/ContactSolverSystem.h" - "include/reactphysics3d/systems/DynamicsSystem.h" - "include/reactphysics3d/systems/CollisionDetectionSystem.h" - "include/reactphysics3d/systems/SolveBallAndSocketJointSystem.h" - "include/reactphysics3d/systems/SolveFixedJointSystem.h" - "include/reactphysics3d/systems/SolveHingeJointSystem.h" - "include/reactphysics3d/systems/SolveSliderJointSystem.h" - "include/reactphysics3d/engine/PhysicsWorld.h" - "include/reactphysics3d/engine/EventListener.h" - "include/reactphysics3d/engine/Island.h" - "include/reactphysics3d/engine/Islands.h" - "include/reactphysics3d/engine/Material.h" - "include/reactphysics3d/engine/OverlappingPairs.h" - "include/reactphysics3d/systems/BroadPhaseSystem.h" - "include/reactphysics3d/components/Components.h" - "include/reactphysics3d/components/BodyComponents.h" - "include/reactphysics3d/components/RigidBodyComponents.h" - "include/reactphysics3d/components/TransformComponents.h" - "include/reactphysics3d/components/ColliderComponents.h" - "include/reactphysics3d/components/JointComponents.h" - "include/reactphysics3d/components/BallAndSocketJointComponents.h" - "include/reactphysics3d/components/FixedJointComponents.h" - "include/reactphysics3d/components/HingeJointComponents.h" - "include/reactphysics3d/components/SliderJointComponents.h" - "include/reactphysics3d/collision/CollisionCallback.h" - "include/reactphysics3d/collision/OverlapCallback.h" - "include/reactphysics3d/mathematics/mathematics.h" - "include/reactphysics3d/mathematics/mathematics_common.h" - "include/reactphysics3d/mathematics/mathematics_functions.h" - "include/reactphysics3d/mathematics/Matrix2x2.h" - "include/reactphysics3d/mathematics/Matrix3x3.h" - "include/reactphysics3d/mathematics/Quaternion.h" - "include/reactphysics3d/mathematics/Transform.h" - "include/reactphysics3d/mathematics/Vector2.h" - "include/reactphysics3d/mathematics/Vector3.h" - "include/reactphysics3d/mathematics/Ray.h" - "include/reactphysics3d/memory/MemoryAllocator.h" - "include/reactphysics3d/memory/PoolAllocator.h" - "include/reactphysics3d/memory/SingleFrameAllocator.h" - "include/reactphysics3d/memory/HeapAllocator.h" - "include/reactphysics3d/memory/DefaultAllocator.h" - "include/reactphysics3d/memory/MemoryManager.h" - "include/reactphysics3d/containers/Stack.h" - "include/reactphysics3d/containers/LinkedList.h" - "include/reactphysics3d/containers/Array.h" - "include/reactphysics3d/containers/Map.h" - "include/reactphysics3d/containers/Set.h" - "include/reactphysics3d/containers/Pair.h" - "include/reactphysics3d/containers/Deque.h" - "include/reactphysics3d/utils/Profiler.h" - "include/reactphysics3d/utils/Logger.h" - "include/reactphysics3d/utils/Message.h" - "include/reactphysics3d/utils/DefaultLogger.h" - "include/reactphysics3d/utils/DebugRenderer.h" - "include/reactphysics3d/utils/quickhull/QuickHull.h" - "include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h" -) +if(RP3D_COMPILE_LIBRARY) + # Headers files + set (REACTPHYSICS3D_HEADERS + "include/reactphysics3d/configuration.h" + "include/reactphysics3d/decimal.h" + "include/reactphysics3d/reactphysics3d.h" + "include/reactphysics3d/body/Body.h" + "include/reactphysics3d/body/RigidBody.h" + "include/reactphysics3d/collision/ContactPointInfo.h" + "include/reactphysics3d/collision/ContactManifoldInfo.h" + "include/reactphysics3d/collision/ContactPair.h" + "include/reactphysics3d/collision/broadphase/DynamicAABBTree.h" + "include/reactphysics3d/collision/narrowphase/CollisionDispatch.h" + "include/reactphysics3d/collision/narrowphase/GJK/VoronoiSimplex.h" + "include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h" + "include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h" + "include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h" + "include/reactphysics3d/collision/shapes/AABB.h" + "include/reactphysics3d/collision/shapes/ConvexShape.h" + "include/reactphysics3d/collision/shapes/ConvexPolyhedronShape.h" + "include/reactphysics3d/collision/shapes/ConcaveShape.h" + "include/reactphysics3d/collision/shapes/BoxShape.h" + "include/reactphysics3d/collision/shapes/CapsuleShape.h" + "include/reactphysics3d/collision/shapes/CollisionShape.h" + "include/reactphysics3d/collision/shapes/ConvexMeshShape.h" + "include/reactphysics3d/collision/shapes/SphereShape.h" + "include/reactphysics3d/collision/shapes/TriangleShape.h" + "include/reactphysics3d/collision/shapes/ConcaveMeshShape.h" + "include/reactphysics3d/collision/shapes/HeightFieldShape.h" + "include/reactphysics3d/collision/RaycastInfo.h" + "include/reactphysics3d/collision/Collider.h" + "include/reactphysics3d/collision/TriangleVertexArray.h" + "include/reactphysics3d/collision/PolygonVertexArray.h" + "include/reactphysics3d/collision/VertexArray.h" + "include/reactphysics3d/collision/TriangleMesh.h" + "include/reactphysics3d/collision/HeightField.h" + "include/reactphysics3d/collision/ConvexMesh.h" + "include/reactphysics3d/collision/HalfEdgeStructure.h" + "include/reactphysics3d/collision/ContactManifold.h" + "include/reactphysics3d/constraint/BallAndSocketJoint.h" + "include/reactphysics3d/constraint/ContactPoint.h" + "include/reactphysics3d/constraint/FixedJoint.h" + "include/reactphysics3d/constraint/HingeJoint.h" + "include/reactphysics3d/constraint/Joint.h" + "include/reactphysics3d/constraint/SliderJoint.h" + "include/reactphysics3d/engine/Entity.h" + "include/reactphysics3d/engine/EntityManager.h" + "include/reactphysics3d/engine/PhysicsCommon.h" + "include/reactphysics3d/systems/ConstraintSolverSystem.h" + "include/reactphysics3d/systems/ContactSolverSystem.h" + "include/reactphysics3d/systems/DynamicsSystem.h" + "include/reactphysics3d/systems/CollisionDetectionSystem.h" + "include/reactphysics3d/systems/SolveBallAndSocketJointSystem.h" + "include/reactphysics3d/systems/SolveFixedJointSystem.h" + "include/reactphysics3d/systems/SolveHingeJointSystem.h" + "include/reactphysics3d/systems/SolveSliderJointSystem.h" + "include/reactphysics3d/engine/PhysicsWorld.h" + "include/reactphysics3d/engine/EventListener.h" + "include/reactphysics3d/engine/Island.h" + "include/reactphysics3d/engine/Islands.h" + "include/reactphysics3d/engine/Material.h" + "include/reactphysics3d/engine/OverlappingPairs.h" + "include/reactphysics3d/systems/BroadPhaseSystem.h" + "include/reactphysics3d/components/Components.h" + "include/reactphysics3d/components/BodyComponents.h" + "include/reactphysics3d/components/RigidBodyComponents.h" + "include/reactphysics3d/components/TransformComponents.h" + "include/reactphysics3d/components/ColliderComponents.h" + "include/reactphysics3d/components/JointComponents.h" + "include/reactphysics3d/components/BallAndSocketJointComponents.h" + "include/reactphysics3d/components/FixedJointComponents.h" + "include/reactphysics3d/components/HingeJointComponents.h" + "include/reactphysics3d/components/SliderJointComponents.h" + "include/reactphysics3d/collision/CollisionCallback.h" + "include/reactphysics3d/collision/OverlapCallback.h" + "include/reactphysics3d/mathematics/mathematics.h" + "include/reactphysics3d/mathematics/mathematics_common.h" + "include/reactphysics3d/mathematics/mathematics_functions.h" + "include/reactphysics3d/mathematics/Matrix2x2.h" + "include/reactphysics3d/mathematics/Matrix3x3.h" + "include/reactphysics3d/mathematics/Quaternion.h" + "include/reactphysics3d/mathematics/Transform.h" + "include/reactphysics3d/mathematics/Vector2.h" + "include/reactphysics3d/mathematics/Vector3.h" + "include/reactphysics3d/mathematics/Ray.h" + "include/reactphysics3d/memory/MemoryAllocator.h" + "include/reactphysics3d/memory/PoolAllocator.h" + "include/reactphysics3d/memory/SingleFrameAllocator.h" + "include/reactphysics3d/memory/HeapAllocator.h" + "include/reactphysics3d/memory/DefaultAllocator.h" + "include/reactphysics3d/memory/MemoryManager.h" + "include/reactphysics3d/containers/Stack.h" + "include/reactphysics3d/containers/LinkedList.h" + "include/reactphysics3d/containers/Array.h" + "include/reactphysics3d/containers/Map.h" + "include/reactphysics3d/containers/Set.h" + "include/reactphysics3d/containers/Pair.h" + "include/reactphysics3d/containers/Deque.h" + "include/reactphysics3d/utils/Profiler.h" + "include/reactphysics3d/utils/Logger.h" + "include/reactphysics3d/utils/Message.h" + "include/reactphysics3d/utils/DefaultLogger.h" + "include/reactphysics3d/utils/DebugRenderer.h" + "include/reactphysics3d/utils/quickhull/QuickHull.h" + "include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h" + ) -# Source files -set (REACTPHYSICS3D_SOURCES - "src/body/Body.cpp" - "src/body/RigidBody.cpp" - "src/collision/broadphase/DynamicAABBTree.cpp" - "src/collision/narrowphase/CollisionDispatch.cpp" - "src/collision/narrowphase/GJK/VoronoiSimplex.cpp" - "src/collision/narrowphase/GJK/GJKAlgorithm.cpp" - "src/collision/narrowphase/SAT/SATAlgorithm.cpp" - "src/collision/narrowphase/SphereVsSphereAlgorithm.cpp" - "src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp" - "src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp" - "src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp" - "src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp" - "src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp" - "src/collision/narrowphase/NarrowPhaseInput.cpp" - "src/collision/narrowphase/NarrowPhaseInfoBatch.cpp" - "src/collision/shapes/AABB.cpp" - "src/collision/shapes/ConvexShape.cpp" - "src/collision/shapes/ConvexPolyhedronShape.cpp" - "src/collision/shapes/ConcaveShape.cpp" - "src/collision/shapes/BoxShape.cpp" - "src/collision/shapes/CapsuleShape.cpp" - "src/collision/shapes/CollisionShape.cpp" - "src/collision/shapes/ConvexMeshShape.cpp" - "src/collision/shapes/SphereShape.cpp" - "src/collision/shapes/TriangleShape.cpp" - "src/collision/shapes/ConcaveMeshShape.cpp" - "src/collision/shapes/HeightFieldShape.cpp" - "src/collision/RaycastInfo.cpp" - "src/collision/Collider.cpp" - "src/collision/TriangleVertexArray.cpp" - "src/collision/PolygonVertexArray.cpp" - "src/collision/VertexArray.cpp" - "src/collision/TriangleMesh.cpp" - "src/collision/HeightField.cpp" - "src/collision/ConvexMesh.cpp" - "src/collision/HalfEdgeStructure.cpp" - "src/collision/ContactManifold.cpp" - "src/constraint/BallAndSocketJoint.cpp" - "src/constraint/ContactPoint.cpp" - "src/constraint/FixedJoint.cpp" - "src/constraint/HingeJoint.cpp" - "src/constraint/Joint.cpp" - "src/constraint/SliderJoint.cpp" - "src/engine/PhysicsCommon.cpp" - "src/systems/ConstraintSolverSystem.cpp" - "src/systems/ContactSolverSystem.cpp" - "src/systems/DynamicsSystem.cpp" - "src/systems/CollisionDetectionSystem.cpp" - "src/systems/SolveBallAndSocketJointSystem.cpp" - "src/systems/SolveFixedJointSystem.cpp" - "src/systems/SolveHingeJointSystem.cpp" - "src/systems/SolveSliderJointSystem.cpp" - "src/engine/PhysicsWorld.cpp" - "src/engine/Island.cpp" - "src/engine/Material.cpp" - "src/engine/OverlappingPairs.cpp" - "src/engine/Entity.cpp" - "src/engine/EntityManager.cpp" - "src/systems/BroadPhaseSystem.cpp" - "src/components/Components.cpp" - "src/components/BodyComponents.cpp" - "src/components/RigidBodyComponents.cpp" - "src/components/TransformComponents.cpp" - "src/components/ColliderComponents.cpp" - "src/components/JointComponents.cpp" - "src/components/BallAndSocketJointComponents.cpp" - "src/components/FixedJointComponents.cpp" - "src/components/HingeJointComponents.cpp" - "src/components/SliderJointComponents.cpp" - "src/collision/CollisionCallback.cpp" - "src/collision/OverlapCallback.cpp" - "src/mathematics/Matrix2x2.cpp" - "src/mathematics/Matrix3x3.cpp" - "src/mathematics/Quaternion.cpp" - "src/mathematics/Transform.cpp" - "src/mathematics/Vector2.cpp" - "src/mathematics/Vector3.cpp" - "src/memory/PoolAllocator.cpp" - "src/memory/SingleFrameAllocator.cpp" - "src/memory/HeapAllocator.cpp" - "src/memory/MemoryManager.cpp" - "src/memory/MemoryAllocator.cpp" - "src/utils/Profiler.cpp" - "src/utils/DefaultLogger.cpp" - "src/utils/DebugRenderer.cpp" - "src/utils/quickhull/QuickHull.cpp" - "src/utils/quickhull/QHHalfEdgeStructure.cpp" -) + # Source files + set (REACTPHYSICS3D_SOURCES + "src/body/Body.cpp" + "src/body/RigidBody.cpp" + "src/collision/broadphase/DynamicAABBTree.cpp" + "src/collision/narrowphase/CollisionDispatch.cpp" + "src/collision/narrowphase/GJK/VoronoiSimplex.cpp" + "src/collision/narrowphase/GJK/GJKAlgorithm.cpp" + "src/collision/narrowphase/SAT/SATAlgorithm.cpp" + "src/collision/narrowphase/SphereVsSphereAlgorithm.cpp" + "src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp" + "src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp" + "src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp" + "src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp" + "src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp" + "src/collision/narrowphase/NarrowPhaseInput.cpp" + "src/collision/narrowphase/NarrowPhaseInfoBatch.cpp" + "src/collision/shapes/AABB.cpp" + "src/collision/shapes/ConvexShape.cpp" + "src/collision/shapes/ConvexPolyhedronShape.cpp" + "src/collision/shapes/ConcaveShape.cpp" + "src/collision/shapes/BoxShape.cpp" + "src/collision/shapes/CapsuleShape.cpp" + "src/collision/shapes/CollisionShape.cpp" + "src/collision/shapes/ConvexMeshShape.cpp" + "src/collision/shapes/SphereShape.cpp" + "src/collision/shapes/TriangleShape.cpp" + "src/collision/shapes/ConcaveMeshShape.cpp" + "src/collision/shapes/HeightFieldShape.cpp" + "src/collision/RaycastInfo.cpp" + "src/collision/Collider.cpp" + "src/collision/TriangleVertexArray.cpp" + "src/collision/PolygonVertexArray.cpp" + "src/collision/VertexArray.cpp" + "src/collision/TriangleMesh.cpp" + "src/collision/HeightField.cpp" + "src/collision/ConvexMesh.cpp" + "src/collision/HalfEdgeStructure.cpp" + "src/collision/ContactManifold.cpp" + "src/constraint/BallAndSocketJoint.cpp" + "src/constraint/ContactPoint.cpp" + "src/constraint/FixedJoint.cpp" + "src/constraint/HingeJoint.cpp" + "src/constraint/Joint.cpp" + "src/constraint/SliderJoint.cpp" + "src/engine/PhysicsCommon.cpp" + "src/systems/ConstraintSolverSystem.cpp" + "src/systems/ContactSolverSystem.cpp" + "src/systems/DynamicsSystem.cpp" + "src/systems/CollisionDetectionSystem.cpp" + "src/systems/SolveBallAndSocketJointSystem.cpp" + "src/systems/SolveFixedJointSystem.cpp" + "src/systems/SolveHingeJointSystem.cpp" + "src/systems/SolveSliderJointSystem.cpp" + "src/engine/PhysicsWorld.cpp" + "src/engine/Island.cpp" + "src/engine/Material.cpp" + "src/engine/OverlappingPairs.cpp" + "src/engine/Entity.cpp" + "src/engine/EntityManager.cpp" + "src/systems/BroadPhaseSystem.cpp" + "src/components/Components.cpp" + "src/components/BodyComponents.cpp" + "src/components/RigidBodyComponents.cpp" + "src/components/TransformComponents.cpp" + "src/components/ColliderComponents.cpp" + "src/components/JointComponents.cpp" + "src/components/BallAndSocketJointComponents.cpp" + "src/components/FixedJointComponents.cpp" + "src/components/HingeJointComponents.cpp" + "src/components/SliderJointComponents.cpp" + "src/collision/CollisionCallback.cpp" + "src/collision/OverlapCallback.cpp" + "src/mathematics/Matrix2x2.cpp" + "src/mathematics/Matrix3x3.cpp" + "src/mathematics/Quaternion.cpp" + "src/mathematics/Transform.cpp" + "src/mathematics/Vector2.cpp" + "src/mathematics/Vector3.cpp" + "src/memory/PoolAllocator.cpp" + "src/memory/SingleFrameAllocator.cpp" + "src/memory/HeapAllocator.cpp" + "src/memory/MemoryManager.cpp" + "src/memory/MemoryAllocator.cpp" + "src/utils/Profiler.cpp" + "src/utils/DefaultLogger.cpp" + "src/utils/DebugRenderer.cpp" + "src/utils/quickhull/QuickHull.cpp" + "src/utils/quickhull/QHHalfEdgeStructure.cpp" + ) -# Create the library -add_library(reactphysics3d ${REACTPHYSICS3D_HEADERS} ${REACTPHYSICS3D_SOURCES}) + # Create the library + add_library(reactphysics3d ${REACTPHYSICS3D_HEADERS} ${REACTPHYSICS3D_SOURCES}) -# Creates an Alias Target, such that "ReactPhysics3D::reactphysics3d" can be used -# to refer to "reactphysics3d" in subsequent commands -add_library(ReactPhysics3D::reactphysics3d ALIAS reactphysics3d) + # Creates an Alias Target, such that "ReactPhysics3D::reactphysics3d" can be used + # to refer to "reactphysics3d" in subsequent commands + add_library(ReactPhysics3D::reactphysics3d ALIAS reactphysics3d) -# C++11 compiler features -target_compile_features(reactphysics3d PUBLIC cxx_std_17) -set_target_properties(reactphysics3d PROPERTIES CXX_EXTENSIONS OFF) + # C++11 compiler features + target_compile_features(reactphysics3d PUBLIC cxx_std_17) + set_target_properties(reactphysics3d PROPERTIES CXX_EXTENSIONS OFF) -# Library headers -target_include_directories(reactphysics3d PUBLIC - $ - $ -) + # Library headers + target_include_directories(reactphysics3d PUBLIC + $ + $ + ) +endif() # If we need to compile the testbed application if(RP3D_COMPILE_TESTBED) @@ -274,6 +282,11 @@ if(RP3D_COMPILE_TESTS) add_subdirectory(test/) endif() +# Documentation generation +if(RP3D_GENERATE_DOCUMENTATION) + add_subdirectory(documentation/) +endif() + # Enable profiling if necessary if(RP3D_PROFILING_ENABLED) target_compile_definitions(reactphysics3d PUBLIC IS_RP3D_PROFILING_ENABLED) @@ -284,52 +297,58 @@ if(RP3D_DOUBLE_PRECISION_ENABLED) target_compile_definitions(reactphysics3d PUBLIC IS_RP3D_DOUBLE_PRECISION_ENABLED) endif() -# Version number and soname for the library -set_target_properties(reactphysics3d PROPERTIES - VERSION "0.10.0" - SOVERSION "0.10" -) +if(RP3D_COMPILE_LIBRARY) -# Install target (install library only, not headers) -install(TARGETS reactphysics3d - EXPORT reactphysics3d-targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) + # Extract SOVERSION from VERSION number (for instance extract 0.10 from 0.10.0) + string(REGEX MATCH "^[0-9]+\.[0-9]+" RP3D_SOVERSION "${RP3D_VERSION}") -# Install the headers separately (because INSTALL(TARGETS ... PUBLIC_HEADER DESTINATION ...) does -# not support subfolders -install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + # Version number and soname for the library + set_target_properties(reactphysics3d PROPERTIES + VERSION "${RP3D_VERSION}" + SOVERSION "${RP3D_SO_VERSION}" + ) -# This is required so that the exported target has the name ReactPhysics3D and not reactphysics3d -set_target_properties(reactphysics3d PROPERTIES EXPORT_NAME ReactPhysics3D) + # Install target (install library only, not headers) + install(TARGETS reactphysics3d + EXPORT reactphysics3d-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) -# Give CMake access to the version number of the library so that the user is able to use the find_package() function -# with a given version number to select the version of the library he/she wants to use. This will create a -# "ReactPhysics3DConfigVersion.cmake" file that will later be copied into the install destination folder (see below) -include(CMakePackageConfigHelpers) -write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/ReactPhysics3DConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY AnyNewerVersion -) + # Install the headers separately (because INSTALL(TARGETS ... PUBLIC_HEADER DESTINATION ...) does + # not support subfolders + install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -# When the user runs "make install", this will export the targets into -# a "ReactPhysics3DConfig.cmake" file in the install destination -install(EXPORT reactphysics3d-targets - FILE - ReactPhysics3DConfig.cmake - NAMESPACE - ReactPhysics3D:: - DESTINATION - ${CMAKE_INSTALL_LIBDIR}/cmake/ReactPhysics3D -) + # This is required so that the exported target has the name ReactPhysics3D and not reactphysics3d + set_target_properties(reactphysics3d PROPERTIES EXPORT_NAME ReactPhysics3D) -# When the user runs "make install", this will copy the "ReactPhysics3DConfigVersion.cmake" file -# we have previously created into the install destination -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/ReactPhysics3DConfigVersion.cmake - DESTINATION lib/cmake/ReactPhysics3D -) + # Give CMake access to the version number of the library so that the user is able to use the find_package() function + # with a given version number to select the version of the library he/she wants to use. This will create a + # "ReactPhysics3DConfigVersion.cmake" file that will later be copied into the install destination folder (see below) + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/ReactPhysics3DConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion + ) + # When the user runs "make install", this will export the targets into + # a "ReactPhysics3DConfig.cmake" file in the install destination + install(EXPORT reactphysics3d-targets + FILE + ReactPhysics3DConfig.cmake + NAMESPACE + ReactPhysics3D:: + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/ReactPhysics3D + ) + + # When the user runs "make install", this will copy the "ReactPhysics3DConfigVersion.cmake" file + # we have previously created into the install destination + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/ReactPhysics3DConfigVersion.cmake + DESTINATION lib/cmake/ReactPhysics3D + ) + +endif() diff --git a/GenerateNewVersion.py b/GenerateNewVersion.py index 7293aaeb4..055faa550 100644 --- a/GenerateNewVersion.py +++ b/GenerateNewVersion.py @@ -40,10 +40,6 @@ def findReplaceText(directory, findRegex, substituteExpr, filePattern): findReplaceText("./", r'([ \t]VERSION[ \t]+)"[\d\.]+"', r'\g<1>"' + newVersion + '"', "CMakeLists.txt") print("Version number has been updated in CMakeLists.txt file") -# Update the RP3D version number in the documentation/API/Doxyfile file -findReplaceText("documentation/API/", r'(PROJECT_NUMBER[ \t]+=[ \t]+)"[\d\.]+"', r'\g<1>"' + newVersion + '"', "Doxyfile") -print("Version number has been updated in documentation/API/Doxyfile file") - # Update the RP3D version number in the documentation/UserManual/title.tex file findReplaceText("documentation/UserManual/", r'(Version:[\s]+)[\d\.]+', r'\g<1>' + newVersion, "title.tex") print("Version number has been updated in documentation/UserManual/title.tex file") @@ -64,5 +60,3 @@ def findReplaceText(directory, findRegex, substituteExpr, filePattern): findReplaceText("include/", '(Copyright ' + re.escape("(c)") + r' 2010-)[\d]+', r'\g<1>' + str(date.today().year), "*.h") findReplaceText("src/", '(Copyright ' + re.escape("(c)") + r' 2010-)[\d]+', r'\g<1>' + str(date.today().year), "*.cpp") print("Copyright date in license has been updated in all source code files") - -print("WARNING: Do not forget to manually update the SOVERSION number in the CMakeLists.txt file") diff --git a/VERSION b/VERSION index 78bc1abd1..571215736 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.10.0 +0.10.1 diff --git a/documentation/API/ReactPhysics3DLogo.png b/documentation/API/ReactPhysics3DLogo.png deleted file mode 100644 index 12b7ceb4d..000000000 Binary files a/documentation/API/ReactPhysics3DLogo.png and /dev/null differ diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt new file mode 100644 index 000000000..e1bd9e49d --- /dev/null +++ b/documentation/CMakeLists.txt @@ -0,0 +1,55 @@ + +# ---------- DOXYGEN ---------- # + +# Find the installed doxygen executable +find_package(Doxygen REQUIRED) +find_package(Git QUIET) + +# ---- Make sure to recursively clone all the git submodules for external libraries (doxygen-awesome) --- # +if(GIT_FOUND) + if (EXISTS "${CMAKE_SOURCE_DIR}/.git") + # Update submodules as needed + option(CLONE_DOCUMENTATION_GIT_SUBMODULES "Check submodules during build" ON) + if(CLONE_DOCUMENTATION_GIT_SUBMODULES) + message(STATUS "Documentation Git Submodules update") + message(STATUS "Working directory ${CMAKE_CURRENT_SOURCE_DIR}") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + endif() + else() + message(FATAL_ERROR "This is not a Git repository. In order to build the documentation, you need to clone the ReactPhysics3D repository.") + endif() +else() + message(FATAL_ERROR "Git has not been found on your system. This is necessary to build the documentation because git submodules command is used to get the documentation dependencies (doxygen-awesome library). You need to fill in the GIT_EXECUTABLE CMake variable with the path to the Git executable on your system to continue.") +endif() + +# ---------- Doxygen --------- # + +# Set variables +set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/html/index.html) +set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) +set(DOXYFILE_OUT ${DOXYGEN_OUTPUT_DIR}/Doxyfile) + +# Replace variables inside @@ with the current values in the Doxyfile.in and export to Doxyfile +configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) + +# Copy the "doxygen-awesome-css" folder to build directory +file(COPY doxygen-awesome-css DESTINATION ${DOXYGEN_OUTPUT_DIR}) + +# Copy the "images" folder to build directory +file(COPY images DESTINATION ${DOXYGEN_OUTPUT_DIR}/html) + +# Create the Doxygen output directory +file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) +add_custom_command(OUTPUT ${DOXYGEN_INDEX_FILE} + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT} + MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/UserDocumentation.md + COMMENT "Generating documentation with Doxygen") + +add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE}) diff --git a/documentation/API/Doxyfile b/documentation/Doxyfile.in similarity index 98% rename from documentation/API/Doxyfile rename to documentation/Doxyfile.in index 95819ca7d..4a6d6a4d6 100644 --- a/documentation/API/Doxyfile +++ b/documentation/Doxyfile.in @@ -38,7 +38,7 @@ PROJECT_NAME = "ReactPhysics3D" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "0.10.0" +PROJECT_NUMBER = "v@RP3D_VERSION@" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -51,14 +51,14 @@ PROJECT_BRIEF = "C++ Physics engine library" # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = "ReactPhysics3DLogo.png" +PROJECT_LOGO = "html/images/ReactPhysics3DLogo.png" # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@" # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -152,7 +152,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/" # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -161,7 +161,7 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = "@PROJECT_SOURCE_DIR@/" # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -230,12 +230,6 @@ TAB_SIZE = 4 ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -491,7 +485,7 @@ HIDE_UNDOC_CLASSES = NO # included in the documentation. # The default value is: NO. -HIDE_FRIEND_COMPOUNDS = NO +HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these @@ -730,7 +724,7 @@ WARNINGS = YES # will automatically be disabled. # The default value is: YES. -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters @@ -780,7 +774,9 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = "../../src/" "../../include/reactphysics3d/" +INPUT = "@PROJECT_SOURCE_DIR@/include/reactphysics3d/" \ + "@PROJECT_SOURCE_DIR@/src/" \ + "@PROJECT_SOURCE_DIR@/documentation/UserDocumentation.md" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1016,7 +1012,7 @@ USE_HTAGS = NO # See also: Section \class. # The default value is: YES. -VERBATIM_HEADERS = YES +VERBATIM_HEADERS = NO # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the @@ -1048,13 +1044,6 @@ CLANG_OPTIONS = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored @@ -1070,7 +1059,7 @@ IGNORE_PREFIX = # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. -GENERATE_HTML = YES +GENERATE_HTML = TRUE # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1105,7 +1094,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = "@PROJECT_SOURCE_DIR@/documentation/header.html" # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1140,7 +1129,10 @@ HTML_STYLESHEET = # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css \ + doxygen-awesome-css/doxygen-awesome-sidebar-only.css \ + doxygen-awesome-css/doxygen-awesome-sidebar-only-darkmode-toggle.css \ + "@PROJECT_SOURCE_DIR@/documentation/custom.css" # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1150,7 +1142,20 @@ HTML_EXTRA_STYLESHEET = # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to @@ -1161,7 +1166,7 @@ HTML_EXTRA_FILES = # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_HUE = 209 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A @@ -1169,7 +1174,7 @@ HTML_COLORSTYLE_HUE = 220 # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_SAT = 255 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 @@ -1180,7 +1185,7 @@ HTML_COLORSTYLE_SAT = 100 # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_GAMMA = 80 +HTML_COLORSTYLE_GAMMA = 113 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this @@ -1443,7 +1448,7 @@ ENUM_VALUES_PER_LINE = 4 # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. -TREEVIEW_WIDTH = 250 +TREEVIEW_WIDTH = 400 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. @@ -2114,12 +2119,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2133,15 +2132,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. diff --git a/documentation/README.md b/documentation/README.md new file mode 100644 index 000000000..e246fa3db --- /dev/null +++ b/documentation/README.md @@ -0,0 +1,16 @@ + + This file describes how the documentation of the project is generated. + + # Dependencies + + - Doxygen + - CMake (to build the documentation) + - LateX (for formulas in the documentation) + - doxygen-awesome-css (https://jothepro.github.io/doxygen-awesome-css) + + # How to generate the documentation + + 1. First, we need to configure CMake to generate the documentation by enabling the RP3D_GENERATE_DOCUMENTATION option + 2. The simply run 'make' to generate the documentation + + diff --git a/documentation/UserDocumentation.md b/documentation/UserDocumentation.md new file mode 100644 index 000000000..e88577b33 --- /dev/null +++ b/documentation/UserDocumentation.md @@ -0,0 +1,2106 @@ +# User Documentation {#mainpage} + +## Introduction {#introduction} + +ReactPhysics3D is an open source C++ physics engine library that can be used +in 3D simulations and games. The library is released under the ZLib license. + +## Features {#features} + + The ReactPhysics3D library has the following features: + + - Rigid body dynamics + - Discrete collision detection + - Collision shapes (Sphere, Box, Capsule, Convex Mesh, Static Concave Mesh, Height Field) + - Multiple collision shapes per body + - Broadphase collision detection (Dynamic AABB tree) + - Narrowphase collision detection (SAT/GJK) + - Collision response and friction (Sequential Impulses Solver) + - Joints (Ball and Socket, Hinge, Slider, Fixed) + - Collision filtering with categories + - Ray casting + - Sleeping technique for inactive bodies + - Multi-platform (Windows, Linux, Mac OS X) + - No external libraries (do not use STL containers) + - Documentation (user manual and Doxygen API) + - Testbed application with demos + - Integrated profiler + - Debug renderer + - Logs + - Unit tests + +## License {#license} + +The ReactPhysics3D library is released under the open-source ZLib license. For more information, read the "LICENSE" file. + +## Building and installing the library {#building} + +In order to build the library on your system, you first need to clone the code repository with the following command: + +~~~ + git clone https://github.com/DanielChappuis/reactphysics3d.git +~~~ + +Note that the *git* versioning software needs to be installed on your system. + +Then, you will need to build (compile) the library and install it on your system in order to use it in your project. +The best way is to use CMake for that. CMake will generate the necessary files on your platform (Windows, OS X or Linux) to build +the library. + +CMake can be downloaded at [http://www.cmake.org](http://www.cmake.org) or using your package-management program (apt, yum, ...) on Linux. +If you have never used CMake before, you should read [this page](http://www.cmake.org/cmake/help/runningcmake.html) as +it contains a lot of useful information. + +The remaining of this section will describe how to build and install the library with CMake. + +### Configure and generate the native tool files {#configurecmake} + +Now we need to configure CMake to tell it what you want to build. Maybe you simply want to build the library in **debug** or **release** +mode or maybe you also want to build the unit tests or the testbed application with demos. At the end of this step, CMake will generate the +native build tool files on your platform that you will use to build the library. For instance, it can generate a Visual Studio solution on Windows, +a XCode project on OS X or files for the `make` command on OS X or Linux. + +#### Configure and generate with the command line (Linux and Mac OS X) + +First, we will see how to configure CMake and generate the native build tool files using the CMake tool with the command line. +First, you need to create a folder where you want to build the library. Then go into that folder and run the following `ccmake` command: + +~~~ +ccmake path_to_library_source +~~~ + +where `path_to_library_source` must be replaced +by the path to the the `reactphysics3d/` folder of the repository you have cloned. It is the folder that +contains the `CMakeLists.txt` file of ReactPhysics3D. Running this command will launch the CMake command line interface. +Hit the 'c' key to configure the project. There, you can also change some predefined options (see this [section](#cmakevariables) for more details) +and then, hit the 'c' key again to configure the build. Once you have set all the values as you like, you can hit the 'g' key to generate the +native build tool files in the build directory that you have created before. Finally, you can exit the CMake interface. + +#### Configure and generate using the CMake graphical interface (Linux, Mac OS X and Windows) + +If your prefer, you can use the graphical user interface of CMake instead. To do this, +run the `cmake-gui` program. First, the program will ask you for the +source folder. You need to select the `reactphysics3d/` folder of the repository you have cloned. You will also have to select a +folder where you want to +build the library. Select any empty folder that is on your system. Then, you can click on **Configure**. CMake will ask you to +choose an IDE that is on your system that will be used to compile the library. For instance, you can select Visual Studio, +Qt Creator, XCode, ... Then, click on the **Finish** button. Then, you can change the compilation options. See this +[section](#cmake-options) to see what are the possible options. +Once this is done, click on **Configure** again and finally on **Generate** as you can see in the following picture. + +![CMakeWin](images/CMakeWin.png) + +Now, if you go into the folder you have chosen to build the library, you should find the native build tool files that you will use to build +the library on your platform. + +### Building the library {#buildinglibrary} + +Now, that you have generated the native build tool files on your system, you will need to build (compile) the library. + +#### Building the library using **make** on the command line (Linux, Mac OS X) + +On Linux or Mac OS X, you can compile the library on the command line using the `make` command. Go into the directory where you have generated the +native build tool files and run the following command: + +~~~ +make +~~~ + +The library will start compiling. + +#### Building the library with Visual Studio (Windows) + +If you have generated the native build tool files in the previous step on Windows, you should have obtained a Visual Studio solution of ReactPhysics3D. +Now, you can open the Visual Studio solution (.sln file). Once Visual Studio is open, you first need to change the compilation mode to **Release** +at the top instead of **Debug**. Then, right click on the **reactphysics** project in the Solution +Explorer and click on **Build** in order to compile the library (see the following picture). + +![VSBuild](images/VSBuild.png) + +The library will start compiling. + +### Installing the library {#installinglibrary} + +Now that you have compiled the library, you can install it on your system in order to put the compiled library file, the header files and the exported +CMake targets in a standard location on your system so that it can be easily imported into your project. + +#### Installing the library using the `make` on the command line (Linux, Mac OS X) + +On Linux or Mac OS X, you can use the `make` command to install the library. You simply need to run the following command: + +~~~ +sudo make install +~~~ + +The library is now installed on your system. For instance, On Linux Ubuntu, the library may have been installed in the `/usr/local/lib/` folder +and the header files in the `/usr/local/include/` folder. + +#### Installing the library on Windows with Visual Studio + +In order to install the library on your system using Visual Studio, you need to open Visual Studio with administrator rights. This is needed in order +to have the correct rights to write the files in the `C:\Program Files (x86)\` folder on your computer for instance. To do that, type +**Visual Studio** in the Start Menu, when Visual Studio has been found, right click on it and click on **Run as administrator**. This will open +Visual Studio with administrator rights. + +Then, you need to open the Visual Studio solution (.sln file) of ReactPhysics3D that has been generated +previously with CMake. To do that, click on **File** in the top menu of Visual Studio, then on **Open** and **Project/Solution...**. Then, +you need to select the ReactPhysics3D Visual Studio solution (.sln file) on your system. Once the solution is open, you first need to change the mode +at the top to **Release**} instead of **Debug**. Then, right click on the **INSTALL** +project in the Solution Explorer menu and click on **Build** (see the following picture). This will install the ReactPhysics3D library in a +standard location on your system like `C:\Program Files (x86)\ReactPhysics3D\` for instance. + +![VSInstall](images/VSInstall.png) + +### CMake Options {#cmakevariables} + +You can find below the different CMake options that you can set before building the library: + + - **CMAKE_BUILD_TYPE** If this variable is set to **Debug**, the library will be compiled in debugging mode. + This mode should be used during development stage to know where things might crash. + In debugging mode, the library might run a bit slow due to all the debugging information and asserts. + However, if this variable is set to `Release`, no debugging information is generated + and therefore, it will run much faster. This mode must be used when you compile the final + release of your application. + + - **RP3D_COMPILE_TESTBED** If this variable is **ON**, the tesbed application with demos will be compiled. + The testbed application uses OpenGL for rendering. + Take a look at this [section](#testbed) for more information about the testbed application. + + - **RP3D_COMPILE_TESTS** If this variable is **ON**, the unit tests of the library will be compiled. You will then + be able to launch the tests to make sure that they are running fine on your system. + + - **RP3D_PROFILING_ENABLED** If this variable is **ON**, the integrated profiler will collect data during the execution of the application. + This might be useful to see which part of the ReactPhysics3D + library takes time during its execution. This variable must be set to **OFF** when you compile + the final release of your application. You can find more information about the profiler in this [section](#profiler). + + - **RP3D_DOUBLE_PRECISION_ENABLED** If this variable is **ON**, the library will be compiled with double floating point precision. + Otherwise, the library will be compiled with single precision. + + - **RP3D_GENERATE_DOCUMENTATION** If this variable is **ON**, the library will be also generate the documentation. + + +## Using ReactPhysics3D in your application {#usingrp3d} + +If you have built and installed the ReactPhysics3D on your system with CMake as explained in this [section](#building), it is easy to import the +library in your project. You probably already have a `CMakeLists.txt` file for your project. Therefore, to import the ReactPhysics3D +library, you simply need to add the following line in the `CMakeLists.txt` file of your project. + + +~~~ +find_package(ReactPhysics3D REQUIRED) +~~~ + +This will tell CMake to find the installed ReactPhysics3D library on your system and import the library file and headers so that you can +link it to your project. Note that if you are working on Windows or Mac OS X, you might need to use the following code in your `CMakeLists.txt file +before calling the previous function. This will help CMake to find the installed ReactPhysics3D library on Windows or Mac OS X. + +~~~ +if(WIN32) + list(APPEND CMAKE_PREFIX_PATH "C:\\Program Files (x86)\\ReactPhysics3D") +elseif(APPLE) + list(APPEND CMAKE_PREFIX_PATH "/usr/local/lib/cmake/ReactPhysics3D") +endif() +~~~ + +Then, you need to tell CMake that your project (executable) depends on ReactPhysics3D with the following line in your `CMakeLists.txt` file: + +~~~ +target_link_libraries(helloworld ReactPhysics3D::ReactPhysics3D) +~~~ + +The ReactPhyscis3D repository contains a folder with an **Hello World** project [here](https://github.com/DanielChappuis/reactphysics3d/tree/master/helloworld). In this folder, you can find a `CMakeLists.txt and a `Main.cpp` file that show how to import and use the ReactPhysics3D library in a simple project. + +Here is the example `CMakeLists.txt file of the **Hello World** project: + +~~~ +# Minimum cmake version required +cmake_minimum_required(VERSION 3.8) + +# Help CMake to find the installed library on Windows or Mac OS X +if(WIN32) + list(APPEND CMAKE_PREFIX_PATH "C:\\Program Files (x86)\\ReactPhysics3D") +elseif(APPLE) + list(APPEND CMAKE_PREFIX_PATH "/usr/local/lib/cmake/ReactPhysics3D") +endif() + +# Import the ReactPhysics3D library +find_package(ReactPhysics3D REQUIRED) + +# Project +project(HelloWorld) + +# Create the executable +add_executable(helloworld Main.cpp) + +# Link with the ReactPhysics3D library +target_link_libraries(helloworld ReactPhysics3D::ReactPhysics3D) +~~~ + +Then in your C++ source file, you need to include the main ReactPhysics3D header file with the following line: + +~~~.cpp +// Include the main ReactPhysics3D header file +#include +~~~ + + +Also note that all the classes of the library are available in the `reactphysics3d` namespace or its shorter alias +`rp3d`. Therefore, you can use this namespace in your code with the following declaration: + +~~~.cpp +// Use the ReactPhysics3D namespace +using namespace reactphysics3d; +~~~ + +Here is the `Main.cpp` file of the **Hello World** project: + +~~~.cpp +// Libraries +#include +#include + +// ReactPhysics3D namespace +using namespace reactphysics3d; + +// Main function +int main(int argc, char** argv) { + + // First you need to create the PhysicsCommon object. + // This is a factory module that you can use to create physics + // world and other objects. It is also responsible for + // logging and memory management + PhysicsCommon physicsCommon; + + // Create a physics world + PhysicsWorld* world = physicsCommon.createPhysicsWorld(); + + // Create a rigid body in the world + Vector3 position(0, 20, 0); + Quaternion orientation = Quaternion::identity(); + Transform transform(position, orientation); + RigidBody* body = world->createRigidBody(transform); + + const decimal timeStep = 1.0f / 60.0f; + + // Step the simulation a few steps + for (int i=0; i < 20; i++) { + + world->update(timeStep); + + // Get the updated position of the body + const Transform& transform = body->getTransform(); + const Vector3& position = transform.getPosition(); + + // Display the position of the body + std::cout << "Body Position: (" << position.x << ", " << + position.y << ", " << position.z << ")" << std::endl; + } + + return 0; +} +~~~ + +## The PhysicsCommon object {#physicscommon} + +The first thing you need to do when you want to use ReactPhysics3D is to instantiate the `PhysicsCommon` class. +This main object will then be used as a factory to instantiate one or multiple physics worlds and other objects. This class is also +responsible for the memory management of the library. All the memory allocations are centralized into this `PhysicsCommon` object. +This class also contains the logger for the different events that can occur. + +In order to use ReactPhysics3D, you have to create an instance of the `PhysicsCommon class: + +~~~.cpp +// First you need to create the PhysicsCommon object. +PhysicsCommon physicsCommon; +~~~ + +Then, you can use this object to instantiate a physics world for instance: + +~~~.cpp +// Create a physics world +PhysicsWorld* world = physicsCommon.createPhysicsWorld(); +~~~ + +When you will need to add a body into your world, you will probably need to create a collider with a given type of collision shape. +Again, you will need to use the `PhysicsCommon` object to instantiate a collision shape as in the following example: + +~~~.cpp +// Instanciate a sphere collision shape +SphereShape* sphereShape = physicsCommon.createSphereShape(radius); +~~~ + +As you can see, the `PhysicsCommon` object is the first thing you will need to instantiate in order to use ReactPhycsi3D in your code. + +## Memory Management {#memorymanagement} + +The `PhysicsCommon` class is responsible for all the memory allocations that occur in ReactPhysics3D. The base memory allocations in ReactPhysics3D +are done by default using the `std::malloc()` and `std::free()` methods. If you want to use your own behavior to allocate and free +memory, you can pass a custom memory allocator to the constructor of the `PhysicsCommon` object. You simply need to create a class +that inherits from the `MemoryAllocator` class of ReactPhysics3D and overrides the `allocate()` and `release()` methods. + +Note that the allocated memory returned by the `allocate()` method must be 16 bytes aligned. + +Note that several methods of ReactPhysics3D will create an instance of an object and return a pointer so that you can use that object. This the case +for the creation of a `PhysicsWorld` or a `RigidBody` as you can see in the following code: + +~~~.cpp +// Create a physics world +PhysicsWorld* world = physicsCommon.createPhysicsWorld(); + +... + +// Create a rigid body +RigidBody* body = world->createRigidBody(transform); +~~~ + +Note that because those objects have been instantiated by ReactPhysics3D and not by you, the library is responsible to delete those objects. Therefore, +you must not call the C++ `delete` operator on those objects. There are methods that you can call to destroy those objects when you do not need +them anymore to release memory but if you don't do it, the library will do it for you when the `PhysicsCommon` object is deleted. The +following example shows how to destroy previously created `RigidBody` and `PhysicsWorld`: + +~~~.cpp +// Destroy a rigid body +world->destroyRigidBody(body); + +... + +// Destroy a physics world +physicsCommon.destroyPhysicsWorld(world); +~~~ + +## Physics World {#physicsworld} + +Once you have created a `PhysicsCommon` object (see this [section](#physicscommon)), you will have to create a physics world. A physics world is +a place where you can add the bodies that you want to simulate. It is possible to create multiple physics worlds but you will probably never need more +than one. + +- `testOverlap()` This group of methods can be used to test whether the colliders of two bodies overlap or not. You can use this if you just want to + know if bodies are colliding but your are not interested in the contact information. +- `testCollision()` This group of methods will give you the collision information (contact points, normals, ...) for colliding bodies. +- `testPointInside()` This method will tell you if a 3D point is inside a `RigidBody` or `Collider`. + +The second way to use the library is to create bodies and let ReactPhysics3D animate their motions automatically using the laws of physics. This is +done by creating rigid bodies (class `RigidBody`) in your physics world and by updating the simulation by calling the +`PhysicsWorld::update()` method each frame. The rigid bodies will move according to the forces, collision between bodies and joint constraints of +the physics world. A typical use case is a 3D real-time game for instance. + +### Creating the Physics World {#creatingphysicsworld} + +In order to create a physics world, you need to call the `createPhysicsWorld()` method of the main `PhysicsCommon` object: + +~~~.cpp +// Create the physics world +PhysicsWorld* world = physicsCommon.createPhysicsWorld(); +~~~ + +This method will return a pointer to the physics world that has been created. + +#### World settings + +When you create a physics world as in the previous example, it will have some default settings. If you want to customize some settings, you need to +create a `PhysicsWorld::WorldSettings` object and give it in parameter when you create your physics world as in the following example: + +~~~.cpp +// Create the world settings +PhysicsWorld::WorldSettings settings; +settings.defaultVelocitySolverNbIterations = 20; +settings.isSleepingEnabled = false; +settings.gravity = Vector3(0, -9.81, 0); + +// Create the physics world with your settings +PhysicsWorld* world = physicsCommon.createPhysicsWorld(settings); +~~~ + +The settings are copied into the world at its creation. Therefore, changing the values of your `PhysicsWorld::WorldSettings` instance after the +world creation will not have any effect. However, some methods are available to change settings after the world creation. You can take a +look at the API documentation to see what world settings can be changed in the `PhysicsWorld` class. + +### Customizing the Physics World + +#### Solver parameters + +ReactPhysics3D uses an iterative solver to simulate the contacts and joints. For contacts, there is a unique velocity solver and for +joints there is a velocity and a position solver. By default, the number of iterations of the velocity solver is 10 and the number of iterations +for the position solver is 5. It is possible to change the number of iterations for both solvers. + +To do this, you need to use the following two methods: + +~~~.cpp +// Change the number of iterations of the velocity solver +world->setNbIterationsVelocitySolver(15); + +// Change the number of iterations of the position solver +world->setNbIterationsPositionSolver(8); +~~~ + +Increasing the number of iterations of the solvers will make the simulation more precise but also more expensive to compute. Therefore, you should change +those values only if necessary. + +#### Sleeping {#sleeping} + +The purpose of the sleeping technique is to deactivate resting bodies so that they are not simulated anymore. This is used to save computation +time because simulating many bodies is costly. A sleeping body (or group of sleeping bodies) is awaken as soon as another body collides with it or +a joint in which it is involed is enabled. The sleeping technique is enabled by default. You can disable it using the following method: + +~~~.cpp +// Disable the sleeping technique +world->enableSleeping(false); +~~~ + +Note that it is not recommended to disable the sleeping technique because the simulation might become slower. It is also possible to deactivate +the sleeping technique on a per body basis. See this [section](#rigidbodysleeping) for more information. + +A body is put to sleep when its linear and angular velocity stay under a given velocity threshold for a certain amount of time +(one second by default). It is possible to change the linear and angular velocity thresholds using the two methods +`PhysicsWorld::setSleepLinearVelocity()` and `PhysicsWorld::setSleepAngularVelocity()`. Note that the velocities must +be specified in meters per second. You can also change the amount of time (in seconds) the velocity of a body needs to stay under the +threshold to be considered sleeping. To do this, use the `PhysicsWorld::setTimeBeforeSleep()` method. + +### Updating the Physics World {#updatingphysicsworld} + +When the `PhysicsWorld` is used to animate the bodies through time according to the laws of physics, the world has to be updated each time you +want to simulate a step forward in time (for instance each frame in a real-time simulation). + +To update the physics world, you need to use the `PhysicsWorld::update()` method. This method will perform collision detection and update the +position and orientation of the bodies according to the forces, joints constraints and collision contacts. Once you have updated the world, you will be +able to retrieve the new position and orientation of your bodies in order to render the next frame. The `PhysicsWorld::update()` method +requires a **timeStep** parameter. This is the amount of time you want to advance the physics simulation (in seconds). + +The smaller the time step you pick, the more precise the simulation will be. For a real-time application, you probably want to use a time step of +at most \f$ \frac{1}{60} \f$ seconds to have at least a 60 Hz framerate. Most of the time, physics engines prefer to work with a constant time step. +It means that you should always call the `PhysicsWorld::update()` method with the same time step parameter. You do not want to use the exact time +between two frames as your time step because it will not be constant. + +You can use the following technique. First, you need to choose a constant time step. Let say the time step is \f$\frac{1}{60}\f$ seconds. +Then, at each frame, you compute the time difference between the current frame and the previous one and you accumulate this difference in a variable +called **accumulator**. The accumulator is initialized to zero at the beginning of your application and is updated at each frame. The idea is to +divide the time in the accumulator in several constant time steps. For instance, if your accumulator contains 0.145 seconds, it means that +we can take 8 physics steps of \f$\frac{1}{60}\f$ seconds during the current frame. Note that 0.012 seconds will remain in the accumulator +and will probably be used in the next frame. As you can see, with this technique, multiple physics steps can be taken at each frame. +It is important to understand that each call to the `PhysicsWorld::update()` method is done using a constant time step that is +not varying with the framerate of the application. + +Here is what the code looks like at each frame: + +~~~.cpp +// Constant physics time step +const float timeStep = 1.0f / 60.0f; + +// Get the current system time +long double currentFrameTime = getCurrentSystemTime(); + +// Compute the time difference between the two frames +long double deltaTime = currentFrameTime - previousFrameTime; + +// Update the previous time +previousFrameTime = currentFrameTime; + +// Add the time difference in the accumulator +accumulator += mDeltaTime; + +// While there is enough accumulated time to take +// one or several physics steps +while (accumulator >= timeStep) { + + // Update the Dynamics world with a constant time step + world->update(timeStep); + + // Decrease the accumulated time + accumulator -= timeStep; +} + +~~~ + +If you want to know more about physics simulation time interpolation, you can read the nice article from Glenn Fiedler +at [https://gafferongames.com/post/fix_your_timestep](https://gafferongames.com/post/fix_your_timestep). + +### Retrieving contacts {#retrievingcontacts} + +Sometimes, you might need to get the contacts information (contact point, normal, penetration depth, ...) that occurs in your physics world. + +If you are using a physics world to only test for collisions (you never call the `PhysicsWorld::update()` method), you can retrieve contacts +information directly when you call the `PhysicsWorld::testCollision()` group of methods. Those methods take a pointer to a +`CollisionCallback` class. You simply need to create a custom class that inherits from this class and override the +`CollisionCallback::onContact()` method. When you call one of the `PhysicsWorld::testCollision()` methods, the `onContact()` method +of your class will be called with all the information about the contacts in parameters. + +However, if you are using ReactPhysics3D for a real-time simulation by calling the `PhysicsWorld::update()` method each frame, you should +use the `EventListener` class to retrieve contacts as described in this [section](#receiving_feedback). + +### Destroying the Physics World {#destroyingworld} + +When you don't need the physics world anymore, you can destroy it to release some memory. +When the physics world is destroyed, all the bodies that have been added into it and that have not been destroyed already will +be destroyed. + +~~~.cpp +// Destroy the physics world +physicsCommon.destroyPhysicsWorld(world); +~~~ + +Note that the pointer to the physics world and all the objects that have been created inside it (bodies, colliders, ...) will become invalid after +this call. + +## Rigid Body {#rigidbody} + +Once the physics world has been created, you can add rigid bodies into it. A rigid body is an object that can be simulated using the laws of physics. +It has a mass, a position, an orientation and one or several colliders. A rigid body can be used in different situations. It can be used to simulate a +real world object simulated by the laws of physics (it will react to forces and collisions) or moved manually (like a character).  It can also be used +to represent a static objects that is not moving in the world (like a building for instance). We can also use a rigid body that is not simulated but +there to launch a given action when colliding or overlapping with another rigid body of the world. In ReactPhysics3D, the `RigidBody` class +is used to describe a rigid body. + +### Creating a Rigid Body {#creatingbody} + +In order to create a rigid body, you need to specify its transform. The transform describes the initial +position and orientation of the body in the world. You need to create an instance of the `Transform` class with a vector describing the +initial position and a quaternion for the initial orientation of the body. + +You have to call the `PhysicsWorld::createRigidBody()` method to create a rigid body in the world. This method will return a pointer to the +instance of the `RigidBody` object that has been created internally. You will then be able to use that pointer to get or set values to the body. + +You can see in the following code how to create a rigid body in your world: + +~~~.cpp + +// Initial position and orientation of the rigid body +Vector3 position(0.0, 3.0, 0.0); +Quaternion orientation = Quaternion::identity(); +Transform transform(position, orientation); + +// Create a rigid body in the world +RigidBody* body = world->createRigidBody(transform); +~~~ + +Once your rigid body has been created in the world, you will probably want to add it one or more colliders as described in this [section](#collider). + +### Type of a Rigid Body (static, kinematic or dynamic) + +There are three types of bodies: **static**, **kinematic** and **dynamic**. + +A **static** body has infinite mass, zero velocity but +its position can be changed manually. Moreover, a static body does not collide with other static or kinematic bodies. + +On the other side, a **kinematic** body has infinite mass, its velocity can be changed manually and its position is computed by the physics engine. +A kinematic body does not collide with other static or kinematic bodies. + +Finally, A **dynamic** body has non-zero mass, non-zero velocity determined +by forces and its position is determined by the physics engine. Moreover, a dynamic body can collide with other dynamic, static or +kinematic bodies. + +For instance, you can use a **static** body for the floor, a **kinematic** body for a moving platform and a **dynamic** body for a +rock that could fall on the floor. + +When you create a new body in the world, it is of dynamic type by default. You can change the type of the body using the `RigidBody::setType()` +method as follows: + +~~~.cpp +// Change the type of the body to kinematic +body->setType(BodyType::KINEMATIC); +~~~ + +### Gravity {#gravity} + +By default, all the rigid bodies with react to the gravity force of the world. If you do not want the gravity to be applied to a given body, you can disable +it using the `RigidBody::enableGravity()` method as in the following example: + +~~~.cpp +// Disable gravity for this body +rigidBody->enableGravity(false); +~~~ + +### Velocity Damping {#velocitydamping} + +Damping is the effect of reducing the velocity of the rigid body during the simulation to simulate effects like air friction for instance. By default, +no damping is applied. However, you can choose to damp the linear or/and the angular velocity of a rigid body. For instance, without angular +damping a pendulum will never come to rest. You need to use the `RigidBody::setLinearDamping()` and `RigidBody::setAngularDamping()` methods to +change the damping values. The damping value has to be positive and a value of zero means no damping at all. + +### Sleeping {#rigidbodysleeping} + +As described in this [section](#sleeping), the sleeping technique is used to disable the simulation of resting bodies. By default, the bodies are +allowed to sleep when they come to rest. However, if you do not want a given body to be put to sleep, you can use the +`RigidBody::setIsAllowedToSleep()` method as in the next example: + +~~~.cpp +// This rigid body cannot sleep +rigidBody->setIsAllowedToSleep(false); +~~~ + +### Applying Force or Torque to a Rigid Body {#applyingforcetorque} + +During the simulation, you can apply a force or a torque to a given rigid body. This force can be applied to the center of mass of the rigid body +by using the `RigidBody::applyForceToCenterOfMass()` method. You need to specify the force vector (in Newton) as a parameter. If +the force is applied to the center of mass, no torque will be created and only the linear motion of the body will be affected. + +~~~.cpp +// Force vector (in Newton) +Vector3 force(2.0, 0.0, 0.0); + +// Apply a force to the center of the body +rigidBody->applyForceToCenterOfMass(force); +~~~ + +You can also apply a force to any given point in world-space using the `RigidBody::applyForceAtWorldPosition()` method or in +local-space with the `RigidBody::applyForceAtLocalPosition()` method. You need to specify the force vector (in Newton) and the point +where to apply the given force. Note that if the point is not the center of mass of the body, applying a force will generate some torque and +therefore, the angular motion of the body will be affected as well. + +~~~.cpp +// Force vector (in Newton) +Vector3 force(2.0, 0.0, 0.0); + +// Point where the force is applied +Vector3 point(4.0, 5.0, 6.0); + +// Apply a force to the body +rigidBody->applyForceAtLocalPosition(force, point); +~~~ + +It is also possible to apply a torque to a given body using the `RigidBody::applyTorque()` method. You simply need to specify +the torque vector (in Newton \f$\cdot\f$ meter) as in the following example: + +~~~.cpp +// Torque vector +Vector3 torque(0.0, 3.0, 0.0); + +// Apply a torque to the body +rigidBody->applyTorque(torque); +~~~ + +Note that when you call the previous methods, the specified force/torque will be added to the total force/torque applied to the rigid body and that +at the end of each call to the `PhysicsWorld::update()`, the total force/torque of all the rigid bodies will be reset to zero. +Therefore, you need to call the previous methods during several frames if you want the force/torque to be applied during a certain amount of time. + +### Updating a Rigid Body {#updatingrigidbody} + +When you call the `PhysicsWorld::update()` method, the bodies positions and orientations are updated to satisfy the contacts and joints +constraint between the bodies. After calling this method, you can retrieve the updated position and orientation of each body to render it. +To do that, you simply need to use the `RigidBody::getTransform()` method to get the updated transform. This transform represents the +current local-to-world-space transform of the body. + +As described in this [section](#updatingphysicsworld), at the end of a frame, there might still be some remaining time in the time accumulator. +Therefore, you should not use the updated transform directly for rendering but you need to perform some interpolation between the updated transform +and the one from the previous frame to get a smooth real-time simulation. First, you need to compute the interpolation factor as folows: + +~~~.cpp +// Compute the time interpolation factor +float factor = accumulator / timeStep; +~~~ + +Then, you can use the `Transform::interpolateTransforms()` method to compute the linearly interpolated transform: + +~~~.cpp +// Compute the interpolated transform of the rigid body +Transform interpolatedTransform = Transform::interpolateTransforms(prevTransform, currTransform, factor); +~~~ + +The following code is the one from this [section](#updatingphysicsworld) for the physics simulation loop but with the update of a given rigid body. + +~~~.cpp + +// Constant physics time step +const float timeStep = 1.0 / 60.0; + +// Get the current system time +long double currentFrameTime = getCurrentSystemTime(); + +// Compute the time difference between the two frames +long double deltaTime = currentFrameTime - previousFrameTime; + +// Update the previous time +previousFrameTime = currentFrameTime; + +// Add the time difference in the accumulator +accumulator += mDeltaTime; + +// While there is enough accumulated time to take +// one or several physics steps +while (accumulator >= timeStep) { + + // Update the physics world with a constant time step + physicsWorld->update(timeStep); + + // Decrease the accumulated time + accumulator -= timeStep; +} + +// Compute the time interpolation factor +decimal factor = accumulator / timeStep; + +// Get the updated transform of the body +Transform currTransform = body->getTransform(); + +// Compute the interpolated transform of the rigid body +Transform interpolatedTransform = Transform::interpolateTransforms(prevTransform, currTransform, factor); + +// Now you can render your body using the interpolated transform here + +// Update the previous transform +prevTransform = currTranform; + +~~~ + +If you need the array with the corresponding \f$4 \times 4\f$ OpenGL transformation matrix for rendering, you can use +the `Transform::getOpenGLMatrix()` method as in the following code: + +~~~.cpp +// Get the OpenGL matrix array of the transform +float matrix[16]; +transform.getOpenGLMatrix(matrix); +~~~ + +A nice article to read about this time interpolation is the one from Glenn Fiedler at [https://gafferongames.com/post/fix_your_timestep](https://gafferongames.com/post/fix_your_timestep). + +### Mass, Center of Mass, Inertia Tensor {#rigidbodymass} + +The mass, center of mass and inertia tensor of a rigid body are important parameters for the physical simulation of a rigid body. + +#### Mass + +The `RigidBody` has a mass value (in kilograms) which is 1 kilogram by default. There are two ways to set the mass of a rigid body. First, you +can set it directly using the `RigidBody::setMass()` method. Secondly, it is also possible to compute this mass +automatically using the mass of the colliders of the rigid body. As described in this [section](#material), the material of each collider has a +mass density value. This value is 1 by default. You change change the mass density value of the colliders of a rigid body and then use the +`RigidBody::updateMassFromColliders()` method to automatically compute the mass of the rigid body using the mass density and shape of +its colliders. Note that you will need to call this method again if you add another collider to the rigid body. + +#### Center of mass + +The center of mass of a `RigidBody` is the mean location of the distribution of mass of the body in space. By default the center of mass +of the rigid body is located at its origin. There are two ways to set the center of mass of a rigid body. First, you can set it directly using the +`RigidBody::setLocalCenterOfMass()` method. Secondly, as for the mass, the center of mass can also be computed automatically using the +mass, shape and transform of all the colliders of the rigid body. As described in this [section](#material), the material of each collider has a +mass density value. This value is 1 by default. You can set the mass density value of the colliders and then use the +`RigidBody::updateLocalCenterOfMassFromColliders()` method to automatically compute the center of mass of the rigid body. +Note that you will need to call this method again if you add another collider to the rigid body. + +#### Inertia Tensor + +The inertia tensor of a `RigidBody` is a \f$3 \times 3\f$ matrix describing how the mass is distributed inside the rigid body which is +used to calculate its rotation. The inertia tensor depends on the mass and the shape of the body. By default the local inertia tensor of a rigid body +is the identity matrix. There are two ways to set the inertia tensor of +a rigid body. First, you can set it directly using the `RigidBody::setLocalInertiaTensor()` method. Note that this will set the inertia tensor +of the body in local-space coordinates which is usually a diagonal matrix. This method takes a `Vector3` with the three diagonal entries of the +matrix. Secondly, the local inertia tensor can be computed automatically using the mass density, shape and transform of all the colliders of the body. +As described in this [section](#material), the material of each collider has a mass density value which is 1 by default. You can set the mass density +value of the colliders and then use the `RigidBody::updateLocalInertiaTensorFromColliders()` method to automatically compute the local inertia +tensor of the body. Note that you will need to call this method again if you add another collider to the rigid body. + +Note that it is also possible to automatically compute the mass, center of mass and inertia tensor of a rigid body at the same time using the +`RigidBody::updateMassPropertiesFromColliders()`. + +### Restricting linear/angular motion of a Rigid Body {#restrictmotion} + +It is possible to use the `RigidBody::setLinearLockAxisFactor()` method to restrict the linear motion of a rigid body along the world-space +X,Y and Z axes. For instance, the following code shows how to disable the linear motion of a rigid body along the world-space Y axis by setting the lock +factor to zero for this axis. + +~~~.cpp +// Disable motion along the Y axis +rigidBody->setLinearAxisFactor(Vector3(1, 0, 1)); +~~~ + +In the same way, you can use the `RigidBody::setAngularLockAxisFactor()` method to restrict the angular motion of a rigid body around the +world-space X,Y and Z axes. For instance, the following code shows how to disable the angular motion of a rigid body around the world-space Y axis +by setting the lock factor to zero for this axis. + +~~~.cpp +// Disable rotation around the Y axis +rigidBody->setAngularAxisFactor(Vector3(1, 0, 1)); +~~~ + +### Destroying a Rigid Body {#destroyingbody} + +It is really simple to destroy a rigid body when you don't need it anymore. You simply need to use the `PhysicsWorld::destroyRigidBody()` +method. You need to use the pointer to the body you want to destroy as a parameter. Note that after calling that method, the pointer will not be valid +anymore and therefore, you should not use it. When you destroy a rigid body that was part of a joint, that joint will be automatically destroyed as +well. + +Here is how to destroy a rigid body: + +~~~.cpp +// Here, world is an instance of the PhysicsWorld class +// and body is a RigidBody* pointer + +// Destroy the rigid body +world->destroyRigidBody(body); +~~~ + + +## Collider {#collider} + +To allow bodies to collide against each others, we need `colliders`. A collider (class `Collider`) describes the collision shape +of a body. A body can have multiple colliders attached to it. When adding a collider to a body, you need to specify its collision +shape (box, sphere, capsule, ...) and its transform relative to the origin of the body. + +Before adding a collider to a body, you need to create a collision shape. A collision shape can be instantiated by calling a method of the +main `PhysicsCommon` object. The following example shows how instantiate a collision shape (a sphere shape) from the `PhysicsCommon` +object and use it to add a new collider to a body. + +~~~.cpp +// Instantiate a sphere collision shape +float radius = 3.0f; +SphereShape* sphereShape = physicsCommon.createSphereCollisionShape(radius); + +// Relative transform of the collider relative to the body origin +Transform transform = Transform::identity(); + +// Add the collider to the rigid body +Collider* collider; +collider = body->addCollider(&shape, transform); +~~~ + +Note that a given collision shape instance can be shared between multiple colliders. The next section presents the different types of collision +shapes that are available in ReactPhysics3D. + +### Collision Shapes {#collisionshapes} + +As we have just seen, a collision shape is used to describe the shape of a collider for collision detection. They are many types of +collision shapes that you can use. They all inherit from the `CollisionShape` class. + +Note that ReactPhysics3D does not support collision between two concave shapes `ConcaveMeshShape` and `HeightFieldShape`. + +#### Box Shape {#boxshape} + +![BoxShape](images/boxshape.png) + +The `BoxShape` class describes a box collision shape centered at the origin of the collider. The box is aligned with the shape +local X, Y and Z axis. In order to create a box shape, you only need to specify the three half extents dimensions of the box in the three X, Y and +Z directions. + +For instance, if you want to create a box shape with dimensions of 4 meters, 6 meters and 10 meters along the X, Y and Z axis respectively, you +need to use the following code: + +~~~.cpp +// Half extents of the box in the x, y and z directions +const Vector3 halfExtents(2.0, 3.0, 5.0); + +// Create the box shape +BoxShape* boxShape = phycsicsCommon.createBoxShape(halfExtents); +~~~ + +#### Sphere Shape {#sphereshape} + +![SphereShape](images/sphereshape.png) + +The `SphereShape` class describes a sphere collision shape centered at the origin of the collider. You only need to specify the +radius of the sphere to create it. + +For instance, if you want to create a sphere shape with a radius of 2 meters, you need to use the following code: + +~~~.cpp +// Create the sphere shape with a radius of 2m +SphereShape* sphereShape = physicsCommon.createSphereShape(2.0); +~~~ + +#### Capsule Shape {#capsuleshape} + +![CapsuleShape](images/capsuleshape.png) + +The `CapsuleShape` class describes a capsule collision shape around the local Y axis and centered at the origin of the collider. +It is the convex hull of two spheres. It can also be seen as an elongated sphere. In order to create it, you only need to specify the +radius of the two spheres and the height of the capsule (distance between the centers of the two spheres). + +For instance, if you want to create a capsule shape with a radius of 1 meter and the height of 2 meters, you need to use the following code: + +~~~.cpp +// Create the capsule shape +CapsuleShape* capsuleShape = physicsCommon.createCapsuleShape(1.0, 2.0); +~~~ + +#### Convex Mesh Shape {#convexmeshshape} + +The `ConvexMeshShape` class can be used to describe the shape of a convex mesh centered at the origin of the collider. + +![ConvexMeshShape](images/convexshape.png) + +In order to create a `ConvexMeshShape`, we first need to create a `ConvexMesh`. A `ConvexMesh` can be instanciated once and +used to create multiple `ConvexMeshShapes` with different scaling if necessary. There are two ways to create a `ConvexMesh`. + +If you know the vertices and faces of your mesh, you can use the following method to create a `ConvexMesh`: + +~~~.cpp +PhysicsCommon::createConvexMesh(const PolygonVertexArray& polygonVertexArray, std::vector& messages) +~~~ + +To use this method, you need to create an array of `PolygonFace` to describe each face of your mesh. You also need to have an array with +the vertices coordinates and an array with the vertex indices of each face of you mesh. Then, you have to create a `PolygonVertexArray` with +your vertices coordinates and indices array. You also need to specify your array of `PolygonFace`. Then, you can call the previous method +to instantiate a `ConvexMesh` using your `PolygonVertexArray`. + +The following example shows how to create a `ConvexMeshShape` when you know the vertices and faces of your mesh. +In this example, we create a cube as a convex mesh shape. Of course, this is only for the example. If you really need a cube collision +shape, you should use the `BoxShape` instead. + +~~~.cpp + +// Array with the vertices coordinates of the convex mesh +float vertices[24] = {-3, -3, 3, + 3, -3, 3, + 3, -3, -3, + -3, -3, -3, + -3, 3, 3, + 3, 3, 3, + 3, 3, -3, + -3, 3, -3}; + + +// Array with the vertices indices for each face of the mesh +int indices[24] = {0, 3, 2, 1, + 4, 5, 6, 7, + 0, 1, 5, 4, + 1, 2, 6, 5, + 2, 3, 7, 6, + 0, 4, 7, 3}; + +// Description of the six faces of the convex mesh +PolygonVertexArray::PolygonFace* polygonFaces = new PolygonVertexArray::PolygonFace[6]; +PolygonVertexArray::PolygonFace* face = polygonFaces; +for (int f = 0; f < 6; f++) { + + // First vertex of the face in the indices array + face->indexBase = f * 4; + + // Number of vertices in the face + face->nbVertices = 4; + + face++; +} + +// Create the polygon vertex array +PolygonVertexArray* polygonVertexArray = new PolygonVertexArray(8, vertices, 3 * sizeof(float), indices, sizeof(int), 6, polygonFaces, +PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE, +PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE); + +// Create the convex mesh +std::vector messages; +ConvexMesh* convexMesh = physicsCommon.createConvexMesh(polygonVertexArray, messages); + +// Display the messages (info, warning and errors) +if (messages.size() > 0) { + + for (const rp3d::Message& message: messages) { + + std::string messageType; + switch(message.type) { + case rp3d::Message::Type::Information: + messageType = "info"; + break; + case rp3d::Message::Type::Warning: + messageType = "warning"; + break; + case rp3d::Message::Type::Error: + messageType = "error"; + break; + } + + std::cout << "Message (" << messageType << "): " << message.text << std::endl; + } +} + +// Make sure there was no errors during mesh creation +assert(convexMesh != nullptr); + +~~~ + +Note that the vertex coordinates and indices array are copied into the `ConvexMesh`. Therefore, you can release the memory of the +`PolygonVertexArray` after the `ConvexMesh` creation. However, the mesh data is stored inside the `ConvexMesh`. Therefore, +you should not release it until all the `ConvexMeshShapes` that have been instantiated from it are destroyed. + +You need to make sure that the mesh you provide is indeed convex. Secondly, you should provide the simplest possible convex mesh. It means +that you need to avoid coplanar faces in your convex mesh shape. Coplanar faces have to be merged together. Remember that convex meshes are +not limited to triangular faces, you can create faces with more than three vertices. + +Also note that meshes with duplicated vertices are not supported. The number of vertices you pass to create the `PolygonVertexArray` must +be exactly the number of vertices in your convex mesh. The `PhysicsCommon::createConvexMesh()` will also report errors if some faces of your +mesh have almost zero area (degenerated face). You will need to fix those errors in order to create the `ConvexMesh`. + +When you specify the vertices for each face of your convex mesh, be careful with their order. The vertices of a face must be specified in +counter clockwise order as seen from the outside of your convex mesh. + +You also need to make sure that the origin of your mesh is inside the convex mesh. A mesh with an origin outside the +convex mesh is not currently supported by the library. + +As discussed before, there is a second way to create a `ConvexMesh`. In case you only know the vertices of your +mesh (you don't know the faces), you can use the following method: + +~~~.cpp +PhysicsCommon::createConvexMesh(const VertexArray& vertexArray, std::vector& messages) +~~~ + +With this method, you only need to specify the array of vertices (`VertexArray`) of your mesh (not the faces). The library will use the +QuickHull algorithm to compute a convex mesh that includes all the vertices. + +~~~.cpp +// Array with the vertices coordinates of the convex mesh +float vertices[24] = {-3, -3, 3, + 3, -3, 3, + 3, -3, -3, + -3, -3, -3, + -3, 3, 3, + 3, 3, 3, + 3, 3, -3, + -3, 3, -3}; + +// Vertex array with all vertices of the mesh +rp3d::VertexArray vertexArray(vertices, 3 * sizeof(float), 8, rp3d::VertexArray::DataType::VERTEX_FLOAT_TYPE); + +// Compute the convex mesh using only the array of vertices +std::vector messages; +ConvexMesh* convexMesh = physicsCommon.createConvexMesh(vertexArray, messages); + +// Display the messages (info, warning and errors) +if (messages.size() > 0) { + + for (const rp3d::Message& message: messages) { + + std::string messageType; + switch(message.type) { + case rp3d::Message::Type::Information: + messageType = "info"; + break; + case rp3d::Message::Type::Warning: + messageType = "warning"; + break; + case rp3d::Message::Type::Error: + messageType = "error"; + break; + } + + std::cout << "Message (" << messageType << "): " << message.text << std::endl; + } +} + +// Make sure there was no errors during mesh creation +assert(convexMesh != nullptr); + +~~~ + +Note that the `PhysicsCommon::createConvexMesh()` method takes a reference to an array of messages as parameter. If there are errors +during the convex mesh creation, the method will return `null` and the array with contain some error messages describing the issues. + +Now that we have a `ConvexMesh`, we can create a `ConvexMeshShape` as in the following example: + +~~~.cpp +// Scaling factor +Vector3 scaling(1, 1, 1); + +// Create the ConvexMeshShape +ConvexMeshShape* convexMeshShape = physicsCommon.createConvexMeshShape(convexMesh, scaling); +~~~ + +As you can see, you can specify a scaling factor in the `PhysicsCommon::createConvexMeshShape()` method when you create a +`ConvexMeshShape`. All the vertices of your mesh will be scaled from the local origin of the mesh by this factor. +It means that you can use the same `ConvexMesh` for multiple `ConvexMeshShape` with a different scaling factor each time. + +Note that collision detection with a `ConvexMeshShape` is more expensive than with a `SphereShape` or a `CapsuleShape`. + +#### Concave Mesh Shape {#concavemeshshape} + +![ConcaveMeshShape](images/concavemeshshape.png) + +The `ConcaveMeshShape` class can be used for a static concave triangular mesh. It can be used to describe an environment for +instance. Note that it cannot be used with a dynamic body that is allowed to move. Moreover, make sure to use a `ConcaveMeshShape` only +when you are not able to use a convex shape and also try to limit the number of triangles of that mesh because collision detection +with `ConcaveMeshShape` is quite expensive compared to convex shapes. Note that collsions between two concave mesh shape are not supported. + +In order to create a `ConcaveMeshShape`, we first need to create a `TriangleMesh`. A `TriangleMesh` describes your +mesh made of triangles and can be reused multiple times to instantiate `ConcaveMeshShapes` with different scaling factors for instance. +In order to create the `TriangleMesh`, we first need to instantiate a `TriangleVertexArray` describing the vertices and triangle faces +of the mesh. We then pass the `TriangleVertexArray` to the `PhysicsCommon::createTriangleMesh()` method to create the `TriangleMesh`. + +When you specify the vertices for each triangle face of your mesh, be careful with the order of the vertices. They must be specified in counter +clockwise order as seen from the outside of your mesh. + +The following code shows how to create a `TriangleVertexArray` and use it to create a `TriangleMesh`: + +~~~.cpp +// Create the TriangleVertexArray +const int nbVertices = 8; +const int nbTriangles = 12; +float vertices[3 * nbVertices] = ...; +int indices[3 * nbTriangles] = ...; +TriangleVertexArray* triangleArray = +new TriangleVertexArray(nbVertices, vertices, 3 * sizeof(float), nbTriangles, +indices, 3 * sizeof(int), +TriangleVertexArray::VertexDataType::VERTEX_FLOAT_TYPE, +TriangleVertexArray::IndexDataType::INDEX_INTEGER_TYPE); + +// Create the TriangleMesh +std::vector messages; +TriangleMesh* triangleMesh = physicsCommon.createTriangleMesh(vertexArray, messages); + +// Display the messages (info, warning and errors) +if (messages.size() > 0) { + + for (const rp3d::Message& message: messages) { + + std::string messageType; + switch(message.type) { + case rp3d::Message::Type::Information: + messageType = "info"; + break; + case rp3d::Message::Type::Warning: + messageType = "warning"; + break; + case rp3d::Message::Type::Error: + messageType = "error"; + break; + } + + std::cout << "Message (" << messageType << "): " << message.text << std::endl; + } +} + +// Make sure there was no errors during mesh creation +assert(triangleMesh != nullptr); +~~~ + +The vertices and faces data are copied into the `TriangleMesh`. Therefore, after the `TriangleMesh` creation, you can release your +`TriangleVertexArray` but you need to keep the `TriangleMesh` until all the `ConcaveMeshShapes` that have been instantiated from +it are destroyed. + +In the previous example, the vertices normals that are needed for collision detection are automatically computed. However, you can specify your own +vertices normals by using another constructor for the `TriangleVertexArray`. Note that each vertex normal is computed as weighted average +of the face normals of all the neighboring triangle faces. Therefore, if you specify your mesh with duplicated vertices when you create the +`TriangleVertexArray`, the automatic vertices normals computation will not give correct normals because each vertex of the mesh will only be +part of a single triangle face. In this case, you must provide your own vertices normals when you create the `TriangleVertexArray`. + +The `PhysicsCommon::createTriangleMesh()` will also report errors if some faces of your mesh have almost zero area +(degenerated face). You will need to fix those errors in order to create the `TriangleMesh`. It is also a good idea to avoid coplanar faces +for better collision detection. + +Now that we have a `TriangleMesh`, we can create the actual `ConcaveMeshShape`. + +~~~.cpp + +const Vector3 scaling(1, 1, 1); + +// Create the ConcaveMeshShape using the TriangleMesh +ConcaveMeshShape* concaveMeshShape = physicsCommon.createConcaveMeshShape(triangleMesh, scaling); +~~~ + +As you can see, you can specify a scaling factor in the `PhysicsCommon::createConcaveMeshShape()` method when you create a +`ConcaveMeshShape`. All the vertices of your mesh will be scaled from the local origin of the mesh by this factor. +Note that you can use the same `TriangleMesh` instance for creating multiple `ConcaveMeshShape` with a different scaling +factor each time. + +#### Heightfield Shape {#heightfieldshape} + +![HeightFieldShape](images/heightfieldshape.png) + +The `HeightFieldShape` is a collision shape that can be used to represent a static terrain for instance. This shape is +a two dimensional grid that has a given height value at each point of the grid. + +First, we need to create an `HeightField`. To do that, we need to have an array with all the height values of our field. +We can have height values of type integer, float or double. We need to specify the number of rows and columns of our two +dimensional grid. Note that the height values in our array must be organized such that the value at row +`indexRow` and column `indexColumn` is located at the following position in the array: + +~~~.cpp +heightValues[indexRow * nbColumns + indexColumn] +~~~ + +Here is an example that shows how to create a `HeightField`: + +~~~.cpp +const int nbRows = 40; +const int nbColumns = 50; +float minHeight = 100; +float maxHeight = 500; + +// Height values +float heightValues[nbRows * nbColumns] = ...; + +std::vector messages; +HeightField* heightField = physicsCommon.createHeightField(NB_POINTS_WIDTH, NB_POINTS_LENGTH, + heightValues, rp3d::HeightField::HeightDataType::HEIGHT_FLOAT_TYPE, + messages); + +// Display the messages (info, warning and errors) +if (messages.size() > 0) { + + for (const rp3d::Message& message: messages) { + + std::string messageType; + + switch(message.type) { + case rp3d::Message::Type::Information: + messageType = "info"; + break; + case rp3d::Message::Type::Warning: + messageType = "warning"; + break; + case rp3d::Message::Type::Error: + messageType = "error"; + break; + } + + std::cout << "Message (" << messageType << "): " << message.text << std::endl; + } +} + +// Make sure there was no errors during the height field creation +assert(heightField != nullptr); +~~~ + +The height values are copied into the `HeightField` instance. Therefore, after the `HeightField` creation, you can release the memory +of your array of height values but you need to keep the `TriangleMesh` until all the `HeightFieldShapes` that have been instantiated from +it are destroyed. + +Once we have our `HeightField` instance, we can use it to create an actual `HeightFieldShape` as in the following example: + +~~~.cpp +HeightFieldShape* heightFieldShape = physicsCommon.createHeightFieldShape(heightField); +~~~ + +You can also specify a scaling factor in the `PhysicsCommon::createHeightShape()` method when you create a `HeightFieldShape`. +All the vertices of your mesh will be scaled from the local origin of the shape by this factor. + +When creating a `HeightFieldShape`, the origin of the shape will be at the center of its bounding volume. +Therefore, if you create a `HeightFieldShape` with a minimum height of 100 and a maximum height of 500, the +maximum coordinates of the shape on the Y axis will be 200 and the minimum coordinates will be -200. + +### Collision filtering {#collisionfiltering} + +By default all the colliders of the bodies are able to collide with each other in the world. However, sometimes we want a body to collide only +with a given group of bodies and not with other bodies. This is called collision filtering. The idea is to group the colliders of bodies into +categories. Then we can specify for each collider against which categories of colliders it will be able to collide. + +ReactPhysics3D uses bits masks to represent categories. The first thing to do is to assign a category to the colliders of your body. To do +this, you need to call the `Collider::setCollisionCategoryBits()` method on the corresponding collider as in the following example. Here +we consider that we have four bodies where each one has a single collider. + +~~~.cpp +// Enumeration for categories +enum Category { +CATEGORY1 = 0x0001, +CATEGORY2 = 0x0002, +CATEGORY3 = 0x0004 +}; + + +// Set the collision category for each collider of +// each of the four bodies +colliderBody1->setCollisionCategoryBits(CATEGORY1); +colliderBody2->setCollisionCategoryBits(CATEGORY2); +colliderBody3->setCollisionCategoryBits(CATEGORY3); +colliderBody4->setCollisionCategoryBits(CATEGORY3); +~~~ + +As you can see, the collider of body 1 will be part of the category 1, the collider of body 2 will be part of the category 2 and the colliders of +bodies 3 and 4 will be part of the category 3. + +Now, for each collider, we need to specify with which categories it is allowed to collide with. To do this, you need to use the +`Collider::setCollideWithMaskBits()` method. Note that you can specify one or more categories using the bitwise OR operator. The +following example shows how to specify with which categories the collider can collide. + +~~~.cpp +// For each collider, we specify with which categories it +// is allowed to collide +colliderBody1->setCollideWithMaskBits(CATEGORY3); +colliderBody2->setCollideWithMaskBits(CATEGORY1 | CATEGORY3); +colliderBody3->setCollideWithMaskBits(CATEGORY2); +colliderBody4->setCollideWithMaskBits(CATEGORY2); +~~~ + +As you can see, we specify that the body 1 will be allowed to collide with bodies from the categorie 3. We also indicate that the body 2 will be +allowed to collide with bodies from the category 1 and 3 (using the bitwise OR operator). Finally, we specify that bodies 3 and 4 will be allowed +to collide against bodies of the category 2. + +A collider is able to collide with another only if you have specify that the category mask of the first collider is part of the +**collide with** mask of the second collider. It is also important to understand that this condition must be satisfied in both directions. For +instance in the previous example, the body 1 (of category 1) says that it wants to collide against bodies of the category 3 (for instance against +body 3). However, body 1 and body 3 will not be able to collide because the body 3 does not say that it wants to collide +with bodies from category 1. Therefore, in the previous example, the body 2 is allowed to collide against bodies 3 and 4 but no other collision +is allowed. + +In the same way, you can perform this filtering for ray casting (described in this [section](#raycasting)). For instance, you can perform a ray cast test +against a given subset of categories of colliders only. + +### Material {#material} + +The material of a collider is used to describe the physical properties it is made of. This is represented by the `Material` class. Each collider that you create will have a default material. You can get the material of the collider using the `Collider::getMaterial()` method. + +You can use the material to set those physical properties. + +For instance, you can change the bounciness of the collider. The bounciness is a value between 0 and 1. The value 1 is used for +a very bouncy object and the value 0 means that the collider will not be bouncy at all. To change the bounciness of the material, +you can use the `Material::setBounciness()` method. + +It is also possible to set the mass density of the collider which has a default value of 1. As described in this [section](#rigidbodymass), the +mass density of a collider can be used to automatically compute the mass, center of mass and inertia tensor of a rigid body. In order to change the +mass density of a collider, you need to use the `Material::setMassDensity()` method. + +You are also able to change the friction coefficient of the collider. This value needs to be between 0 and 1. If the value is 0, +no friction will be applied when the collider is in contact with another collider. However, if the value is 1, the friction force will be high. You can +change the friction coefficient of the material with the `Material::setFrictionCoefficient()` method. + +Here is how to get the material of a collider and how to modify some of its properties: + +~~~.cpp +// Get the current material of the collider +Material& material = collider->getMaterial(); + +// Change the bounciness of the collider +material.setBounciness(0.4); + +// Change the friction coefficient of the collider +material.setFrictionCoefficient(0.2); +~~~ + +A collider can have different behaviors. It can be a **simulation collider**, a **world query collider** or a +**trigger**. It can have also have two behaviors (except **simulation collider** and **trigger** +at the same time). By default, when you create a collider, it will be a **simulation collider** and a **world query collider** but not a +**trigger**. We will now take a look at those behaviors. + +### Simulation Collider {#simulationcollider} + +A simulation collider is a collider that will be able to collide against other simulation colliders in the world. It means that when two simulation +colliders collide, contacts will be created and used to compute the forces/torques needed to make their bodies to bump into each other. For instance, +if you want to simulate a body that is falling on the floor and bumping against it, you will need to have a least of simulation collider in both the +falling body and the floor body. + +To set a collider as being a simulation collider, you need to use the +`Collider::setIsSimulationCollider()` method as in the following example: + +~~~.cpp +bombCollider->setIsSimulationCollider(true); +~~~ + +When you create a collider, it is a simulation collider by default. + +### World Query Collider {#worldquerycollider} + +A world query collider is a collider used to perform world queries on it. World queries are manual queries perform on the physics world like raycasting +or testing if two collider collide or overlap using the following method for instance: + +- `testOverlap()` Those methods can be used to test whether the colliders of two bodies overlap or not. +- `testCollision()` Those methods will give you the collision information (contact points, normals, ...) between two colliding bodies. +- `testPointInside()` This method will tell you if a 3D point is inside a `RigidBody` or `Collider`. +- `raycast()` Those methods will try to find intersection between a ray and colliders of the world. + +For instance, if you want to test a body of your scene with raycasting, you need to set the collider of that body as being a world query collider. + +To set a collider as being a world query collider, you need to use the `Collider::setIsWorldQueryCollider()` method as in the following example: + +~~~.cpp +bombCollider->setIsWorldQueryCollider(true); +~~~ + +A collider can be a world query collider and a simulation collider at the same time. When you create a collider, it is a world query collider by default. + +### Trigger {#trigger} + +A trigger, is a collider that cannot collide with any other colliders but can only report when it is overlapping with another collider. For instance, +consider a game where a player moves around and has to avoid touching some bombs. The player has a rigid body with a capsule collider for instance and +the bombs are rigid bodies where each one has a sphere collider. Now, you do not want the player to bump against the bombs but you only want to know +when the collider of the player overlaps with the collider of a bomb. In this case, you can set bombs colliders as being triggers. In this +case, no rigid bodies in the world will be able to collide against the bombs colliders but you can receive notifications when a collider (the player +body collider for instance) overlaps with a bomb collider. Therefore, you can use this to know when the player touches a bomb of the world. + +To set a collider as being a trigger, you need to use the `Collider::setIsTrigger()` method as in the following example: + +~~~.cpp +bombCollider->setIsTrigger(true); +~~~ + +Note that a collider cannot be a trigger and a simulation collider at the same time. When you create a collide it is not a trigger by default. + +This [section](#eventlistenertriggers) describes how to use the `EventListener` class to receive notifications when colliders overlap +with some triggers. + +## Joints {#joints} + +Joints are used to constrain the motion of the rigid bodies between each other. A single joint represents a constraint between two rigid bodies. +When the motion of the first body of the joint is known, the relative motion of the second body has at most six degrees of freedom (three for the +translation and three for the rotation). The different joints can reduce the number of degrees of freedom between two rigid bodies. + +Some joints have limits to control the range of motion and some joints have motors to automatically move the bodies of the joint at a given speed. + +### Ball and Socket Joint {#ballsocketjoint} + +The `BallAndSocketJoint` class describes a ball and socket joint between two bodies. In a ball and socket joint, the two bodies cannot +translate with respect to each other. However, they can rotate freely around a common anchor point. This joint has three degrees of freedom +and can be used to simulate a chain of bodies for instance. + +In order to create a ball and socket joint, you first need to create an instance of the `BallAndSocketJointInfo` class with the necessary +information. You need to provide the pointers to the two rigid bodies and also the coordinates of the anchor point. The `BallAndSocketJointInfo` +class contains different constructors that you can use whether you want to specify the anchor point in world-space or local-space coordinates. +The joint will make sure that the two local-space anchor points match in world-space. Make sure that the two bodies are in a valid position (with +respect to the joint constraint) at the beginning of the simulation. + +Here is the code to create the `BallAndSocketJointInfo` object: + +~~~.cpp +// Anchor point in world-space +const Vector3 anchorPoint(2.0, 4.0, 0.0); + +// Create the joint info object +BallAndSocketJointInfo jointInfo(body1, body2, anchorPoint); +~~~ + +Now, it is time to create the actual joint in the physics world using the `PhysicsWorld::createJoint()` method. +Note that this method will also return a pointer to the `BallAndSocketJoint` object that has been created internally. You will then +be able to use that pointer to change properties of the joint and also to destroy it at the end. + +Here is how to create the joint in the world: + +~~~.cpp +// Create the joint in the physics world +BallAndSocketJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +#### Cone limit + +With the ball and socket joint, it is possible to enable a cone limit. Let's call the **anchor axis** for body 1 the axis from body 1 center of mass +to the joint anchor point. The cone limit can be used to constraint the angle between the anchor axis of body 1 and the anchor axis of body 2. +The idea is to limit the angle of the anchor axis of body 2 inside a cone around the anchor axis of body 1. The cone is defined by its main axis +which is the anchor axis of body 1 and is also defined by the cone half angle. + +In the following example, we enable the cone limit for a given ball and socket joint. + +~~~.cpp +// Set the maximum half angle (in radians) +joint->setConeLimitHalfAngle(45.0 * PI / 180.0); + +// Enable the cone limit for this joint +joint->enableConeLimit(true); +~~~ + +### Hinge Joint {#hingejoint} + +The `HingeJoint? class describes a hinge joint (or revolute joint) between two rigid bodies. The hinge joint only allows rotation around an +anchor point and around a single axis (the hinge axis). This joint can be used to simulate doors or pendulums for instance. + +In order to create a hinge joint, you first need to create a `HingeJointInfo` object with the necessary information. You need to provide +the pointers to the two rigid bodies, the coordinates of the anchor point and also the hinge rotation axis. The `HingeJointInfo` class contains +different constructors that you can use whether you want to specify the anchor point or the rotation axis in local-space or world-space. +Make sure that the two bodies are in a valid position (with respect to the joint constraint) at the beginning of the simulation. + +Here is the code to create the `HingeJointInfo` object: + +~~~.cpp +// Anchor point in world-space +const Vector3 anchorPoint(2.0, 4.0, 0.0); + +// Hinge rotation axis in world-space +const Vector3 axis(0.0, 0.0, 1.0); + +// Create the joint info object +HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); +~~~ + +Now, it is time to create the actual joint in the physics world using the `PhysicsWorld::createJoint()` method. +Note that this method will also return a pointer to the `HingeJoint` object that has been created internally. You will then +be able to use that pointer to change properties of the joint and also to destroy it at the end. + +Here is how to create the joint in the world: + +~~~.cpp +// Create the hinge joint in the physics world +HingeJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +#### Limits + +With the hinge joint, you can constrain the motion range using limits. The limits of the hinge joint are the minimum and maximum angle of +rotation allowed with respect to the initial angle between the bodies when the joint is created. The limits are disabled by default. +If you want to use the limits, you first need to enable them by setting the `isLimitEnabled` variable of the `HingeJointInfo` +object to `true` before you create the joint. You also have to specify the minimum and maximum limit angles (in radians) using +the `minAngleLimit` and `maxAngleLimit` variables of the joint info object. Note that the minimum limit angle must be in the +range \f$[ -2 \pi; 0 ]\f$ and the maximum limit angle must be in the range \f$[ 0; 2 \pi ]\f$. + +For instance, here is the way to use the limits for a hinge joint when the joint is created: + +~~~.cpp +// Create the joint info object +HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); + +// Enable the limits of the joint +jointInfo.isLimitEnabled = true; + +// Minimum limit angle +jointInfo.minAngleLimit = -PI / 2.0; + +// Maximum limit angle +jointInfo.maxAngleLimit = PI / 2.0; + +// Create the hinge joint in the physis world +HingeJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +It is also possible to use the `HingeJoint::enableLimit()`, `HingeJoint::setMinAngleLimit()` and `HingeJoint::setMaxAngleLimit()` methods to specify +the limits of the joint after its creation. See the API documentation for more information. + +#### Motor + +A motor is also available for the hinge joint. It can be used to rotate the bodies around the hinge axis at a given angular speed and such that the torque applied to +rotate the bodies does not exceed a maximum allowed torque. The motor is disabled by default. If you want to use it, you first have to activate it using the +`isMotorEnabled` boolean variable of the `HingeJointInfo` object before you create the joint. Then, you need to specify the angular motor speed (in radians/seconds) +using the `motorSpeed` variable and also the maximum allowed torque (in Newton \f$\cdot\f$ meters) with the ̀maxMotorTorque` variable. + +For instance, here is how to enable the motor of the hinge joint when the joint is created: + +~~~.cpp +// Create the joint info object +HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); + +// Enable the motor of the joint +jointInfo.isMotorEnabled = true; + +// Motor angular speed +jointInfo.motorSpeed = PI / 4.0; + +// Maximum allowed torque +jointInfo.maxMotorTorque = 10.0; + +// Create the hinge joint in the physics world +HingeJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +It is also possible to use the `HingeJoint::enableMotor()`, `HingeJoint::setMotorSpeed()` and `HingeJoint::setMaxMotorTorque()` methods to +enable the motor of the joint after its creation. See the API documentation for more information. + +### Slider Joint {#sliderjoint} + +The `SliderJoint` class describes a slider joint (or prismatic joint) that only allows relative translation along a single direction. +It has a single degree of freedom and allows no relative rotation. + +In order to create a slider joint, you first need to specify the anchor point and the slider axis direction. +The constructor of the `SliderJointInfo` object needs two pointers to the bodies of the joint, the anchor point and the axis direction. +The `SliderJointInfo` class contains different constructors that you can use whether you want to specify the anchor point and the direction axis +in local-space or world-space. Make sure that the two bodies are in a valid position (with respect to the joint constraint) at the beginning +of the simulation. + +You can see in the following code how to specify the information to create a slider joint: + +~~~.cpp +// Anchor point in world-space +const Vector3 anchorPoint = 0.5 * (body2Position + body1Position); + +// Slider axis in world-space +const Vector3 axis = (body2Position - body1Position); + +// Create the joint info object +SliderJointInfo jointInfo(body1, body2, anchorPoint, axis); +~~~ + +Now, it is possible to create the actual joint in the physics world using the `PhysicsWorld::createJoint()` method. +Note that this method will also return a pointer to the `SliderJoint` object that has been created internally. You will then +be able to use that pointer to change properties of the joint and also to destroy it at the end. + +Here is how to create the joint in the world: + +~~~.cpp +// Create the slider joint in the physics world +SliderJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +#### Limits + +It is also possible to control the range of the slider joint motion using limits. The limits are disabled by default. In order to use the limits when the joint is created, you first +need to activate them using the `isLimitEnabled` variable of the `SliderJointInfo` class. Then, you need to specify the minimum and maximum translation limits +(in meters) using the `minTranslationLimit` and `maxTranslationLimit` variables. Note that the initial position of the two bodies when the joint is created +corresponds to a translation of zero. Therefore, the minimum limit must be smaller or equal to zero and the maximum limit must be larger or equal to zero. + +You can see in the following example how to set the limits when the slider joint is created: + +~~~.cpp +// Create the joint info object +SliderJointInfo jointInfo(body1, body2, anchorPoint, axis); + +// Enable the limits of the joint +jointInfo.isLimitEnabled = true; + +// Minimum translation limit +jointInfo.minTranslationLimit = -1.7; + +// Maximum translation limit +jointInfo.maxTranslationLimit = 1.7; + +// Create the hinge joint in the physics world +SliderJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +You can also use the `SliderJoint::enableLimit()`, `SliderJoint::setMinTranslationLimit()` and `SliderJoint::setMaxTranslationLimit()` methods to +enable the limits of the joint after its creation. See the API documentation for more information. + +#### Motor + +The slider joint also has a motor. You can use it to translate the bodies along the slider axis at a given linear speed and such that the force applied to +move the bodies does not exceed a maximum allowed force. The motor is disabled by default. If you want to use it when the joint is created, you first have to activate it using the +`isMotorEnabled` boolean variable of the `SliderJointInfo` object before you create the joint. Then, you need to specify the linear motor speed (in meters/seconds) +using the `motorSpeed` variable and also the maximum allowed force (in Newtons) with the `maxMotorForce` variable. + +For instance, here is how to enable the motor of the slider joint when the joint is created: + +~~~.cpp +// Create the joint info object +SliderJointInfo jointInfo(body1, body2, anchorPoint, axis); + +// Enable the motor of the joint +jointInfo.isMotorEnabled = true; + +// Motor linear speed +jointInfo.motorSpeed = 2.0; + +// Maximum allowed force +jointInfo.maxMotorForce = 10.0; + +// Create the slider joint in the physics world +SliderJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +It is also possible to use the `SliderJoint::enableMotor()`, `SliderJoint::setMotorSpeed()` and `SliderJoint::setMaxMotorForce()` methods to enable the +motor of the joint after its creation. See the API documentation for more information. + +### Fixed Joint {#fixedjoint} + +The `FixedJoint` class describes a fixed joint between two bodies. In a fixed joint, there is no degree of freedom, the bodies are not +allowed to translate or rotate with respect to each other. + +In order to create a fixed joint, you simply need to specify an anchor point to create the `FixedJointInfo` +object. The `FixedJointInfo` class contains different constructors that you can use whether you want to specify the anchor point in local-space +or world-space. Make sure that the two bodies are in a valid position (with respect to the joint constraint) at the beginning of the simulation. + +For instance, here is how to create the joint info object for a fixed joint: + +~~~.cpp +// Anchor point in world-space +Vector3 anchorPoint(2.0, 3.0, 4.0); + +// Create the joint info object +FixedJointInfo jointInfo1(body1, body2, anchorPoint); +~~~ + +Now, it is possible to create the actual joint in the physics world using the `PhysicsWorld::createJoint()` method. +Note that this method will also return a pointer to the `FixedJoint` object that has been created internally. You will then +be able to use that pointer to change properties of the joint and also to destroy it at the end. + +Here is how to create the joint in the world: + +~~~.cpp +// Create the fixed joint in the physics world +FixedJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +### Collision between the bodies of a Joint + +By default, the two bodies involved in a joint are able to collide with each other. However, it is possible to disable the collision between the two bodies that are part +of the joint. To do it, you simply need to set the variable `isCollisionEnabled` of the joint info object to **false** when you create the joint. + +For instance, when you create a `HingeJointInfo` object in order to construct a hinge joint, you can disable the collision between the two bodies of the joint as in the following example: + +~~~.cpp +// Create the joint info object +HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); + +// Disable the collision between the bodies +jointInfo.isCollisionEnabled = false; + +// Create the joint in the physics world +HingeJoint* joint; +joint = dynamic_cast(world->createJoint(jointInfo)); +~~~ + +### Destroying a Joint {#destroyjoint} + +In order to destroy a joint, you simply need to call the `PhysicsWorld::destroyJoint()` method using the pointer to +a previously created joint object as argument as shown in the following code: + +~~~.cpp +// BallAndSocketJoint* joint is a previously created joint + +// Destroy the joint +world->destroyJoint(joint); +~~~ + +It is important that you destroy all the joints that you have created at the end of the simulation. Also note that destroying a +rigid body involved in a joint will automatically destroy that joint. + +## Ray casting {#raycasting} + +You can use ReactPhysics3D to test intersection between a ray and the bodies of the world you have created. Ray casting can be performed against +multiple bodies, a single body or any collider of a given body. Note that ray casting only works from the outside of the bodies. If the origin +of a ray is inside a collision shape, no hit will be reported. + +The first thing you need to do is to create a ray using the `Ray` class of ReactPhysics3D. As you can see in the following example, this is +very easy. You simply need to specify the point where the ray starts and the point where the ray ends (in world-space coordinates). \\ + +~~~.cpp +// Start and end points of the ray +Vector3 startPoint(0.0, 5.0, 1.0); +Vector3 endPoint(0.0, 5.0, 30); + +// Create the ray +Ray ray(startPoint, endPoint); +~~~ + +Any ray casting test that will be described in the following sections returns a `RaycastInfo` object in case of intersection with the ray. +This structure contains the following attributes: + + - **worldPoint** Hit point in world-space coordinates + - **worldNormal** Surface normal of the collider at the hit point in world-space coordinates + - **hitFraction** Fraction distance of the hit point between **startPoint** and **endPoint** of the ray. The hit point **p** is such that + \f$p = startPoint + hitFraction \cdot (endPoint - startPoint)\f$ + - **body** Pointer to the rigid body that has been hit by the ray + - **collide** Pointer to the collider that has been hit by the ray + +Note that you can also use collision filtering with ray casting in order to only test ray intersection with specific colliders. +Collision filtering is described in this [section](#collisionfiltering). + +### Ray casting against multiple bodies + +This section describes how to get all the colliders of all bodies in the world that are intersected by a given ray. + +#### The RaycastCallback class + +First, you have to implement your own class that inherits from the `RaycastCallback` class. Then, you need to override the +`RaycastCallback::notifyRaycastHit()` method in your own class. An instance of your class have to be provided as a parameter +of the raycast method and the `notifyRaycastHit()` method will be called for each collider that is hit by the ray. You will receive, as a parameter +of this method, a `RaycastInfo` object that will contain the information about the raycast hit (hit point, hit surface normal, hit body, hit collider, ...). + +In your `notifyRaycastHit()` method, you need to return a fraction value that will specify the continuation of the ray cast after a hit. +The return value is the next maxFraction value to use. If you return a fraction of 0.0, it means that the raycast should terminate. If you return a +fraction of 1.0, it indicates that the ray is not clipped and the ray cast should continue as if no hit occurred. If you return the fraction in the +parameter (hitFraction value in the `RaycastInfo` object), the current ray will be clipped to this fraction in the next queries. If you return -1.0, it will +ignore this collider and continue the ray cast. Note that no assumption can be done about the order of the calls of the `notifyRaycastHit()` method. + +Here is an example about creating your own raycast callback class that inherits from the `RaycastCallback` class and how to override the +`notifyRaycastHit()` method: + +~~~.cpp +// Class WorldRaycastCallback +class MyCallbackClass : public RaycastCallback { + + public: + + virtual decimal notifyRaycastHit(const RaycastInfo& info) { + + // Display the world hit point coordinates + std::cout << "Hit point : " << + info.worldPoint.x << + info.worldPoint.y << + info.worldPoint.z << + std::endl; + + // Return a fraction of 1.0 to gather all hits + return decimal(1.0); + } +}; +~~~ + +#### Raycast query in the world + +Now that you have your own raycast callback class, you can use the `PhysicsWorld::raycast()` method to perform a ray casting test +on a physics world. + +The first parameter of this method is a reference to the `Ray` object representing the ray you need to test intersection with. The second parameter is a pointer to the object of your raycast callback object. You can specify an optional third parameter which is the bit mask for collision filtering. +It can be used to raycast only against selected categories of colliders as described in this [section](#collisionfiltering). + +~~~.cpp +// Create the ray +Vector3 startPoint(1 , 2, 10); +Vector3 endPoint(1, 2, -20); +Ray ray(startPoint, endPoint); + +// Create an instance of your callback class +MyCallbackClass callbackObject; + +// Raycast test +world->raycast(ray, &callbackObject); +~~~ + +### Ray casting against a single body + +You can also perform ray casting against a single specific rigid body of the world. To do this, you need to use the +`RigidBody::raycast()` method. This method takes two parameters. The first one is a reference to the `Ray` object and the second one +is a reference to the `RaycastInfo` object that will contain hit information if the ray hits the body. This method returns true if the ray hits the +body. The `RaycastInfo` object will only be valid if the returned value is **true** (a hit occured). + +The following example shows how test ray intersection with a body: + +~~~.cpp +// Create the ray +Vector3 startPoint(1 , 2, 10); +Vector3 endPoint(1, 2, -20); +Ray ray(startPoint, endPoint); + +// Create the raycast info object for the +// raycast result +RaycastInfo raycastInfo; + +// Raycast test +bool isHit = body->raycast(ray, raycastInfo); +~~~ + +### Ray casting against the collider of a body + +You can also perform ray casting against a single specific collider of a rigid body of the world. To do this, you need to use the +`Collider::raycast()` method of the given collider. This method takes two parameters. The first one is a reference to the `Ray` +object and the second one is a reference to the ̀RaycastInfo} object that will contain hit information if the ray hits the body. This method returns +true if the ray hits the body. The `RaycastInfo` object will only be valid if the returned value is **true** (a hit occured). + +The following example shows how to test ray intersection with a given collider: + +~~~.cpp +// Create the ray +Vector3 startPoint(1 , 2, 10); +Vector3 endPoint(1, 2, -20); +Ray ray(startPoint, endPoint); + +// Create the raycast info object for the +// raycast result +RaycastInfo raycastInfo; + +// Test raycasting against a collider +bool isHit = collider->raycast(ray, raycastInfo); +~~~ + +## Testbed application {#testbed} + +![Testbed](images/testbed.png) + +The testbed application contains a graphical interface where you can select and see several demo scenes using the ReactPhysics3D library. + +As described in this [section](#building), you can enable a `CMake` option to build the testbed application when you build the library. +Note that OpenGL is required to compile it. + +The testbed application can be found in the `testbed/` folder of +the ReactPhysics3D library. Do not hesitate to take a look at the code of the demo scenes to better understand how +to use the library in your application. + +## Receiving Feedback {#receiving_feedback} + +Sometimes, you want to receive notifications from the physics engine when a given event occurs. The `EventListener` class can be used for that +purpose. In order to use it, you need to create a new class that inherits from the `EventListener` class and overrides some methods that will +be called by the ReactPhysics3D library when some events occur. You also need to register your class in the physics world using the +`PhysicsWorld::setEventListener()` as in the following code: + +~~~.cpp +// Your event listener class +class YourEventListener : public EventListener { + +... +}; + +YourEventListener listener; + +// Register your event listener class +world->setEventListener(&listener); +~~~ + +### Contacts + +The `EventListener` contains a `onContact()` method. If you override this method in your custom event listener class, this method will +be called when you call the `PhysicsWorld::update()` method if there are contacts to report during the current update. + +The `onContact()` method will be called with a `CollisionCallback::CallbackData` object in parameter. This object has a +`getNbContactPairs()` and a `getContactPair()` methods that will allow you to get each contact pair (class +`CollisionCallback::ContactPair`) that occured during the physics world update. Note that a contact pair contains a list of contact +points between two bodies. Once you get a contact pair, you can use the `getBody1()` or `getBody2()` methods to get the bodies in +contact, you can use the `getCollider1()` or `getCollider2()` methods to get the colliders in contact and the +`getNbContactPoints()` and `getContactPoint()` methods to get the contact points (class `CollisionCallback::ContactPoint`) +of the contact pair. Note that the `onContact()` method will report contacts after the collision detection phase and before the contact +resolution. + +The contact pair also has a `getEventType()` method that gives information about the type of contact event for the pair. It can be +`ContactStart` if the two bodies of the contact pair where not colliding in the previous call to `PhysicsWorld::update()` and are now +colliding. It can be `ContactStay`, if the two bodies where colliding in the previous frame and are still colliding or it can be +`ContactExit` if the two bodies where colliding in the previous frame but are not colliding anymore. Note that in this last situation, there +will be no contact points in the contact pair. + +As you know, rigid bodies can sleep. During that time, no contacts will be reported using the `EventListener::onContact()` method. Therefore, +If you stop receiving contact notifications for a rigid body, it does not necessarily mean that there is no contacts anymore but it could mean that the +body has fallen asleep. You need to use the `getEventType()` method to know if the body is not colliding anymore (when you receive an event of type +`ContactExit`). + +In the following example, you can see how to override the `EventListener::onContact()` method to get the current contact points of the physics +world: + +~~~.cpp +// Your event listener class +class YourEventListener : public EventListener { + +// Override the onContact() method +virtual void onContact(const CollisionCallback::CallbackData& callbackData) overrride { + + // For each contact pair + for (uint p = 0; p < callbackData.getNbContactPairs(); p++) { + + // Get the contact pair + CollisionCallback::ContactPair contactPair = callbackData.getContactPair(p); + + // For each contact point of the contact pair + for (uint c = 0; c < contactPair.getNbContactPoints(); c++) { + + // Get the contact point + CollisionCallback::ContactPoint contactPoint = contactPair.getContactPoint(c); + + // Get the contact point on the first collider and convert it in world-space + Vector3 worldPoint = contactPair.getCollider1()->getLocalToWorldTransform() * contactPoint.getLocalPointOnCollider1(); + + ... + } + } +} + +}; +~~~ + +### Triggers {#eventlistenertriggers} + +As described in this [section](#trigger) it is possible to set a collider as being a trigger. You can do this if you do not want any collision with +this collider but you are only interested in knowing when another collider enters or exit the volume of the collider. If you do this, you will need +to override the `onTrigger()` method of the `EventListener`. This method will be called and report all the collision with your triggers +when you call the `PhysicsWorld::update()` method. + +The `onTrigger()` method will be called with an `OverlapCallback::CallbackData` object in parameter. This object has a +`getNbOverlappingPairs()` and a `getOverlappingPair()` methods that will allow you to get each overlapping pair (class +`OverlapCallback::OverlapPair`) that occured during the physics world update. Once you get an overlapping pair, you can use +the `getBody1()` or `getBody2()` methods to get the bodies in +contact and the `getCollider1()` or `getCollider2()` methods to get the trigger colliders in contact. +Note that the `onTrigger()` method will report overlaping triggers after the collision detection phase and before the contact resolution. + +The overlapping pair also has a `getEventType()` method that gives information about the type of overlapping event for the pair. It can be +`OverlapStart` if the two bodies of the pair where not overlapping in the previous call to `PhysicsWorld::update()` and are now +overlapping. It can be `OverlapStay`, if the two bodies where overlapping in the previous frame and are still overlapping or it can be +`OverlapExit` if the two bodies where overlapping in the previous frame but are not overlapping anymore. + +As you know, rigid bodies can sleep. During that time, no overlap will be reported using the `EventListener::onTrigger()` method. Therefore, +If you stop receiving trigger overlap notifications for a rigid body, it does not necessarily mean that there is no overlap anymore but it could mean +that the body has fallen asleep. You need to use the `getEventType()` method to know if the body is not overlapping anymore (when you receive an +event of type `OverlapExit`). + +Note that the `onTrigger()` method is only called for colliders that you have set as triggers using the `Collider::setIsTrigger()` method. + +## Profiler {#profiler} + +If you build the library with the `RP3D_PROFILING_ENABLED` variable enabled (see this [section](#cmakevariables)), a real-time profiler +will collect information while the application is running. Then, at the end of your application, when the destructor of the `PhysicsWorld` +class is called, information about the running time of the library will written to a file. +This can be useful to know where time is spent in the different parts of the ReactPhysics3D library in case your application is too slow. + +Each physics world has its own profiler. By default, the profiling report wil be written in a text file next to the executable. +If you have multiple worlds in your application, there will be one profile file for each world. The profile files will be named after the +name of the worlds. By defaults worlds will have names: world, world1, world2, world3, ... You can change the name of the world by +setting it into the `WorldSettings` object when you create the world (see this [section](#physicsworld)). + +## Logger {#logger} + +ReactPhysics3D has an internal logger that can be used to get logs from the library while the application is running. This can be useful for +debugging for instance. + +The logger is part of the `PhysicsCommon` class. By default there is no logger set. If you want to create your custom logger to receive logs +from ReactPhysics3D, you can inherit the `Logger` class and override the `Logger::log()` method. Then, you have to use the +`PhysicsCommon::setLogger()` method to set the logger. + +Getting the logs is the way for you to see the errors reported by the library and therefore, you should not just ignore the errors from the logs. +You should at least redirect the logs (warnings and errors) in the standard output while debugging your application as described in the example +below. + +If you don't want to create your custom logger class, you can use the default logger of ReactPhysics3D (class `DefaultLogger`). With this +logger, you can output the logs into +a file or a std::stream. The logs can have different format like raw text of HTML. In order to instantiate the default logger, you need to call the +`PhysicsCommon::createDefaultLogger()` method. Then, you can customize this logger and finally, you need to set this logger using the +`PhysicsCommon::setLogger()` method. + +The following code shows how to create a default logger that will output logs (warning and errors) in HTML into a file and in raw text into +the standard output: + +~~~.cpp +// Create the default logger +DefaultLogger* logger = physicsCommon.createDefaultLogger(); + +// Log level (warnings and errors) +uint logLevel = static_cast(static_cast(Logger::Level::Warning) | static_cast(Logger::Level::Error); + +// Output the logs into an HTML file +logger->addFileDestination("rp3d_log_" + name + ".html", logLevel, DefaultLogger::Format::HTML); + +// Output the logs into the standard output +logger->addStreamDestination(std::cout, logLevel, DefaultLogger::Format::Text); + +// Set the logger +physicsCommon.setLogger(logger); +~~~ + +## Debug Renderer {#debugrenderer} + +For debugging purpose, it can be useful to display the physics objects of ReactPhysics3D on top of your simulation. This is possible using the +`DebugRenderer` class of the library that allows you to display the colliders shapes, the AABB of the colliders or the contact points for +instance. This can help you to check for instance if the physics collider of ReactPhysics3D is correctly aligned with the object you are rendering. +When debug rendering is enabled, ReactPhysics3D will generate each time you call the `PhysicsWorld::update()` method (each frame), some rendering +primitives (lines and triangles) that you will be able to retrieve and display on you simulation (with OpenGL or DirectX for instance). Note that +ReactPhysics3D will only generate the arrays of lines and triangles but you are responsible to draw them. + +![Debug Rendering](images/DebugRendering.png) + +By default, the debug rendering is disabled. You can activate it using the `PhysicsWorld::setIsDebugRenderingEnabled()` method. Note that you +should disable it for the final release because this can be quite expensive to compute. You can get a reference to `DebugRenderer` of the physics +world using the `PhysicsWorld::getDebugRenderer()` method. You also need to enable debugging for each body that you want to debug using the +`Body::setIsDebugEnabled()` method. By default, debuging is disabled for all bodies. Then, you need to select the debug items +(class `DebugRenderer:DebugItem`) you want to display. For instance, you might want to display the shapes of the colliders +(`DebugItem::COLLISION_SHAPE`), the broad-phase AABBs of the colliders (`DebugItem::COLLIDER_BROADPHASE_AABB`), the colliders AABBs +(`DebugItem::COLLIDER_AABB`), the contact points (`DebugItem::CONTACT_POINT`) or the contact normals (`DebugItem::CONTACT_NORMAL`). +You need to use the `DebugRenderer::setIsDebugItemDisplayed()` method to select if you want or not a given debug item to be displayed. + +The following code shows how to enable debug rendering in order to display contact points and contact normals: + +~~~.cpp +// Enable debug rendering +physicsWorld->setIsDebugRenderingEnabled(true); + +// Enable debugging for each rigid body that we want to display +for (RigidBody* body: rigidBodies) { + body->setIsDebugEnabled(true); +} + +// Get a reference to the debug renderer +DebugRenderer& debugRenderer = physicsWorld->getDebugRenderer(); + +// Select the contact points and contact normals to be displayed +debugRenderer.setIsDebugItemDisplayed(DebugRenderer::DebugItem::CONTACT_POINT, true); +debugRenderer.setIsDebugItemDisplayed(DebugRenderer::DebugItem::CONTACT_NORMAL, true); +~~~ + +Now each time you call the `PhysicsWorld::update()` method, ReactPhysics3D will generate arrays with lines and triangles so that you can +draw them in your application. You can use the `DebugRenderer::getNbLines()` and `DebugRenderer::getNbTriangles()` methods to know +the number of lines and triangles to draw. Then, you need to call the `DebugRenderer::getLinesArray()` and +`DebugRenderer::getTrianglesArray()` methods to get pointers to the beginning of the lines and triangles arrays. The lines array contain +elements of type `DebugRenderer:DebugLine` with the two points of a line and their colors and the triangles array has elements of +type `DebugRenderer:DebugTriangle` with the three points of each triangle and their colors. Note that the vertices of the lines and triangles +are defined in world-space coordinates of the physics world. + +## Determinism {#determinism} + +Sometimes, it is important that the simulation of a physics world behaves exactly the same each time we run it. Because of the differences in +compilers and computer hardware it is quite difficult to achieve this between different machines. However, ReactPhysics3D should be deterministic when +compiled with the same compiler and running on the same machine. In order to obtain two similar runs of the simulation of a physics world, it is +adviced to completely destroy and recreate the physics world, the bodies and the joints inside it (in order to reset all the internal data cached +for the simulation). You must also create the bodies and joints in the same order each time and make sure that all the calls to the methods +of your physics world, bodies and joints happen in the same order. + +## Discussions {#discussions} + +If you have any questions about the library, you can ask them [here](https://github.com/DanielChappuis/reactphysics3d/discussions) in +the Discussions section. Ask your question there instead of opening a new issue on the repository. + +## Issues {#issues} + +If you find some bugs, do not hesitate to report them on our issue tracker here: + +[https://github.com/DanielChappuis/reactphysics3d/issues](https://github.com/DanielChappuis/reactphysics3d/issues) + +Thanks a lot for reporting the issues that you find. It will help us correct and improve the library. diff --git a/documentation/UserManual/ReactPhysics3D-UserManual.pdf b/documentation/UserManual/ReactPhysics3D-UserManual.pdf deleted file mode 100644 index 805e3cf05..000000000 Binary files a/documentation/UserManual/ReactPhysics3D-UserManual.pdf and /dev/null differ diff --git a/documentation/UserManual/ReactPhysics3D-UserManual.tex b/documentation/UserManual/ReactPhysics3D-UserManual.tex deleted file mode 100644 index 6a9432986..000000000 --- a/documentation/UserManual/ReactPhysics3D-UserManual.tex +++ /dev/null @@ -1,2436 +0,0 @@ -\documentclass[a4paper,12pt]{article} - -% packages -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\usepackage{amsmath} -\usepackage{amsfonts} -\usepackage{amssymb} -\usepackage{graphicx} -\usepackage{listings} -\usepackage[obeyspaces,spaces]{url} -\usepackage{hyperref} -\usepackage[dvipsnames]{xcolor} -\hypersetup{ - colorlinks, - linkcolor={MidnightBlue}, - citecolor={MidnightBlue}, - urlcolor={MidnightBlue} -} -\usepackage[top=3cm, bottom=3cm, left=3cm, right=3cm]{geometry} - -\usepackage[scaled]{helvet} -\renewcommand\familydefault{\sfdefault} - -% Path to the pictures -\graphicspath{ {images/} } - -% Customized style to display c++ source code -\lstdefinestyle{customcpp}{ - backgroundcolor=\color{gray!15}, - belowcaptionskip=1\baselineskip, - breaklines=true, - frame=single, - xleftmargin=\parindent, - language=C++, - showstringspaces=false, - basicstyle=\small\ttfamily, - keywordstyle=\bfseries\color{purple}, - commentstyle=\bfseries\color{gray}, - identifierstyle=\bfseries\color{black}, - stringstyle=\bfseries\color{blue}, - tabsize=2 -} -\lstset{style=customcpp} - -% Customized style to display c++ source code -\lstdefinestyle{customcmake}{ - backgroundcolor=\color{gray!15}, - belowcaptionskip=1\baselineskip, - breaklines=true, - frame=single, - xleftmargin=\parindent, - language=bash, - showstringspaces=false, - basicstyle=\small\ttfamily, - keywordstyle=\bfseries\color{purple}, - commentstyle=\bfseries\color{gray}, - identifierstyle=\bfseries\color{black}, - stringstyle=\bfseries\color{blue}, - tabsize=2 -} - -\input{title} - -\begin{document} - \author{Daniel Chappuis} - \title{ReactPhysics3D \\ User Manual} - \maketitle - - \tableofcontents - - \newpage - - - \section{Introduction} - - ReactPhysics3D is an open source C++ physics engine library that can be used - in 3D simulations and games. The library is released under the ZLib license. - - \section{Features} - - The ReactPhysics3D library has the following features: - - \begin{itemize} - \item Rigid body dynamics - \item Discrete collision detection - \item Collision shapes (Sphere, Box, Capsule, Convex Mesh, Static Concave Mesh, Height Field) - \item Multiple collision shapes per body - \item Broadphase collision detection (Dynamic AABB tree) - \item Narrowphase collision detection (SAT/GJK) - \item Collision response and friction (Sequential Impulses Solver) - \item Joints (Ball and Socket, Hinge, Slider, Fixed) - \item Collision filtering with categories - \item Ray casting - \item Sleeping technique for inactive bodies - \item Multi-platform (Windows, Linux, Mac OS X) - \item No external libraries (do not use STL containers) - \item Documentation (user manual and Doxygen API) - \item Testbed application with demos - \item Integrated profiler - \item Debug renderer - \item Logs - \item Unit tests - \end{itemize} - - \section{License} - - The ReactPhysics3D library is released under the open-source ZLib license. For more information, read the "LICENSE" file. - - \section{Building and installing the library} - \label{sec:building} - - In order to build the library on your system, you first need to clone the code repository with the following command: \\ - - \texttt{git clone https://github.com/DanielChappuis/reactphysics3d.git} \\ - - Note that the \emph{git} versioning software needs to be installed on your system. \\ - - Then, you will need to build (compile) the library and install it on your system in order to use it in your project. - The best way is to use CMake for that. CMake will generate the necessary files on your platform (Windows, OS X or Linux) to build - the library. \\ - - CMake can be downloaded at \url{http://www.cmake.org} or using your package-management program (apt, yum, \dots) on Linux. - If you have never used CMake before, you should read the page \url{http://www.cmake.org/cmake/help/runningcmake.html} as - it contains a lot of useful information. \\ - - The remaining of this section will describe how to build and install the library with CMake. \\ - - \subsection{Configure and generate the native tool files} - - Now we need to configure CMake to tell it what you want to build. Maybe you simply want to build the library in \emph{debug} or \emph{release} - mode or maybe you also want to build the unit tests or the testbed application with demos. At the end of this step, CMake will generate the - native build tool files on your platform that you will use to build the library. For instance, it can generate a Visual Studio solution on Windows, - a XCode project on OS X or files for the \texttt{make} command on OS X or Linux. - - \subsubsection{Configure and generate with the command line (Linux and Mac OS X)} - - First, we will see how to configure CMake and generate the native build tool files using the CMake tool with the command line. - First, you need to create a folder where you want to build the library. Then go into that folder and run the following \texttt{ccmake} command: \\ - - \texttt{ccmake \textless path\_to\_library\_source\textgreater} \\ - - \begin{sloppypar} - where \texttt{\textless path\_to\_library\_source\textgreater} must be replaced - by the path to the \path{reactphysics3d/} folder of the repository you have cloned. It is the folder that - contains the \texttt{CMakeLists.txt} file of ReactPhysics3D. Running this command will launch the CMake command line interface. - Hit the 'c' key to configure the project. There, you can also change some predefined options (see section \ref{sec:cmakevariables} for more details) - and then, hit the 'c' key again to configure the build. Once you have set all the values as you like, you can hit the 'g' key to generate the - native build tool files in the build directory that you have created before. Finally, you can exit the CMake interface. \\ - \end{sloppypar} - - \subsubsection{Configure and generate using the CMake graphical interface (Linux, Mac OS X and Windows)} - - If your prefer, you can use the graphical user interface of CMake instead. To do this, - run the \texttt{cmake-gui} program. First, the program will ask you for the - source folder. You need to select the \path{reactphysics3d/} folder of the repository you have cloned. You will also have to select a - folder where you want to - build the library. Select any empty folder that is on your system. Then, you can click on \emph{Configure}. CMake will ask you to - choose an IDE that is on your system that will be used to compile the library. For instance, you can select Visual Studio, - Qt Creator, XCode, ... Then, click on the \emph{Finish} button. Then, you can change the compilation options. See - section \ref{sec:cmakevariables} to see what are the possible options. - Once this is done, click on \emph{Configure} again and finally on \emph{Generate} as you can see in the following picture. \\ - - \begin{figure}[!ht] - \centering - \includegraphics[scale=0.6]{CMakeWin.png} - \label{fig:cmakewin} - \end{figure} - - Now, if you go into the folder you have chosen to build the library, you should find the native build tool files that you will use to build - the library on your platform. - - \subsection{Building the library} - - Now, that you have generated the native build tool files on your system, you will need to build (compile) the library. - - \subsubsection{Building the library using \texttt{make} on the command line (Linux, Mac OS X)} - - On Linux or Mac OS X, you can compile the library on the command line using the \texttt{make} command. Go into the directory where you have generated the - native build tool files and run the following command: \\ - - \texttt{make} \\ - - The library will start compiling. - - \subsubsection{Building the library with Visual Studio (Windows)} - - If you have generated the native build tool files in the previous step on Windows, you should have obtained a Visual Studio solution of ReactPhysics3D. - Now, you can open the Visual Studio solution (.sln file). Once Visual Studio is open, you first need to change the compilation mode to \emph{Release} - at the top instead of \emph{Debug}. Then, right click on the \emph{reactphysics} project in the Solution - Explorer and click on \emph{Build} in order to compile the library (see the following picture). - - \begin{figure}[!ht] - \centering - \includegraphics[scale=0.6]{VSBuild.png} - \label{fig:vsbuild} - \end{figure} - - The library will start compiling. - - \subsection{Installing the library} - - Now that you have compiled the library, you can install it on your system in order to put the compiled library file, the header files and the exported - CMake targets in a standard location on your system so that it can be easily imported into your project. - - \subsubsection{Installing the library using the \texttt{make} on the command line (Linux, Mac OS X)} - - On Linux or Mac OS X, you can use the \texttt{make} command to install the library. You simply need to run the following command: \\ - - \texttt{sudo make install} \\ - - The library is now installed on your system. For instance, On Linux Ubuntu, the library may have been installed in the \path{/usr/local/lib/} folder - and the header files in the \path{/usr/local/include/} folder. - - \subsubsection{Installing the library on Windows with Visual Studio} - - In order to install the library on your system using Visual Studio, you need to open Visual Studio with administrator rights. This is needed in order - to have the correct rights to write the files in the \path{C:\Program Files (x86)\} folder on your computer for instance. To do that, type - \emph{Visual Studio} in the Start Menu, when Visual Studio has been found, right click on it and click on \emph{Run as administrator}. This will open - Visual Studio with administrator rights. \\ - - Then, you need to open the Visual Studio solution (.sln file) of ReactPhysics3D that has been generated - previously with CMake. To do that, click on \emph{File} in the top menu of Visual Studio, then on \emph{Open} and \emph{Project/Solution...}. Then, - you need to select the ReactPhysics3D Visual Studio solution (.sln file) on your system. Once the solution is open, you first need to change the mode - at the top to \emph{Release} instead of \emph{Debug}. Then, right click on the \emph{INSTALL} - project in the Solution Explorer menu and click on \emph{Build} (see the following picture). This will install the ReactPhysics3D library in a - standard location on your system like \path{C:\Program Files (x86)\ReactPhysics3D\} for instance. - - \begin{figure} - \centering - \includegraphics[scale=0.6]{VSInstall.png} - \label{fig:vsinstall} - \end{figure} - - \vspace{5.0cm} - - \subsection{CMake Options} - \label{sec:cmakevariables} - - You can find below the different CMake options that you can set before building the library: - - \begin{description} - \item[CMAKE\_BUILD\_TYPE] If this variable is set to \texttt{Debug}, the library will be compiled in debugging mode. - This mode should be used during development stage to know where things might crash. - In debugging mode, the library might run a bit slow due to all the debugging information and asserts. - However, if this variable is set to \texttt{Release}, no debugging information is generated - and therefore, it will run much faster. This mode must be used when you compile the final - release of your application. - - \item[RP3D\_COMPILE\_TESTBED] If this variable is \texttt{ON}, the tesbed application with demos will be compiled. - The testbed application uses OpenGL for rendering. - Take a look at the section \ref{sec:testbed} for more information about the testbed application. - - \item[RP3D\_COMPILE\_TESTS] If this variable is \texttt{ON}, the unit tests of the library will be compiled. You will then - be able to launch the tests to make sure that they are running fine on your system. - - \item[RP3D\_PROFILING\_ENABLED] If this variable is \texttt{ON}, the integrated profiler will collect data during the execution of the application. - This might be useful to see which part of the ReactPhysics3D - library takes time during its execution. This variable must be set to \texttt{OFF} when you compile - the final release of your application. You can find more information about the profiler in section \ref{sec:profiler}. - - \item[RP3D\_DOUBLE\_PRECISION\_ENABLED] If this variable is \texttt{ON}, the library will be compiled with double floating point precision. - Otherwise, the library will be compiled with single precision. - \end{description} - - \section{Using ReactPhysics3D in your application} - - If you have built and installed the ReactPhysics3D on your system with CMake as explained in the section \ref{sec:building}, it is easy to import the - library in your project. You probably already have a \path{CMakeLists.txt} file for your project. Therefore, to import the ReactPhysics3D - library, you simply need to add the following line in the \path{CMakeLists.txt} file of your project. - - \lstset{style=customcmake} - - \vspace{0.6cm} - - \begin{lstlisting} -find_package(ReactPhysics3D REQUIRED) - \end{lstlisting} - - \vspace{0.6cm} - - This will tell CMake to find the installed ReactPhysics3D library on your system and import the library file and headers so that you can - link it to your project. Note that if you are working on Windows or Mac OS X, you might need to use the following code in your \path{CMakeLists.txt} file - before calling the previous function. This will help CMake to find the installed ReactPhysics3D library on Windows or Mac OS X. - - \vspace{0.6cm} - - \begin{lstlisting} -if(WIN32) - list(APPEND CMAKE_PREFIX_PATH "C:\\Program Files (x86)\\ReactPhysics3D") -elseif(APPLE) - list(APPEND CMAKE_PREFIX_PATH "/usr/local/lib/cmake/ReactPhysics3D") -endif() - \end{lstlisting} - - \vspace{0.6cm} - - Then, you need to tell CMake that your project (executable) depends on ReactPhysics3D with the following line in your \path{CMakeLists.txt} file: - - \vspace{0.6cm} - - \begin{lstlisting} -target_link_libraries(helloworld ReactPhysics3D::ReactPhysics3D) - \end{lstlisting} - - \vspace{0.6cm} - - The ReactPhyscis3D repository contains a folder with an \emph{Hello World} project \href{https://github.com/DanielChappuis/reactphysics3d/tree/master/helloworld}{here}. In this folder, you can find a \path{CMakeLists.txt} and a \path{Main.cpp} file that show how to import and use the ReactPhysics3D library in a simple project. \\ - - Here is the example \path{CMakeLists.txt} file of the \emph{Hello World} project: - - \vspace{0.6cm} - - \begin{lstlisting} -# Minimum cmake version required -cmake_minimum_required(VERSION 3.8) - -# Help CMake to find the installed library on Windows or Mac OS X -if(WIN32) - list(APPEND CMAKE_PREFIX_PATH "C:\\Program Files (x86)\\ReactPhysics3D") -elseif(APPLE) - list(APPEND CMAKE_PREFIX_PATH "/usr/local/lib/cmake/ReactPhysics3D") -endif() - -# Import the ReactPhysics3D library -find_package(ReactPhysics3D REQUIRED) - -# Project -project(HelloWorld) - -# Create the executable -add_executable(helloworld Main.cpp) - -# Link with the ReactPhysics3D library -target_link_libraries(helloworld ReactPhysics3D::ReactPhysics3D) - \end{lstlisting} - - \lstset{style=customcpp} - - \vspace{0.6cm} - - Then in your C++ source file, you need to include the main ReactPhysics3D header file with the following line: \\ - - \begin{lstlisting} -// Include the main ReactPhysics3D header file -#include - \end{lstlisting} - - \vspace{0.6cm} - - Also note that all the classes of the library are available in the \texttt{reactphysics3d} namespace or its shorter alias - \texttt{rp3d}. Therefore, you can use this namespace in your code with the following declaration: \\ - - \begin{lstlisting} -// Use the ReactPhysics3D namespace -using namespace reactphysics3d; - \end{lstlisting} - - \vspace{0.6cm} - - Here is the \path{Main.cpp} file of the \emph{Hello World} project: \\ - - \begin{lstlisting} -// Libraries -#include -#include - -// ReactPhysics3D namespace -using namespace reactphysics3d; - -// Main function -int main(int argc, char** argv) { - - // First you need to create the PhysicsCommon object. - // This is a factory module that you can use to create physics - // world and other objects. It is also responsible for - // logging and memory management - PhysicsCommon physicsCommon; - - // Create a physics world - PhysicsWorld* world = physicsCommon.createPhysicsWorld(); - - // Create a rigid body in the world - Vector3 position(0, 20, 0); - Quaternion orientation = Quaternion::identity(); - Transform transform(position, orientation); - RigidBody* body = world->createRigidBody(transform); - - const decimal timeStep = 1.0f / 60.0f; - - // Step the simulation a few steps - for (int i=0; i < 20; i++) { - - world->update(timeStep); - - // Get the updated position of the body - const Transform& transform = body->getTransform(); - const Vector3& position = transform.getPosition(); - - // Display the position of the body - std::cout << "Body Position: (" << position.x << ", " << - position.y << ", " << position.z << ")" << std::endl; - } - - return 0; -} - \end{lstlisting} - - \section{The PhysicsCommon object} - \label{sec:physicscommon} - - The first thing you need to do when you want to use ReactPhysics3D is to instantiate the \texttt{PhysicsCommon} class. - This main object will then be used as a factory to instantiate one or multiple physics worlds and other objects. This class is also - responsible for the memory management of the library. All the memory allocations are centralized into this \texttt{PhysicsCommon} object. - This class also contains the logger for the different events that can occur. \\ - - In order to use ReactPhysics3D, you have to create an instance of the \texttt{PhysicsCommon} class: \\ - - \begin{lstlisting} -// First you need to create the PhysicsCommon object. -PhysicsCommon physicsCommon; - \end{lstlisting} - - \vspace{0.6cm} - - Then, you can use this object to instantiate a physics world for instance: \\ - - \begin{lstlisting} -// Create a physics world -PhysicsWorld* world = physicsCommon.createPhysicsWorld(); - \end{lstlisting} - - \vspace{0.6cm} - - When you will need to add a body into your world, you will probably need to create a collider with a given type of collision shape. - Again, you will need to use the \texttt{PhysicsCommon} object to instantiate a collision shape as in the following example: \\ - - \begin{lstlisting} -// Instanciate a sphere collision shape -SphereShape* sphereShape = physicsCommon.createSphereShape(radius); - \end{lstlisting} - - \vspace{0.6cm} - - As you can see, the \texttt{PhysicsCommon} object is the first thing you will need to instantiate in order to use ReactPhycsi3D in your code. - - \section{Memory Management} - - The \texttt{PhysicsCommon} class is responsible for all the memory allocations that occur in ReactPhysics3D. The base memory allocations in ReactPhysics3D - are done by default using the \texttt{std::malloc()} and \texttt{std::free()} methods. If you want to use your own behavior to allocate and free - memory, you can pass a custom memory allocator to the constructor of the \texttt{PhysicsCommon} object. You simply need to create a class - that inherits from the \texttt{MemoryAllocator} class of ReactPhysics3D and overrides the \texttt{allocate()} and \texttt{release()} methods. \\ - - Note that the allocated memory returned by the \texttt{allocate()} method must be 16 bytes aligned. \\ - - Note that several methods of ReactPhysics3D will create an instance of an object and return a pointer so that you can use that object. This the case - for the creation of a \texttt{PhysicsWorld} or a \texttt{RigidBody} as you can see in the following code: \\ - - \begin{lstlisting} -// Create a physics world -PhysicsWorld* world = physicsCommon.createPhysicsWorld(); - -... - -// Create a rigid body -RigidBody* body = world->createRigidBody(transform); - \end{lstlisting} - - \vspace{0.6cm} - - Note that because those objects have been instantiated by ReactPhysics3D and not by you, the library is responsible to delete those objects. Therefore, - you must not call the C++ \texttt{delete} operator on those objects. There are methods that you can call to destroy those objects when you do not need - them anymore to release memory but if you don't do it, the library will do it for you when the \texttt{PhysicsCommon} object is deleted. The - following example shows how to destroy previously created \texttt{RigidBody} and \texttt{PhysicsWorld}: \\ - - \begin{lstlisting} -// Destroy a rigid body -world->destroyRigidBody(body); - -... - -// Destroy a physics world -physicsCommon.destroyPhysicsWorld(world); - \end{lstlisting} - - \section{Physics World} - \label{sec:physicsworld} - - Once you have created a \texttt{PhysicsCommon} object (see section \ref{sec:physicscommon}), you will have to create a physics world. A physics world is - a place where you can add the bodies that you want to simulate. It is possible to create multiple physics worlds but you will probably never need more - than one. \\ - - \begin{description} - \item[testOverlap()] This group of methods can be used to test whether the colliders of two bodies overlap or not. You can use this if you just want to - know if bodies are colliding but your are not interested in the contact information. - \item[testCollision()] This group of methods will give you the collision information (contact points, normals, ...) for colliding bodies. - \item[testPointInside()] This method will tell you if a 3D point is inside a \texttt{RigidBody} or \texttt{Collider}. - \end{description} - - The second way to use the library is to create bodies and let ReactPhysics3D animate their motions automatically using the laws of physics. This is - done by creating rigid bodies (class \texttt{RigidBody}) in your physics world and by updating the simulation by calling the - \texttt{PhysicsWorld::update()} method each frame. The rigid bodies will move according to the forces, collision between bodies and joint constraints of - the physics world. A typical use case is a 3D real-time game for instance. - - \subsection{Creating the Physics World} - - In order to create a physics world, you need to call the \texttt{createPhysicsWorld()} method of the main \texttt{PhysicsCommon} object: \\ - - \begin{lstlisting} -// Create the physics world -PhysicsWorld* world = physicsCommon.createPhysicsWorld(); - \end{lstlisting} - - \vspace{0.6cm} - - This method will return a pointer to the physics world that has been created. - - \subsubsection{World settings} - - \begin{sloppypar} - When you create a physics world as in the previous example, it will have some default settings. If you want to customize some settings, you need to - create a \texttt{PhysicsWorld::WorldSettings} object and give it in parameter when you create your physics world as in the following example: \\ - \end{sloppypar} - - \begin{lstlisting} -// Create the world settings -PhysicsWorld::WorldSettings settings; -settings.defaultVelocitySolverNbIterations = 20; -settings.isSleepingEnabled = false; -settings.gravity = Vector3(0, -9.81, 0); - -// Create the physics world with your settings -PhysicsWorld* world = physicsCommon.createPhysicsWorld(settings); - \end{lstlisting} - - \vspace{0.6cm} - - The settings are copied into the world at its creation. Therefore, changing the values of your \texttt{PhysicsWorld::WorldSettings} instance after the - world creation will not have any effect. However, some methods are available to change settings after the world creation. You can take a - look at the API documentation to see what world settings can be changed in the \texttt{PhysicsWorld} class. \\ - - \subsection{Customizing the Physics World} - - \subsubsection{Solver parameters} - - ReactPhysics3D uses an iterative solver to simulate the contacts and joints. For contacts, there is a unique velocity solver and for - joints there is a velocity and a position solver. By default, the number of iterations of the velocity solver is 10 and the number of iterations - for the position solver is 5. It is possible to change the number of iterations for both solvers. \\ - - To do this, you need to use the following two methods: \\ - - \begin{lstlisting} -// Change the number of iterations of the velocity solver -world->setNbIterationsVelocitySolver(15); - -// Change the number of iterations of the position solver -world->setNbIterationsPositionSolver(8); - \end{lstlisting} - - \vspace{0.6cm} - - Increasing the number of iterations of the solvers will make the simulation more precise but also more expensive to compute. Therefore, you should change - those values only if necessary. - - \subsubsection{Sleeping} - \label{sec:sleeping} - - The purpose of the sleeping technique is to deactivate resting bodies so that they are not simulated anymore. This is used to save computation - time because simulating many bodies is costly. A sleeping body (or group of sleeping bodies) is awaken as soon as another body collides with it or - a joint in which it is involed is enabled. The sleeping technique is enabled by default. You can disable it using the following method: \\ - - \begin{lstlisting} -// Disable the sleeping technique -world->enableSleeping(false); - \end{lstlisting} - - \vspace{0.6cm} - - Note that it is not recommended to disable the sleeping technique because the simulation might become slower. It is also possible to deactivate - the sleeping technique on a per body basis. See section \ref{sec:rigidbodysleeping} for more information. \\ - - \begin{sloppypar} - A body is put to sleep when its linear and angular velocity stay under a given velocity threshold for a certain amount of time - (one second by default). It is possible to change the linear and angular velocity thresholds using the two methods - \texttt{PhysicsWorld::setSleepLinearVelocity()} and \texttt{PhysicsWorld::setSleepAngularVelocity()}. Note that the velocities must - be specified in meters per second. You can also change the amount of time (in seconds) the velocity of a body needs to stay under the - threshold to be considered sleeping. To do this, use the \texttt{PhysicsWorld::setTimeBeforeSleep()} method. - \end{sloppypar} - - \subsection{Updating the Physics World} - \label{sec:updatingphysicsworld} - - When the \texttt{PhysicsWorld} is used to animate the bodies through time according to the laws of physics, the world has to be updated each time you - want to simulate a step forward in time (for instance each frame in a real-time simulation). \\ - - \begin{sloppypar} - To update the physics world, you need to use the \texttt{PhysicsWorld::update()} method. This method will perform collision detection and update the - position and orientation of the bodies according to the forces, joints constraints and collision contacts. Once you have updated the world, you will be - able to retrieve the new position and orientation of your bodies in order to render the next frame. The \texttt{PhysicsWorld::update()} method - requires a \emph{timeStep} parameter. This is the amount of time you want to advance the physics simulation (in seconds). \\ - \end{sloppypar} - - The smaller the time step you pick, the more precise the simulation will be. For a real-time application, you probably want to use a time step of - at most $\frac{1}{60}$ seconds to have at least a 60 Hz framerate. Most of the time, physics engines prefer to work with a constant time step. - It means that you should always call the \texttt{PhysicsWorld::update()} method with the same time step parameter. You do not want to use the exact time - between two frames as your time step because it will not be constant. \\ - - You can use the following technique. First, you need to choose a constant time step. Let say the time step is $\frac{1}{60}$ seconds. - Then, at each frame, you compute the time difference between the current frame and the previous one and you accumulate this difference in a variable - called \emph{accumulator}. The accumulator is initialized to zero at the beginning of your application and is updated at each frame. The idea is to - divide the time in the accumulator in several constant time steps. For instance, if your accumulator contains $0.145$ seconds, it means that - we can take $8$ physics steps of $\frac{1}{60}$ seconds during the current frame. Note that $0.012$ seconds will remain in the accumulator - and will probably be used in the next frame. As you can see, with this technique, multiple physics steps can be taken at each frame. - It is important to understand that each call to the \texttt{PhysicsWorld::update()} method is done using a constant time step that is - not varying with the framerate of the application. \\ - - Here is what the code looks like at each frame: \\ - - \begin{lstlisting} - -// Constant physics time step -const float timeStep = 1.0f / 60.0f; - -// Get the current system time -long double currentFrameTime = getCurrentSystemTime(); - -// Compute the time difference between the two frames -long double deltaTime = currentFrameTime - previousFrameTime; - -// Update the previous time -previousFrameTime = currentFrameTime; - -// Add the time difference in the accumulator -accumulator += mDeltaTime; - -// While there is enough accumulated time to take -// one or several physics steps -while (accumulator >= timeStep) { - - // Update the Dynamics world with a constant time step - world->update(timeStep); - - // Decrease the accumulated time - accumulator -= timeStep; -} - - \end{lstlisting} - - \vspace{0.6cm} - - If you want to know more about physics simulation time interpolation, you can read the nice article from Glenn Fiedler - at \url{https://gafferongames.com/post/fix_your_timestep/}. - - \subsection{Retrieving contacts} - - Sometimes, you might need to get the contacts information (contact point, normal, penetration depth, \dots) that occurs in your physics world. \\ - - If you are using a physics world to only test for collisions (you never call the \texttt{PhysicsWorld::update()} method), you can retrieve contacts - information directly when you call the \texttt{PhysicsWorld::testCollision()} group of methods. Those methods take a pointer to a - \texttt{CollisionCallback} class. You simply need to create a custom class that inherits from this class and override the - \texttt{CollisionCallback::onContact()} method. When you call one of the \texttt{PhysicsWorld::testCollision()} methods, the \texttt{onContact()} method - of your class will be called with all the information about the contacts in parameters. \\ - - However, if you are using ReactPhysics3D for a real-time simulation by calling the \texttt{PhysicsWorld::update()} method each frame, you should - use the \texttt{EventListener} class to retrieve contacts as described in section \ref{sec:receiving_feedback}. - - \subsection{Destroying the Physics World} - - When you don't need the physics world anymore, you can destroy it to release some memory. - When the physics world is destroyed, all the bodies that have been added into it and that have not been destroyed already will - be destroyed. \\ - - \begin{lstlisting} -// Destroy the physics world -physicsCommon.destroyPhysicsWorld(world); - \end{lstlisting} - - \vspace{0.6cm} - - Note that the pointer to the physics world and all the objects that have been created inside it (bodies, colliders, \dots) will become invalid after - this call. - - \section{Rigid Body} - \label{sec:rigidbody} - - Once the physics world has been created, you can add rigid bodies into it. A rigid body is an object that can be simulated using the laws of physics. - It has a mass, a position, an orientation and one or several colliders. A rigid body can be used in different situations. It can be used to simulate a - real world object simulated by the laws of physics (it will react to forces and collisions) or moved manually (like a character).  It can also be used - to represent a static objects that is not moving in the world (like a building for instance). We can also use a rigid body that is not simulated but - there to launch a given action when colliding or overlapping with another rigid body of the world. In ReactPhysics3D, the \texttt{RigidBody} class - is used to describe a rigid body. - - \subsection{Creating a Rigid Body} - - In order to create a rigid body, you need to specify its transform. The transform describes the initial - position and orientation of the body in the world. You need to create an instance of the \texttt{Transform} class with a vector describing the - initial position and a quaternion for the initial orientation of the body. \\ - - You have to call the \texttt{PhysicsWorld::createRigidBody()} method to create a rigid body in the world. This method will return a pointer to the - instance of the \texttt{RigidBody} object that has been created internally. You will then be able to use that pointer to get or set values to the body. \\ - - You can see in the following code how to create a rigid body in your world: \\ - - \begin{lstlisting} -// Initial position and orientation of the rigid body -Vector3 position(0.0, 3.0, 0.0); -Quaternion orientation = Quaternion::identity(); -Transform transform(position, orientation); - -// Create a rigid body in the world -RigidBody* body = world->createRigidBody(transform); - \end{lstlisting} - - \vspace{0.6cm} - - Once your rigid body has been created in the world, you will probably want to add it one or more colliders as described in section \ref{sec:collider}. - - \subsection{Type of a Rigid Body (static, kinematic or dynamic)} - - There are three types of bodies: \emph{static}, \emph{kinematic} and \emph{dynamic}. \\ - - A \emph{static} body has infinite mass, zero velocity but - its position can be changed manually. Moreover, a static body does not collide with other static or kinematic bodies. \\ - - On the other side, a \emph{kinematic} body has infinite mass, its velocity can be changed manually and its position is computed by the physics engine. - A kinematic body does not collide with other static or kinematic bodies. \\ - - Finally, A \emph{dynamic} body has non-zero mass, non-zero velocity determined - by forces and its position is determined by the physics engine. Moreover, a dynamic body can collide with other dynamic, static or - kinematic bodies. \\ - - For instance, you can use a \emph{static} body for the floor, a \emph{kinematic} body for a moving platform and a \emph{dynamic} body for a - rock that could fall on the floor. \\ - - When you create a new body in the world, it is of dynamic type by default. You can change the type of the body using the \texttt{RigidBody::setType()} - method as follows:\\ - - \begin{lstlisting} -// Change the type of the body to kinematic -body->setType(BodyType::KINEMATIC); - \end{lstlisting} - - \subsection{Gravity} - - By default, all the rigid bodies with react to the gravity force of the world. If you do not want the gravity to be applied to a given body, you can disable - it using the \texttt{RigidBody::enableGravity()} method as in the following example: \\ - - \begin{lstlisting} -// Disable gravity for this body -rigidBody->enableGravity(false); - \end{lstlisting} - - - \subsection{Velocity Damping} - - \begin{sloppypar} - Damping is the effect of reducing the velocity of the rigid body during the simulation to simulate effects like air friction for instance. By default, no damping - is applied. However, you can choose to damp the linear or/and the angular velocity of a rigid body. For instance, without angular damping a pendulum will never come - to rest. You need to use the \texttt{RigidBody::setLinearDamping()} and \texttt{RigidBody::setAngularDamping()} methods to change the damping values. The damping - value has to be positive and a value of zero means no damping at all. - \end{sloppypar} - - \subsection{Sleeping} - \label{sec:rigidbodysleeping} - - As described in section \ref{sec:sleeping}, the sleeping technique is used to disable the simulation of resting bodies. By default, the bodies are - allowed to sleep when they come to rest. However, if you do not want a given body to be put to sleep, you can use the - \texttt{RigidBody::setIsAllowedToSleep()} method as in the next example: \\ - - \begin{lstlisting} -// This rigid body cannot sleep -rigidBody->setIsAllowedToSleep(false); - \end{lstlisting} - - \subsection{Applying Force or Torque to a Rigid Body} - - During the simulation, you can apply a force or a torque to a given rigid body. This force can be applied to the center of mass of the rigid body - by using the \texttt{RigidBody::\allowbreak applyForceToCenterOfMass()} method. You need to specify the force vector (in Newton) as a parameter. If - the force is applied to the center of mass, no torque will be created and only the linear motion of the body will be affected. \\ - - \begin{lstlisting} -// Force vector (in Newton) -Vector3 force(2.0, 0.0, 0.0); - -// Apply a force to the center of the body -rigidBody->applyForceToCenterOfMass(force); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - You can also apply a force to any given point in world-space using the \texttt{RigidBody::applyForceAtWorldPosition()} method or in - local-space with the \texttt{RigidBody::applyForceAtLocalPosition()} method. You need to specify the force vector (in Newton) and the point - where to apply the given force. Note that if the point is not the center of mass of the body, applying a force will generate some torque and - therefore, the angular motion of the body will be affected as well. \\ - \end{sloppypar} - - \begin{lstlisting} -// Force vector (in Newton) -Vector3 force(2.0, 0.0, 0.0); - -// Point where the force is applied -Vector3 point(4.0, 5.0, 6.0); - -// Apply a force to the body -rigidBody->applyForceAtLocalPosition(force, point); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - It is also possible to apply a torque to a given body using the \texttt{RigidBody::applyTorque()} method. You simply need to specify - the torque vector (in Newton $\cdot$ meter) as in the following example: \\ - \end{sloppypar} - - \begin{lstlisting} -// Torque vector -Vector3 torque(0.0, 3.0, 0.0); - -// Apply a torque to the body -rigidBody->applyTorque(torque); - \end{lstlisting} - - \vspace{0.6cm} - - Note that when you call the previous methods, the specified force/torque will be added to the total force/torque applied to the rigid body and that - at the end of each call to the \texttt{PhysicsWorld::update()}, the total force/torque of all the rigid bodies will be reset to zero. - Therefore, you need to call the previous methods during several frames if you want the force/torque to be applied during a certain amount of time. - - \subsection{Updating a Rigid Body} - - When you call the \texttt{PhysicsWorld::update()} method, the bodies positions and orientations are updated to satisfy the contacts and joints - constraint between the bodies. After calling this method, you can retrieve the updated position and orientation of each body to render it. - To do that, you simply need to use the \texttt{RigidBody::getTransform()} method to get the updated transform. This transform represents the - current local-to-world-space transform of the body. \\ - - As described in section \ref{sec:updatingphysicsworld}, at the end of a frame, there might still be some remaining time in the time accumulator. - Therefore, you should not use the updated transform directly for rendering but you need to perform some interpolation between the updated transform - and the one from the previous frame to get a smooth real-time simulation. First, you need to compute the interpolation factor as folows: \\ - - \begin{lstlisting} -// Compute the time interpolation factor -float factor = accumulator / timeStep; - \end{lstlisting} - - \vspace{0.6cm} - - Then, you can use the \texttt{Transform::interpolateTransforms()} method to compute the linearly interpolated transform: \\ - - \begin{lstlisting} -// Compute the interpolated transform of the rigid body -Transform interpolatedTransform = Transform::interpolateTransforms(prevTransform, currTransform, factor); - \end{lstlisting} - - \vspace{0.6cm} - - The following code is the one from section \ref{sec:updatingphysicsworld} for the physics simulation loop but with the update of a given rigid body. \\ - - \begin{lstlisting} - -// Constant physics time step -const float timeStep = 1.0 / 60.0; - -// Get the current system time -long double currentFrameTime = getCurrentSystemTime(); - -// Compute the time difference between the two frames -long double deltaTime = currentFrameTime - previousFrameTime; - -// Update the previous time -previousFrameTime = currentFrameTime; - -// Add the time difference in the accumulator -accumulator += mDeltaTime; - -// While there is enough accumulated time to take -// one or several physics steps -while (accumulator >= timeStep) { - - // Update the physics world with a constant time step - physicsWorld->update(timeStep); - - // Decrease the accumulated time - accumulator -= timeStep; -} - -// Compute the time interpolation factor -decimal factor = accumulator / timeStep; - -// Get the updated transform of the body -Transform currTransform = body->getTransform(); - -// Compute the interpolated transform of the rigid body -Transform interpolatedTransform = Transform::interpolateTransforms(prevTransform, currTransform, factor); - -// Now you can render your body using the interpolated transform here - -// Update the previous transform -prevTransform = currTranform; - - \end{lstlisting} - - \vspace{0.6cm} - - If you need the array with the corresponding $4 \times 4$ OpenGL transformation matrix for rendering, you can use - the \texttt{Transform::getOpenGLMatrix()} method as in the following code: \\ - - \begin{lstlisting} -// Get the OpenGL matrix array of the transform -float matrix[16]; -transform.getOpenGLMatrix(matrix); - \end{lstlisting} - - \vspace{0.6cm} - - A nice article to read about this time interpolation is the one from Glenn Fiedler at \url{https://gafferongames.com/post/fix_your_timestep/}. - - \subsection{Mass, Center of Mass, Inertia Tensor} - \label{sec:rigidbodymass} - - The mass, center of mass and inertia tensor of a rigid body are important parameters for the physical simulation of a rigid body. - - \subsubsection{Mass} - - The \texttt{RigidBody} has a mass value (in kilograms) which is 1 kilogram by default. There are two ways to set the mass of a rigid body. First, you - can set it directly using the \texttt{RigidBody::setMass()} method. Secondly, it is also possible to compute this mass - automatically using the mass of the colliders of the rigid body. As described in section \ref{sec:material}, the material of each collider has a - mass density value. This value is 1 by default. You change change the mass density value of the colliders of a rigid body and then use the - \texttt{RigidBody::updateMassFromColliders()} method to automatically compute the mass of the rigid body using the mass density and shape of - its colliders. Note that you will need to call this method again if you add another collider to the rigid body. - - \subsubsection{Center of mass} - - The center of mass of a \texttt{RigidBody} is the mean location of the distribution of mass of the body in space. By default the center of mass - of the rigid body is located at its origin. There are two ways to set the center of mass of a rigid body. First, you can set it directly using the - \texttt{RigidBody::setLocalCenterOfMass()} method. Secondly, as for the mass, the center of mass can also be computed automatically using the - mass, shape and transform of all the colliders of the rigid body. As described in section \ref{sec:material}, the material of each collider has a - mass density value. This value is 1 by default. You can set the mass density value of the colliders and then use the - \texttt{RigidBody::updateLocalCenterOfMassFromColliders()} method to automatically compute the center of mass of the rigid body. - Note that you will need to call this method again if you add another collider to the rigid body. - - \subsubsection{Inertia Tensor} - - \begin{sloppypar} - The inertia tensor of a \texttt{RigidBody} is a $3 \times 3$ matrix describing how the mass is distributed inside the rigid body which is - used to calculate its rotation. The inertia tensor depends on the mass and the shape of the body. By default the local inertia tensor of a rigid body - is the identity matrix. There are two ways to set the inertia tensor of - a rigid body. First, you can set it directly using the \texttt{RigidBody::setLocalInertiaTensor()} method. Note that this will set the inertia tensor - of the body in local-space coordinates which is usually a diagonal matrix. This method takes a \texttt{Vector3} with the three diagonal entries of the - matrix. Secondly, the local inertia tensor can be computed automatically using the mass density, shape and transform of all the colliders of the body. - As described in section \ref{sec:material}, the material of each collider has a mass density value which is 1 by default. You can set the mass density - value of the colliders and then use the \texttt{RigidBody::updateLocalInertiaTensorFromColliders()} method to automatically compute the local inertia - tensor of the body. Note that you will need to call this method again if you add another collider to the rigid body. - \end{sloppypar} - - \vspace{0.6cm} - - \begin{sloppypar} - Note that it is also possible to automatically compute the mass, center of mass and inertia tensor of a rigid body at the same time using the - \texttt{RigidBody::updateMassPropertiesFromColliders()}. - \end{sloppypar} - - \subsection{Restricting linear/angular motion of a Rigid Body} - - It is possible to use the \texttt{RigidBody::setLinearLockAxisFactor()} method to restrict the linear motion of a rigid body along the world-space - X,Y and Z axes. For instance, the following code shows how to disable the linear motion of a rigid body along the world-space Y axis by setting the lock - factor to zero for this axis. \\ - - \begin{lstlisting} -// Disable motion along the Y axis -rigidBody->setLinearAxisFactor(Vector3(1, 0, 1)); - \end{lstlisting} - - \vspace{0.6cm} - - In the same way, you can use the \texttt{RigidBody::setAngularLockAxisFactor()} method to restrict the angular motion of a rigid body around the - world-space X,Y and Z axes. For instance, the following code shows how to disable the angular motion of a rigid body around the world-space Y axis - by setting the lock factor to zero for this axis. \\ - - \begin{lstlisting} -// Disable rotation around the Y axis -rigidBody->setAngularAxisFactor(Vector3(1, 0, 1)); - \end{lstlisting} - - \subsection{Destroying a Rigid Body} - - \begin{sloppypar} - It is really simple to destroy a rigid body when you don't need it anymore. You simply need to use the \texttt{PhysicsWorld::destroyRigidBody()} - method. You need to use the pointer to the body you want to destroy as a parameter. Note that after calling that method, the pointer will not be valid - anymore and therefore, you should not use it. When you destroy a rigid body that was part of a joint, that joint will be automatically destroyed as - well. \\ - \end{sloppypar} - - Here is how to destroy a rigid body: \\ - - \begin{lstlisting} -// Here, world is an instance of the PhysicsWorld class -// and body is a RigidBody* pointer - -// Destroy the rigid body -world->destroyRigidBody(body); - \end{lstlisting} - - \section{Collider} - \label{sec:collider} - - To allow bodies to collide against each others, we need \texttt{colliders}. A collider (class \texttt{Collider}) describes the collision shape - of a body. A body can have multiple colliders attached to it. When adding a collider to a body, you need to specify its collision - shape (box, sphere, capsule, \dots) and its transform relative to the origin of the body. \\ - - Before adding a collider to a body, you need to create a collision shape. A collision shape can be instantiated by calling a method of the - main \texttt{PhysicsCommon} object. The following example shows how instantiate a collision shape (a sphere shape) from the \texttt{PhysicsCommon} - object and use it to add a new collider to a body. \\ - - \begin{lstlisting} -// Instantiate a sphere collision shape -float radius = 3.0f; -SphereShape* sphereShape = physicsCommon.createSphereCollisionShape(radius); - -// Relative transform of the collider relative to the body origin -Transform transform = Transform::identity(); - -// Add the collider to the rigid body -Collider* collider; -collider = body->addCollider(&shape, transform); - - \end{lstlisting} - - \vspace{0.6cm} - - Note that a given collision shape instance can be shared between multiple colliders. The next section presents the different types of collision - shapes that are available in ReactPhysics3D. - - \subsection{Collision Shapes} - \label{sec:collisionshapes} - - As we have just seen, a collision shape is used to describe the shape of a collider for collision detection. They are many types of - collision shapes that you can use. They all inherit from the \texttt{CollisionShape} class. \\ - - Note that ReactPhysics3D does not support collision between two concave shapes (\texttt{ConcaveMeshShape} and \texttt{HeightFieldShape}). - - \subsubsection{Box Shape} - - \begin{figure}[h] - \centering - \includegraphics{boxshape.png} - \label{fig:boxshape} - \end{figure} - - The \texttt{BoxShape} class describes a box collision shape centered at the origin of the collider. The box is aligned with the shape - local X, Y and Z axis. In order to create a box shape, you only need to specify the three half extents dimensions of the box in the three X, Y and - Z directions. \\ - - For instance, if you want to create a box shape with dimensions of 4 meters, 6 meters and 10 meters along the X, Y and Z axis respectively, you - need to use the following code: \\ - - \begin{lstlisting} -// Half extents of the box in the x, y and z directions -const Vector3 halfExtents(2.0, 3.0, 5.0); - -// Create the box shape -BoxShape* boxShape = phycsicsCommon.createBoxShape(halfExtents); - \end{lstlisting} - - \vspace{0.6cm} - - \subsubsection{Sphere Shape} - - \begin{figure}[h] - \centering - \includegraphics{sphereshape.png} - \label{fig:sphereshape} - \end{figure} - - The \texttt{SphereShape} class describes a sphere collision shape centered at the origin of the collider. You only need to specify the - radius of the sphere to create it. \\ - - For instance, if you want to create a sphere shape with a radius of 2 meters, you need to use the following code: \\ - - \begin{lstlisting} -// Create the sphere shape with a radius of 2m -SphereShape* sphereShape = physicsCommon.createSphereShape(2.0); - \end{lstlisting} - - \vspace{0.6cm} - - \subsubsection{Capsule Shape} - - \begin{figure}[h] - \centering - \includegraphics{capsuleshape.png} - \label{fig:capsuleshape} - \end{figure} - - The \texttt{CapsuleShape} class describes a capsule collision shape around the local Y axis and centered at the origin of the collider. - It is the convex hull of two spheres. It can also be seen as an elongated sphere. In order to create it, you only need to specify the - radius of the two spheres and the height of the capsule (distance between the centers of the two spheres). \\ - - For instance, if you want to create a capsule shape with a radius of 1 meter and the height of 2 meters, you need to use the following code: \\ - - \begin{lstlisting} -// Create the capsule shape -CapsuleShape* capsuleShape = physicsCommon.createCapsuleShape(1.0, 2.0); - \end{lstlisting} - - \vspace{0.6cm} - - \subsubsection{Convex Mesh Shape} - - The \texttt{ConvexMeshShape} class can be used to describe the shape of a convex mesh centered at the origin of the collider. - - \begin{figure}[!ht] - \centering - \includegraphics{convexshape.png} - \label{fig:convexshape} - \end{figure} - - In order to create a \texttt{ConvexMeshShape}, we first need to create a \texttt{ConvexMesh}. A \texttt{ConvexMesh} can be instanciated once and - used to create multiple \texttt{ConvexMeshShapes} with different scaling if necessary. There are two ways to create a \texttt{ConvexMesh}. \\ - - If you know the vertices and faces of your mesh, you can use the following method to create a \texttt{ConvexMesh}: \\ - - \begin{lstlisting} -PhysicsCommon::createConvexMesh(const PolygonVertexArray& polygonVertexArray, std::vector& messages) - \end{lstlisting} - - \vspace{0.6cm} - - To use this method, you need to create an array of \texttt{PolygonFace} to describe each face of your mesh. You also need to have an array with - the vertices coordinates and an array with the vertex indices of each face of you mesh. Then, you have to create a \texttt{PolygonVertexArray} with - your vertices coordinates and indices array. You also need to specify your array of \texttt{PolygonFace}. Then, you can call the previous method - to instantiate a \texttt{ConvexMesh} using your \texttt{PolygonVertexArray}. \\  - - The following example shows how to create a \texttt{ConvexMeshShape} when you know the vertices and faces of your mesh. - In this example, we create a cube as a convex mesh shape. Of course, this is only for the example. If you really need a cube collision - shape, you should use the \texttt{BoxShape} instead. \\ - - \begin{lstlisting} -// Array with the vertices coordinates of the convex mesh -float vertices[24] = {-3, -3, 3, - 3, -3, 3, - 3, -3, -3, - 3, -3, -3, - -3, 3, 3, - 3, 3, 3, - 3, 3, -3, - -3, 3, -3}; - -// Vertex array with all vertices of the mesh -rp3d::VertexArray vertexArray(vertices, 3 * sizeof(float), - 24, rp3d::VertexArray::DataType::VERTEX_FLOAT_TYPE); - -// Compute the convex mesh from the array of vertices -std::vector messages; -ConvexMesh* convexMesh = physicsCommon.createConvexMesh(vertexArray, messages); - -// Display the messages (info, warning and errors) -if (messages.size() > 0) { - - for (const rp3d::Message& message: messages) { - - std::string messageType; - switch(message.type) { - case rp3d::Message::Type::Information: - messageType = "info"; - break; - case rp3d::Message::Type::Warning: - messageType = "warning"; - break; - case rp3d::Message::Type::Error: - messageType = "error"; - break; - } - - std::cout << "Message (" << messageType << "): " << message.text << std::endl; - } -} - -// Make sure there was no errors during mesh creation -assert(convexMesh != nullptr); - - \end{lstlisting} - - \vspace{0.6cm} - - Note that the vertex coordinates and indices array are copied into the \texttt{ConvexMesh}. Therefore, you can release the memory of the - \texttt{PolygonVertexArray} after the \texttt{ConvexMesh} creation. However, the mesh data is stored inside the \texttt{ConvexMesh}. Therefore, - you should not release it until all the \texttt{ConvexMeshShapes} that have been instantiated from it are destroyed. \\ - - You need to make sure that the mesh you provide is indeed convex. Secondly, you should provide the simplest possible convex mesh. It means - that you need to avoid coplanar faces in your convex mesh shape. Coplanar faces have to be merged together. Remember that convex meshes are - not limited to triangular faces, you can create faces with more than three vertices. \\ - - Also note that meshes with duplicated vertices are not supported. The number of vertices you pass to create the \texttt{PolygonVertexArray} must - be exactly the number of vertices in your convex mesh. The \texttt{PhysicsCommon::createConvexMesh()} will also report errors if some faces of your - mesh have almost zero area (degenerated face). You will need to fix those errors in order to create the \texttt{ConvexMesh}. \\ - - When you specify the vertices for each face of your convex mesh, be careful with their order. The vertices of a face must be specified in - counter clockwise order as seen from the outside of your convex mesh. \\ - - You also need to make sure that the origin of your mesh is inside the convex mesh. A mesh with an origin outside the - convex mesh is not currently supported by the library. \\ - - As discussed before, there is a second way to create a \texttt{ConvexMesh}. In case you only know the vertices of your - mesh (you don't know the faces), you can use the following method: \\ - - \begin{lstlisting} -PhysicsCommon::createConvexMesh(const VertexArray& vertexArray, std::vector& messages) - \end{lstlisting} - - With this method, you only need to specify the array of vertices (\texttt{VertexArray}) of your mesh (not the faces). The library will use the - QuickHull algorithm to compute a convex mesh that includes all the vertices. \\ - - \begin{lstlisting} -// Array with the vertices coordinates of the convex mesh -float vertices[24] = {-3, -3, 3, - 3, -3, 3, - 3, -3, -3, - 3, -3, -3, - -3, 3, 3, - 3, 3, 3, - 3, 3, -3, - -3, 3, -3}; - -// Vertex array with all vertices of the mesh -rp3d::VertexArray vertexArray(vertices, 3 * sizeof(float), - 24, rp3d::VertexArray::DataType::VERTEX_FLOAT_TYPE); - -// Compute the convex mesh using only the array of vertices -std::vector messages; -ConvexMesh* convexMesh = physicsCommon.createConvexMesh(vertexArray, messages); - -// Display the messages (info, warning and errors) -if (messages.size() > 0) { - - for (const rp3d::Message& message: messages) { - - std::string messageType; - switch(message.type) { - case rp3d::Message::Type::Information: - messageType = "info"; - break; - case rp3d::Message::Type::Warning: - messageType = "warning"; - break; - case rp3d::Message::Type::Error: - messageType = "error"; - break; - } - - std::cout << "Message (" << messageType << "): " << message.text << std::endl; - } -} - -// Make sure there was no errors during mesh creation -assert(convexMesh != nullptr); - - \end{lstlisting} - - \vspace{0.6cm} - - Note that the \texttt{PhysicsCommon::createConvexMesh()} method takes a reference to an array of messages as parameter. If there are errors - during the convex mesh creation, the method will return \texttt{null} and the array with contain some error messages describing the issues. \\ - - Now that we have a \texttt{ConvexMesh}, we can create a \texttt{ConvexMeshShape} as in the following example: \\ - - \begin{lstlisting} -// Scaling factor -Vector3 scaling(1, 1, 1); - -// Create the ConvexMeshShape -ConvexMeshShape* convexMeshShape = physicsCommon.createConvexMeshShape(convexMesh, scaling); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - As you can see, you can specify a scaling factor in the \texttt{PhysicsCommon::createConvexMeshShape()} method when you create a - \texttt{Convex\allowbreak MeshShape}. All the vertices of your mesh will be scaled from the local origin of the mesh by this factor. - It means that you can use the same \texttt{ConvexMesh} for multiple \texttt{ConvexMeshShape} with a different scaling factor each time. \\ - \end{sloppypar} - - Note that collision detection with a \texttt{ConvexMeshShape} is more expensive than with a \texttt{SphereShape} or a \texttt{CapsuleShape}. \\ - - \subsubsection{Concave Mesh Shape} - - \begin{figure}[h] - \centering - \includegraphics{concavemeshshape.png} - \label{fig:concaveshape} - \end{figure} - - The \texttt{ConcaveMeshShape} class can be used for a static concave triangular mesh. It can be used to describe an environment for - instance. Note that it cannot be used with a dynamic body that is allowed to move. Moreover, make sure to use a \texttt{ConcaveMeshShape} only - when you are not able to use a convex shape and also try to limit the number of triangles of that mesh because collision detection - with \texttt{ConcaveMeshShape} is quite expensive compared to convex shapes. Note that collsions between two concave mesh shape are not supported. \\ - - In order to create a \texttt{ConcaveMeshShape}, we first need to create a \texttt{TriangleMesh}. A \texttt{TriangleMesh} describes your - mesh made of triangles and can be reused multiple times to instantiate \texttt{ConcaveMeshShapes} with different scaling factors for instance. - In order to create the \texttt{TriangleMesh}, we first need to instantiate a \texttt{TriangleVertexArray} describing the vertices and triangle faces - of the mesh. We then pass the \texttt{TriangleVertex\allowbreak Array} to the \texttt{PhysicsCommon::createTriangleMesh()} method to create the - \texttt{TriangleMesh}. \\ - - When you specify the vertices for each triangle face of your mesh, be careful with the order of the vertices. They must be specified in counter - clockwise order as seen from the outside of your mesh. \\ - - The following code shows how to create a \texttt{TriangleVertexArray} and use it to create a \texttt{TriangleMesh}: \\ - - \begin{lstlisting} -// Create the TriangleVertexArray -const int nbVertices = 8; -const int nbTriangles = 12; -float vertices[3 * nbVertices] = ...; -int indices[3 * nbTriangles] = ...; -TriangleVertexArray* triangleArray = -new TriangleVertexArray(nbVertices, vertices, 3 * sizeof(float), nbTriangles, -indices, 3 * sizeof(int), -TriangleVertexArray::VertexDataType::VERTEX_FLOAT_TYPE, -TriangleVertexArray::IndexDataType::INDEX_INTEGER_TYPE); - -// Create the TriangleMesh -std::vector messages; -TriangleMesh* triangleMesh = physicsCommon.createTriangleMesh(vertexArray, messages); - -// Display the messages (info, warning and errors) -if (messages.size() > 0) { - - for (const rp3d::Message& message: messages) { - - std::string messageType; - switch(message.type) { - case rp3d::Message::Type::Information: - messageType = "info"; - break; - case rp3d::Message::Type::Warning: - messageType = "warning"; - break; - case rp3d::Message::Type::Error: - messageType = "error"; - break; - } - - std::cout << "Message (" << messageType << "): " << message.text << std::endl; - } -} - -// Make sure there was no errors during mesh creation -assert(triangleMesh != nullptr); - \end{lstlisting} - - \vspace{0.6cm} - - The vertices and faces data are copied into the \texttt{TriangleMesh}. Therefore, after the \texttt{TriangleMesh} creation, you can release your - \texttt{TriangleVertexArray} but you need to keep the \texttt{TriangleMesh} until all the \texttt{ConcaveMeshShapes} that have been instantiated from - it are destroyed. \\ - - In the previous example, the vertices normals that are needed for collision detection are automatically computed. However, you can specify your own - vertices normals by using another constructor for the \texttt{TriangleVertexArray}. Note that each vertex normal is computed as weighted average - of the face normals of all the neighboring triangle faces. Therefore, if you specify your mesh with duplicated vertices when you create the - \emph{TriangleVertexArray}, the automatic vertices normals computation will not give correct normals because each vertex of the mesh will only be - part of a single triangle face. In this case, you must provide your own vertices normals when you create the \emph{TriangleVertexArray}. \\ - - The \texttt{PhysicsCommon::createTriangleMesh()} will also report errors if some faces of your mesh have almost zero area - (degenerated face). You will need to fix those errors in order to create the \texttt{TriangleMesh}. It is also a good idea to avoid coplanar faces - for better collision detection. \\ - - Now that we have a \texttt{TriangleMesh}, we can create the actual \texttt{ConcaveMeshShape}. \\ - - \begin{lstlisting} - -const Vector3 scaling(1, 1, 1); - -// Create the ConcaveMeshShape using the TriangleMesh -ConcaveMeshShape* concaveMeshShape = physicsCommon.createConcaveMeshShape(triangleMesh, scaling); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - As you can see, you can specify a scaling factor in the \texttt{PhysicsCommon::createConcaveMeshShape()} method when you create a - \texttt{Concave\allowbreak MeshShape}. All the vertices of your mesh will be scaled from the local origin of the mesh by this factor. - Note that you can use the same \texttt{TriangleMesh} instance for creating multiple \texttt{ConcaveMeshShape} with a different scaling - factor each time. \\ - \end{sloppypar} - - \subsubsection{Heightfield Shape} - - \begin{figure}[h] - \centering - \includegraphics{heightfieldshape.png} - \label{fig:heightfieldshape} - \end{figure} - - The \texttt{HeightFieldShape} is a collision shape that can be used to represent a static terrain for instance. This shape is - a two dimensional grid that has a given height value at each point of the grid. \\ - - First, we need to create an \texttt{HeightField}. To do that, we need to have an array with all the height values of our field. - We can have height values of type integer, float or double. We need to specify the number of rows and columns of our two - dimensional grid. Note that the height values in our array must be organized such that the value at row - \texttt{indexRow} and column \texttt{indexColumn} is located at the following position in the array: \\ - - \begin{lstlisting} -heightValues[indexRow * nbColumns + indexColumn] - \end{lstlisting} - - \vspace{0.6cm} - - Here is an example that shows how to create a \texttt{HeightField}: \\ - - \begin{lstlisting} -const int nbRows = 40; -const int nbColumns = 50; -float minHeight = 100; -float maxHeight = 500; - -// Height values -float heightValues[nbRows * nbColumns] = ...; - -std::vector messages; -HeightField* heightField = physicsCommon.createHeightField(NB_POINTS_WIDTH, NB_POINTS_LENGTH, - heightValues, rp3d::HeightField::HeightDataType::HEIGHT_FLOAT_TYPE, - messages); - -// Display the messages (info, warning and errors) -if (messages.size() > 0) { - - for (const rp3d::Message& message: messages) { - - std::string messageType; - switch(message.type) { - case rp3d::Message::Type::Information: - messageType = "info"; - break; - case rp3d::Message::Type::Warning: - messageType = "warning"; - break; - case rp3d::Message::Type::Error: - messageType = "error"; - break; - } - - std::cout << "Message (" << messageType << "): " << message.text << std::endl; - } -} - -// Make sure there was no errors during the height field creation -assert(heightField != nullptr); - \end{lstlisting} - - \vspace{0.6cm} - - The height values are copied into the \texttt{HeightField} instance. Therefore, after the \texttt{HeightField} creation, you can release the memory - of your array of height values but you need to keep the \texttt{TriangleMesh} until all the \texttt{HeightFieldShapes} that have been instantiated from - it are destroyed. \\ - - Once we have our \texttt{HeightField} instance, we can use it to create an actual \texttt{HeightFieldShape} as in the following example: \\ - - \begin{lstlisting} - HeightFieldShape* heightFieldShape = physicsCommon.createHeightFieldShape(heightField); - \end{lstlisting} - - \vspace{0.6cm} - You can also specify a scaling factor in the \texttt{PhysicsCommon::createHeightShape()} method when you create a \texttt{Height\allowbreak FieldShape}. - All the vertices of your mesh will be scaled from the local origin of the shape by this factor. \\ - - When creating a \texttt{HeightFieldShape}, the origin of the shape will be at the center of its bounding volume. - Therefore, if you create a \texttt{HeightFieldShape} with a minimum height of 100 and a maximum height of 500, the - maximum coordinates of the shape on the Y axis will be 200 and the minimum coordinates will be -200. - - \subsection{Collision filtering} - \label{sec:collisionfiltering} - - By default all the colliders of the bodies are able to collide with each other in the world. However, sometimes we want a body to collide only - with a given group of bodies and not with other bodies. This is called collision filtering. The idea is to group the colliders of bodies into - categories. Then we can specify for each collider against which categories of colliders it will be able to collide. \\ - - ReactPhysics3D uses bits masks to represent categories. The first thing to do is to assign a category to the colliders of your body. To do - this, you need to call the \texttt{Collider::setCollisionCategoryBits()} method on the corresponding collider as in the following example. Here - we consider that we have four bodies where each one has a single collider. \\ - - \begin{lstlisting} -// Enumeration for categories -enum Category { - CATEGORY1 = 0x0001, - CATEGORY2 = 0x0002, - CATEGORY3 = 0x0004 -}; - - -// Set the collision category for each collider of -// each of the four bodies -colliderBody1->setCollisionCategoryBits(CATEGORY1); -colliderBody2->setCollisionCategoryBits(CATEGORY2); -colliderBody3->setCollisionCategoryBits(CATEGORY3); -colliderBody4->setCollisionCategoryBits(CATEGORY3); - \end{lstlisting} - - \vspace{0.6cm} - - As you can see, the collider of body 1 will be part of the category 1, the collider of body 2 will be part of the category 2 and the colliders of - bodies 3 and 4 will be part of the category 3. \\ - - \begin{sloppypar} - Now, for each collider, we need to specify with which categories it is allowed to collide with. To do this, you need to use the - \texttt{Collider::setCollideWithMaskBits()} method. Note that you can specify one or more categories using the bitwise OR operator. The - following example shows how to specify with which categories the collider can collide. \\ - \end{sloppypar} - - \begin{lstlisting} -// For each collider, we specify with which categories it -// is allowed to collide -colliderBody1->setCollideWithMaskBits(CATEGORY3); -colliderBody2->setCollideWithMaskBits(CATEGORY1 | CATEGORY3); -colliderBody3->setCollideWithMaskBits(CATEGORY2); -colliderBody4->setCollideWithMaskBits(CATEGORY2); - \end{lstlisting} - - \vspace{0.6cm} - - As you can see, we specify that the body 1 will be allowed to collide with bodies from the categorie 3. We also indicate that the body 2 will be - allowed to collide with bodies from the category 1 and 3 (using the bitwise OR operator). Finally, we specify that bodies 3 and 4 will be allowed - to collide against bodies of the category 2. \\ - - A collider is able to collide with another only if you have specify that the category mask of the first collider is part of the - \emph{collide with} mask of the second collider. It is also important to understand that this condition must be satisfied in both directions. For - instance in the previous example, the body 1 (of category 1) says that it wants to collide against bodies of the category 3 (for instance against - body 3). However, body 1 and body 3 will not be able to collide because the body 3 does not say that it wants to collide - with bodies from category 1. Therefore, in the previous example, the body 2 is allowed to collide against bodies 3 and 4 but no other collision - is allowed. \\ - - In the same way, you can perform this filtering for ray casting (described in section \ref{sec:raycasting}). For instance, you can perform a ray cast test - against a given subset of categories of colliders only. - - \subsection{Material} - \label{sec:material} - - The material of a collider is used to describe the physical properties it is made of. This is represented by the \texttt{Material} class. Each collider that you create will have a default material. You can get the material of the collider using the \texttt{Collider::\allowbreak getMaterial()} method. \\ - - You can use the material to set those physical properties. \\ - - For instance, you can change the bounciness of the collider. The bounciness is a value between 0 and 1. The value 1 is used for - a very bouncy object and the value 0 means that the collider will not be bouncy at all. To change the bounciness of the material, - you can use the \texttt{Material::\allowbreak setBounciness()} method. \\ - - \begin{sloppypar} - It is also possible to set the mass density of the collider which has a default value of 1. As described in section \ref{sec:rigidbodymass}, the - mass density of a collider can be used to automatically compute the mass, center of mass and inertia tensor of a rigid body. In order to change the - mass density of a collider, you need to use the \texttt{Material::setMassDensity()} method. \\ - \end{sloppypar} - - You are also able to change the friction coefficient of the collider. This value needs to be between 0 and 1. If the value is 0, - no friction will be applied when the collider is in contact with another collider. However, if the value is 1, the friction force will be high. You can - change the friction coefficient of the material with the \texttt{Material::\allowbreak setFrictionCoefficient()} method. \\ - - Here is how to get the material of a collider and how to modify some of its properties: \\ - - \begin{lstlisting} -// Get the current material of the collider -Material& material = collider->getMaterial(); - -// Change the bounciness of the collider -material.setBounciness(0.4); - -// Change the friction coefficient of the collider -material.setFrictionCoefficient(0.2); - \end{lstlisting} - - \vspace{0.6cm} - - A collider can have different behaviors. It can be a \emph{simulation collider}, a \emph{world query collider} or a - \emph{trigger}. It can have also have two behaviors (except \emph{simulation collider} and \emph{trigger} - at the same time). By default, when you create a collider, it will be a \emph{simulation collider} and a \emph{world query collider} but not a - \emph{trigger}. We will now take a look at those behaviors. \\ - - \subsection{Simulation Collider} - \label{sec:simulationcollider} - - A simulation collider is a collider that will be able to collide against other simulation colliders in the world. It means that when two simulation - colliders collide, contacts will be created and used to compute the forces/torques needed to make their bodies to bump into each other. For instance, - if you want to simulate a body that is falling on the floor and bumping against it, you will need to have a least of simulation collider in both the - falling body and the floor body. \\ - - To set a collider as being a simulation collider, you need to use the - \texttt{Collider::\allowbreak setIsSimulationCollider()} method as in the following example: \\ - - \begin{lstlisting} -bombCollider->setIsSimulationCollider(true); - \end{lstlisting} - - \vspace{0.6cm} - - When you create a collider, it is a simulation collider by default. - - \subsection{World Query Collider} - \label{sec:worldquerycollider} - - A world query collider is a collider used to perform world queries on it. World queries are manual queries perform on the physics world like raycasting - or testing if two collider collide or overlap using the following method for instance: - - \begin{description} - \item[testOverlap()] Those methods can be used to test whether the colliders of two bodies overlap or not. - \item[testCollision()] Those methods will give you the collision information (contact points, normals, ...) between two colliding bodies. - \item[testPointInside()] This method will tell you if a 3D point is inside a \texttt{RigidBody} or \texttt{Collider}. - \item[raycast()] Those methods will try to find intersection between a ray and colliders of the world. - \end{description} - - For instance, if you want to test a body of your scene with raycasting, you need to set the collider of that body as being a world query collider. - - To set a collider as being a world query collider, you need to use the \texttt{Collider::\allowbreak setIsWorldQueryCollider()} method as in the following example: \\ - - \begin{lstlisting} -bombCollider->setIsWorldQueryCollider(true); - \end{lstlisting} - - \vspace{0.6cm} - - A collider can be a world query collider and a simulation collider at the same time. When you create a collider, it is a world query collider by default. - - \subsection{Trigger} - \label{sec:trigger} - - A trigger, is a collider that cannot collide with any other colliders but can only report when it is overlapping with another collider. For instance, - consider a game where a player moves around and has to avoid touching some bombs. The player has a rigid body with a capsule collider for instance and - the bombs are rigid bodies where each one has a sphere collider. Now, you do not want the player to bump against the bombs but you only want to know - when the collider of the player overlaps with the collider of a bomb. In this case, you can set bombs colliders as being triggers. In this - case, no rigid bodies in the world will be able to collide against the bombs colliders but you can receive notifications when a collider (the player - body collider for instance) overlaps with a bomb collider. Therefore, you can use this to know when the player touches a bomb of the world. \\ - - To set a collider as being a trigger, you need to use the \texttt{Collider::setIsTrigger()} method as in the following example: \\ - - \begin{lstlisting} -bombCollider->setIsTrigger(true); - \end{lstlisting} - - \vspace{0.6cm} - - Note that a collider cannot be a trigger and a simulation collider at the same time. When you create a collide it is not a trigger by default. - - \vspace{0.6cm} - - The section \ref{sec:eventlistenertriggers} describes how to use the \texttt{EventListener} class to receive notifications when colliders overlap - with some triggers. - - \section{Joints} - - Joints are used to constrain the motion of the rigid bodies between each other. A single joint represents a constraint between two rigid bodies. - When the motion of the first body of the joint is known, the relative motion of the second body has at most six degrees of freedom (three for the - translation and three for the rotation). The different joints can reduce the number of degrees of freedom between two rigid bodies. \\ - - Some joints have limits to control the range of motion and some joints have motors to automatically move the bodies of the joint at a given speed. \\ - - \subsection{Ball and Socket Joint} - - The \texttt{BallAndSocketJoint} class describes a ball and socket joint between two bodies. In a ball and socket joint, the two bodies cannot - translate with respect to each other. However, they can rotate freely around a common anchor point. This joint has three degrees of freedom - and can be used to simulate a chain of bodies for instance. \\ - - In order to create a ball and socket joint, you first need to create an instance of the \texttt{BallAndSocketJointInfo} class with the necessary - information. You need to provide the pointers to the two rigid bodies and also the coordinates of the anchor point. The \texttt{BallAndSocketJointInfo} - class contains different constructors that you can use whether you want to specify the anchor point in world-space or local-space coordinates. - The joint will make sure that the two local-space anchor points match in world-space. Make sure that the two bodies are in a valid position (with - respect to the joint constraint) at the beginning of the simulation. \\ - - Here is the code to create the \texttt{BallAndSocketJointInfo} object: \\ - - \begin{lstlisting} -// Anchor point in world-space -const Vector3 anchorPoint(2.0, 4.0, 0.0); - -// Create the joint info object -BallAndSocketJointInfo jointInfo(body1, body2, anchorPoint); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - Now, it is time to create the actual joint in the physics world using the \texttt{PhysicsWorld::createJoint()} method. - Note that this method will also return a pointer to the \texttt{BallAndSocketJoint} object that has been created internally. You will then - be able to use that pointer to change properties of the joint and also to destroy it at the end. \\ - \end{sloppypar} - - Here is how to create the joint in the world: \\ - - \begin{lstlisting} -// Create the joint in the physics world -BallAndSocketJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \subsubsection{Cone limit} - - With the ball and socket joint, it is possible to enable a cone limit. Let's call the \texttt{anchor axis} for body 1 the axis from body 1 center of mass - to the joint anchor point. The cone limit can be used to constraint the angle between the anchor axis of body 1 and the anchor axis of body 2. - The idea is to limit the angle of the anchor axis of body 2 inside a cone around the anchor axis of body 1. The cone is defined by its main axis - which is the anchor axis of body 1 and is also defined by the cone half angle. \\ - - In the following example, we enable the cone limit for a given ball and socket joint. \\ - - \begin{lstlisting} -// Set the maximum half angle (in radians) -joint->setConeLimitHalfAngle(45.0 * PI / 180.0); - -// Enable the cone limit for this joint -joint->enableConeLimit(true); - \end{lstlisting} - - \subsection{Hinge Joint} - - The \texttt{HingeJoint} class describes a hinge joint (or revolute joint) between two rigid bodies. The hinge joint only allows rotation around an - anchor point and around a single axis (the hinge axis). This joint can be used to simulate doors or pendulums for instance. \\ - - In order to create a hinge joint, you first need to create a \texttt{HingeJointInfo} object with the necessary information. You need to provide - the pointers to the two rigid bodies, the coordinates of the anchor point and also the hinge rotation axis. The \texttt{HingeJointInfo} class contains - different constructors that you can use whether you want to specify the anchor point or the rotation axis in local-space or world-space. - Make sure that the two bodies are in a valid position (with respect to the joint constraint) at the beginning of the simulation. \\ - - Here is the code to create the \texttt{HingeJointInfo} object: \\ - - \begin{lstlisting} -// Anchor point in world-space -const Vector3 anchorPoint(2.0, 4.0, 0.0); - -// Hinge rotation axis in world-space -const Vector3 axis(0.0, 0.0, 1.0); - -// Create the joint info object -HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - Now, it is time to create the actual joint in the physics world using the \texttt{PhysicsWorld::createJoint()} method. - Note that this method will also return a pointer to the \texttt{HingeJoint} object that has been created internally. You will then - be able to use that pointer to change properties of the joint and also to destroy it at the end. \\ - \end{sloppypar} - - Here is how to create the joint in the world: \\ - - \begin{lstlisting} -// Create the hinge joint in the physics world -HingeJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \subsubsection{Limits} - - With the hinge joint, you can constrain the motion range using limits. The limits of the hinge joint are the minimum and maximum angle of - rotation allowed with respect to the initial angle between the bodies when the joint is created. The limits are disabled by default. - If you want to use the limits, you first need to enable them by setting the \texttt{isLimitEnabled} variable of the \texttt{HingeJointInfo} - object to \emph{true} before you create the joint. You also have to specify the minimum and maximum limit angles (in radians) using - the \texttt{minAngleLimit} and \texttt{maxAngleLimit} variables of the joint info object. Note that the minimum limit angle must be in the - range $[ -2 \pi; 0 ]$ and the maximum limit angle must be in the range $[ 0; 2 \pi ]$. \\ - - For instance, here is the way to use the limits for a hinge joint when the joint is created: \\ - - \begin{lstlisting} -// Create the joint info object -HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); - -// Enable the limits of the joint -jointInfo.isLimitEnabled = true; - -// Minimum limit angle -jointInfo.minAngleLimit = -PI / 2.0; - -// Maximum limit angle -jointInfo.maxAngleLimit = PI / 2.0; - -// Create the hinge joint in the physis world -HingeJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - It is also possible to use the \texttt{HingeJoint::enableLimit()}, \texttt{HingeJoint::setMinAngleLimit()} and \texttt{HingeJoint::setMaxAngleLimit()} methods to specify - the limits of the joint after its creation. See the API documentation for more information. - \end{sloppypar} - - \subsubsection{Motor} - - A motor is also available for the hinge joint. It can be used to rotate the bodies around the hinge axis at a given angular speed and such that the torque applied to - rotate the bodies does not exceed a maximum allowed torque. The motor is disabled by default. If you want to use it, you first have to activate it using the - \texttt{isMotorEnabled} boolean variable of the \texttt{HingeJointInfo} object before you create the joint. Then, you need to specify the angular motor speed (in radians/seconds) - using the \texttt{motorSpeed} variable and also the maximum allowed torque (in Newton $\cdot$ meters) with the \texttt{maxMotorTorque} variable. \\ - - For instance, here is how to enable the motor of the hinge joint when the joint is created: \\ - - \begin{lstlisting} -// Create the joint info object -HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); - -// Enable the motor of the joint -jointInfo.isMotorEnabled = true; - -// Motor angular speed -jointInfo.motorSpeed = PI / 4.0; - -// Maximum allowed torque -jointInfo.maxMotorTorque = 10.0; - -// Create the hinge joint in the physics world -HingeJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - It is also possible to use the \texttt{HingeJoint::enableMotor()}, \texttt{HingeJoint::setMotorSpeed()} and - \texttt{HingeJoint::setMaxMotorTorque()} methods to - enable the motor of the joint after its creation. See the API documentation for more information. - \end{sloppypar} - - \subsection{Slider Joint} - - The \texttt{SliderJoint} class describes a slider joint (or prismatic joint) that only allows relative translation along a single direction. - It has a single degree of freedom and allows no relative rotation. \\ - - In order to create a slider joint, you first need to specify the anchor point and the slider axis direction. - The constructor of the \texttt{SliderJointInfo} object needs two pointers to the bodies of the joint, the anchor point and the axis direction. - The \texttt{SliderJointInfo} class contains different constructors that you can use whether you want to specify the anchor point and the direction axis - in local-space or world-space. Make sure that the two bodies are in a valid position (with respect to the joint constraint) at the beginning - of the simulation.\\ - - You can see in the following code how to specify the information to create a slider joint: \\ - - \begin{lstlisting} -// Anchor point in world-space -const Vector3 anchorPoint = 0.5 * (body2Position + body1Position); - -// Slider axis in world-space -const Vector3 axis = (body2Position - body1Position); - -// Create the joint info object -SliderJointInfo jointInfo(body1, body2, anchorPoint, axis); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - Now, it is possible to create the actual joint in the physics world using the \texttt{PhysicsWorld::createJoint()} method. - Note that this method will also return a pointer to the \texttt{SliderJoint} object that has been created internally. You will then - be able to use that pointer to change properties of the joint and also to destroy it at the end. \\ - \end{sloppypar} - - Here is how to create the joint in the world: \\ - - \begin{lstlisting} -// Create the slider joint in the physics world -SliderJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \subsubsection{Limits} - - It is also possible to control the range of the slider joint motion using limits. The limits are disabled by default. In order to use the limits when the joint is created, you first - need to activate them using the \texttt{isLimitEnabled} variable of the \texttt{SliderJointInfo} class. Then, you need to specify the minimum and maximum translation limits - (in meters) using the \texttt{minTranslationLimit} and \texttt{maxTranslation\-Limit} variables. Note that the initial position of the two bodies when the joint is created - corresponds to a translation of zero. Therefore, the minimum limit must be smaller or equal to zero and the maximum limit must be larger or equal to zero. \\ - - You can see in the following example how to set the limits when the slider joint is created: \\ - - \begin{lstlisting} -// Create the joint info object -SliderJointInfo jointInfo(body1, body2, anchorPoint, axis); - -// Enable the limits of the joint -jointInfo.isLimitEnabled = true; - -// Minimum translation limit -jointInfo.minTranslationLimit = -1.7; - -// Maximum translation limit -jointInfo.maxTranslationLimit = 1.7; - -// Create the hinge joint in the physics world -SliderJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - You can also use the \texttt{SliderJoint::enableLimit()}, \texttt{SliderJoint::\-setMinTranslationLimit()} and \texttt{SliderJoint::setMaxTranslationLimit()} methods to - enable the limits of the joint after its creation. See the API documentation for more information. - \end{sloppypar} - - \subsubsection{Motor} - - The slider joint also has a motor. You can use it to translate the bodies along the slider axis at a given linear speed and such that the force applied to - move the bodies does not exceed a maximum allowed force. The motor is disabled by default. If you want to use it when the joint is created, you first have to activate it using the - \texttt{isMotorEnabled} boolean variable of the \texttt{SliderJointInfo} object before you create the joint. Then, you need to specify the linear motor speed (in meters/seconds) - using the \texttt{motorSpeed} variable and also the maximum allowed force (in Newtons) with the \texttt{maxMotorForce} variable. \\ - - For instance, here is how to enable the motor of the slider joint when the joint is created: \\ - - \begin{lstlisting} -// Create the joint info object -SliderJointInfo jointInfo(body1, body2, anchorPoint, axis); - -// Enable the motor of the joint -jointInfo.isMotorEnabled = true; - -// Motor linear speed -jointInfo.motorSpeed = 2.0; - -// Maximum allowed force -jointInfo.maxMotorForce = 10.0; - -// Create the slider joint in the physics world -SliderJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - It is also possible to use the \texttt{SliderJoint::enableMotor()}, \texttt{SliderJoint::setMotorSpeed()} and \texttt{SliderJoint::setMaxMotorForce()} methods to enable the - motor of the joint after its creation. See the API documentation for more information. - \end{sloppypar} - - \subsection{Fixed Joint} - - The \texttt{FixedJoint} class describes a fixed joint between two bodies. In a fixed joint, there is no degree of freedom, the bodies are not - allowed to translate or rotate with respect to each other. \\ - - In order to create a fixed joint, you simply need to specify an anchor point to create the \texttt{FixedJointInfo} - object. The \texttt{FixedJointInfo} class contains different constructors that you can use whether you want to specify the anchor point in local-space - or world-space. Make sure that the two bodies are in a valid position (with respect to the joint constraint) at the beginning of the simulation.\\ - - For instance, here is how to create the joint info object for a fixed joint: \\ - - \begin{lstlisting} -// Anchor point in world-space -Vector3 anchorPoint(2.0, 3.0, 4.0); - -// Create the joint info object -FixedJointInfo jointInfo1(body1, body2, anchorPoint); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - Now, it is possible to create the actual joint in the physics world using the \texttt{PhysicsWorld::createJoint()} method. - Note that this method will also return a pointer to the \texttt{FixedJoint} object that has been created internally. You will then - be able to use that pointer to change properties of the joint and also to destroy it at the end. \\ - \end{sloppypar} - - Here is how to create the joint in the world: \\ - - \begin{lstlisting} -// Create the fixed joint in the physics world -FixedJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \subsection{Collision between the bodies of a Joint} - - By default, the two bodies involved in a joint are able to collide with each other. However, it is possible to disable the collision between the two bodies that are part - of the joint. To do it, you simply need to set the variable \texttt{isCollisionEnabled} of the joint info object to \emph{false} when you create the joint. \\ - - For instance, when you create a \texttt{HingeJointInfo} object in order to construct a hinge joint, you can disable the collision between the two bodies of the joint as in the - following example: \\ - - \begin{lstlisting} -// Create the joint info object -HingeJointInfo jointInfo(body1, body2, anchorPoint, axis); - -// Disable the collision between the bodies -jointInfo.isCollisionEnabled = false; - -// Create the joint in the physics world -HingeJoint* joint; -joint = dynamic_cast(world->createJoint(jointInfo)); - \end{lstlisting} - - \subsection{Destroying a Joint} - - \begin{sloppypar} - In order to destroy a joint, you simply need to call the \texttt{PhysicsWorld::destroyJoint()} method using the pointer to - a previously created joint object as argument as shown in the following code: \\ - \end{sloppypar} - - \begin{lstlisting} -// BallAndSocketJoint* joint is a previously created joint - -// Destroy the joint -world->destroyJoint(joint); - \end{lstlisting} - - \vspace{0.6cm} - - It is important that you destroy all the joints that you have created at the end of the simulation. Also note that destroying a - rigid body involved in a joint will automatically destroy that joint. - - \section{Ray casting} - \label{sec:raycasting} - - You can use ReactPhysics3D to test intersection between a ray and the bodies of the world you have created. Ray casting can be performed against - multiple bodies, a single body or any collider of a given body. Note that ray casting only works from the outside of the bodies. If the origin - of a ray is inside a collision shape, no hit will be reported. \\ - - The first thing you need to do is to create a ray using the \texttt{Ray} class of ReactPhysics3D. As you can see in the following example, this is - very easy. You simply need to specify the point where the ray starts and the point where the ray ends (in world-space coordinates). \\ - - \begin{lstlisting} -// Start and end points of the ray -Vector3 startPoint(0.0, 5.0, 1.0); -Vector3 endPoint(0.0, 5.0, 30); - -// Create the ray -Ray ray(startPoint, endPoint); - \end{lstlisting} - - \vspace{0.6cm} - - Any ray casting test that will be described in the following sections returns a \texttt{RaycastInfo} object in case of intersection with the ray. - This structure contains the following attributes: \\ - - \begin{description} - \item[worldPoint] Hit point in world-space coordinates - \item[worldNormal] Surface normal of the collider at the hit point in world-space coordinates - \item[hitFraction] Fraction distance of the hit point between \emph{startPoint} and \emph{endPoint} of the ray. The hit point \emph{p} is such that - $p = startPoint + hitFraction \cdot (endPoint - startPoint)$ - \item[body] Pointer to the rigid body that has been hit by the ray - \item[collider] Pointer to the collider that has been hit by the ray - \end{description} - - Note that you can also use collision filtering with ray casting in order to only test ray intersection with specific colliders. - Collision filtering is described in section \ref{sec:collisionfiltering}. - - \subsection{Ray casting against multiple bodies} - - This section describes how to get all the colliders of all bodies in the world that are intersected by a given ray. - - \subsubsection{The RaycastCallback class} - - First, you have to implement your own class that inherits from the \texttt{RaycastCallback} class. Then, you need to override the - \texttt{RaycastCallback::notifyRaycastHit()} method in your own class. An instance of your class have to be provided as a parameter - of the raycast method and the \texttt{notifyRaycastHit()} method will be called for each collider that is hit by the ray. You will receive, as a parameter - of this method, a \texttt{RaycastInfo} object that will contain the information about the raycast hit (hit point, hit surface normal, hit body, hit collider, \dots). \\ - - In your \texttt{notifyRaycastHit()} method, you need to return a fraction value that will specify the continuation of the ray cast after a hit. - The return value is the next maxFraction value to use. If you return a fraction of 0.0, it means that the raycast should terminate. If you return a - fraction of 1.0, it indicates that the ray is not clipped and the ray cast should continue as if no hit occurred. If you return the fraction in the - parameter (hitFraction value in the \texttt{RaycastInfo} object), the current ray will be clipped to this fraction in the next queries. If you return -1.0, it will - ignore this collider and continue the ray cast. Note that no assumption can be done about the order of the calls of the \texttt{notifyRaycastHit()} method. \\ - - Here is an example about creating your own raycast callback class that inherits from the \texttt{RaycastCallback} class and how to override the - \texttt{notifyRaycastHit()} method: \\ - - \begin{lstlisting} -// Class WorldRaycastCallback -class MyCallbackClass : public RaycastCallback { - -public: - - virtual decimal notifyRaycastHit(const RaycastInfo& info) { - - // Display the world hit point coordinates - std::cout << "Hit point : " << - info.worldPoint.x << - info.worldPoint.y << - info.worldPoint.z << - std::endl; - - // Return a fraction of 1.0 to gather all hits - return decimal(1.0); - } -}; - \end{lstlisting} - - \subsubsection{Raycast query in the world} - - \begin{sloppypar} - Now that you have your own raycast callback class, you can use the \texttt{PhysicsWorld::raycast()} method to perform a ray casting test - on a physics world. \\ - \end{sloppypar} - - The first parameter of this method is a reference to the \texttt{Ray} object representing the ray you need to test intersection with. The second parameter is a pointer to - the object of your raycast callback object. You can specify an optional third parameter which is the bit mask for collision filtering. - It can be used to raycast only against selected categories of colliders as described in section \ref{sec:collisionfiltering}. \\ - - \begin{lstlisting} -// Create the ray -Vector3 startPoint(1 , 2, 10); -Vector3 endPoint(1, 2, -20); -Ray ray(startPoint, endPoint); - -// Create an instance of your callback class -MyCallbackClass callbackObject; - -// Raycast test -world->raycast(ray, &callbackObject); - \end{lstlisting} - - \vspace{0.6cm} - - \subsection{Ray casting against a single body} - - \begin{sloppypar} - - You can also perform ray casting against a single specific rigid body of the world. To do this, you need to use the - \texttt{RigidBody::raycast()} method. This method takes two parameters. The first one is a reference to the \texttt{Ray} object and the second one - is a reference to the \texttt{RaycastInfo} object that will contain hit information if the ray hits the body. This method returns true if the ray hits the - body. The \texttt{RaycastInfo} object will only be valid if the returned value is \emph{true} (a hit occured). \\ - - \end{sloppypar} - - The following example shows how test ray intersection with a body: \\ - - \begin{lstlisting} -// Create the ray -Vector3 startPoint(1 , 2, 10); -Vector3 endPoint(1, 2, -20); -Ray ray(startPoint, endPoint); - -// Create the raycast info object for the -// raycast result -RaycastInfo raycastInfo; - -// Raycast test -bool isHit = body->raycast(ray, raycastInfo); - \end{lstlisting} - - \vspace{0.6cm} - - \subsection{Ray casting against the collider of a body} - - You can also perform ray casting against a single specific collider of a rigid body of the world. To do this, you need to use the - \texttt{Collider::raycast()} method of the given collider. This method takes two parameters. The first one is a reference to the \texttt{Ray} - object and the second one is a reference to the \texttt{RaycastInfo} object that will contain hit information if the ray hits the body. This method returns - true if the ray hits the body. The \texttt{RaycastInfo} object will only be valid if the returned value is \emph{true} (a hit occured). \\ - - The following example shows how to test ray intersection with a given collider: \\ - - \begin{lstlisting} -// Create the ray -Vector3 startPoint(1 , 2, 10); -Vector3 endPoint(1, 2, -20); -Ray ray(startPoint, endPoint); - -// Create the raycast info object for the -// raycast result -RaycastInfo raycastInfo; - -// Test raycasting against a collider -bool isHit = collider->raycast(ray, raycastInfo); - \end{lstlisting} - - \vspace{0.6cm} - - \section{Testbed application} - \label{sec:testbed} - - \begin{figure}[!ht] - \centering - \includegraphics[scale=0.5]{testbed.png} - \label{fig:testbed} - \end{figure} - - The testbed application contains a graphical interface where you can select and see several demo scenes using the ReactPhysics3D library. \\ - - As described in section \ref{sec:building}, you can enable a \texttt{CMake} option to build the testbed application when you build the library. - Note that OpenGL is required to compile it. \\ - - The testbed application can be found in the \texttt{testbed/} folder of - the ReactPhysics3D library. Do not hesitate to take a look at the code of the demo scenes to better understand how - to use the library in your application. \\ - - \section{Receiving Feedback} - \label{sec:receiving_feedback} - - Sometimes, you want to receive notifications from the physics engine when a given event occurs. The \texttt{EventListener} class can be used for that - purpose. In order to use it, you need to create a new class that inherits from the \texttt{EventListener} class and overrides some methods that will - be called by the ReactPhysics3D library when some events occur. You also need to register your class in the physics world using the - \texttt{PhysicsWorld::setEventListener()} as in the following code: \\ - - \begin{lstlisting} -// Your event listener class -class YourEventListener : public EventListener { - - ... -}; - -YourEventListener listener; - -// Register your event listener class -world->setEventListener(&listener); - \end{lstlisting} - - \subsection{Contacts} - - \begin{sloppypar} - The \texttt{EventListener} contains a \texttt{onContact()} method. If you override this method in your custom event listener class, this method will - be called when you call the \texttt{PhysicsWorld::update()} method if there are contacts to report during the current update. \\ - - The \texttt{onContact()} method will be called with a \texttt{CollisionCallback::CallbackData} object in parameter. This object has a - \texttt{getNbContactPairs()} and a \texttt{getContactPair()} methods that will allow you to get each contact pair (class - \texttt{CollisionCallback::ContactPair}) that occured during the physics world update. Note that a contact pair contains a list of contact - points between two bodies. Once you get a contact pair, you can use the \texttt{getBody1()} or \texttt{getBody2()} methods to get the bodies in - contact, you can use the \texttt{getCollider1()} or \texttt{getCollider2()} methods to get the colliders in contact and the - \texttt{getNbContactPoints()} and \texttt{getContactPoint()} methods to get the contact points (class \texttt{CollisionCallback::ContactPoint}) - of the contact pair. Note that the \texttt{onContact()} method will report contacts after the collision detection phase and before the contact - resolution. \\ - \end{sloppypar} - - \begin{sloppypar} - The contact pair also has a \texttt{getEventType()} method that gives information about the type of contact event for the pair. It can be - \texttt{ContactStart} if the two bodies of the contact pair where not colliding in the previous call to \texttt{PhysicsWorld::update()} and are now - colliding. It can be \texttt{ContactStay}, if the two bodies where colliding in the previous frame and are still colliding or it can be - \texttt{ContactExit} if the two bodies where colliding in the previous frame but are not colliding anymore. Note that in this last situation, there - will be no contact points in the contact pair. \\ - \end{sloppypar} - - \begin{sloppypar} - As you know, rigid bodies can sleep. During that time, no contacts will be reported using the \texttt{EventListener::onContact()} method. Therefore, - If you stop receiving contact notifications for a rigid body, it does not necessarily mean that there is no contacts anymore but it could mean that the - body has fallen asleep. You need to use the \texttt{getEventType()} method to know if the body is not colliding anymore (when you receive an event of type - \texttt{ContactExit}). \\ - \end{sloppypar} - - \begin{sloppypar} - In the following example, you can see how to override the \texttt{EventListener::onContact()} method to get the current contact points of the physics - world: \\ - \end{sloppypar} - - \begin{lstlisting} -// Your event listener class -class YourEventListener : public EventListener { - - // Override the onContact() method - virtual void onContact(const CollisionCallback::CallbackData& callbackData) overrride { - - // For each contact pair - for (uint p = 0; p < callbackData.getNbContactPairs(); p++) { - - // Get the contact pair - CollisionCallback::ContactPair contactPair = callbackData.getContactPair(p); - - // For each contact point of the contact pair - for (uint c = 0; c < contactPair.getNbContactPoints(); c++) { - - // Get the contact point - CollisionCallback::ContactPoint contactPoint = contactPair.getContactPoint(c); - - // Get the contact point on the first collider and convert it in world-space - Vector3 worldPoint = contactPair.getCollider1()->getLocalToWorldTransform() * contactPoint.getLocalPointOnCollider1(); - - ... - } - } - } - -}; - \end{lstlisting} - - \subsection{Triggers} - \label{sec:eventlistenertriggers} - - As described in section \ref{sec:trigger} it is possible to set a collider as being a trigger. You can do this if you do not want any collision with - this collider but you are only interested in knowing when another collider enters or exit the volume of the collider. If you do this, you will need - to override the \texttt{onTrigger()} method of the \texttt{EventListener}. This method will be called and report all the collision with your triggers - when you call the \texttt{PhysicsWorld::update()} method. \\ - - \begin{sloppypar} - The \texttt{onTrigger()} method will be called with an \texttt{OverlapCallback::CallbackData} object in parameter. This object has a - \texttt{getNbOverlappingPairs()} and a \texttt{getOverlappingPair()} methods that will allow you to get each overlapping pair (class - \texttt{OverlapCallback::OverlapPair}) that occured during the physics world update. Once you get an overlapping pair, you can use - the \texttt{getBody1()} or \texttt{getBody2()} methods to get the bodies in - contact and the \texttt{getCollider1()} or \texttt{getCollider2()} methods to get the trigger colliders in contact. - Note that the \texttt{onTrigger()} method will report overlaping triggers after the collision detection phase and before the contact resolution. \\ - \end{sloppypar} - - \begin{sloppypar} - The overlapping pair also has a \texttt{getEventType()} method that gives information about the type of overlapping event for the pair. It can be - \texttt{OverlapStart} if the two bodies of the pair where not overlapping in the previous call to \texttt{PhysicsWorld::update()} and are now - overlapping. It can be \texttt{OverlapStay}, if the two bodies where overlapping in the previous frame and are still overlapping or it can be - \texttt{OverlapExit} if the two bodies where overlapping in the previous frame but are not overlapping anymore. \\ - \end{sloppypar} - - \begin{sloppypar} - As you know, rigid bodies can sleep. During that time, no overlap will be reported using the \texttt{EventListener::onTrigger()} method. Therefore, - If you stop receiving trigger overlap notifications for a rigid body, it does not necessarily mean that there is no overlap anymore but it could mean - that the body has fallen asleep. You need to use the \texttt{getEventType()} method to know if the body is not overlapping anymore (when you receive an - event of type \texttt{OverlapExit}). \\ - \end{sloppypar} - - Note that the \texttt{onTrigger()} method is only called for colliders that you have set as triggers using the \texttt{Collider::setIsTrigger()} method. - - \section{Profiler} - \label{sec:profiler} - - If you build the library with the \texttt{RP3D\_PROFILING\_ENABLED} variable enabled (see section \ref{sec:cmakevariables}), a real-time profiler - will collect information while the application is running. Then, at the end of your application, when the destructor of the \texttt{PhysicsWorld} - class is called, information about the running time of the library will written to a file. - This can be useful to know where time is spent in the different parts of the ReactPhysics3D library in case your application is too slow. \\ - - Each physics world has its own profiler. By default, the profiling report wil be written in a text file next to the executable. - If you have multiple worlds in your application, there will be one profile file for each world. The profile files will be named after the - name of the worlds. By defaults worlds will have names: world, world1, world2, world3, \dots You can change the name of the world by - setting it into the \texttt{WorldSettings} object when you create the world (see section \ref{sec:physicsworld}). \\ - - \section{Logger} - \label{sec:logger} - - ReactPhysics3D has an internal logger that can be used to get logs from the library while the application is running. This can be useful for - debugging for instance. \\ - - The logger is part of the \texttt{PhysicsCommon} class. By default there is no logger set. If you want to create your custom logger to receive logs - from ReactPhysics3D, you can inherit the \texttt{Logger} class and override the \texttt{Logger::log()} method. Then, you have to use the - \texttt{PhysicsCommon::setLogger()} method to set the logger. \\ - - Getting the logs is the way for you to see the errors reported by the library and therefore, you should not just ignore the errors from the logs. - You should at least redirect the logs (warnings and errors) in the standard output while debugging your application as described in the example - below. \\ - - \begin{sloppypar} - If you don't want to create your custom logger class, you can use the default logger of ReactPhysics3D (class \texttt{DefaultLogger}). With this - logger, you can output the logs into - a file or a std::stream. The logs can have different format like raw text of HTML. In order to instantiate the default logger, you need to call the - \texttt{PhysicsCommon::createDefaultLogger()} method. Then, you can customize this logger and finally, you need to set this logger using the - \texttt{PhysicsCommon::setLogger()} method. \\ - \end{sloppypar} - - The following code shows how to create a default logger that will output logs (warning and errors) in HTML into a file and in raw text into - the standard output: \\ - - \begin{lstlisting} -// Create the default logger -DefaultLogger* logger = physicsCommon.createDefaultLogger(); - -// Log level (warnings and errors) -uint logLevel = static_cast(static_cast(Logger::Level::Warning) | static_cast(Logger::Level::Error); - -// Output the logs into an HTML file -logger->addFileDestination("rp3d_log_" + name + ".html", logLevel, DefaultLogger::Format::HTML); - -// Output the logs into the standard output -logger->addStreamDestination(std::cout, logLevel, DefaultLogger::Format::Text); - -// Set the logger -physicsCommon.setLogger(logger); - \end{lstlisting} - - \section{Debug Renderer} - - \begin{sloppypar} - For debugging purpose, it can be useful to display the physics objects of ReactPhysics3D on top of your simulation. This is possible using the - \texttt{DebugRenderer} class of the library that allows you to display the colliders shapes, the AABB of the colliders or the contact points for - instance. This can help you to check for instance if the physics collider of ReactPhysics3D is correctly aligned with the object you are rendering. - When debug rendering is enabled, ReactPhysics3D will generate each time you call the \texttt{PhysicsWorld::update()} method (each frame), some rendering - primitives (lines and triangles) that you will be able to retrieve and display on you simulation (with OpenGL or DirectX for instance). Note that - ReactPhysics3D will only generate the arrays of lines and triangles but you are responsible to draw them. \\ - \end{sloppypar} - - \begin{figure}[!ht] - \centering - \includegraphics[scale=0.45]{DebugRendering.png} - \label{fig:debugrendering} - \end{figure} - - \begin{sloppypar} - By default, the debug rendering is disabled. You can activate it using the \texttt{PhysicsWorld::setIsDebugRenderingEnabled()} method. Note that you - should disable it for the final release because this can be quite expensive to compute. You can get a reference to \texttt{DebugRenderer} of the physics - world using the \texttt{PhysicsWorld::getDebugRenderer()} method. You also need to enable debugging for each body that you want to debug using the - \texttt{Body::setIsDebugEnabled()} method. By default, - debuging is disabled for all bodies. Then, you need to select the debug items - (class \texttt{DebugRenderer:DebugItem}) you want to display. For instance, you might want to display the shapes of the colliders - (\texttt{DebugItem::COLLISION\_SHAPE}), the broad-phase AABBs of the colliders (\texttt{DebugItem::COLLIDER\_BROADPHASE\_AABB}), the colliders AABBs - (\texttt{DebugItem::COLLIDER\_AABB}), the contact points (\texttt{DebugItem::CONTACT\_POINT}) or the contact normals (\texttt{DebugItem::CONTACT\_NORMAL}). - You need to use the \texttt{DebugRenderer::setIsDebugItemDisplayed()} method to select if you want or not a given debug item to be displayed. \\ - \end{sloppypar} - - The following code shows how to enable debug rendering in order to display contact points and contact normals: \\ - - \begin{lstlisting} -// Enable debug rendering -physicsWorld->setIsDebugRenderingEnabled(true); - -// Enable debugging for each rigid body that we want to display -for (RigidBody* body: rigidBodies) { - body->setIsDebugEnabled(true); -} - -// Get a reference to the debug renderer -DebugRenderer& debugRenderer = physicsWorld->getDebugRenderer(); - -// Select the contact points and contact normals to be displayed -debugRenderer.setIsDebugItemDisplayed(DebugRenderer::DebugItem::CONTACT_POINT, true); -debugRenderer.setIsDebugItemDisplayed(DebugRenderer::DebugItem::CONTACT_NORMAL, true); - \end{lstlisting} - - \vspace{0.6cm} - - \begin{sloppypar} - Now each time you call the \texttt{PhysicsWorld::update()} method, ReactPhysics3D will generate arrays with lines and triangles so that you can - draw them in your application. You can use the \texttt{DebugRenderer::getNbLines()} and \texttt{DebugRenderer::getNbTriangles()} methods to know - the number of lines and triangles to draw. Then, you need to call the \texttt{DebugRenderer::getLinesArray()} and - \texttt{DebugRenderer::getTrianglesArray()} methods to get pointers to the beginning of the lines and triangles arrays. The lines array contain - elements of type \texttt{DebugRenderer:DebugLine} with the two points of a line and their colors and the triangles array has elements of - type \texttt{DebugRenderer:DebugTriangle} with the three points of each triangle and their colors. Note that the vertices of the lines and triangles - are defined in world-space coordinates of the physics world. - \end{sloppypar} - - \section{Determinism} - \label{sec:determinism} - - Sometimes, it is important that the simulation of a physics world behaves exactly the same each time we run it. Because of the differences in - compilers and computer hardware it is quite difficult to achieve this between different machines. However, ReactPhysics3D should be deterministic when - compiled with the same compiler and running on the same machine. In order to obtain two similar runs of the simulation of a physics world, it is - adviced to completely destroy and recreate the physics world, the bodies and the joints inside it (in order to reset all the internal data cached - for the simulation). You must also create the bodies and joints in the same order each time and make sure that all the calls to the methods - of your physics world, bodies and joints happen in the same order. - - \section{API Reference Documentation} - - ReactPhysics3D contains Doxygen documentation for its API. \\ - - The API reference documentation is also available online here: \url{http://www.reactphysics3d.com/documentation/api/html/} \\ - - You can also find it in the library archive in the folder \texttt{/documentation/API/html/}. You just - need to open the \texttt{index.html} file with your favorite web browser. - - \section{Issues} - - If you find some bugs, do not hesitate to report them on our issue tracker here: \\ - - \url{https://github.com/DanielChappuis/reactphysics3d/issues} \\ - - Thanks a lot for reporting the issues that you find. It will help us correct and improve the library. - -\end{document} diff --git a/documentation/UserManual/configHTLatex.cfg b/documentation/UserManual/configHTLatex.cfg deleted file mode 100644 index e2e626f02..000000000 --- a/documentation/UserManual/configHTLatex.cfg +++ /dev/null @@ -1,15 +0,0 @@ -\Preamble{html} -\begin{document} -% Upper case PNG file extensions -\Configure{graphics*} -{PNG} -{\Picture[pict]{\csname Gin@base\endcsname.PNG align=center border=0}} -% Lower case png extensions -\Configure{graphics*} -{png} -{\Picture[pict]{\csname Gin@base\endcsname.png align=center border=0}} -% Problems with spanish babel -\makeatletter -\let\ifes@LaTeXe\iftrue -\makeatother -\EndPreamble \ No newline at end of file diff --git a/documentation/UserManual/generateHTMLManual.sh b/documentation/UserManual/generateHTMLManual.sh deleted file mode 100755 index d5a0033d1..000000000 --- a/documentation/UserManual/generateHTMLManual.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# Delete the /html folder -rm -R html/ - -# Create the /html folder -mkdir html - -# Use the htlatex command to generate the HTML user manual from the .tex file -htlatex ReactPhysics3D-UserManual.tex "configHTLatex.cfg,html" "" -dhtml/ - -# Copy the images/ folder into the html/ folder -cp -R images/ html/images/ diff --git a/documentation/UserManual/title.tex b/documentation/UserManual/title.tex deleted file mode 100644 index e4e5b4bb1..000000000 --- a/documentation/UserManual/title.tex +++ /dev/null @@ -1,27 +0,0 @@ -% Title page definition - -\makeatletter -\def\maketitle{% - \null - \thispagestyle{empty}% - \begin{center}\leavevmode - \normalfont - {} - \vskip 1.3cm - {\Huge \@title\par}% - \vskip 0.3cm - {\Large Version: 0.10.0\par}% - \vskip 0.3cm - {\Large \@author\par}% - \vskip 2cm - {\includegraphics[height=5cm]{images/ReactPhysics3DLogo.png}} - \vskip 2cm - {\large{\url{http://www.reactphysics3d.com}}} - \vskip 0.2cm - {\large \@date}% - \end{center}% - \vfill - \null - \cleardoublepage - } -\makeatother diff --git a/documentation/custom.css b/documentation/custom.css new file mode 100644 index 000000000..96188ca78 --- /dev/null +++ b/documentation/custom.css @@ -0,0 +1,8 @@ + +html { + --side-nav-fixed-width: 400px; +} + +html.dark-mode { + --side-nav-fixed-width: 400px; +} diff --git a/documentation/delete_me.css b/documentation/delete_me.css new file mode 100644 index 000000000..ffbff0224 --- /dev/null +++ b/documentation/delete_me.css @@ -0,0 +1,1793 @@ +/* The standard CSS for doxygen 1.9.1 */ + +body, table, div, p, dl { + font: 400 14px/22px Roboto,sans-serif; +} + +p.reference, p.definition { + font: 400 14px/22px Roboto,sans-serif; +} + +/* @group Heading Levels */ + +h1.groupheader { + font-size: 150%; +} + +.title { + font: 400 14px/28px Roboto,sans-serif; + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2.groupheader { + border-bottom: 1px solid #879ECB; + color: #354C7B; + font-size: 150%; + font-weight: normal; + margin-top: 1.75em; + padding-top: 8px; + padding-bottom: 4px; + width: 100%; +} + +h3.groupheader { + font-size: 100%; +} + +h1, h2, h3, h4, h5, h6 { + -webkit-transition: text-shadow 0.5s linear; + -moz-transition: text-shadow 0.5s linear; + -ms-transition: text-shadow 0.5s linear; + -o-transition: text-shadow 0.5s linear; + transition: text-shadow 0.5s linear; + margin-right: 15px; +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; +} + +dt { + font-weight: bold; +} + +ul.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; +} + +p.startli, p.startdd { + margin-top: 2px; +} + +th p.starttd, th p.intertd, th p.endtd { + font-size: 100%; + font-weight: 700; +} + +p.starttd { + margin-top: 0px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +p.interli { +} + +p.interdd { +} + +p.intertd { +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.navtab { + border-right: 1px solid #A3B4D7; + padding-right: 15px; + text-align: right; + line-height: 110%; +} + +div.navtab table { + border-spacing: 0; +} + +td.navtab { + padding-right: 6px; + padding-left: 6px; +} +td.navtabHL { + background-image: url('tab_a.png'); + background-repeat:repeat-x; + padding-right: 6px; + padding-left: 6px; +} + +td.navtabHL a, td.navtabHL a:visited { + color: #fff; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); +} + +a.navtab { + font-weight: bold; +} + +div.qindex{ + text-align: center; + width: 100%; + line-height: 140%; + font-size: 130%; + color: #A0A0A0; +} + +dt.alphachar{ + font-size: 180%; + font-weight: bold; +} + +.alphachar a{ + color: black; +} + +.alphachar a:hover, .alphachar a:visited{ + text-decoration: none; +} + +.classindex dl { + padding: 25px; + column-count:1 +} + +.classindex dd { + display:inline-block; + margin-left: 50px; + width: 90%; + line-height: 1.15em; +} + +.classindex dl.odd { + background-color: #F8F9FC; +} + +@media(min-width: 1120px) { + .classindex dl { + column-count:2 + } +} + +@media(min-width: 1320px) { + .classindex dl { + column-count:3 + } +} + + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +.contents a.qindexHL:visited { + color: #FFFFFF; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited, a.line, a.line:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +ul { + overflow: hidden; /*Fixed: list item bullets overlap floating elements*/ +} + +#side-nav ul { + overflow: visible; /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ +} + +#main-nav ul { + overflow: visible; /* reset ul rule for the navigation bar drop down lists */ +} + +.fragment { + text-align: left; + direction: ltr; + overflow-x: auto; /*Fixed: fragment lines overlap floating elements*/ + overflow-y: hidden; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; + font-family: monospace, fixed; + font-size: 105%; +} + +div.fragment { + padding: 0 0 1px 0; /*Fixed: last line underline overlap border*/ + margin: 4px 8px 4px 2px; + background-color: #FBFCFD; + border: 1px solid #C4CFE5; +} + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +div.line:after { + content:"\000A"; + white-space: pre; +} + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + + +span.lineno { + padding-right: 4px; + text-align: right; + border-right: 2px solid #0F0; + background-color: #E8E8E8; + white-space: pre; +} +span.lineno a { + background-color: #D8D8D8; +} + +span.lineno a:hover { + background-color: #C8C8C8; +} + +.lineno { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +div.ah, span.ah { + background-color: black; + font-weight: bold; + color: #FFFFFF; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000 110%); +} + +div.classindex ul { + list-style: none; + padding-left: 0; +} + +div.classindex span.ai { + display: inline-block; +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 12px; + margin-right: 8px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl, img.inline { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +blockquote.DocNodeRTL { + border-left: 0; + border-right: 2px solid #9CAFD4; + margin: 0 4px 0 24px; + padding: 0 16px 0 12px; +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.memberdecls td, .fieldtable tr { + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memSeparator { + border-bottom: 1px solid #DEE4F0; + line-height: 1px; + margin: 0px; + padding: 0px; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight, .memTemplItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; + font-size: 80%; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtitle { + padding: 8px; + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + margin-bottom: -1px; + background-image: url('nav_f.png'); + background-repeat: repeat-x; + background-color: #E2E8F2; + line-height: 1.25; + font-weight: 300; + float:left; +} + +.permalink +{ + font-size: 65%; + display: inline-block; + vertical-align: middle; +} + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; + -webkit-transition: box-shadow 0.5s linear; + -moz-transition: box-shadow 0.5s linear; + -ms-transition: box-shadow 0.5s linear; + -o-transition: box-shadow 0.5s linear; + transition: box-shadow 0.5s linear; + display: table !important; + width: 100%; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; +} + +.memname { + font-weight: 400; + margin-left: 6px; +} + +.memname td { + vertical-align: bottom; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + background-color: #DFE5F1; + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 4px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 4px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 4px; + +} + +.overload { + font-family: "courier new",courier,monospace; + font-size: 65%; +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 10px 2px 10px; + background-color: #FBFCFD; + border-top-width: 0; + background-image:url('nav_g.png'); + background-repeat:repeat-x; + background-color: #FFFFFF; + /* opera specific markup */ + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-bottomright: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} +.paramname code { + line-height: 14px; +} + +.params, .retval, .exception, .tparams { + margin-left: 0px; + padding-left: 0px; +} + +.params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype, .tparams .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir, .tparams .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + +table.mlabels { + border-spacing: 0px; +} + +td.mlabels-left { + width: 100%; + padding: 0px; +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; +} + +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #728DC1; + border-top:1px solid #5373B4; + border-left:1px solid #5373B4; + border-right:1px solid #C4CFE5; + border-bottom:1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; +} + + + +/* @end */ + +/* these are for tree view inside a (index) page */ + +div.directory { + margin: 10px 0px; + border-top: 1px solid #9CAFD4; + border-bottom: 1px solid #9CAFD4; + width: 100%; +} + +.directory table { + border-collapse:collapse; +} + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; +} + +.directory td.entry a { + outline:none; +} + +.directory td.entry a img { + border: none; +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + padding-top: 3px; + border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory tr.even { + padding-left: 6px; + background-color: #F7F8FB; +} + +.directory img { + vertical-align: -30%; +} + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; +} + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #3D578C; +} + +.arrow { + color: #9CAFD4; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + width: 16px; + height: 22px; +} + +.icon { + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #728DC1; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; +} + +.icona { + width: 24px; + height: 22px; + display: inline-block; +} + +.iconfopen { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('folderopen.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +.iconfclosed { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('folderclosed.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +.icondoc { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('doc.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +table.directory { + font: 400 14px Roboto,sans-serif; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable caption { + caption-side: top; +} + +table.doxtable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +table.fieldtable { + /*width: 100%;*/ + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fieldname { + padding-top: 3px; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + /*width: 100%;*/ +} + +.fieldtable td.fielddoc p:first-child { + margin-top: 0px; +} + +.fieldtable td.fielddoc p:last-child { + margin-bottom: 2px; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + background-position: 0 -5px; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; + color: #283A5D; + font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +table.classindex +{ + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; +} + +div.ingroups +{ + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +.PageDocRTL-title div.headertitle { + text-align: right; + direction: rtl; +} + +dl { + padding: 0 0 0 0; +} + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ +dl.section { + margin-left: 0px; + padding-left: 0px; +} + +dl.section.DocNodeRTL { + margin-right: 0px; + padding-right: 0px; +} + +dl.note { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #D0C000; +} + +dl.note.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #D0C000; +} + +dl.warning, dl.attention { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #FF0000; +} + +dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00D000; +} + +dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00D000; +} + +dl.deprecated { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #505050; +} + +dl.deprecated.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #505050; +} + +dl.todo { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00C0E0; +} + +dl.todo.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00C0E0; +} + +dl.test { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #3030E0; +} + +dl.test.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #3030E0; +} + +dl.bug { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #C08050; +} + +dl.bug.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #C08050; +} + +dl.section dd { + margin-bottom: 6px; +} + + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectalign +{ + vertical-align: middle; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.plantumlgraph +{ + text-align: center; +} + +.diagraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; + text-align:right; + width:52px; +} + +dl.citelist dd { + margin:2px 0 2px 72px; + padding:5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; +} + +.PageDocRTL-title div.toc { + float: left !important; + text-align: right; +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +.PageDocRTL-title div.toc li { + background-position-x: right !important; + padding-left: 0 !important; + padding-right: 10px; +} + +div.toc h3 { + font: bold 12px/1.2 Arial,FreeSans,sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.level2 { + margin-left: 15px; +} + +div.toc li.level3 { + margin-left: 30px; +} + +div.toc li.level4 { + margin-left: 45px; +} + +span.emoji { + /* font family used at the site: https://unicode.org/emoji/charts/full-emoji-list.html + * font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort; + */ +} + +.PageDocRTL-title div.toc li.level1 { + margin-left: 0 !important; + margin-right: 0; +} + +.PageDocRTL-title div.toc li.level2 { + margin-left: 0 !important; + margin-right: 15px; +} + +.PageDocRTL-title div.toc li.level3 { + margin-left: 0 !important; + margin-right: 30px; +} + +.PageDocRTL-title div.toc li.level4 { + margin-left: 0 !important; + margin-right: 45px; +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.inherit_header td { + padding: 6px 0px 2px 5px; +} + +.inherit { + display: none; +} + +tr.heading h2 { + margin-top: 12px; + margin-bottom: 4px; +} + +/* tooltip related style info */ + +.ttc { + position: absolute; + display: none; +} + +#powerTip { + cursor: default; + white-space: nowrap; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; +} + +#powerTip div.ttdoc { + color: grey; + font-style: italic; +} + +#powerTip div.ttname a { + font-weight: bold; +} + +#powerTip div.ttname { + font-weight: bold; +} + +#powerTip div.ttdeci { + color: #006318; +} + +#powerTip div { + margin: 0px; + padding: 0px; + font: 12px/16px Roboto,sans-serif; +} + +#powerTip:before, #powerTip:after { + content: ""; + position: absolute; + margin: 0px; +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.s:after, #powerTip.s:before, +#powerTip.w:after, #powerTip.w:before, +#powerTip.e:after, #powerTip.e:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.nw:after, #powerTip.nw:before, +#powerTip.sw:after, #powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; +} + +#powerTip.n:after, #powerTip.s:after, +#powerTip.w:after, #powerTip.e:after, +#powerTip.nw:after, #powerTip.ne:after, +#powerTip.sw:after, #powerTip.se:after { + border-color: rgba(255, 255, 255, 0); +} + +#powerTip.n:before, #powerTip.s:before, +#powerTip.w:before, #powerTip.e:before, +#powerTip.nw:before, #powerTip.ne:before, +#powerTip.sw:before, #powerTip.se:before { + border-color: rgba(128, 128, 128, 0); +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.nw:after, #powerTip.nw:before { + top: 100%; +} + +#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { + border-top-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; +} +#powerTip.n:before { + border-top-color: #808080; + border-width: 11px; + margin: 0px -11px; +} +#powerTip.n:after, #powerTip.n:before { + left: 50%; +} + +#powerTip.nw:after, #powerTip.nw:before { + right: 14px; +} + +#powerTip.ne:after, #powerTip.ne:before { + left: 14px; +} + +#powerTip.s:after, #powerTip.s:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.sw:after, #powerTip.sw:before { + bottom: 100%; +} + +#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { + border-bottom-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; +} + +#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { + border-bottom-color: #808080; + border-width: 11px; + margin: 0px -11px; +} + +#powerTip.s:after, #powerTip.s:before { + left: 50%; +} + +#powerTip.sw:after, #powerTip.sw:before { + right: 14px; +} + +#powerTip.se:after, #powerTip.se:before { + left: 14px; +} + +#powerTip.e:after, #powerTip.e:before { + left: 100%; +} +#powerTip.e:after { + border-left-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; +} +#powerTip.e:before { + border-left-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +#powerTip.w:after, #powerTip.w:before { + right: 100%; +} +#powerTip.w:after { + border-right-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; +} +#powerTip.w:before { + border-right-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } +} + +/* @group Markdown */ + +table.markdownTable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.markdownTable tr { +} + +th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +th.markdownTableHeadLeft, td.markdownTableBodyLeft { + text-align: left +} + +th.markdownTableHeadRight, td.markdownTableBodyRight { + text-align: right +} + +th.markdownTableHeadCenter, td.markdownTableBodyCenter { + text-align: center +} + +.DocNodeRTL { + text-align: right; + direction: rtl; +} + +.DocNodeLTR { + text-align: left; + direction: ltr; +} + +table.DocNodeRTL { + width: auto; + margin-right: 0; + margin-left: auto; +} + +table.DocNodeLTR { + width: auto; + margin-right: auto; + margin-left: 0; +} + +tt, code, kbd, samp +{ + display: inline-block; + direction:ltr; +} +/* @end */ + +u { + text-decoration: underline; +} + diff --git a/documentation/delete_me.html b/documentation/delete_me.html new file mode 100644 index 000000000..efa3357e5 --- /dev/null +++ b/documentation/delete_me.html @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/documentation/doxygen-awesome-css b/documentation/doxygen-awesome-css new file mode 160000 index 000000000..5b27b3a74 --- /dev/null +++ b/documentation/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit 5b27b3a747ca1e559fa54149762cca0bad6036fb diff --git a/documentation/header.html b/documentation/header.html new file mode 100644 index 000000000..fe2ffcd4c --- /dev/null +++ b/documentation/header.html @@ -0,0 +1,61 @@ + + + + + + + + +$projectname: $title +$title + + + +$treeview +$search +$mathjax + +$extrastylesheet + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+
$projectname +  $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
+
+ + diff --git a/documentation/UserManual/images/CMakeWin.png b/documentation/images/CMakeWin.png similarity index 100% rename from documentation/UserManual/images/CMakeWin.png rename to documentation/images/CMakeWin.png diff --git a/documentation/UserManual/images/DebugRendering.png b/documentation/images/DebugRendering.png similarity index 100% rename from documentation/UserManual/images/DebugRendering.png rename to documentation/images/DebugRendering.png diff --git a/documentation/UserManual/images/ReactPhysics3DLogo.png b/documentation/images/ReactPhysics3DLogo.png similarity index 100% rename from documentation/UserManual/images/ReactPhysics3DLogo.png rename to documentation/images/ReactPhysics3DLogo.png diff --git a/documentation/UserManual/images/VSBuild.png b/documentation/images/VSBuild.png similarity index 100% rename from documentation/UserManual/images/VSBuild.png rename to documentation/images/VSBuild.png diff --git a/documentation/UserManual/images/VSInstall.png b/documentation/images/VSInstall.png similarity index 100% rename from documentation/UserManual/images/VSInstall.png rename to documentation/images/VSInstall.png diff --git a/documentation/UserManual/images/boxshape.png b/documentation/images/boxshape.png similarity index 100% rename from documentation/UserManual/images/boxshape.png rename to documentation/images/boxshape.png diff --git a/documentation/UserManual/images/capsuleshape.png b/documentation/images/capsuleshape.png similarity index 100% rename from documentation/UserManual/images/capsuleshape.png rename to documentation/images/capsuleshape.png diff --git a/documentation/UserManual/images/concavemeshshape.png b/documentation/images/concavemeshshape.png similarity index 100% rename from documentation/UserManual/images/concavemeshshape.png rename to documentation/images/concavemeshshape.png diff --git a/documentation/UserManual/images/convexshape.png b/documentation/images/convexshape.png similarity index 100% rename from documentation/UserManual/images/convexshape.png rename to documentation/images/convexshape.png diff --git a/documentation/UserManual/images/heightfieldshape.png b/documentation/images/heightfieldshape.png similarity index 100% rename from documentation/UserManual/images/heightfieldshape.png rename to documentation/images/heightfieldshape.png diff --git a/documentation/UserManual/images/sphereshape.png b/documentation/images/sphereshape.png similarity index 100% rename from documentation/UserManual/images/sphereshape.png rename to documentation/images/sphereshape.png diff --git a/documentation/UserManual/images/testbed.png b/documentation/images/testbed.png similarity index 100% rename from documentation/UserManual/images/testbed.png rename to documentation/images/testbed.png diff --git a/include/reactphysics3d/collision/HeightField.h b/include/reactphysics3d/collision/HeightField.h index 79a3871d1..6ba859f0e 100644 --- a/include/reactphysics3d/collision/HeightField.h +++ b/include/reactphysics3d/collision/HeightField.h @@ -124,7 +124,7 @@ class HeightField { /// Raycast method with feedback information bool raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* collider, TriangleRaycastSide testSide, - MemoryAllocator& allocator, const Vector3& scale) const; + 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; diff --git a/include/reactphysics3d/collision/OverlapCallback.h b/include/reactphysics3d/collision/OverlapCallback.h index df5251855..4f12816e8 100644 --- a/include/reactphysics3d/collision/OverlapCallback.h +++ b/include/reactphysics3d/collision/OverlapCallback.h @@ -93,7 +93,7 @@ class OverlapCallback { // -------------------- Methods -------------------- // /// Copy constructor - OverlapPair(const OverlapPair& contactPair) = delete; + OverlapPair(const OverlapPair& contactPair) = default; /// Assignment operator OverlapPair& operator=(const OverlapPair& contactPair) = delete; diff --git a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h index 3b229ed42..505182c03 100644 --- a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h +++ b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h @@ -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 diff --git a/include/reactphysics3d/collision/shapes/AABB.h b/include/reactphysics3d/collision/shapes/AABB.h index 98c6fed30..e18070618 100644 --- a/include/reactphysics3d/collision/shapes/AABB.h +++ b/include/reactphysics3d/collision/shapes/AABB.h @@ -28,6 +28,7 @@ // Libraries #include +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -101,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; @@ -206,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/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h b/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h index e903cada3..2f9ac10e4 100644 --- a/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h +++ b/include/reactphysics3d/collision/shapes/ConcaveMeshShape.h @@ -40,6 +40,9 @@ class TriangleShape; class TriangleMesh; // class ConvexTriangleAABBOverlapCallback +/** + * This class represents a callback when an overlap occurs + */ class ConvexTriangleAABBOverlapCallback : public DynamicAABBTreeOverlapCallback { private: @@ -75,7 +78,6 @@ class ConcaveMeshRaycastCallback : public DynamicAABBTreeRaycastCallback { const Ray& mRay; bool mIsHit; MemoryAllocator& mAllocator; - const Vector3& mMeshScale; #ifdef IS_RP3D_PROFILING_ENABLED @@ -88,9 +90,9 @@ class ConcaveMeshRaycastCallback : public DynamicAABBTreeRaycastCallback { // Constructor ConcaveMeshRaycastCallback(const ConcaveMeshShape& concaveMeshShape, - Collider* collider, RaycastInfo& raycastInfo, const Ray& ray, const Vector3& meshScale, MemoryAllocator& allocator) + Collider* collider, RaycastInfo& raycastInfo, const Ray& ray, MemoryAllocator& allocator) : mHitAABBNodes(allocator), mConcaveMeshShape(concaveMeshShape), mCollider(collider), - mRaycastInfo(raycastInfo), mRay(ray), mIsHit(false), mAllocator(allocator), mMeshScale(meshScale) { + mRaycastInfo(raycastInfo), mRay(ray), mIsHit(false), mAllocator(allocator) { } diff --git a/include/reactphysics3d/collision/shapes/TriangleShape.h b/include/reactphysics3d/collision/shapes/TriangleShape.h index df9e8f525..584eb4bee 100644 --- a/include/reactphysics3d/collision/shapes/TriangleShape.h +++ b/include/reactphysics3d/collision/shapes/TriangleShape.h @@ -221,9 +221,8 @@ RP3D_FORCE_INLINE AABB TriangleShape::getLocalBounds() const { // 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/include/reactphysics3d/configuration.h b/include/reactphysics3d/configuration.h index df744e6ba..1481c8497 100644 --- a/include/reactphysics3d/configuration.h +++ b/include/reactphysics3d/configuration.h @@ -149,7 +149,7 @@ constexpr decimal SAME_CONTACT_POINT_DISTANCE_THRESHOLD = decimal(0.01); constexpr uint8 GLOBAL_ALIGNMENT = 16; /// Current version of ReactPhysics3D -const std::string RP3D_VERSION = std::string("0.10.0"); +const std::string RP3D_VERSION = std::string("0.10.1"); } diff --git a/include/reactphysics3d/containers/Deque.h b/include/reactphysics3d/containers/Deque.h index 7658c1b53..2a7977b14 100644 --- a/include/reactphysics3d/containers/Deque.h +++ b/include/reactphysics3d/containers/Deque.h @@ -296,8 +296,13 @@ class Deque { clear(); - // Release the chunks array - mAllocator.release(mBuffer, sizeof(T) * mCapacity); + if (mCapacity > 0) { + + assert(mBuffer != nullptr); + + // Release the chunks array + mAllocator.release(mBuffer, sizeof(T) * mCapacity); + } mCapacity = 0; mBuffer = nullptr; diff --git a/include/reactphysics3d/containers/Pair.h b/include/reactphysics3d/containers/Pair.h index 37b5dec7a..ee26b1a5a 100644 --- a/include/reactphysics3d/containers/Pair.h +++ b/include/reactphysics3d/containers/Pair.h @@ -70,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/include/reactphysics3d/engine/Entity.h b/include/reactphysics3d/engine/Entity.h index 535782948..cf4535606 100644 --- a/include/reactphysics3d/engine/Entity.h +++ b/include/reactphysics3d/engine/Entity.h @@ -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/include/reactphysics3d/engine/OverlappingPairs.h b/include/reactphysics3d/engine/OverlappingPairs.h index 1d1e70014..b1c0ed39b 100644 --- a/include/reactphysics3d/engine/OverlappingPairs.h +++ b/include/reactphysics3d/engine/OverlappingPairs.h @@ -109,6 +109,10 @@ class OverlappingPairs { public: + // Struct OverlappingPair + /** + * A base overlapping pair + */ struct OverlappingPair { /// Ids of the convex vs convex pairs @@ -156,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. @@ -173,7 +180,10 @@ class OverlappingPairs { } }; - // 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: diff --git a/include/reactphysics3d/reactphysics3d.h b/include/reactphysics3d/reactphysics3d.h index 30a1a7514..e429a88af 100644 --- a/include/reactphysics3d/reactphysics3d.h +++ b/include/reactphysics3d/reactphysics3d.h @@ -26,7 +26,7 @@ /******************************************************************************** * ReactPhysics3D * -* Version 0.10.0 * +* Version 0.10.1 * * http://www.reactphysics3d.com * * Daniel Chappuis * ********************************************************************************/ diff --git a/include/reactphysics3d/systems/BroadPhaseSystem.h b/include/reactphysics3d/systems/BroadPhaseSystem.h index 1d3aa077b..4311b3082 100644 --- a/include/reactphysics3d/systems/BroadPhaseSystem.h +++ b/include/reactphysics3d/systems/BroadPhaseSystem.h @@ -47,6 +47,9 @@ class MemoryManager; class Profiler; // class AABBOverlapCallback +/** + * This class represents a callback when two AABB overlap + */ class AABBOverlapCallback : public DynamicAABBTreeOverlapCallback { public: diff --git a/include/reactphysics3d/utils/DebugRenderer.h b/include/reactphysics3d/utils/DebugRenderer.h index 3775c608d..779c7472d 100644 --- a/include/reactphysics3d/utils/DebugRenderer.h +++ b/include/reactphysics3d/utils/DebugRenderer.h @@ -31,7 +31,6 @@ #include #include #include -#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -213,7 +212,7 @@ class DebugRenderer : public EventListener { uint32 colorShape, uint32 colorShapeNormals); /// Draw the collision shape of a collider - void drawCollisionShapeOfCollider(const Collider* collider, uint32 color); + void drawCollisionShapeOfCollider(const Collider* collider); public : @@ -378,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/include/reactphysics3d/utils/DefaultLogger.h b/include/reactphysics3d/utils/DefaultLogger.h index 01d056e2a..1088d1e92 100644 --- a/include/reactphysics3d/utils/DefaultLogger.h +++ b/include/reactphysics3d/utils/DefaultLogger.h @@ -104,6 +104,10 @@ class DefaultLogger : public Logger { }; + // Class TextFormatter + /** + * Format the logs with simple text + */ class TextFormatter : public Formatter { public: @@ -168,6 +172,10 @@ class DefaultLogger : public Logger { } }; + // Class HtmlFormatter + /** + * Format the logs with HTML + */ class HtmlFormatter : public Formatter { private: @@ -344,7 +352,10 @@ class DefaultLogger : public Logger { }; - /// Log destination + // Class Destination + /** + * destination for the logs + */ class Destination { public: @@ -373,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: @@ -423,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: @@ -514,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/include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h b/include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h index bec12fa89..a7a86b0cd 100644 --- a/include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h +++ b/include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h @@ -52,17 +52,35 @@ class QHHalfEdgeStructure { using VerticesPair = Pair; using EdgeVertices = Pair; - /// Edge + // Struct Edge + /** + * An half-edge + */ struct Edge { - Vertex* startVertex; // Vertex at the beginning of the edge - Vertex* endVertex; // Vertex at the end of the edge - Face* face; // Adjacent face of the edge - Edge* previousEdge; // Previous edge in the linked-list of edges - Edge* nextEdge; // Next edge in the linked-list of edges - Edge* previousFaceEdge; // Previous edge around the face of the edge - Edge* nextFaceEdge; // Next edge around the face of the edge - Edge* twinEdge; // Twin 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), @@ -88,16 +106,32 @@ class QHHalfEdgeStructure { } }; - /// Face + // Struct Face + /** + * A face + */ struct Face { + /// Pointer to the next face Face* nextFace; + + /// Pointer to the previous face Face* previousFace; - Edge* edge; // One half-edge of the face + + /// One half-edge of the face + Edge* edge; + + /// Face normal Vector3 normal; - Vector3 centroid; // Center of the face (average of the face vertices) - decimal area; // Area of the face - Array conflictPoints; // Array with some remaining points visible from this face that need to be processed + + /// 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) @@ -105,12 +139,12 @@ class QHHalfEdgeStructure { } - // Return a vertex of the face + /// 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) + /// Recalculate the face centroid and normal to better fit its new vertices (using Newell method) void recalculateFace(const Array& points) { centroid.setToZero(); @@ -144,7 +178,7 @@ class QHHalfEdgeStructure { area = normalLength * decimal(0.5); } - // Return a string with the vertices of the face + /// Return a string with the vertices of the face std::string verticesString() const { std::string verticesString = "("; @@ -167,13 +201,13 @@ class QHHalfEdgeStructure { return verticesString; } - // Return true if the face is a triangle + /// 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) + /// Return true if the face structure is valid (for debugging purpose) bool isValid() { bool isValid = true; @@ -198,12 +232,19 @@ class QHHalfEdgeStructure { }; - /// Vertex + // Struct Vertex + /** + * A vertex + */ struct Vertex { - uint32 externalIndex; // Index of the vertex point in the user vertex array + /// 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 diff --git a/include/reactphysics3d/utils/quickhull/QuickHull.h b/include/reactphysics3d/utils/quickhull/QuickHull.h index 27a5029e0..d393cba40 100644 --- a/include/reactphysics3d/utils/quickhull/QuickHull.h +++ b/include/reactphysics3d/utils/quickhull/QuickHull.h @@ -44,7 +44,10 @@ template class Array; // Class QuickHull -// This algorithm is based on 'Implementing Quickhull' presentation at GDC from Dirk Gregorius +// 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: diff --git a/src/body/Body.cpp b/src/body/Body.cpp index 5d99d8010..891ec3174 100644 --- a/src/body/Body.cpp +++ b/src/body/Body.cpp @@ -36,9 +36,8 @@ 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 */ Body::Body(PhysicsWorld& world, Entity entity) : mEntity(entity), mWorld(world), mIsDebugEnabled(false) { @@ -133,8 +132,8 @@ uint32 Body::getNbColliders() const { // 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* Body::getCollider(uint32 colliderIndex) const { @@ -147,8 +146,8 @@ const Collider* Body::getCollider(uint32 colliderIndex) const { // 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* Body::getCollider(uint32 colliderIndex) { diff --git a/src/body/RigidBody.cpp b/src/body/RigidBody.cpp index b155ca57a..da1627072 100644 --- a/src/body/RigidBody.cpp +++ b/src/body/RigidBody.cpp @@ -35,9 +35,8 @@ 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) : Body(world, entity) { diff --git a/src/collision/Collider.cpp b/src/collision/Collider.cpp index cb74a3ef1..53bc1374e 100644 --- a/src/collision/Collider.cpp +++ b/src/collision/Collider.cpp @@ -35,10 +35,9 @@ 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, Body* body, MemoryManager& memoryManager) :mMemoryManager(memoryManager), mEntity(entity), mBody(body), diff --git a/src/collision/HeightField.cpp b/src/collision/HeightField.cpp index 4fa590f27..4ee27006f 100644 --- a/src/collision/HeightField.cpp +++ b/src/collision/HeightField.cpp @@ -256,7 +256,7 @@ void HeightField::computeMinMaxGridCoordinates(uint32* minCoords, uint32* maxCoo /// 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 Vector3& scale) const { + MemoryAllocator& allocator) const { RP3D_PROFILE("HeightField::raycast()", mProfiler); @@ -290,10 +290,10 @@ bool HeightField::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* co while (i >= 0 && i < nbCellsI && j >= 0 && j < nbCellsJ) { // 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; + 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); @@ -403,7 +403,7 @@ Vector3 HeightField::getVertexAt(uint32 x, uint32 y) const { const Vector3 vertex = Vector3(-mWidth * decimal(0.5) + x, mHeightOrigin + height, -mLength * decimal(0.5) + y); - assert(mBounds.contains(vertex)); + assert(mBounds.contains(vertex, decimal(0.0001))); return vertex; } diff --git a/src/collision/PolygonVertexArray.cpp b/src/collision/PolygonVertexArray.cpp index 4bc6e7e69..a5fba9e96 100644 --- a/src/collision/PolygonVertexArray.cpp +++ b/src/collision/PolygonVertexArray.cpp @@ -45,7 +45,7 @@ PolygonVertexArray::PolygonVertexArray() { * @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 */ diff --git a/src/collision/TriangleVertexArray.cpp b/src/collision/TriangleVertexArray.cpp index 46466169c..56a1cd7b4 100644 --- a/src/collision/TriangleVertexArray.cpp +++ b/src/collision/TriangleVertexArray.cpp @@ -80,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, @@ -107,7 +108,9 @@ TriangleVertexArray::TriangleVertexArray(uint32 nbVertices, const void* vertices // 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& outV1Index, uint32& outV2Index, uint32& outV3Index) const { @@ -135,7 +138,7 @@ void TriangleVertexArray::getTriangleVerticesIndices(uint32 triangleIndex, uint3 // 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 */ Vector3 TriangleVertexArray::getVertex(uint32 vertexIndex) const { @@ -162,7 +165,7 @@ Vector3 TriangleVertexArray::getVertex(uint32 vertexIndex) const { // 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 */ Vector3 TriangleVertexArray::getVertexNormal(uint32 vertexIndex) const { diff --git a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp index 6c5df317b..7110f3bb3 100755 --- a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp +++ b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp @@ -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/src/collision/shapes/ConcaveMeshShape.cpp b/src/collision/shapes/ConcaveMeshShape.cpp index dfa98547d..0843aff5d 100644 --- a/src/collision/shapes/ConcaveMeshShape.cpp +++ b/src/collision/shapes/ConcaveMeshShape.cpp @@ -186,7 +186,7 @@ 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(*this, collider, raycastInfo, scaledRay, mScale, allocator); + ConcaveMeshRaycastCallback raycastCallback(*this, collider, raycastInfo, ray, allocator); #ifdef IS_RP3D_PROFILING_ENABLED @@ -197,7 +197,8 @@ 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. + // 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(); @@ -239,7 +240,6 @@ void ConcaveMeshRaycastCallback::raycastTriangles() { #ifdef IS_RP3D_PROFILING_ENABLED - // Set the profiler to the triangle shape triangleShape.setProfiler(mProfiler); @@ -257,7 +257,7 @@ 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.triangleIndex = data; diff --git a/src/collision/shapes/HeightFieldShape.cpp b/src/collision/shapes/HeightFieldShape.cpp index ec528d7c7..2765732f3 100644 --- a/src/collision/shapes/HeightFieldShape.cpp +++ b/src/collision/shapes/HeightFieldShape.cpp @@ -77,12 +77,20 @@ bool HeightFieldShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collide RP3D_PROFILE("HeightFieldShape::raycast()", mProfiler); - // Apply the collision shape inverse scaling factor because the height-field 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); - return mHeightField->raycast(scaledRay, raycastInfo, collider, getRaycastTestType(), allocator, mScale); + if (mHeightField->raycast(scaledRay, raycastInfo, collider, getRaycastTestType(), allocator)) { + + // Scale back the contact point because we used a ray scale with inverse height-field scale + raycastInfo.worldPoint = raycastInfo.worldPoint * mScale; + + return true; + } + + return false; } // Return the string representation of the shape diff --git a/src/constraint/SliderJoint.cpp b/src/constraint/SliderJoint.cpp index fc8c5b8bd..59b106d54 100644 --- a/src/constraint/SliderJoint.cpp +++ b/src/constraint/SliderJoint.cpp @@ -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/src/engine/PhysicsCommon.cpp b/src/engine/PhysicsCommon.cpp index b5ce23964..fa536d698 100644 --- a/src/engine/PhysicsCommon.cpp +++ b/src/engine/PhysicsCommon.cpp @@ -473,7 +473,8 @@ void PhysicsCommon::deleteConvexMeshShape(ConvexMeshShape* convexMeshShape) { * @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 integerHeightScale Scaling factor used to scale the height values (only used 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, @@ -505,6 +506,7 @@ HeightField* PhysicsCommon::createHeightField(int nbGridColumns, int nbGridRows, // 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(HeightField* heightField, const Vector3& scaling) { @@ -615,7 +617,7 @@ ConvexMesh* PhysicsCommon::createConvexMesh(const PolygonVertexArray& polygonVer // If the mesh is not valid if (!isValid) { mesh->~ConvexMesh(); - allocator.release(mesh, sizeof(ConvexMesh)); + mMemoryManager.release(MemoryManager::AllocationType::Pool, mesh, sizeof(ConvexMesh)); return nullptr; } @@ -659,7 +661,7 @@ ConvexMesh* PhysicsCommon::createConvexMesh(const VertexArray& vertexArray, std: // If the mesh is not valid if (!isValid) { mesh->~ConvexMesh(); - allocator.release(mesh, sizeof(ConvexMesh)); + mMemoryManager.release(MemoryManager::AllocationType::Pool, mesh, sizeof(ConvexMesh)); return nullptr; } @@ -696,6 +698,8 @@ void PhysicsCommon::deleteConvexMesh(ConvexMesh* convexMesh) { // 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(const TriangleVertexArray& triangleVertexArray, std::vector& messages) { @@ -719,7 +723,7 @@ TriangleMesh* PhysicsCommon::createTriangleMesh(const TriangleVertexArray& trian // 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) { @@ -729,6 +733,9 @@ void PhysicsCommon::destroyTriangleMesh(TriangleMesh* triangleMesh) { } // Destroy a height-field +/** + * @param heightField A pointer to the height field to destroy + */ void PhysicsCommon::destroyHeightField(HeightField* heightField) { deleteHeightField(heightField); @@ -738,7 +745,7 @@ void PhysicsCommon::destroyHeightField(HeightField* 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) { @@ -751,7 +758,7 @@ void PhysicsCommon::deleteTriangleMesh(TriangleMesh* triangleMesh) { // Delete a height-field /** - * @param A pointer to the height-field to destroy + * @param heightField A pointer to the height-field to destroy */ void PhysicsCommon::deleteHeightField(HeightField* heightField) { @@ -777,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) { @@ -788,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/src/engine/PhysicsWorld.cpp b/src/engine/PhysicsWorld.cpp index 9db61253c..80a425c56 100644 --- a/src/engine/PhysicsWorld.cpp +++ b/src/engine/PhysicsWorld.cpp @@ -728,9 +728,6 @@ void PhysicsWorld::createIslands() { // If the body is static, we go to the next body if (mRigidBodyComponents.mBodyTypes[b] == BodyType::STATIC) continue; - // If the body does not have any simulation collider, we skip it - if (!mBodyComponents.getHasSimulationCollider(mRigidBodyComponents.mRigidBodies[b]->getEntity())) continue; - // Reset the stack of bodies to visit bodyEntitiesToVisit.clear(); diff --git a/src/systems/BroadPhaseSystem.cpp b/src/systems/BroadPhaseSystem.cpp index 04e2ceb9f..560d1ff1d 100644 --- a/src/systems/BroadPhaseSystem.cpp +++ b/src/systems/BroadPhaseSystem.cpp @@ -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); } diff --git a/src/utils/DebugRenderer.cpp b/src/utils/DebugRenderer.cpp index 99fb90acf..cc329003c 100644 --- a/src/utils/DebugRenderer.cpp +++ b/src/utils/DebugRenderer.cpp @@ -428,7 +428,7 @@ void DebugRenderer::drawHeightFieldShape(const Transform& transform, const Heigh } // 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]; @@ -519,7 +519,7 @@ void DebugRenderer::computeDebugRenderingPrimitives(const PhysicsWorld& world) { // If we need to draw the collision shape if (drawCollisionShape || drawCollisionShapeNormals) { - drawCollisionShapeOfCollider(collider, mMapDebugItemWithColor[DebugItem::COLLISION_SHAPE]); + drawCollisionShapeOfCollider(collider); } } } diff --git a/src/utils/quickhull/QHHalfEdgeStructure.cpp b/src/utils/quickhull/QHHalfEdgeStructure.cpp index e54f96741..124f28d00 100644 --- a/src/utils/quickhull/QHHalfEdgeStructure.cpp +++ b/src/utils/quickhull/QHHalfEdgeStructure.cpp @@ -79,7 +79,8 @@ QHHalfEdgeStructure::~QHHalfEdgeStructure() { // Add a vertex /** - * @param vertexPointIndex Index of the vertex in the external user vertex data array + * @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) { @@ -101,6 +102,9 @@ QHHalfEdgeStructure::Vertex* QHHalfEdgeStructure::addVertex(uint32 externalIndex /** * @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) { diff --git a/testbed/common/ConcaveMesh.cpp b/testbed/common/ConcaveMesh.cpp index e0de6fbb9..cd966ae07 100644 --- a/testbed/common/ConcaveMesh.cpp +++ b/testbed/common/ConcaveMesh.cpp @@ -67,7 +67,7 @@ ConcaveMesh::ConcaveMesh(reactphysics3d::BodyType type, bool isSimulationCollide // Create the collision shape for the rigid body (convex mesh shape) and // do not forget to delete it at the end - mConcaveShape = mPhysicsCommon.createConcaveMeshShape(mPhysicsTriangleMesh, rp3d::Vector3(1, 1, 1)); + mConcaveShape = mPhysicsCommon.createConcaveMeshShape(mPhysicsTriangleMesh); mConcaveShape->setScale(scaling); mPreviousTransform = rp3d::Transform::identity(); diff --git a/testbed/common/ConcaveMesh.h b/testbed/common/ConcaveMesh.h index ae61b0489..179a82d91 100644 --- a/testbed/common/ConcaveMesh.h +++ b/testbed/common/ConcaveMesh.h @@ -76,7 +76,7 @@ class ConcaveMesh : public PhysicsObject { /// Constructor ConcaveMesh(reactphysics3d::BodyType type, bool isSimulationCollider, reactphysics3d::PhysicsCommon& physicsCommon, rp3d::PhysicsWorld* physicsWorld, - const std::string& meshPath, const reactphysics3d::Vector3& scaling = rp3d::Vector3(1, 1, 1)); + const std::string& meshPath, const reactphysics3d::Vector3& scaling = reactphysics3d::Vector3(1, 1, 1)); /// Destructor virtual ~ConcaveMesh() override; diff --git a/testbed/common/HeightField.cpp b/testbed/common/HeightField.cpp index 229b61c55..0caecfcd6 100644 --- a/testbed/common/HeightField.cpp +++ b/testbed/common/HeightField.cpp @@ -28,13 +28,14 @@ #include "PerlinNoise.h" // Constructor -HeightField::HeightField(reactphysics3d::BodyType type, bool isSimulationCollider, reactphysics3d::PhysicsCommon& physicsCommon, rp3d::PhysicsWorld* physicsWorld) +HeightField::HeightField(reactphysics3d::BodyType type, bool isSimulationCollider, reactphysics3d::PhysicsCommon& physicsCommon, rp3d::PhysicsWorld* physicsWorld, + const reactphysics3d::Vector3& scaling) : PhysicsObject(physicsCommon), mPhysicsWorld(physicsWorld), mVBOVertices(GL_ARRAY_BUFFER), mVBONormals(GL_ARRAY_BUFFER), mVBOTextureCoords(GL_ARRAY_BUFFER), mVBOIndices(GL_ELEMENT_ARRAY_BUFFER) { // Compute the scaling matrix - mScalingMatrix = openglframework::Matrix4(0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1); + mScalingMatrix = openglframework::Matrix4(scaling.x, 0, 0, 0, 0, scaling.y, 0, 0, 0, 0, scaling.z, 0, 0, 0, 0, 1); // Generate the height field generateHeightField(); @@ -50,7 +51,7 @@ HeightField::HeightField(reactphysics3d::BodyType type, bool isSimulationCollide messages); assert(mHeightField != nullptr); mHeightFieldShape = mPhysicsCommon.createHeightFieldShape(mHeightField); - mHeightFieldShape->setScale(rp3d::Vector3(0.5, 0.5, 0.5)); + mHeightFieldShape->setScale(scaling); mPreviousTransform = rp3d::Transform::identity(); diff --git a/testbed/common/HeightField.h b/testbed/common/HeightField.h index 1e419e338..e341d97c1 100644 --- a/testbed/common/HeightField.h +++ b/testbed/common/HeightField.h @@ -90,7 +90,8 @@ class HeightField : public PhysicsObject { /// Constructor HeightField(reactphysics3d::BodyType type, bool isSimulationCollider, - rp3d::PhysicsCommon& physicsCommon, rp3d::PhysicsWorld* physicsWorld); + rp3d::PhysicsCommon& physicsCommon, rp3d::PhysicsWorld* physicsWorld, + const reactphysics3d::Vector3& scaling = reactphysics3d::Vector3(1, 1, 1)); /// Destructor virtual ~HeightField() override; diff --git a/testbed/common/VisualContactPoint.cpp b/testbed/common/VisualContactPoint.cpp index c0a41d4c4..3a88ba1c6 100644 --- a/testbed/common/VisualContactPoint.cpp +++ b/testbed/common/VisualContactPoint.cpp @@ -28,9 +28,13 @@ // Initialization of static variables openglframework::VertexBufferObject VisualContactPoint::mVBOVertices(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject VisualContactPoint::mVBOVerticesNormalLine(GL_ARRAY_BUFFER); openglframework::VertexBufferObject VisualContactPoint::mVBONormals(GL_ARRAY_BUFFER); openglframework::VertexBufferObject VisualContactPoint::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); openglframework::VertexArrayObject VisualContactPoint::mVAO; +openglframework::VertexArrayObject VisualContactPoint::mVAONormalLine; +openglframework::Vector3 VisualContactPoint::mContactNormalLineStaticPoints[2] = {openglframework::Vector3(0, 0, 0), openglframework::Vector3(0, 1, 0)}; + int VisualContactPoint::mNbTotalPoints = 0; openglframework::Mesh VisualContactPoint::mMesh; bool VisualContactPoint::mStaticDataCreated = false; @@ -38,7 +42,7 @@ bool VisualContactPoint::mStaticDataCreated = false; // Constructor VisualContactPoint::VisualContactPoint(const openglframework::Vector3& position, const openglframework::Vector3& normalLineEndPointLocal, const openglframework::Color& color) - : mVBOVerticesNormalLine(GL_ARRAY_BUFFER), mColor(color) { + : mColor(color) { mContactNormalLinePoints[0] = openglframework::Vector3(0, 0, 0); mContactNormalLinePoints[1] = (normalLineEndPointLocal - position) * 0.5f; @@ -46,14 +50,34 @@ VisualContactPoint::VisualContactPoint(const openglframework::Vector3& position, // Initialize the position where the mesh will be rendered translateWorld(position); - // Create the VBO and VAO to render the contact normal line - createContactNormalLineVBOAndVAO(); -} - -// Destructor -VisualContactPoint::~VisualContactPoint() { - mVAONormalLine.destroy(); - mVBOVerticesNormalLine.destroy(); + // Compute the rotation quaternion from unit (0, 1, 0) to the actual normal vector in local-space + openglframework::Vector3 normalLineEndPointLocalVec(normalLineEndPointLocal.x, normalLineEndPointLocal.y, normalLineEndPointLocal.z); + openglframework::Vector3 positionVec(position.x, position.y, position.z); + openglframework::Vector3 v1(0, 1, 0); + openglframework::Vector3 v2 = (normalLineEndPointLocalVec - positionVec); + v2.normalize(); + const float dot = v1.dot(v2); + openglframework::Vector3 xUnitVec(1, 0, 0); + openglframework::Vector3 yUnitVec(0, 1, 0); + openglframework::Vector3 rotVector; + // Handle parallel vectors + if (dot < -0.99999f) { + rotVector = xUnitVec.cross(v1); + if (rotVector.lengthSquared() < 0.000001f) { + rotVector = yUnitVec.cross(v1); + } + rotVector.normalize(); + mNormalRotation.setAllValues(rotVector.x, rotVector.y, rotVector.z, openglframework::PI); + } + else if (dot > 0.99999f) { + mNormalRotation.setAllValues(0, 0, 0, 1); + } + else { // Handlee other vectors + rotVector = v1.cross(v2); + const float w = sqrt(v1.lengthSquared() * v2.lengthSquared()); + mNormalRotation.setAllValues(rotVector.x, rotVector.y, rotVector.z, w + dot); + mNormalRotation.normalize(); + } } // Load and initialize the mesh for all the contact points @@ -69,7 +93,9 @@ void VisualContactPoint::createStaticData(const std::string& meshFolderPath) { mMesh.scaleVertices(VISUAL_CONTACT_POINT_RADIUS); - createVBOAndVAO(); + createSphereVBOAndVAO(); + + createContactNormalLineVBOAndVAO(); mStaticDataCreated = true; } @@ -85,6 +111,9 @@ void VisualContactPoint::destroyStaticData() { mVBONormals.destroy(); mVAO.destroy(); + mVAONormalLine.destroy(); + mVBOVerticesNormalLine.destroy(); + mMesh.destroy(); mStaticDataCreated = false; @@ -161,8 +190,13 @@ void VisualContactPoint::renderContactNormalLine(openglframework::Shader& shader mVBOVerticesNormalLine.bind(); // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + const rp3d::Matrix3x3 m = mNormalRotation.getMatrix(); + const openglframework::Matrix4 contactNormalRotation(m[0][0], m[0][1], m[0][2], 0, + m[1][0], m[1][1], m[1][2], 0, + m[2][0], m[2][1], m[2][2], 0, + 0, 0, 0, 1); + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix * contactNormalRotation; + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix* contactNormalRotation); shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the @@ -197,7 +231,7 @@ void VisualContactPoint::renderContactNormalLine(openglframework::Shader& shader // Create the Vertex Buffer Objects used to render the contact point sphere with OpenGL. /// We create two VBOs (one for vertices and one for indices) -void VisualContactPoint::createVBOAndVAO() { +void VisualContactPoint::createSphereVBOAndVAO() { // Create the VBO for the vertices data mVBOVertices.create(); @@ -244,7 +278,7 @@ void VisualContactPoint::createContactNormalLineVBOAndVAO() { mVBOVerticesNormalLine.create(); mVBOVerticesNormalLine.bind(); size_t sizeNormalLineVertices = 2 * sizeof(openglframework::Vector3); - mVBOVerticesNormalLine.copyDataIntoVBO(sizeNormalLineVertices, &mContactNormalLinePoints[0], GL_STATIC_DRAW); + mVBOVerticesNormalLine.copyDataIntoVBO(sizeNormalLineVertices, &mContactNormalLineStaticPoints[0], GL_STATIC_DRAW); mVBOVerticesNormalLine.unbind(); // Create the VAO for both VBOs diff --git a/testbed/common/VisualContactPoint.h b/testbed/common/VisualContactPoint.h index 042fdf68b..b77df7f5f 100644 --- a/testbed/common/VisualContactPoint.h +++ b/testbed/common/VisualContactPoint.h @@ -58,25 +58,31 @@ class VisualContactPoint : public openglframework::Object3D { static openglframework::VertexArrayObject mVAO; /// Vertex Buffer Object for the vertices data - openglframework::VertexBufferObject mVBOVerticesNormalLine; + static openglframework::VertexBufferObject mVBOVerticesNormalLine; /// Vertex Array Object for the vertex data - openglframework::VertexArrayObject mVAONormalLine; + static openglframework::VertexArrayObject mVAONormalLine; /// True if static data (VBO, VAO) has been created already static bool mStaticDataCreated; - // Two end-points of the contact normal line + /// Two end-points of the contact normal line (static points for the VBO) + static openglframework::Vector3 mContactNormalLineStaticPoints[2]; + + /// Two end-points of the contact normal line openglframework::Vector3 mContactNormalLinePoints[2]; /// Color openglframework::Color mColor; - // Create the Vertex Buffer Objects used to render the contact point sphere with OpenGL. - static void createVBOAndVAO(); + /// Rotation of the contact normal + rp3d::Quaternion mNormalRotation; + + /// Create the Vertex Buffer Objects used to render the contact point sphere with OpenGL. + static void createSphereVBOAndVAO(); - // Create the Vertex Buffer Objects used to render the contact normal line with OpenGL. - void createContactNormalLineVBOAndVAO(); + /// Create the Vertex Buffer Objects used to render the contact normal line with OpenGL. + static void createContactNormalLineVBOAndVAO(); /// Render the contact normal line void renderContactNormalLine(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); @@ -91,9 +97,6 @@ class VisualContactPoint : public openglframework::Object3D { VisualContactPoint(const openglframework::Vector3& position, const openglframework::Vector3& normalLineEndPointLocal, const openglframework::Color& color); - /// Destructor - ~VisualContactPoint(); - /// Load and initialize the mesh for all the contact points static void createStaticData(const std::string& meshFolderPath); diff --git a/testbed/scenes/raycast/RaycastScene.cpp b/testbed/scenes/raycast/RaycastScene.cpp index d431d31c4..dcd6be669 100644 --- a/testbed/scenes/raycast/RaycastScene.cpp +++ b/testbed/scenes/raycast/RaycastScene.cpp @@ -105,7 +105,7 @@ RaycastScene::RaycastScene(const std::string& name, EngineSettings& settings, re // ---------- Concave Mesh ---------- // // Create a convex mesh and a corresponding collision body in the physics world - mConcaveMesh = new ConcaveMesh(rp3d::BodyType::STATIC, false, mPhysicsCommon, mPhysicsWorld, mMeshFolderPath + "castle.obj"); + mConcaveMesh = new ConcaveMesh(rp3d::BodyType::STATIC, false, mPhysicsCommon, mPhysicsWorld, mMeshFolderPath + "castle.obj", rp3d::Vector3(0.7, 0.7, 0.7)); // Set the color mConcaveMesh->setColor(mObjectColorDemo); @@ -115,7 +115,7 @@ RaycastScene::RaycastScene(const std::string& name, EngineSettings& settings, re // ---------- Heightfield ---------- // // Create a convex mesh and a corresponding collision body in the physics world - mHeightField = new HeightField(rp3d::BodyType::STATIC, false, mPhysicsCommon, mPhysicsWorld); + mHeightField = new HeightField(rp3d::BodyType::STATIC, false, mPhysicsCommon, mPhysicsWorld, rp3d::Vector3(0.7, 0.7, 0.7)); // Set the color mHeightField->setColor(mObjectColorDemo);