Compare commits

...

21 Commits

Author SHA1 Message Date
7851f45134 [DEV] integrate GLD 2021-12-15 22:35:42 +01:00
bc8f81bb4c [DEV] uptate new lutin declaration model 2021-10-25 07:52:11 +02:00
3a73c9aa4b [DEV] Stupid commit 2021-08-23 11:10:00 +02:00
08acb860c1 [DEBUG] update new API of lutin log 2019-05-03 10:18:23 +02:00
beba85290c [DEBUG] correct segmentation fault 2019-04-01 22:07:43 +02:00
640d3fdcbe [DEV] update new etk Uri API 2018-10-23 22:19:32 +02:00
42f5d84744 [DEV] update etk null 2018-06-19 22:13:48 +02:00
59280cc06c [DEV] try find collision error 2018-06-19 21:24:54 +02:00
4525e80a60 [DEV] continue rework 2018-06-19 21:24:54 +02:00
696a979395 [DEV] continue rework 2018-06-19 21:24:54 +02:00
b1fe6eebb4 [DEV] refacto 2018-06-19 21:24:54 +02:00
b85098fcaa [STYLE] correct some coding style 2018-06-19 21:24:54 +02:00
33f24d6b17 [DEV] basic update 2018-06-19 21:24:54 +02:00
14644e4f5f [DEV] update to the system allocator ETK_NEW 2018-06-19 21:24:54 +02:00
96d6ff59d1 [DEV] remove an assert ==> create some eratic comportment, and coorect build after licence change 2018-06-19 21:24:54 +02:00
078fc33309 [LICENCE] change ZLib to MPL-2 with athoriqzation of author:
Email exchange:

On 29.09.2017 14:35 Edouard DUPIN wrote:
Hello,

I have fork your physic engine and update it to fit better with my 3d
game engine and my framework.

I do many change:
  - on assert policy (I do not like it)
  - the back-end use of the vector3D and quaternion, that I have
already an other implementation...
  - Log management
  - Other build system (lutin)
  - organisation of doxygen (all in the header)
  - remove dependency of the STL (in progress)
  - some bugs ...
  - ...

And I have a problem with the licence. You use BSD-3 that is a good
point, but this licence does not force the user to send your their
modification. Then I ask you to permit me to change the licence of the
FORK in MPL-2 that have the benefit to force developer to publish the
modification and permit to use the software in a proprietary
application without contamination.

Best regards

Edouard DUPIN

https://github.com/HeeroYui
https://github.com/atria-soft
https://github.com/musicdsp

answers from chappuis.daniel@XXXXXXX.yyy
Hello,

I am glad that you have found the ReactPhysics3D library to be useful.
You said that you have found bugs. Maybe I have already found and fixed some of them but do not hesitate to report them
here (https://github.com/DanielChappuis/reactphysics3d/issues) if not done yet.

Concerning the license. The library is not under the BSD-3 license but under the zlib license (https://opensource.org/licenses/Zlib).
With this license, it is allowed to use the software in a proprietary application without contamination. Regarding your request to
change the license to the MPL-2, you can do it with the following condition : You need to add the following text in all the source files
of ReactPhysics3D where the license will change. It must always be clear where the original code is coming from.

--------------- TEXT TO ADD TO THE LICENSE IN EACH FILE -------------------------
Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/>
This code is re-licensed with permission from ReactPhysics3D author.
---------------------------------------------------------------------------------

For example, you can see here how the flow/react library (java port of ReactPhysics3D) has been re-licensed from
ReactPhysics3D (https://github.com/flow/react/blob/develop/src/main/java/com/flowpowered/react/constraint/ContactPoint.java)

I hope it fits your needs.

Best Regards,

Daniel Chappuis
2018-06-19 21:24:54 +02:00
7689205279 [DEV] remove some unneeded log 2018-06-19 21:24:54 +02:00
619246407c [DEBUG] correct the collision Detection 2018-06-19 21:24:54 +02:00
8d77c326de [DEV] clean some unuse of container 2018-06-19 21:24:54 +02:00
cccb93fd34 [DEV] regactor the EPA triangle store 2018-06-19 21:24:54 +02:00
a353893266 [DEV] update the 2 callback herited in function lanmba availlable 2018-06-18 21:26:38 +02:00
122 changed files with 3571 additions and 3765 deletions

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
__pycache__
.bck
out
target
build
# Compiled source # # Compiled source #
################### ###################
*.com *.com

29
GLD_ephysics-test.json Normal file
View File

@ -0,0 +1,29 @@
{
"type":"BINARY",
"sub-type":"TEST",
"group-id":"com.atria-soft",
"description":"Ewol Physic engine TEST UNIT",
"license":"MPL-2",
"license-file":"file://LICENSE",
"maintainer":"file://authors.txt",
"author":"file://authors.txt",
"version":"file://version.txt",
"code-quality":"MEDIUM",
"source": [
"test/main.cpp",
"test/testAABB.cpp",
"test/testCollisionWorld.cpp",
"test/testDynamicAABBTree.cpp",
"test/testPointInside.cpp",
"test/testRaycast.cpp"
],
"path":[
"."
],
"dependency": [
"ephysics",
"etest",
"test-debug"
]
}

145
GLD_ephysics.json Normal file
View File

@ -0,0 +1,145 @@
{
"type":"LIBRARY",
"group-id":"com.atria-soft",
"description":"Algorithm generic",
"license":"MPL-2",
"license-file":"file://LICENSE",
"maintainer":"file://authors.txt",
"author":"file://authors.txt",
"version":"file://version.txt",
"code-quality":"MEDIUM",
"source": [
"ephysics/debug.cpp",
"ephysics/constraint/Joint.cpp",
"ephysics/constraint/HingeJoint.cpp",
"ephysics/constraint/ContactPoint.cpp",
"ephysics/constraint/BallAndSocketJoint.cpp",
"ephysics/constraint/SliderJoint.cpp",
"ephysics/constraint/FixedJoint.cpp",
"ephysics/collision/ContactManifoldSet.cpp",
"ephysics/collision/RaycastInfo.cpp",
"ephysics/collision/narrowphase/GJK/Simplex.cpp",
"ephysics/collision/narrowphase/GJK/GJKAlgorithm.cpp",
"ephysics/collision/narrowphase/DefaultCollisionDispatch.cpp",
"ephysics/collision/narrowphase/SphereVsSphereAlgorithm.cpp",
"ephysics/collision/narrowphase/NarrowPhaseAlgorithm.cpp",
"ephysics/collision/narrowphase/ConcaveVsConvexAlgorithm.cpp",
"ephysics/collision/narrowphase/EPA/EPAAlgorithm.cpp",
"ephysics/collision/narrowphase/EPA/TrianglesStore.cpp",
"ephysics/collision/narrowphase/EPA/TriangleEPA.cpp",
"ephysics/collision/narrowphase/EPA/EdgeEPA.cpp",
"ephysics/collision/ProxyShape.cpp",
"ephysics/collision/shapes/ConcaveShape.cpp",
"ephysics/collision/shapes/CylinderShape.cpp",
"ephysics/collision/shapes/SphereShape.cpp",
"ephysics/collision/shapes/CapsuleShape.cpp",
"ephysics/collision/shapes/ConvexMeshShape.cpp",
"ephysics/collision/shapes/CollisionShape.cpp",
"ephysics/collision/shapes/BoxShape.cpp",
"ephysics/collision/shapes/TriangleShape.cpp",
"ephysics/collision/shapes/HeightFieldShape.cpp",
"ephysics/collision/shapes/ConvexShape.cpp",
"ephysics/collision/shapes/ConeShape.cpp",
"ephysics/collision/shapes/ConcaveMeshShape.cpp",
"ephysics/collision/shapes/AABB.cpp",
"ephysics/collision/TriangleMesh.cpp",
"ephysics/collision/CollisionDetection.cpp",
"ephysics/collision/TriangleVertexArray.cpp",
"ephysics/collision/ContactManifold.cpp",
"ephysics/collision/broadphase/DynamicAABBTree.cpp",
"ephysics/collision/broadphase/BroadPhaseAlgorithm.cpp",
"ephysics/body/RigidBody.cpp",
"ephysics/body/Body.cpp",
"ephysics/body/CollisionBody.cpp",
"ephysics/mathematics/mathematics_functions.cpp",
"ephysics/engine/CollisionWorld.cpp",
"ephysics/engine/OverlappingPair.cpp",
"ephysics/engine/Material.cpp",
"ephysics/engine/Island.cpp",
"ephysics/engine/Profiler.cpp",
"ephysics/engine/ConstraintSolver.cpp",
"ephysics/engine/DynamicsWorld.cpp",
"ephysics/engine/ContactSolver.cpp",
"ephysics/engine/Timer.cpp"
],
"header": [
"ephysics/debug.hpp",
"ephysics/memory/Stack.hpp",
"ephysics/constraint/BallAndSocketJoint.hpp",
"ephysics/constraint/Joint.hpp",
"ephysics/constraint/FixedJoint.hpp",
"ephysics/constraint/HingeJoint.hpp",
"ephysics/constraint/ContactPoint.hpp",
"ephysics/constraint/SliderJoint.hpp",
"ephysics/collision/TriangleVertexArray.hpp",
"ephysics/collision/ContactManifold.hpp",
"ephysics/collision/ContactManifoldSet.hpp",
"ephysics/collision/narrowphase/SphereVsSphereAlgorithm.hpp",
"ephysics/collision/narrowphase/GJK/Simplex.hpp",
"ephysics/collision/narrowphase/GJK/GJKAlgorithm.hpp",
"ephysics/collision/narrowphase/ConcaveVsConvexAlgorithm.hpp",
"ephysics/collision/narrowphase/CollisionDispatch.hpp",
"ephysics/collision/narrowphase/DefaultCollisionDispatch.hpp",
"ephysics/collision/narrowphase/NarrowPhaseAlgorithm.hpp",
"ephysics/collision/narrowphase/EPA/EdgeEPA.hpp",
"ephysics/collision/narrowphase/EPA/EPAAlgorithm.hpp",
"ephysics/collision/narrowphase/EPA/TrianglesStore.hpp",
"ephysics/collision/narrowphase/EPA/TriangleEPA.hpp",
"ephysics/collision/CollisionDetection.hpp",
"ephysics/collision/shapes/TriangleShape.hpp",
"ephysics/collision/shapes/AABB.hpp",
"ephysics/collision/shapes/CapsuleShape.hpp",
"ephysics/collision/shapes/SphereShape.hpp",
"ephysics/collision/shapes/CollisionShape.hpp",
"ephysics/collision/shapes/BoxShape.hpp",
"ephysics/collision/shapes/ConcaveMeshShape.hpp",
"ephysics/collision/shapes/ConvexMeshShape.hpp",
"ephysics/collision/shapes/HeightFieldShape.hpp",
"ephysics/collision/shapes/CylinderShape.hpp",
"ephysics/collision/shapes/ConeShape.hpp",
"ephysics/collision/shapes/ConvexShape.hpp",
"ephysics/collision/shapes/ConcaveShape.hpp",
"ephysics/collision/CollisionShapeInfo.hpp",
"ephysics/collision/TriangleMesh.hpp",
"ephysics/collision/RaycastInfo.hpp",
"ephysics/collision/ProxyShape.hpp",
"ephysics/collision/broadphase/DynamicAABBTree.hpp",
"ephysics/collision/broadphase/BroadPhaseAlgorithm.hpp",
"ephysics/configuration.hpp",
"ephysics/ephysics.hpp",
"ephysics/body/Body.hpp",
"ephysics/body/RigidBody.hpp",
"ephysics/body/CollisionBody.hpp",
"ephysics/mathematics/mathematics.hpp",
"ephysics/mathematics/Ray.hpp",
"ephysics/mathematics/mathematics_functions.hpp",
"ephysics/engine/CollisionWorld.hpp",
"ephysics/engine/DynamicsWorld.hpp",
"ephysics/engine/ConstraintSolver.hpp",
"ephysics/engine/OverlappingPair.hpp",
"ephysics/engine/Island.hpp",
"ephysics/engine/ContactSolver.hpp",
"ephysics/engine/Material.hpp",
"ephysics/engine/Profiler.hpp",
"ephysics/engine/Timer.hpp",
"ephysics/engine/Impulse.hpp",
"ephysics/engine/EventListener.hpp"
],
"path":[
"."
],
"compilation-version": {
"c++": 2011
},
"dependency": [
"m",
"elog",
"etk",
"ememory",
"echrono"
],
"flag":{
"c++": "-Wno-overloaded-virtual"
}
}

389
LICENSE
View File

@ -1,20 +1,379 @@
ReactPhysics3D physics library, http://code.google.com/p/reactphysics3d/ Original Licence
Copyright (c) 2010-2016 Daniel Chappuis ================
This software is provided 'as-is', without any express or implied warranty. Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/>
In no event will the authors be held liable for any damages arising from the This code is re-licensed with permission from ReactPhysics3D author.
use of this software.
Permission is granted to anyone to use this software for any purpose, Mozilla Public License Version 2.0
including commercial applications, and to alter it and redistribute it ==================================
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim 1. Definitions
that you wrote the original software. If you use this software in a --------------
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be 1.1. "Contributor"
misrepresented as being the original software. means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
3. This notice may not be removed or altered from any source distribution. 1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@ -1,18 +1,19 @@
[![Travis Build Status](https://travis-ci.org/DanielChappuis/ephysics.svg?branch=master)](https://travis-ci.org/DanielChappuis/ephysics) [![Travis Build Status](https://travis-ci.org/atria-soft/ephysics.svg?branch=master)](https://travis-ci.org/atria-soft/ephysics)
## ReactPhysics3D ## ephysics
ReactPhysics3D is an open source C++ physics engine library that can be used in 3D simulations and games. ephysics is an open source C++ physics engine library that can be used in 3D simulations and games.
Website : [http://www.ephysics.com](http://www.ephysics.com) Website: [http://www.ephysics.com](http://www.ephysics.com)
Author : Daniel Chappuis Author: Daniel CHAPPUIS (original project ReactPhysics3D)
Author: Edouard DUPIN (forker)
<img src="https://raw.githubusercontent.com/DanielChappuis/ephysics/master/documentation/UserManual/images/testbed.png" alt="Drawing" height="400" /> <img src="https://raw.githubusercontent.com/DanielChappuis/ephysics/master/documentation/UserManual/images/testbed.png" alt="Drawing" height="400" />
## Features ## Features
ReactPhysics3D has the following features : ephysics has the following features:
- Rigid body dynamics - Rigid body dynamics
- Discrete collision detection - Discrete collision detection
@ -34,18 +35,5 @@ ReactPhysics3D has the following features :
## License ## License
The ReactPhysics3D library is released under the open-source BSD 3 clauses license. The ephysics library is released under the MPL-2 license.
## Documentation
You can find the User Manual and the Doxygen API Documentation [here](http://www.ephysics.com/documentation.html)
## Branches
The "master" branch always contains the last released version of the library and some possible bug fixes. This is the most stable version. On the other side,
the "develop" branch is used for development. This branch is frequently updated and can be quite unstable. Therefore, if you want to use the library in
your application, it is recommended to checkout the "master" branch.
## Issues
If you find any issue with the library, you can report it on the issue tracker [here](https://github.com/DanielChappuis/ephysics/issues).

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/body/Body.hpp> #include <ephysics/body/Body.hpp>
@ -15,6 +18,6 @@ ephysics::Body::Body(bodyindex _id):
m_isActive(true), m_isActive(true),
m_isSleeping(false), m_isSleeping(false),
m_sleepTime(0), m_sleepTime(0),
m_userData(nullptr) { m_userData(null) {
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -151,7 +154,9 @@ namespace ephysics {
bool operator!=(const Body& _obj) const { bool operator!=(const Body& _obj) const {
return (m_id != _obj.m_id); return (m_id != _obj.m_id);
} }
// TODO : remove this
friend class DynamicsWorld; friend class DynamicsWorld;
friend class Island;
}; };
} }

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/body/CollisionBody.hpp> #include <ephysics/body/CollisionBody.hpp>
#include <ephysics/engine/CollisionWorld.hpp> #include <ephysics/engine/CollisionWorld.hpp>
#include <ephysics/collision/ContactManifold.hpp> #include <ephysics/collision/ContactManifold.hpp>
@ -17,15 +18,22 @@ CollisionBody::CollisionBody(const etk::Transform3D& _transform, CollisionWorld&
Body(_id), Body(_id),
m_type(DYNAMIC), m_type(DYNAMIC),
m_transform(_transform), m_transform(_transform),
m_proxyCollisionShapes(nullptr), m_proxyCollisionShapes(null),
m_numberCollisionShapes(0), m_numberCollisionShapes(0),
m_contactManifoldsList(nullptr), m_contactManifoldsList(null),
m_world(_world) { m_world(_world) {
EPHY_DEBUG(" set transform: " << _transform);
if (isnan(_transform.getPosition().x()) == true) { // check NAN
EPHY_CRITICAL(" set transform: " << _transform);
}
if (isinf(_transform.getOrientation().z()) == true) {
EPHY_CRITICAL(" set transform: " << _transform);
}
} }
CollisionBody::~CollisionBody() { CollisionBody::~CollisionBody() {
assert(m_contactManifoldsList == nullptr); assert(m_contactManifoldsList == null);
// Remove all the proxy collision shapes of the body // Remove all the proxy collision shapes of the body
removeAllCollisionShapes(); removeAllCollisionShapes();
@ -39,13 +47,24 @@ inline void CollisionBody::setType(BodyType _type) {
} }
} }
void CollisionBody::setTransform(const etk::Transform3D& _transform) {
EPHY_DEBUG(" set transform: " << m_transform << " ==> " << _transform);
if (isnan(_transform.getPosition().x()) == true) { // check NAN
EPHY_CRITICAL(" set transform: " << m_transform << " ==> " << _transform);
}
if (isinf(_transform.getOrientation().z()) == true) {
EPHY_CRITICAL(" set transform: " << m_transform << " ==> " << _transform);
}
m_transform = _transform;
updateBroadPhaseState();
}
ProxyShape* CollisionBody::addCollisionShape(CollisionShape* _collisionShape, ProxyShape* CollisionBody::addCollisionShape(CollisionShape* _collisionShape,
const etk::Transform3D& _transform) { const etk::Transform3D& _transform) {
// Create a new proxy collision shape to attach the collision shape to the body // Create a proxy collision shape to attach the collision shape to the body
ProxyShape* proxyShape = new ProxyShape(this, _collisionShape,_transform, float(1)); ProxyShape* proxyShape = ETK_NEW(ProxyShape, this, _collisionShape,_transform, float(1));
// Add it to the list of proxy collision shapes of the body // Add it to the list of proxy collision shapes of the body
if (m_proxyCollisionShapes == nullptr) { if (m_proxyCollisionShapes == null) {
m_proxyCollisionShapes = proxyShape; m_proxyCollisionShapes = proxyShape;
} else { } else {
proxyShape->m_next = m_proxyCollisionShapes; proxyShape->m_next = m_proxyCollisionShapes;
@ -66,13 +85,13 @@ void CollisionBody::removeCollisionShape(const ProxyShape* _proxyShape) {
if (m_isActive) { if (m_isActive) {
m_world.m_collisionDetection.removeProxyCollisionShape(current); m_world.m_collisionDetection.removeProxyCollisionShape(current);
} }
delete current; ETK_DELETE(ProxyShape, current);
current = nullptr; current = null;
m_numberCollisionShapes--; m_numberCollisionShapes--;
return; return;
} }
// Look for the proxy shape that contains the collision shape in parameter // Look for the proxy shape that contains the collision shape in parameter
while(current->m_next != nullptr) { while(current->m_next != null) {
// If we have found the collision shape to remove // If we have found the collision shape to remove
if (current->m_next == _proxyShape) { if (current->m_next == _proxyShape) {
// Remove the proxy collision shape // Remove the proxy collision shape
@ -81,8 +100,8 @@ void CollisionBody::removeCollisionShape(const ProxyShape* _proxyShape) {
if (m_isActive) { if (m_isActive) {
m_world.m_collisionDetection.removeProxyCollisionShape(elementToRemove); m_world.m_collisionDetection.removeProxyCollisionShape(elementToRemove);
} }
delete elementToRemove; ETK_DELETE(ProxyShape, elementToRemove);
elementToRemove = nullptr; elementToRemove = null;
m_numberCollisionShapes--; m_numberCollisionShapes--;
return; return;
} }
@ -95,36 +114,36 @@ void CollisionBody::removeCollisionShape(const ProxyShape* _proxyShape) {
void CollisionBody::removeAllCollisionShapes() { void CollisionBody::removeAllCollisionShapes() {
ProxyShape* current = m_proxyCollisionShapes; ProxyShape* current = m_proxyCollisionShapes;
// Look for the proxy shape that contains the collision shape in parameter // Look for the proxy shape that contains the collision shape in parameter
while(current != nullptr) { while(current != null) {
// Remove the proxy collision shape // Remove the proxy collision shape
ProxyShape* nextElement = current->m_next; ProxyShape* nextElement = current->m_next;
if (m_isActive) { if (m_isActive) {
m_world.m_collisionDetection.removeProxyCollisionShape(current); m_world.m_collisionDetection.removeProxyCollisionShape(current);
} }
delete current; ETK_DELETE(ProxyShape, current);
// Get the next element in the list // Get the next element in the list
current = nextElement; current = nextElement;
} }
m_proxyCollisionShapes = nullptr; m_proxyCollisionShapes = null;
} }
void CollisionBody::resetContactManifoldsList() { void CollisionBody::resetContactManifoldsList() {
// Delete the linked list of contact manifolds of that body // Delete the linked list of contact manifolds of that body
ContactManifoldListElement* currentElement = m_contactManifoldsList; ContactManifoldListElement* currentElement = m_contactManifoldsList;
while (currentElement != nullptr) { while (currentElement != null) {
ContactManifoldListElement* nextElement = currentElement->next; ContactManifoldListElement* nextElement = currentElement->next;
// Delete the current element // Delete the current element
delete currentElement; ETK_DELETE(ContactManifoldListElement, currentElement);
currentElement = nextElement; currentElement = nextElement;
} }
m_contactManifoldsList = nullptr; m_contactManifoldsList = null;
} }
void CollisionBody::updateBroadPhaseState() const { void CollisionBody::updateBroadPhaseState() const {
// For all the proxy collision shapes of the body // For all the proxy collision shapes of the body
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
// Update the proxy // Update the proxy
updateProxyShapeInBroadPhase(shape); updateProxyShapeInBroadPhase(shape);
} }
@ -146,13 +165,13 @@ void CollisionBody::setIsActive(bool _isActive) {
Body::setIsActive(_isActive); Body::setIsActive(_isActive);
// If we have to activate the body // If we have to activate the body
if (_isActive == true) { if (_isActive == true) {
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
AABB aabb; AABB aabb;
shape->getCollisionShape()->computeAABB(aabb, m_transform * shape->m_localToBodyTransform); shape->getCollisionShape()->computeAABB(aabb, m_transform * shape->m_localToBodyTransform);
m_world.m_collisionDetection.addProxyCollisionShape(shape, aabb); m_world.m_collisionDetection.addProxyCollisionShape(shape, aabb);
} }
} else { } else {
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
m_world.m_collisionDetection.removeProxyCollisionShape(shape); m_world.m_collisionDetection.removeProxyCollisionShape(shape);
} }
resetContactManifoldsList(); resetContactManifoldsList();
@ -161,7 +180,7 @@ void CollisionBody::setIsActive(bool _isActive) {
void CollisionBody::askForBroadPhaseCollisionCheck() const { void CollisionBody::askForBroadPhaseCollisionCheck() const {
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
m_world.m_collisionDetection.askForBroadPhaseCollisionCheck(shape); m_world.m_collisionDetection.askForBroadPhaseCollisionCheck(shape);
} }
} }
@ -172,7 +191,7 @@ int32_t CollisionBody::resetIsAlreadyInIslandAndCountManifolds() {
int32_t nbManifolds = 0; int32_t nbManifolds = 0;
// Reset the m_isAlreadyInIsland variable of the contact manifolds for this body // Reset the m_isAlreadyInIsland variable of the contact manifolds for this body
ContactManifoldListElement* currentElement = m_contactManifoldsList; ContactManifoldListElement* currentElement = m_contactManifoldsList;
while (currentElement != nullptr) { while (currentElement != null) {
currentElement->contactManifold->m_isAlreadyInIsland = false; currentElement->contactManifold->m_isAlreadyInIsland = false;
currentElement = currentElement->next; currentElement = currentElement->next;
nbManifolds++; nbManifolds++;
@ -181,7 +200,7 @@ int32_t CollisionBody::resetIsAlreadyInIslandAndCountManifolds() {
} }
bool CollisionBody::testPointInside(const vec3& _worldPoint) const { bool CollisionBody::testPointInside(const vec3& _worldPoint) const {
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
if (shape->testPointInside(_worldPoint)) return true; if (shape->testPointInside(_worldPoint)) return true;
} }
return false; return false;
@ -193,7 +212,7 @@ bool CollisionBody::raycast(const Ray& _ray, RaycastInfo& _raycastInfo) {
} }
bool isHit = false; bool isHit = false;
Ray rayTemp(_ray); Ray rayTemp(_ray);
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
// Test if the ray hits the collision shape // Test if the ray hits the collision shape
if (shape->raycast(rayTemp, _raycastInfo)) { if (shape->raycast(rayTemp, _raycastInfo)) {
rayTemp.maxFraction = _raycastInfo.hitFraction; rayTemp.maxFraction = _raycastInfo.hitFraction;
@ -205,11 +224,11 @@ bool CollisionBody::raycast(const Ray& _ray, RaycastInfo& _raycastInfo) {
AABB CollisionBody::getAABB() const { AABB CollisionBody::getAABB() const {
AABB bodyAABB; AABB bodyAABB;
if (m_proxyCollisionShapes == nullptr) { if (m_proxyCollisionShapes == null) {
return bodyAABB; return bodyAABB;
} }
m_proxyCollisionShapes->getCollisionShape()->computeAABB(bodyAABB, m_transform * m_proxyCollisionShapes->getLocalToBodyTransform()); m_proxyCollisionShapes->getCollisionShape()->computeAABB(bodyAABB, m_transform * m_proxyCollisionShapes->getLocalToBodyTransform());
for (ProxyShape* shape = m_proxyCollisionShapes->m_next; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes->m_next; shape != null; shape = shape->m_next) {
AABB aabb; AABB aabb;
shape->getCollisionShape()->computeAABB(aabb, m_transform * shape->getLocalToBodyTransform()); shape->getCollisionShape()->computeAABB(aabb, m_transform * shape->getLocalToBodyTransform());
bodyAABB.mergeWithAABB(aabb); bodyAABB.mergeWithAABB(aabb);

View File

@ -1,11 +1,12 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
// Libraries
#include <ephysics/body/Body.hpp> #include <ephysics/body/Body.hpp>
#include <etk/math/Transform3D.hpp> #include <etk/math/Transform3D.hpp>
#include <ephysics/collision/shapes/AABB.hpp> #include <ephysics/collision/shapes/AABB.hpp>
@ -105,10 +106,7 @@ namespace ephysics {
* @brief Set the current position and orientation * @brief Set the current position and orientation
* @param transform The transformation of the body that transforms the local-space of the body int32_to world-space * @param transform The transformation of the body that transforms the local-space of the body int32_to world-space
*/ */
void setTransform(const etk::Transform3D& _transform) { virtual void setTransform(const etk::Transform3D& _transform);
m_transform = _transform;
updateBroadPhaseState();
}
/** /**
* @brief Add a collision shape to the body. Note that you can share a collision shape between several bodies using the same collision shape instance to * @brief Add a collision shape to the body. Note that you can share a collision shape between several bodies using the same collision shape instance to
* when you add the shape to the different bodies. Do not forget to delete the collision shape you have created at the end of your program. * when you add the shape to the different bodies. Do not forget to delete the collision shape you have created at the end of your program.

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/body/RigidBody.hpp> #include <ephysics/body/RigidBody.hpp>
#include <ephysics/constraint/Joint.hpp> #include <ephysics/constraint/Joint.hpp>
#include <ephysics/collision/shapes/CollisionShape.hpp> #include <ephysics/collision/shapes/CollisionShape.hpp>
@ -22,13 +23,13 @@ RigidBody::RigidBody(const etk::Transform3D& _transform, CollisionWorld& _world,
m_isGravityEnabled(true), m_isGravityEnabled(true),
m_linearDamping(0.0f), m_linearDamping(0.0f),
m_angularDamping(float(0.0)), m_angularDamping(float(0.0)),
m_jointsList(nullptr) { m_jointsList(null) {
// Compute the inverse mass // Compute the inverse mass
m_massInverse = 1.0f / m_initMass; m_massInverse = 1.0f / m_initMass;
} }
RigidBody::~RigidBody() { RigidBody::~RigidBody() {
assert(m_jointsList == nullptr); assert(m_jointsList == null);
} }
@ -96,23 +97,23 @@ void RigidBody::setMass(float _mass) {
} }
void RigidBody::removeJointFrom_jointsList(const Joint* _joint) { void RigidBody::removeJointFrom_jointsList(const Joint* _joint) {
assert(_joint != nullptr); assert(_joint != null);
assert(m_jointsList != nullptr); assert(m_jointsList != null);
// Remove the joint from the linked list of the joints of the first body // Remove the joint from the linked list of the joints of the first body
if (m_jointsList->joint == _joint) { // If the first element is the one to remove if (m_jointsList->joint == _joint) { // If the first element is the one to remove
JointListElement* elementToRemove = m_jointsList; JointListElement* elementToRemove = m_jointsList;
m_jointsList = elementToRemove->next; m_jointsList = elementToRemove->next;
delete elementToRemove; ETK_DELETE(JointListElement, elementToRemove);
elementToRemove = nullptr; elementToRemove = null;
} }
else { // If the element to remove is not the first one in the list else { // If the element to remove is not the first one in the list
JointListElement* currentElement = m_jointsList; JointListElement* currentElement = m_jointsList;
while (currentElement->next != nullptr) { while (currentElement->next != null) {
if (currentElement->next->joint == _joint) { if (currentElement->next->joint == _joint) {
JointListElement* elementToRemove = currentElement->next; JointListElement* elementToRemove = currentElement->next;
currentElement->next = elementToRemove->next; currentElement->next = elementToRemove->next;
delete elementToRemove; ETK_DELETE(JointListElement, elementToRemove);
elementToRemove = nullptr; elementToRemove = null;
break; break;
} }
currentElement = currentElement->next; currentElement = currentElement->next;
@ -126,9 +127,9 @@ ProxyShape* RigidBody::addCollisionShape(CollisionShape* _collisionShape,
float _mass) { float _mass) {
assert(_mass > 0.0f); assert(_mass > 0.0f);
// Create a new proxy collision shape to attach the collision shape to the body // Create a new proxy collision shape to attach the collision shape to the body
ProxyShape* proxyShape = new ProxyShape(this, _collisionShape, _transform, _mass); ProxyShape* proxyShape = ETK_NEW(ProxyShape, this, _collisionShape, _transform, _mass);
// Add it to the list of proxy collision shapes of the body // Add it to the list of proxy collision shapes of the body
if (m_proxyCollisionShapes == nullptr) { if (m_proxyCollisionShapes == null) {
m_proxyCollisionShapes = proxyShape; m_proxyCollisionShapes = proxyShape;
} else { } else {
proxyShape->m_next = m_proxyCollisionShapes; proxyShape->m_next = m_proxyCollisionShapes;
@ -181,8 +182,25 @@ void RigidBody::setIsSleeping(bool _isSleeping) {
Body::setIsSleeping(_isSleeping); Body::setIsSleeping(_isSleeping);
} }
void RigidBody::updateTransformWithCenterOfMass() {
// Translate the body according to the translation of the center of mass position
m_transform.setPosition(m_centerOfMassWorld - m_transform.getOrientation() * m_centerOfMassLocal);
if (isnan(m_transform.getPosition().x()) == true) {
EPHY_CRITICAL("updateTransformWithCenterOfMass: " << m_transform);
}
if (isinf(m_transform.getOrientation().z()) == true) {
EPHY_CRITICAL(" set transform: " << m_transform);
}
}
void RigidBody::setTransform(const etk::Transform3D& _transform) { void RigidBody::setTransform(const etk::Transform3D& _transform) {
EPHY_DEBUG(" set transform: " << m_transform << " ==> " << _transform);
if (isnan(_transform.getPosition().x()) == true) {
EPHY_CRITICAL(" set transform: " << m_transform << " ==> " << _transform);
}
if (isinf(_transform.getOrientation().z()) == true) {
EPHY_CRITICAL(" set transform: " << m_transform << " ==> " << _transform);
}
m_transform = _transform; m_transform = _transform;
const vec3 oldCenterOfMass = m_centerOfMassWorld; const vec3 oldCenterOfMass = m_centerOfMassWorld;
// Compute the new center of mass in world-space coordinates // Compute the new center of mass in world-space coordinates
@ -220,7 +238,7 @@ void RigidBody::recomputeMassInformation() {
m_centerOfMassLocal *= m_massInverse; m_centerOfMassLocal *= m_massInverse;
m_centerOfMassWorld = m_transform * m_centerOfMassLocal; m_centerOfMassWorld = m_transform * m_centerOfMassLocal;
// Compute the total mass and inertia tensor using all the collision shapes // Compute the total mass and inertia tensor using all the collision shapes
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
// Get the inertia tensor of the collision shape in its local-space // Get the inertia tensor of the collision shape in its local-space
etk::Matrix3x3 inertiaTensor; etk::Matrix3x3 inertiaTensor;
shape->getCollisionShape()->computeLocalInertiaTensor(inertiaTensor, shape->getMass()); shape->getCollisionShape()->computeLocalInertiaTensor(inertiaTensor, shape->getMass());
@ -253,12 +271,13 @@ void RigidBody::updateBroadPhaseState() const {
DynamicsWorld& world = static_cast<DynamicsWorld&>(m_world); DynamicsWorld& world = static_cast<DynamicsWorld&>(m_world);
const vec3 displacement = world.m_timeStep * m_linearVelocity; const vec3 displacement = world.m_timeStep * m_linearVelocity;
// For all the proxy collision shapes of the body // For all the proxy collision shapes of the body
for (ProxyShape* shape = m_proxyCollisionShapes; shape != nullptr; shape = shape->m_next) { for (ProxyShape* shape = m_proxyCollisionShapes; shape != null; shape = shape->m_next) {
// Recompute the world-space AABB of the collision shape // Recompute the world-space AABB of the collision shape
AABB aabb; AABB aabb;
EPHY_ERROR(" : " << aabb.getMin() << " " << aabb.getMax()); EPHY_VERBOSE(" : " << aabb.getMin() << " " << aabb.getMax());
EPHY_VERBOSE(" m_transform: " << m_transform);
shape->getCollisionShape()->computeAABB(aabb, m_transform *shape->getLocalToBodyTransform()); shape->getCollisionShape()->computeAABB(aabb, m_transform *shape->getLocalToBodyTransform());
EPHY_ERROR(" : " << aabb.getMin() << " " << aabb.getMax()); EPHY_VERBOSE(" : " << aabb.getMin() << " " << aabb.getMax());
// Update the broad-phase state for the proxy collision shape // Update the broad-phase state for the proxy collision shape
m_world.m_collisionDetection.updateProxyCollisionShape(shape, aabb, displacement); m_world.m_collisionDetection.updateProxyCollisionShape(shape, aabb, displacement);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -50,10 +53,7 @@ namespace ephysics {
/** /**
* @brief Update the transform of the body after a change of the center of mass * @brief Update the transform of the body after a change of the center of mass
*/ */
void updateTransformWithCenterOfMass() { void updateTransformWithCenterOfMass();
// Translate the body according to the translation of the center of mass position
m_transform.setPosition(m_centerOfMassWorld - m_transform.getOrientation() * m_centerOfMassLocal);
}
void updateBroadPhaseState() const override; void updateBroadPhaseState() const override;
public : public :
/** /**
@ -72,7 +72,7 @@ namespace ephysics {
* @brief Set the current position and orientation * @brief Set the current position and orientation
* @param[in] _transform The transformation of the body that transforms the local-space of the body int32_to world-space * @param[in] _transform The transformation of the body that transforms the local-space of the body int32_to world-space
*/ */
virtual void setTransform(const etk::Transform3D& _transform); void setTransform(const etk::Transform3D& _transform) override;
/** /**
* @brief Get the mass of the body * @brief Get the mass of the body
* @return The mass (in kilograms) of the body * @return The mass (in kilograms) of the body

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/CollisionDetection.hpp> #include <ephysics/collision/CollisionDetection.hpp>
#include <ephysics/engine/CollisionWorld.hpp> #include <ephysics/engine/CollisionWorld.hpp>
#include <ephysics/body/Body.hpp> #include <ephysics/body/Body.hpp>
@ -39,20 +40,16 @@ void CollisionDetection::computeCollisionDetection() {
computeNarrowPhase(); computeNarrowPhase();
} }
void CollisionDetection::testCollisionBetweenShapes(CollisionCallback* callback, void CollisionDetection::testCollisionBetweenShapes(CollisionCallback* _callback, const etk::Set<uint32_t>& _shapes1, const etk::Set<uint32_t>& _shapes2) {
const etk::Set<uint32_t>& shapes1,
const etk::Set<uint32_t>& shapes2) {
// Compute the broad-phase collision detection // Compute the broad-phase collision detection
computeBroadPhase(); computeBroadPhase();
// Delete all the contact points in the currently overlapping pairs // Delete all the contact points in the currently overlapping pairs
clearContactPoints(); clearContactPoints();
// Compute the narrow-phase collision detection among given sets of shapes // Compute the narrow-phase collision detection among given sets of shapes
computeNarrowPhaseBetweenShapes(callback, shapes1, shapes2); computeNarrowPhaseBetweenShapes(_callback, _shapes1, _shapes2);
} }
void CollisionDetection::reportCollisionBetweenShapes(CollisionCallback* callback, void CollisionDetection::reportCollisionBetweenShapes(CollisionCallback* _callback, const etk::Set<uint32_t>& _shapes1, const etk::Set<uint32_t>& _shapes2) {
const etk::Set<uint32_t>& shapes1,
const etk::Set<uint32_t>& shapes2) {
// For each possible collision pair of bodies // For each possible collision pair of bodies
etk::Map<overlappingpairid, OverlappingPair*>::Iterator it; etk::Map<overlappingpairid, OverlappingPair*>::Iterator it;
for (it = m_overlappingPairs.begin(); it != m_overlappingPairs.end(); ++it) { for (it = m_overlappingPairs.begin(); it != m_overlappingPairs.end(); ++it) {
@ -62,24 +59,24 @@ void CollisionDetection::reportCollisionBetweenShapes(CollisionCallback* callbac
assert(shape1->m_broadPhaseID != shape2->m_broadPhaseID); assert(shape1->m_broadPhaseID != shape2->m_broadPhaseID);
// If both shapes1 and shapes2 sets are non-empty, we check that // If both shapes1 and shapes2 sets are non-empty, we check that
// shape1 is among on set and shape2 is among the other one // shape1 is among on set and shape2 is among the other one
if ( !shapes1.empty() if ( !_shapes1.empty()
&& !shapes2.empty() && !_shapes2.empty()
&& ( shapes1.count(shape1->m_broadPhaseID) == 0 && ( _shapes1.count(shape1->m_broadPhaseID) == 0
|| shapes2.count(shape2->m_broadPhaseID) == 0 ) || _shapes2.count(shape2->m_broadPhaseID) == 0 )
&& ( shapes1.count(shape2->m_broadPhaseID) == 0 && ( _shapes1.count(shape2->m_broadPhaseID) == 0
|| shapes2.count(shape1->m_broadPhaseID) == 0 ) ) { || _shapes2.count(shape1->m_broadPhaseID) == 0 ) ) {
continue; continue;
} }
if ( !shapes1.empty() if ( !_shapes1.empty()
&& shapes2.empty() && _shapes2.empty()
&& shapes1.count(shape1->m_broadPhaseID) == 0 && _shapes1.count(shape1->m_broadPhaseID) == 0
&& shapes1.count(shape2->m_broadPhaseID) == 0) { && _shapes1.count(shape2->m_broadPhaseID) == 0) {
continue; continue;
} }
if ( !shapes2.empty() if ( !_shapes2.empty()
&& shapes1.empty() && _shapes1.empty()
&& shapes2.count(shape1->m_broadPhaseID) == 0 && _shapes2.count(shape1->m_broadPhaseID) == 0
&& shapes2.count(shape2->m_broadPhaseID) == 0) { && _shapes2.count(shape2->m_broadPhaseID) == 0) {
continue; continue;
} }
// For each contact manifold set of the overlapping pair // For each contact manifold set of the overlapping pair
@ -91,15 +88,15 @@ void CollisionDetection::reportCollisionBetweenShapes(CollisionCallback* callbac
ContactPoint* contactPoint = manifold->getContactPoint(i); ContactPoint* contactPoint = manifold->getContactPoint(i);
// Create the contact info object for the contact // Create the contact info object for the contact
ContactPointInfo contactInfo(manifold->getShape1(), manifold->getShape2(), ContactPointInfo contactInfo(manifold->getShape1(), manifold->getShape2(),
manifold->getShape1()->getCollisionShape(), manifold->getShape1()->getCollisionShape(),
manifold->getShape2()->getCollisionShape(), manifold->getShape2()->getCollisionShape(),
contactPoint->getNormal(), contactPoint->getNormal(),
contactPoint->getPenetrationDepth(), contactPoint->getPenetrationDepth(),
contactPoint->getLocalPointOnBody1(), contactPoint->getLocalPointOnBody1(),
contactPoint->getLocalPointOnBody2()); contactPoint->getLocalPointOnBody2());
// Notify the collision callback about this new contact // Notify the collision callback about this new contact
if (callback != nullptr) { if (_callback != null) {
callback->notifyContact(contactInfo); _callback->notifyContact(contactInfo);
} }
} }
} }
@ -136,8 +133,8 @@ void CollisionDetection::computeNarrowPhase() {
!m_broadPhaseAlgorithm.testOverlappingShapes(shape1, shape2)) { !m_broadPhaseAlgorithm.testOverlappingShapes(shape1, shape2)) {
// TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved
// Destroy the overlapping pair // Destroy the overlapping pair
delete it->second; ETK_DELETE(OverlappingPair, it->second);
it->second = nullptr; it->second = null;
it = m_overlappingPairs.erase(it); it = m_overlappingPairs.erase(it);
continue; continue;
} else { } else {
@ -163,7 +160,7 @@ void CollisionDetection::computeNarrowPhase() {
const CollisionShapeType shape2Type = shape2->getCollisionShape()->getType(); const CollisionShapeType shape2Type = shape2->getCollisionShape()->getType();
NarrowPhaseAlgorithm* narrowPhaseAlgorithm = m_collisionMatrix[shape1Type][shape2Type]; NarrowPhaseAlgorithm* narrowPhaseAlgorithm = m_collisionMatrix[shape1Type][shape2Type];
// If there is no collision algorithm between those two kinds of shapes // If there is no collision algorithm between those two kinds of shapes
if (narrowPhaseAlgorithm == nullptr) { if (narrowPhaseAlgorithm == null) {
continue; continue;
} }
// Notify the narrow-phase algorithm about the overlapping pair we are going to test // Notify the narrow-phase algorithm about the overlapping pair we are going to test
@ -183,9 +180,7 @@ void CollisionDetection::computeNarrowPhase() {
addAllContactManifoldsToBodies(); addAllContactManifoldsToBodies();
} }
void CollisionDetection::computeNarrowPhaseBetweenShapes(CollisionCallback* callback, void CollisionDetection::computeNarrowPhaseBetweenShapes(CollisionCallback* _callback, const etk::Set<uint32_t>& _shapes1, const etk::Set<uint32_t>& _shapes2) {
const etk::Set<uint32_t>& shapes1,
const etk::Set<uint32_t>& shapes2) {
m_contactOverlappingPairs.clear(); m_contactOverlappingPairs.clear();
// For each possible collision pair of bodies // For each possible collision pair of bodies
etk::Map<overlappingpairid, OverlappingPair*>::Iterator it; etk::Map<overlappingpairid, OverlappingPair*>::Iterator it;
@ -196,39 +191,39 @@ void CollisionDetection::computeNarrowPhaseBetweenShapes(CollisionCallback* call
assert(shape1->m_broadPhaseID != shape2->m_broadPhaseID); assert(shape1->m_broadPhaseID != shape2->m_broadPhaseID);
// If both shapes1 and shapes2 sets are non-empty, we check that // If both shapes1 and shapes2 sets are non-empty, we check that
// shape1 is among on set and shape2 is among the other one // shape1 is among on set and shape2 is among the other one
if ( !shapes1.empty() if ( !_shapes1.empty()
&& !shapes2.empty() && !_shapes2.empty()
&& ( shapes1.count(shape1->m_broadPhaseID) == 0 && ( _shapes1.count(shape1->m_broadPhaseID) == 0
|| shapes2.count(shape2->m_broadPhaseID) == 0 ) || _shapes2.count(shape2->m_broadPhaseID) == 0 )
&& ( shapes1.count(shape2->m_broadPhaseID) == 0 && ( _shapes1.count(shape2->m_broadPhaseID) == 0
|| shapes2.count(shape1->m_broadPhaseID) == 0 ) ) { || _shapes2.count(shape1->m_broadPhaseID) == 0 ) ) {
++it; ++it;
continue; continue;
} }
if ( !shapes1.empty() if ( !_shapes1.empty()
&& shapes2.empty() && _shapes2.empty()
&& shapes1.count(shape1->m_broadPhaseID) == 0 && _shapes1.count(shape1->m_broadPhaseID) == 0
&& shapes1.count(shape2->m_broadPhaseID) == 0) { && _shapes1.count(shape2->m_broadPhaseID) == 0) {
++it; ++it;
continue; continue;
} }
if ( !shapes2.empty() if ( !_shapes2.empty()
&& shapes1.empty() && _shapes1.empty()
&& shapes2.count(shape1->m_broadPhaseID) == 0 && _shapes2.count(shape1->m_broadPhaseID) == 0
&& shapes2.count(shape2->m_broadPhaseID) == 0) { && _shapes2.count(shape2->m_broadPhaseID) == 0) {
++it; ++it;
continue; continue;
} }
// Check if the collision filtering allows collision between the two shapes and // Check if the collision filtering allows collision between the two shapes and
// that the two shapes are still overlapping. Otherwise, we destroy the // that the two shapes are still overlapping. Otherwise, we destroy the
// overlapping pair // overlapping pair
if (((shape1->getCollideWithMaskBits() & shape2->getCollisionCategoryBits()) == 0 || if ( ( (shape1->getCollideWithMaskBits() & shape2->getCollisionCategoryBits()) == 0
(shape1->getCollisionCategoryBits() & shape2->getCollideWithMaskBits()) == 0) || || (shape1->getCollisionCategoryBits() & shape2->getCollideWithMaskBits()) == 0 )
!m_broadPhaseAlgorithm.testOverlappingShapes(shape1, shape2)) { || !m_broadPhaseAlgorithm.testOverlappingShapes(shape1, shape2) ) {
// TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved
// Destroy the overlapping pair // Destroy the overlapping pair
delete it->second; ETK_DELETE(OverlappingPair, it->second);
it->second = nullptr; it->second = null;
it = m_overlappingPairs.erase(it); it = m_overlappingPairs.erase(it);
continue; continue;
} else { } else {
@ -255,17 +250,23 @@ void CollisionDetection::computeNarrowPhaseBetweenShapes(CollisionCallback* call
const CollisionShapeType shape2Type = shape2->getCollisionShape()->getType(); const CollisionShapeType shape2Type = shape2->getCollisionShape()->getType();
NarrowPhaseAlgorithm* narrowPhaseAlgorithm = m_collisionMatrix[shape1Type][shape2Type]; NarrowPhaseAlgorithm* narrowPhaseAlgorithm = m_collisionMatrix[shape1Type][shape2Type];
// If there is no collision algorithm between those two kinds of shapes // If there is no collision algorithm between those two kinds of shapes
if (narrowPhaseAlgorithm == nullptr) { if (narrowPhaseAlgorithm == null) {
continue; continue;
} }
// Notify the narrow-phase algorithm about the overlapping pair we are going to test // Notify the narrow-phase algorithm about the overlapping pair we are going to test
narrowPhaseAlgorithm->setCurrentOverlappingPair(pair); narrowPhaseAlgorithm->setCurrentOverlappingPair(pair);
// Create the CollisionShapeInfo objects // Create the CollisionShapeInfo objects
CollisionShapeInfo shape1Info(shape1, shape1->getCollisionShape(), shape1->getLocalToWorldTransform(), CollisionShapeInfo shape1Info(shape1,
pair, shape1->getCachedCollisionData()); shape1->getCollisionShape(),
CollisionShapeInfo shape2Info(shape2, shape2->getCollisionShape(), shape2->getLocalToWorldTransform(), shape1->getLocalToWorldTransform(),
pair, shape2->getCachedCollisionData()); pair,
TestCollisionBetweenShapesCallback narrowPhaseCallback(callback); shape1->getCachedCollisionData());
CollisionShapeInfo shape2Info(shape2,
shape2->getCollisionShape(),
shape2->getLocalToWorldTransform(),
pair,
shape2->getCachedCollisionData());
TestCollisionBetweenShapesCallback narrowPhaseCallback(_callback);
// Use the narrow-phase collision detection algorithm to check // Use the narrow-phase collision detection algorithm to check
// if there really is a collision // if there really is a collision
narrowPhaseAlgorithm->testCollision(shape1Info, shape2Info, &narrowPhaseCallback); narrowPhaseAlgorithm->testCollision(shape1Info, shape2Info, &narrowPhaseCallback);
@ -274,73 +275,77 @@ void CollisionDetection::computeNarrowPhaseBetweenShapes(CollisionCallback* call
addAllContactManifoldsToBodies(); addAllContactManifoldsToBodies();
} }
void CollisionDetection::broadPhaseNotifyOverlappingPair(ProxyShape* shape1, ProxyShape* shape2) { void CollisionDetection::broadPhaseNotifyOverlappingPair(ProxyShape* _shape1, ProxyShape* _shape2) {
assert(shape1->m_broadPhaseID != shape2->m_broadPhaseID); assert(_shape1->m_broadPhaseID != _shape2->m_broadPhaseID);
// If the two proxy collision shapes are from the same body, skip it // If the two proxy collision shapes are from the same body, skip it
if (shape1->getBody()->getID() == shape2->getBody()->getID()) { if (_shape1->getBody()->getID() == _shape2->getBody()->getID()) {
return; return;
} }
// Check if the collision filtering allows collision between the two shapes // Check if the collision filtering allows collision between the two shapes
if ( (shape1->getCollideWithMaskBits() & shape2->getCollisionCategoryBits()) == 0 if ( (_shape1->getCollideWithMaskBits() & _shape2->getCollisionCategoryBits()) == 0
|| (shape1->getCollisionCategoryBits() & shape2->getCollideWithMaskBits()) == 0) { || (_shape1->getCollisionCategoryBits() & _shape2->getCollideWithMaskBits()) == 0) {
return; return;
} }
// Compute the overlapping pair ID // Compute the overlapping pair ID
overlappingpairid pairID = OverlappingPair::computeID(shape1, shape2); overlappingpairid pairID = OverlappingPair::computeID(_shape1, _shape2);
// Check if the overlapping pair already exists // Check if the overlapping pair already exists
if (m_overlappingPairs.find(pairID) != m_overlappingPairs.end()) return; if (m_overlappingPairs.find(pairID) != m_overlappingPairs.end()) return;
// Compute the maximum number of contact manifolds for this pair // Compute the maximum number of contact manifolds for this pair
int32_t nbMaxManifolds = CollisionShape::computeNbMaxContactManifolds(shape1->getCollisionShape()->getType(), int32_t nbMaxManifolds = CollisionShape::computeNbMaxContactManifolds(_shape1->getCollisionShape()->getType(),
shape2->getCollisionShape()->getType()); _shape2->getCollisionShape()->getType());
// Create the overlapping pair and add it int32_to the set of overlapping pairs // Create the overlapping pair and add it int32_to the set of overlapping pairs
OverlappingPair* newPair = new OverlappingPair(shape1, shape2, nbMaxManifolds); OverlappingPair* newPair = ETK_NEW(OverlappingPair, _shape1, _shape2, nbMaxManifolds);
assert(newPair != nullptr); assert(newPair != null);
m_overlappingPairs.set(pairID, newPair); m_overlappingPairs.set(pairID, newPair);
// Wake up the two bodies // Wake up the two bodies
shape1->getBody()->setIsSleeping(false); _shape1->getBody()->setIsSleeping(false);
shape2->getBody()->setIsSleeping(false); _shape2->getBody()->setIsSleeping(false);
} }
void CollisionDetection::removeProxyCollisionShape(ProxyShape* proxyShape) { void CollisionDetection::removeProxyCollisionShape(ProxyShape* _proxyShape) {
// Remove all the overlapping pairs involving this proxy shape // Remove all the overlapping pairs involving this proxy shape
etk::Map<overlappingpairid, OverlappingPair*>::Iterator it; etk::Map<overlappingpairid, OverlappingPair*>::Iterator it;
for (it = m_overlappingPairs.begin(); it != m_overlappingPairs.end(); ) { for (it = m_overlappingPairs.begin(); it != m_overlappingPairs.end(); ) {
if (it->second->getShape1()->m_broadPhaseID == proxyShape->m_broadPhaseID|| if (it->second->getShape1()->m_broadPhaseID == _proxyShape->m_broadPhaseID||
it->second->getShape2()->m_broadPhaseID == proxyShape->m_broadPhaseID) { it->second->getShape2()->m_broadPhaseID == _proxyShape->m_broadPhaseID) {
// TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved
// Destroy the overlapping pair // Destroy the overlapping pair
delete it->second; ETK_DELETE(OverlappingPair, it->second);
it->second = nullptr; it->second = null;
it = m_overlappingPairs.erase(it); it = m_overlappingPairs.erase(it);
} else { } else {
++it; ++it;
} }
} }
// Remove the body from the broad-phase // Remove the body from the broad-phase
m_broadPhaseAlgorithm.removeProxyCollisionShape(proxyShape); m_broadPhaseAlgorithm.removeProxyCollisionShape(_proxyShape);
} }
void CollisionDetection::notifyContact(OverlappingPair* overlappingPair, const ContactPointInfo& contactInfo) { void CollisionDetection::notifyContact(OverlappingPair* _overlappingPair, const ContactPointInfo& _contactInfo) {
// If it is the first contact since the pairs are overlapping // If it is the first contact since the pairs are overlapping
if (overlappingPair->getNbContactPoints() == 0) { if (_overlappingPair->getNbContactPoints() == 0) {
// Trigger a callback event // Trigger a callback event
if (m_world->m_eventListener != NULL) m_world->m_eventListener->beginContact(contactInfo); if (m_world->m_eventListener != NULL) {
m_world->m_eventListener->beginContact(_contactInfo);
}
} }
// Create a new contact // Create a new contact
createContact(overlappingPair, contactInfo); createContact(_overlappingPair, _contactInfo);
// Trigger a callback event for the new contact // Trigger a callback event for the new contact
if (m_world->m_eventListener != NULL) m_world->m_eventListener->newContact(contactInfo); if (m_world->m_eventListener != NULL) {
m_world->m_eventListener->newContact(_contactInfo);
}
} }
void CollisionDetection::createContact(OverlappingPair* overlappingPair, const ContactPointInfo& contactInfo) { void CollisionDetection::createContact(OverlappingPair* _overlappingPair, const ContactPointInfo& _contactInfo) {
// Create a new contact // Create a new contact
ContactPoint* contact = new ContactPoint(contactInfo); ContactPoint* contact = ETK_NEW(ContactPoint, _contactInfo);
// Add the contact to the contact manifold set of the corresponding overlapping pair // Add the contact to the contact manifold set of the corresponding overlapping pair
overlappingPair->addContact(contact); _overlappingPair->addContact(contact);
// Add the overlapping pair int32_to the set of pairs in contact during narrow-phase // Add the overlapping pair int32_to the set of pairs in contact during narrow-phase
overlappingpairid pairId = OverlappingPair::computeID(overlappingPair->getShape1(), overlappingpairid pairId = OverlappingPair::computeID(_overlappingPair->getShape1(),
overlappingPair->getShape2()); _overlappingPair->getShape2());
m_contactOverlappingPairs[pairId] = overlappingPair; m_contactOverlappingPairs.set(pairId, _overlappingPair);
} }
void CollisionDetection::addAllContactManifoldsToBodies() { void CollisionDetection::addAllContactManifoldsToBodies() {
@ -353,26 +358,25 @@ void CollisionDetection::addAllContactManifoldsToBodies() {
} }
} }
void CollisionDetection::addContactManifoldToBody(OverlappingPair* pair) { void CollisionDetection::addContactManifoldToBody(OverlappingPair* _pair) {
assert(pair != nullptr); assert(_pair != null);
CollisionBody* body1 = pair->getShape1()->getBody(); CollisionBody* body1 = _pair->getShape1()->getBody();
CollisionBody* body2 = pair->getShape2()->getBody(); CollisionBody* body2 = _pair->getShape2()->getBody();
const ContactManifoldSet& manifoldSet = pair->getContactManifoldSet(); const ContactManifoldSet& manifoldSet = _pair->getContactManifoldSet();
// For each contact manifold in the set of manifolds in the pair // For each contact manifold in the set of manifolds in the pair
for (int32_t i=0; i<manifoldSet.getNbContactManifolds(); i++) { for (int32_t i=0; i<manifoldSet.getNbContactManifolds(); i++) {
ContactManifold* contactManifold = manifoldSet.getContactManifold(i); ContactManifold* contactManifold = manifoldSet.getContactManifold(i);
assert(contactManifold->getNbContactPoints() > 0); assert(contactManifold->getNbContactPoints() > 0);
// Add the contact manifold at the beginning of the linked // Add the contact manifold at the beginning of the linked
// list of contact manifolds of the first body // list of contact manifolds of the first body
body1->m_contactManifoldsList = new ContactManifoldListElement(contactManifold, body1->m_contactManifoldsList);; body1->m_contactManifoldsList = ETK_NEW(ContactManifoldListElement, contactManifold, body1->m_contactManifoldsList);;
// Add the contact manifold at the beginning of the linked // Add the contact manifold at the beginning of the linked
// list of the contact manifolds of the second body // list of the contact manifolds of the second body
body2->m_contactManifoldsList = new ContactManifoldListElement(contactManifold, body2->m_contactManifoldsList);; body2->m_contactManifoldsList = ETK_NEW(ContactManifoldListElement, contactManifold, body2->m_contactManifoldsList);;
} }
} }
void CollisionDetection::clearContactPoints() { void CollisionDetection::clearContactPoints() {
// For each overlapping pair // For each overlapping pair
etk::Map<overlappingpairid, OverlappingPair*>::Iterator it; etk::Map<overlappingpairid, OverlappingPair*>::Iterator it;
for (it = m_overlappingPairs.begin(); it != m_overlappingPairs.end(); ++it) { for (it = m_overlappingPairs.begin(); it != m_overlappingPairs.end(); ++it) {
@ -393,8 +397,7 @@ EventListener* CollisionDetection::getWorldEventListener() {
return m_world->m_eventListener; return m_world->m_eventListener;
} }
void TestCollisionBetweenShapesCallback::notifyContact(OverlappingPair* _overlappingPair, void TestCollisionBetweenShapesCallback::notifyContact(OverlappingPair* _overlappingPair, const ContactPointInfo& _contactInfo) {
const ContactPointInfo& _contactInfo) {
m_collisionCallback->notifyContact(_contactInfo); m_collisionCallback->notifyContact(_contactInfo);
} }
@ -415,41 +418,37 @@ void CollisionDetection::addProxyCollisionShape(ProxyShape* _proxyShape, const A
m_isCollisionShapesAdded = true; m_isCollisionShapesAdded = true;
} }
void CollisionDetection::addNoCollisionPair(CollisionBody* body1, CollisionBody* body2) { void CollisionDetection::addNoCollisionPair(CollisionBody* _body1, CollisionBody* _body2) {
m_noCollisionPairs.set(OverlappingPair::computeBodiesIndexPair(body1, body2)); m_noCollisionPairs.set(OverlappingPair::computeBodiesIndexPair(_body1, _body2));
} }
void CollisionDetection::removeNoCollisionPair(CollisionBody* body1, void CollisionDetection::removeNoCollisionPair(CollisionBody* _body1, CollisionBody* _body2) {
CollisionBody* body2) { m_noCollisionPairs.erase(m_noCollisionPairs.find(OverlappingPair::computeBodiesIndexPair(_body1, _body2)));
m_noCollisionPairs.erase(m_noCollisionPairs.find(OverlappingPair::computeBodiesIndexPair(body1, body2)));
} }
void CollisionDetection::askForBroadPhaseCollisionCheck(ProxyShape* shape) { void CollisionDetection::askForBroadPhaseCollisionCheck(ProxyShape* _shape) {
m_broadPhaseAlgorithm.addMovedCollisionShape(shape->m_broadPhaseID); m_broadPhaseAlgorithm.addMovedCollisionShape(_shape->m_broadPhaseID);
} }
void CollisionDetection::updateProxyCollisionShape(ProxyShape* shape, const AABB& aabb, void CollisionDetection::updateProxyCollisionShape(ProxyShape* _shape, const AABB& _aabb, const vec3& _displacement, bool _forceReinsert) {
const vec3& displacement, bool forceReinsert) { m_broadPhaseAlgorithm.updateProxyCollisionShape(_shape, _aabb, _displacement);
m_broadPhaseAlgorithm.updateProxyCollisionShape(shape, aabb, displacement);
} }
void CollisionDetection::raycast(RaycastCallback* raycastCallback, void CollisionDetection::raycast(RaycastCallback* _raycastCallback, const Ray& _ray, unsigned short _raycastWithCategoryMaskBits) const {
const Ray& ray,
unsigned short raycastWithCategoryMaskBits) const {
PROFILE("CollisionDetection::raycast()"); PROFILE("CollisionDetection::raycast()");
RaycastTest rayCastTest(raycastCallback); RaycastTest rayCastTest(_raycastCallback);
// Ask the broad-phase algorithm to call the testRaycastAgainstShape() // Ask the broad-phase algorithm to call the testRaycastAgainstShape()
// callback method for each proxy shape hit by the ray in the broad-phase // callback method for each proxy shape hit by the ray in the broad-phase
m_broadPhaseAlgorithm.raycast(ray, rayCastTest, raycastWithCategoryMaskBits); m_broadPhaseAlgorithm.raycast(_ray, rayCastTest, _raycastWithCategoryMaskBits);
} }
bool CollisionDetection::testAABBOverlap(const ProxyShape* shape1, bool CollisionDetection::testAABBOverlap(const ProxyShape* _shape1, const ProxyShape* _shape2) const {
const ProxyShape* shape2) const {
// If one of the shape's body is not active, we return no overlap // If one of the shape's body is not active, we return no overlap
if (!shape1->getBody()->isActive() || !shape2->getBody()->isActive()) { if ( !_shape1->getBody()->isActive()
|| !_shape2->getBody()->isActive()) {
return false; return false;
} }
return m_broadPhaseAlgorithm.testOverlappingShapes(shape1, shape2); return m_broadPhaseAlgorithm.testOverlappingShapes(_shape1, _shape2);
} }
CollisionWorld* CollisionDetection::getWorld() { CollisionWorld* CollisionDetection::getWorld() {

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,16 +1,15 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/ContactManifold.hpp> #include <ephysics/collision/ContactManifold.hpp>
using namespace ephysics; using namespace ephysics;
// Constructor
ContactManifold::ContactManifold(ProxyShape* _shape1, ContactManifold::ContactManifold(ProxyShape* _shape1,
ProxyShape* _shape2, ProxyShape* _shape2,
short _normalDirectionId): short _normalDirectionId):
@ -25,101 +24,73 @@ ContactManifold::ContactManifold(ProxyShape* _shape1,
} }
// Destructor
ContactManifold::~ContactManifold() { ContactManifold::~ContactManifold() {
clear(); clear();
} }
// Add a contact point in the manifold
void ContactManifold::addContactPoint(ContactPoint* contact) { void ContactManifold::addContactPoint(ContactPoint* contact) {
// For contact already in the manifold // For contact already in the manifold
for (uint32_t i=0; i<m_nbContactPoints; i++) { for (uint32_t i=0; i<m_nbContactPoints; i++) {
// Check if the new point point does not correspond to a same contact point // Check if the new point point does not correspond to a same contact point
// already in the manifold. // already in the manifold.
float distance = (m_contactPoints[i]->getWorldPointOnBody1() - float distance = (m_contactPoints[i]->getWorldPointOnBody1() - contact->getWorldPointOnBody1()).length2();
contact->getWorldPointOnBody1()).length2();
if (distance <= PERSISTENT_CONTACT_DIST_THRESHOLD*PERSISTENT_CONTACT_DIST_THRESHOLD) { if (distance <= PERSISTENT_CONTACT_DIST_THRESHOLD*PERSISTENT_CONTACT_DIST_THRESHOLD) {
// Delete the new contact // Delete the new contact
delete contact; ETK_DELETE(ContactPoint, contact);
assert(m_nbContactPoints > 0); assert(m_nbContactPoints > 0);
return; return;
} }
} }
// If the contact manifold is full // If the contact manifold is full
if (m_nbContactPoints == MAX_CONTACT_POINTS_IN_MANIFOLD) { if (m_nbContactPoints == MAX_CONTACT_POINTS_IN_MANIFOLD) {
int32_t indexMaxPenetration = getIndexOfDeepestPenetration(contact); int32_t indexMaxPenetration = getIndexOfDeepestPenetration(contact);
int32_t indexToRemove = getIndexToRemove(indexMaxPenetration, contact->getLocalPointOnBody1()); int32_t indexToRemove = getIndexToRemove(indexMaxPenetration, contact->getLocalPointOnBody1());
removeContactPoint(indexToRemove); removeContactPoint(indexToRemove);
} }
// Add the new contact point in the manifold // Add the new contact point in the manifold
m_contactPoints[m_nbContactPoints] = contact; m_contactPoints[m_nbContactPoints] = contact;
m_nbContactPoints++; m_nbContactPoints++;
assert(m_nbContactPoints > 0); assert(m_nbContactPoints > 0);
} }
// Remove a contact point from the manifold
void ContactManifold::removeContactPoint(uint32_t index) { void ContactManifold::removeContactPoint(uint32_t index) {
assert(index < m_nbContactPoints); assert(index < m_nbContactPoints);
assert(m_nbContactPoints > 0); assert(m_nbContactPoints > 0);
// Call the destructor explicitly and tell the memory allocator that // Call the destructor explicitly and tell the memory allocator that
// the corresponding memory block is now free // the corresponding memory block is now free
delete m_contactPoints[index]; ETK_DELETE(ContactPoint, m_contactPoints[index]);
m_contactPoints[index] = nullptr; m_contactPoints[index] = null;
// If we don't remove the last index // If we don't remove the last index
if (index < m_nbContactPoints - 1) { if (index < m_nbContactPoints - 1) {
m_contactPoints[index] = m_contactPoints[m_nbContactPoints - 1]; m_contactPoints[index] = m_contactPoints[m_nbContactPoints - 1];
} }
m_nbContactPoints--; m_nbContactPoints--;
} }
// Update the contact manifold
/// First the world space coordinates of the current contacts in the manifold are recomputed from
/// the corresponding transforms of the bodies because they have moved. Then we remove the contacts
/// with a negative penetration depth (meaning that the bodies are not penetrating anymore) and also
/// the contacts with a too large distance between the contact points in the plane orthogonal to the
/// contact normal.
void ContactManifold::update(const etk::Transform3D& transform1, const etk::Transform3D& transform2) { void ContactManifold::update(const etk::Transform3D& transform1, const etk::Transform3D& transform2) {
if (m_nbContactPoints == 0) {
if (m_nbContactPoints == 0) return; return;
}
// Update the world coordinates and penetration depth of the contact points in the manifold // Update the world coordinates and penetration depth of the contact points in the manifold
for (uint32_t i=0; i<m_nbContactPoints; i++) { for (uint32_t i=0; i<m_nbContactPoints; i++) {
m_contactPoints[i]->setWorldPointOnBody1(transform1 * m_contactPoints[i]->setWorldPointOnBody1(transform1 * m_contactPoints[i]->getLocalPointOnBody1());
m_contactPoints[i]->getLocalPointOnBody1()); m_contactPoints[i]->setWorldPointOnBody2(transform2 * m_contactPoints[i]->getLocalPointOnBody2());
m_contactPoints[i]->setWorldPointOnBody2(transform2 * m_contactPoints[i]->setPenetrationDepth((m_contactPoints[i]->getWorldPointOnBody1() - m_contactPoints[i]->getWorldPointOnBody2()).dot(m_contactPoints[i]->getNormal()));
m_contactPoints[i]->getLocalPointOnBody2());
m_contactPoints[i]->setPenetrationDepth((m_contactPoints[i]->getWorldPointOnBody1() -
m_contactPoints[i]->getWorldPointOnBody2()).dot(m_contactPoints[i]->getNormal()));
} }
const float squarePersistentContactThreshold = PERSISTENT_CONTACT_DIST_THRESHOLD * PERSISTENT_CONTACT_DIST_THRESHOLD;
const float squarePersistentContactThreshold = PERSISTENT_CONTACT_DIST_THRESHOLD *
PERSISTENT_CONTACT_DIST_THRESHOLD;
// Remove the contact points that don't represent very well the contact manifold // Remove the contact points that don't represent very well the contact manifold
for (int32_t i=static_cast<int32_t>(m_nbContactPoints)-1; i>=0; i--) { for (int32_t i=static_cast<int32_t>(m_nbContactPoints)-1; i>=0; i--) {
assert(i < static_cast<int32_t>(m_nbContactPoints)); assert(i < static_cast<int32_t>(m_nbContactPoints));
// Compute the distance between contact points in the normal direction // Compute the distance between contact points in the normal direction
float distanceNormal = -m_contactPoints[i]->getPenetrationDepth(); float distanceNormal = -m_contactPoints[i]->getPenetrationDepth();
// If the contacts points are too far from each other in the normal direction // If the contacts points are too far from each other in the normal direction
if (distanceNormal > squarePersistentContactThreshold) { if (distanceNormal > squarePersistentContactThreshold) {
removeContactPoint(i); removeContactPoint(i);
} } else {
else {
// Compute the distance of the two contact points in the plane // Compute the distance of the two contact points in the plane
// orthogonal to the contact normal // orthogonal to the contact normal
vec3 projOfPoint1 = m_contactPoints[i]->getWorldPointOnBody1() + vec3 projOfPoint1 = m_contactPoints[i]->getWorldPointOnBody1() + m_contactPoints[i]->getNormal() * distanceNormal;
m_contactPoints[i]->getNormal() * distanceNormal;
vec3 projDifference = m_contactPoints[i]->getWorldPointOnBody2() - projOfPoint1; vec3 projDifference = m_contactPoints[i]->getWorldPointOnBody2() - projOfPoint1;
// If the orthogonal distance is larger than the valid distance // If the orthogonal distance is larger than the valid distance
// threshold, we remove the contact // threshold, we remove the contact
if (projDifference.length2() > squarePersistentContactThreshold) { if (projDifference.length2() > squarePersistentContactThreshold) {
@ -129,102 +100,80 @@ void ContactManifold::update(const etk::Transform3D& transform1, const etk::Tran
} }
} }
// Return the index of the contact point with the larger penetration depth.
/// This corresponding contact will be kept in the cache. The method returns -1 is
/// the new contact is the deepest.
int32_t ContactManifold::getIndexOfDeepestPenetration(ContactPoint* newContact) const { int32_t ContactManifold::getIndexOfDeepestPenetration(ContactPoint* newContact) const {
assert(m_nbContactPoints == MAX_CONTACT_POINTS_IN_MANIFOLD); assert(m_nbContactPoints == MAX_CONTACT_POINTS_IN_MANIFOLD);
int32_t indexMaxPenetrationDepth = -1; int32_t indexMaxPenetrationDepth = -1;
float maxPenetrationDepth = newContact->getPenetrationDepth(); float maxPenetrationDepth = newContact->getPenetrationDepth();
// For each contact in the cache // For each contact in the cache
for (uint32_t i=0; i<m_nbContactPoints; i++) { for (uint32_t i=0; i<m_nbContactPoints; i++) {
// If the current contact has a larger penetration depth // If the current contact has a larger penetration depth
if (m_contactPoints[i]->getPenetrationDepth() > maxPenetrationDepth) { if (m_contactPoints[i]->getPenetrationDepth() > maxPenetrationDepth) {
maxPenetrationDepth = m_contactPoints[i]->getPenetrationDepth(); maxPenetrationDepth = m_contactPoints[i]->getPenetrationDepth();
indexMaxPenetrationDepth = i; indexMaxPenetrationDepth = i;
} }
} }
// Return the index of largest penetration depth // Return the index of largest penetration depth
return indexMaxPenetrationDepth; return indexMaxPenetrationDepth;
} }
// Return the index that will be removed.
/// The index of the contact point with the larger penetration
/// depth is given as a parameter. This contact won't be removed. Given this contact, we compute
/// the different area and we want to keep the contacts with the largest area. The new point is also
/// kept. In order to compute the area of a quadrilateral, we use the formula :
/// Area = 0.5 * | AC x BD | where AC and BD form the diagonals of the quadrilateral. Note that
/// when we compute this area, we do not calculate it exactly but we
/// only estimate it because we do not compute the actual diagonals of the quadrialteral. Therefore,
/// this is only a guess that is faster to compute. This idea comes from the Bullet Physics library
/// by Erwin Coumans (http://wwww.bulletphysics.org).
int32_t ContactManifold::getIndexToRemove(int32_t indexMaxPenetration, const vec3& newPoint) const { int32_t ContactManifold::getIndexToRemove(int32_t indexMaxPenetration, const vec3& newPoint) const {
assert(m_nbContactPoints == MAX_CONTACT_POINTS_IN_MANIFOLD); assert(m_nbContactPoints == MAX_CONTACT_POINTS_IN_MANIFOLD);
float area0 = 0.0f; // Area with contact 1,2,3 and newPoint
float area0 = 0.0; // Area with contact 1,2,3 and newPoint float area1 = 0.0f; // Area with contact 0,2,3 and newPoint
float area1 = 0.0; // Area with contact 0,2,3 and newPoint float area2 = 0.0f; // Area with contact 0,1,3 and newPoint
float area2 = 0.0; // Area with contact 0,1,3 and newPoint float area3 = 0.0f; // Area with contact 0,1,2 and newPoint
float area3 = 0.0; // Area with contact 0,1,2 and newPoint
if (indexMaxPenetration != 0) { if (indexMaxPenetration != 0) {
// Compute the area // Compute the area
vec3 vector1 = newPoint - m_contactPoints[1]->getLocalPointOnBody1(); vec3 vector1 = newPoint - m_contactPoints[1]->getLocalPointOnBody1();
vec3 vector2 = m_contactPoints[3]->getLocalPointOnBody1() - vec3 vector2 = m_contactPoints[3]->getLocalPointOnBody1() - m_contactPoints[2]->getLocalPointOnBody1();
m_contactPoints[2]->getLocalPointOnBody1();
vec3 crossProduct = vector1.cross(vector2); vec3 crossProduct = vector1.cross(vector2);
area0 = crossProduct.length2(); area0 = crossProduct.length2();
} }
if (indexMaxPenetration != 1) { if (indexMaxPenetration != 1) {
// Compute the area // Compute the area
vec3 vector1 = newPoint - m_contactPoints[0]->getLocalPointOnBody1(); vec3 vector1 = newPoint - m_contactPoints[0]->getLocalPointOnBody1();
vec3 vector2 = m_contactPoints[3]->getLocalPointOnBody1() - vec3 vector2 = m_contactPoints[3]->getLocalPointOnBody1() - m_contactPoints[2]->getLocalPointOnBody1();
m_contactPoints[2]->getLocalPointOnBody1();
vec3 crossProduct = vector1.cross(vector2); vec3 crossProduct = vector1.cross(vector2);
area1 = crossProduct.length2(); area1 = crossProduct.length2();
} }
if (indexMaxPenetration != 2) { if (indexMaxPenetration != 2) {
// Compute the area // Compute the area
vec3 vector1 = newPoint - m_contactPoints[0]->getLocalPointOnBody1(); vec3 vector1 = newPoint - m_contactPoints[0]->getLocalPointOnBody1();
vec3 vector2 = m_contactPoints[3]->getLocalPointOnBody1() - vec3 vector2 = m_contactPoints[3]->getLocalPointOnBody1() - m_contactPoints[1]->getLocalPointOnBody1();
m_contactPoints[1]->getLocalPointOnBody1();
vec3 crossProduct = vector1.cross(vector2); vec3 crossProduct = vector1.cross(vector2);
area2 = crossProduct.length2(); area2 = crossProduct.length2();
} }
if (indexMaxPenetration != 3) { if (indexMaxPenetration != 3) {
// Compute the area // Compute the area
vec3 vector1 = newPoint - m_contactPoints[0]->getLocalPointOnBody1(); vec3 vector1 = newPoint - m_contactPoints[0]->getLocalPointOnBody1();
vec3 vector2 = m_contactPoints[2]->getLocalPointOnBody1() - vec3 vector2 = m_contactPoints[2]->getLocalPointOnBody1() - m_contactPoints[1]->getLocalPointOnBody1();
m_contactPoints[1]->getLocalPointOnBody1();
vec3 crossProduct = vector1.cross(vector2); vec3 crossProduct = vector1.cross(vector2);
area3 = crossProduct.length2(); area3 = crossProduct.length2();
} }
// Return the index of the contact to remove // Return the index of the contact to remove
return getMaxArea(area0, area1, area2, area3); return getMaxArea(area0, area1, area2, area3);
} }
// Return the index of maximum area
int32_t ContactManifold::getMaxArea(float area0, float area1, float area2, float area3) const { int32_t ContactManifold::getMaxArea(float area0, float area1, float area2, float area3) const {
if (area0 < area1) { if (area0 < area1) {
if (area1 < area2) { if (area1 < area2) {
if (area2 < area3) return 3; if (area2 < area3) {
else return 2; return 3;
} else {
return 2;
}
} else {
if (area1 < area3) {
return 3;
} else {
return 1;
}
} }
else { } else {
if (area1 < area3) return 3;
else return 1;
}
}
else {
if (area0 < area2) { if (area0 < area2) {
if (area2 < area3) return 3; if (area2 < area3) return 3;
else return 2; else return 2;
} } else {
else {
if (area0 < area3) return 3; if (area0 < area3) return 3;
else return 0; else return 0;
} }
@ -237,8 +186,8 @@ void ContactManifold::clear() {
// Call the destructor explicitly and tell the memory allocator that // Call the destructor explicitly and tell the memory allocator that
// the corresponding memory block is now free // the corresponding memory block is now free
delete m_contactPoints[iii]; ETK_DELETE(ContactPoint, m_contactPoints[iii]);
m_contactPoints[iii] = nullptr; m_contactPoints[iii] = null;
} }
m_nbContactPoints = 0; m_nbContactPoints = 0;
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -46,6 +49,17 @@ namespace ephysics {
* The new added point is always kept. * The new added point is always kept.
*/ */
class ContactManifold { class ContactManifold {
public:
/// Constructor
ContactManifold(ProxyShape* _shape1,
ProxyShape* _shape2,
int16_t _normalDirectionId);
/// Destructor
~ContactManifold();
/// DELETE copy-constructor
ContactManifold(const ContactManifold& _contactManifold) = delete;
/// DELETE assignment operator
ContactManifold& operator=(const ContactManifold& _contactManifold) = delete;
private: private:
ProxyShape* m_shape1; //!< Pointer to the first proxy shape of the contact ProxyShape* m_shape1; //!< Pointer to the first proxy shape of the contact
ProxyShape* m_shape2; //!< Pointer to the second proxy shape of the contact ProxyShape* m_shape2; //!< Pointer to the second proxy shape of the contact
@ -59,27 +73,33 @@ namespace ephysics {
float m_frictionTwistImpulse; //!< Twist friction constraint accumulated impulse float m_frictionTwistImpulse; //!< Twist friction constraint accumulated impulse
vec3 m_rollingResistanceImpulse; //!< Accumulated rolling resistance impulse vec3 m_rollingResistanceImpulse; //!< Accumulated rolling resistance impulse
bool m_isAlreadyInIsland; //!< True if the contact manifold has already been added int32_to an island bool m_isAlreadyInIsland; //!< True if the contact manifold has already been added int32_to an island
/// Private copy-constructor
ContactManifold(const ContactManifold& _contactManifold) = delete;
/// Private assignment operator
ContactManifold& operator=(const ContactManifold& _contactManifold) = delete;
/// Return the index of maximum area /// Return the index of maximum area
int32_t getMaxArea(float _area0, float _area1, float _area2, float _area3) const; int32_t getMaxArea(float _area0, float _area1, float _area2, float _area3) const;
/// Return the index of the contact with the larger penetration depth. /**
* @brief Return the index of the contact with the larger penetration depth.
*
* This corresponding contact will be kept in the cache. The method returns -1 is
* the new contact is the deepest.
*/
int32_t getIndexOfDeepestPenetration(ContactPoint* _newContact) const; int32_t getIndexOfDeepestPenetration(ContactPoint* _newContact) const;
/// Return the index that will be removed. /**
* @brief Return the index that will be removed.
* The index of the contact point with the larger penetration
* depth is given as a parameter. This contact won't be removed. Given this contact, we compute
* the different area and we want to keep the contacts with the largest area. The new point is also
* kept. In order to compute the area of a quadrilateral, we use the formula :
* Area = 0.5 * | AC x BD | where AC and BD form the diagonals of the quadrilateral. Note that
* when we compute this area, we do not calculate it exactly but we
* only estimate it because we do not compute the actual diagonals of the quadrialteral. Therefore,
* this is only a guess that is faster to compute. This idea comes from the Bullet Physics library
* by Erwin Coumans (http://wwww.bulletphysics.org).
*/
int32_t getIndexToRemove(int32_t _indexMaxPenetration, const vec3& _newPoint) const; int32_t getIndexToRemove(int32_t _indexMaxPenetration, const vec3& _newPoint) const;
/// Remove a contact point from the manifold /// Remove a contact point from the manifold
void removeContactPoint(uint32_t _index); void removeContactPoint(uint32_t _index);
/// Return true if the contact manifold has already been added int32_to an island /// Return true if the contact manifold has already been added int32_to an island
bool isAlreadyInIsland() const; bool isAlreadyInIsland() const;
public: public:
/// Constructor
ContactManifold(ProxyShape* _shape1,
ProxyShape* _shape2,
int16_t _normalDirectionId);
/// Destructor
~ContactManifold();
/// Return a pointer to the first proxy shape of the contact /// Return a pointer to the first proxy shape of the contact
ProxyShape* getShape1() const; ProxyShape* getShape1() const;
/// Return a pointer to the second proxy shape of the contact /// Return a pointer to the second proxy shape of the contact
@ -92,7 +112,15 @@ namespace ephysics {
int16_t getNormalDirectionId() const; int16_t getNormalDirectionId() const;
/// Add a contact point to the manifold /// Add a contact point to the manifold
void addContactPoint(ContactPoint* _contact); void addContactPoint(ContactPoint* _contact);
/// Update the contact manifold. /**
* @brief Update the contact manifold.
*
* First the world space coordinates of the current contacts in the manifold are recomputed from
* the corresponding transforms of the bodies because they have moved. Then we remove the contacts
* with a negative penetration depth (meaning that the bodies are not penetrating anymore) and also
* the contacts with a too large distance between the contact points in the plane orthogonal to the
* contact normal.
*/
void update(const etk::Transform3D& _transform1, void update(const etk::Transform3D& _transform1,
const etk::Transform3D& _transform2); const etk::Transform3D& _transform2);
/// Clear the contact manifold /// Clear the contact manifold

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/ContactManifoldSet.hpp> #include <ephysics/collision/ContactManifoldSet.hpp>
using namespace ephysics; using namespace ephysics;
@ -74,8 +76,8 @@ void ContactManifoldSet::addContactPoint(ContactPoint* contact) {
// new contact point // new contact point
if (smallestDepthIndex == -1) { if (smallestDepthIndex == -1) {
// Delete the new contact // Delete the new contact
delete contact; ETK_DELETE(ContactPoint, contact);
contact = nullptr; contact = null;
return; return;
} }
assert(smallestDepthIndex >= 0 && smallestDepthIndex < m_nbManifolds); assert(smallestDepthIndex >= 0 && smallestDepthIndex < m_nbManifolds);
@ -153,7 +155,7 @@ void ContactManifoldSet::clear() {
void ContactManifoldSet::createManifold(int16_t normalDirectionId) { void ContactManifoldSet::createManifold(int16_t normalDirectionId) {
assert(m_nbManifolds < m_nbMaxManifolds); assert(m_nbManifolds < m_nbMaxManifolds);
m_manifolds[m_nbManifolds] = new ContactManifold(m_shape1, m_shape2, normalDirectionId); m_manifolds[m_nbManifolds] = ETK_NEW(ContactManifold, m_shape1, m_shape2, normalDirectionId);
m_nbManifolds++; m_nbManifolds++;
} }
@ -161,8 +163,8 @@ void ContactManifoldSet::removeManifold(int32_t index) {
assert(m_nbManifolds > 0); assert(m_nbManifolds > 0);
assert(index >= 0 && index < m_nbManifolds); assert(index >= 0 && index < m_nbManifolds);
// Delete the new contact // Delete the new contact
delete m_manifolds[index]; ETK_DELETE(ContactManifold, m_manifolds[index]);
m_manifolds[index] = nullptr; m_manifolds[index] = null;
for (int32_t i=index; (i+1) < m_nbManifolds; i++) { for (int32_t i=index; (i+1) < m_nbManifolds; i++) {
m_manifolds[i] = m_manifolds[i+1]; m_manifolds[i] = m_manifolds[i+1];
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
using namespace ephysics; using namespace ephysics;

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,11 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/RaycastInfo.hpp> #include <ephysics/collision/RaycastInfo.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -33,8 +36,8 @@ namespace ephysics {
RaycastInfo() : RaycastInfo() :
meshSubpart(-1), meshSubpart(-1),
triangleIndex(-1), triangleIndex(-1),
body(nullptr), body(null),
proxyShape(nullptr) { proxyShape(null) {
} }

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/TriangleMesh.hpp> #include <ephysics/collision/TriangleMesh.hpp>
ephysics::TriangleMesh::TriangleMesh() { ephysics::TriangleMesh::TriangleMesh() {

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
#include <etk/Vector.hpp> #include <etk/Vector.hpp>

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/TriangleVertexArray.hpp> #include <ephysics/collision/TriangleVertexArray.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,60 +1,42 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/broadphase/BroadPhaseAlgorithm.hpp> #include <ephysics/collision/broadphase/BroadPhaseAlgorithm.hpp>
#include <ephysics/collision/CollisionDetection.hpp> #include <ephysics/collision/CollisionDetection.hpp>
#include <ephysics/engine/Profiler.hpp> #include <ephysics/engine/Profiler.hpp>
using namespace ephysics; using namespace ephysics;
BroadPhaseAlgorithm::BroadPhaseAlgorithm(CollisionDetection& collisionDetection) BroadPhaseAlgorithm::BroadPhaseAlgorithm(CollisionDetection& _collisionDetection):
:m_dynamicAABBTree(DYNAMIC_TREE_AABB_GAP), m_numberMovedShapes(0), m_numberAllocatedMovedShapes(8), m_dynamicAABBTree(DYNAMIC_TREE_AABB_GAP),
m_numberNonUsedMovedShapes(0), m_numberPotentialPairs(0), m_numberAllocatedPotentialPairs(8), m_collisionDetection(_collisionDetection) {
m_collisionDetection(collisionDetection) { m_movedShapes.reserve(8);
m_potentialPairs.reserve(8);
// Allocate memory for the array of non-static proxy shapes IDs
m_movedShapes = (int32_t*) malloc(m_numberAllocatedMovedShapes * sizeof(int32_t));
assert(m_movedShapes != NULL);
// Allocate memory for the array of potential overlapping pairs
m_potentialPairs = (BroadPhasePair*) malloc(m_numberAllocatedPotentialPairs * sizeof(BroadPhasePair));
assert(m_potentialPairs != NULL);
} }
BroadPhaseAlgorithm::~BroadPhaseAlgorithm() { BroadPhaseAlgorithm::~BroadPhaseAlgorithm() {
// Release the memory for the array of non-static proxy shapes IDs
free(m_movedShapes);
// Release the memory for the array of potential overlapping pairs
free(m_potentialPairs);
} }
void BroadPhaseAlgorithm::addMovedCollisionShape(int32_t broadPhaseID) { void BroadPhaseAlgorithm::addMovedCollisionShape(int32_t _broadPhaseID) {
m_movedShapes.pushBack(_broadPhaseID);
}
// Allocate more elements in the array of shapes that have moved if necessary void BroadPhaseAlgorithm::removeMovedCollisionShape(int32_t _broadPhaseID) {
if (m_numberAllocatedMovedShapes == m_numberMovedShapes) { auto it = m_movedShapes.begin();
m_numberAllocatedMovedShapes *= 2; while (it != m_movedShapes.end()) {
int32_t* oldArray = m_movedShapes; if (*it == _broadPhaseID) {
m_movedShapes = (int32_t*) malloc(m_numberAllocatedMovedShapes * sizeof(int32_t)); it = m_movedShapes.erase(it);
assert(m_movedShapes != NULL); } else {
memcpy(m_movedShapes, oldArray, m_numberMovedShapes * sizeof(int32_t)); ++it;
free(oldArray); }
} }
/*
// Store the broad-phase ID int32_to the array of shapes that have moved
assert(m_numberMovedShapes < m_numberAllocatedMovedShapes);
assert(m_movedShapes != NULL);
m_movedShapes[m_numberMovedShapes] = broadPhaseID;
m_numberMovedShapes++;
}
void BroadPhaseAlgorithm::removeMovedCollisionShape(int32_t broadPhaseID) {
assert(m_numberNonUsedMovedShapes <= m_numberMovedShapes); assert(m_numberNonUsedMovedShapes <= m_numberMovedShapes);
// If less than the quarter of allocated elements of the non-static shapes IDs array // If less than the quarter of allocated elements of the non-static shapes IDs array
@ -86,28 +68,23 @@ void BroadPhaseAlgorithm::removeMovedCollisionShape(int32_t broadPhaseID) {
break; break;
} }
} }
*/
} }
void BroadPhaseAlgorithm::addProxyCollisionShape(ProxyShape* proxyShape, const AABB& aabb) { void BroadPhaseAlgorithm::addProxyCollisionShape(ProxyShape* _proxyShape, const AABB& _aabb) {
// Add the collision shape int32_to the dynamic AABB tree and get its broad-phase ID // Add the collision shape int32_to the dynamic AABB tree and get its broad-phase ID
int32_t nodeId = m_dynamicAABBTree.addObject(aabb, proxyShape); int32_t nodeId = m_dynamicAABBTree.addObject(_aabb, _proxyShape);
// Set the broad-phase ID of the proxy shape // Set the broad-phase ID of the proxy shape
proxyShape->m_broadPhaseID = nodeId; _proxyShape->m_broadPhaseID = nodeId;
// Add the collision shape int32_to the array of bodies that have moved (or have been created) // Add the collision shape int32_to the array of bodies that have moved (or have been created)
// during the last simulation step // during the last simulation step
addMovedCollisionShape(proxyShape->m_broadPhaseID); addMovedCollisionShape(_proxyShape->m_broadPhaseID);
} }
void BroadPhaseAlgorithm::removeProxyCollisionShape(ProxyShape* proxyShape) { void BroadPhaseAlgorithm::removeProxyCollisionShape(ProxyShape* _proxyShape) {
int32_t broadPhaseID = _proxyShape->m_broadPhaseID;
int32_t broadPhaseID = proxyShape->m_broadPhaseID;
// Remove the collision shape from the dynamic AABB tree // Remove the collision shape from the dynamic AABB tree
m_dynamicAABBTree.removeObject(broadPhaseID); m_dynamicAABBTree.removeObject(broadPhaseID);
// Remove the collision shape int32_to the array of shapes that have moved (or have been created) // Remove the collision shape int32_to the array of shapes that have moved (or have been created)
// during the last simulation step // during the last simulation step
removeMovedCollisionShape(broadPhaseID); removeMovedCollisionShape(broadPhaseID);
@ -130,127 +107,80 @@ void BroadPhaseAlgorithm::updateProxyCollisionShape(ProxyShape* _proxyShape,
} }
} }
static bool sortFunction(const etk::Pair<int32_t,int32_t>& _pair1, const etk::Pair<int32_t,int32_t>& _pair2) {
if (_pair1.first < _pair2.first) {
return true;
}
if (_pair1.first == _pair2.first) {
return _pair1.second < _pair2.second;
}
return false;
}
void BroadPhaseAlgorithm::computeOverlappingPairs() { void BroadPhaseAlgorithm::computeOverlappingPairs() {
m_potentialPairs.clear();
// Reset the potential overlapping pairs
m_numberPotentialPairs = 0;
// For all collision shapes that have moved (or have been created) during the // For all collision shapes that have moved (or have been created) during the
// last simulation step // last simulation step
for (uint32_t i=0; i<m_numberMovedShapes; i++) { for (auto &it: m_movedShapes) {
int32_t shapeID = m_movedShapes[i]; if (it == -1) {
// impossible case ...
if (shapeID == -1) continue; continue;
}
AABBOverlapCallback callback(*this, shapeID);
// Get the AABB of the shape // Get the AABB of the shape
const AABB& shapeAABB = m_dynamicAABBTree.getFatAABB(shapeID); const AABB& shapeAABB = m_dynamicAABBTree.getFatAABB(it);
// Ask the dynamic AABB tree to report all collision shapes that overlap with // Ask the dynamic AABB tree to report all collision shapes that overlap with
// this AABB. The method BroadPhase::notifiyOverlappingPair() will be called // this AABB. The method BroadPhase::notifiyOverlappingPair() will be called
// by the dynamic AABB tree for each potential overlapping pair. // by the dynamic AABB tree for each potential overlapping pair.
m_dynamicAABBTree.reportAllShapesOverlappingWithAABB(shapeAABB, callback); m_dynamicAABBTree.reportAllShapesOverlappingWithAABB(shapeAABB, [&](int32_t _nodeId) mutable {
// If both the nodes are the same, we do not create store the overlapping pair
if (it == _nodeId) {
return;
}
// Add the new potential pair int32_to the array of potential overlapping pairs
m_potentialPairs.pushBack(etk::makePair(etk::min(it, _nodeId), etk::max(it, _nodeId) ));
});
} }
// Reset the array of collision shapes that have move (or have been created) during the last simulation step
// Reset the array of collision shapes that have move (or have been created) during the m_movedShapes.clear();
// last simulation step
m_numberMovedShapes = 0;
// Sort the array of potential overlapping pairs in order to remove duplicate pairs // Sort the array of potential overlapping pairs in order to remove duplicate pairs
std::sort(m_potentialPairs, m_potentialPairs + m_numberPotentialPairs, BroadPhasePair::smallerThan); etk::algorithm::quickSort(m_potentialPairs, sortFunction);
// Check all the potential overlapping pairs avoiding duplicates to report unique // Check all the potential overlapping pairs avoiding duplicates to report unique
// overlapping pairs // overlapping pairs
uint32_t i=0; uint32_t iii=0;
while (i < m_numberPotentialPairs) { while (iii < m_potentialPairs.size()) {
// Get a potential overlapping pair // Get a potential overlapping pair
BroadPhasePair* pair = m_potentialPairs + i; const etk::Pair<int32_t,int32_t>& pair = (m_potentialPairs[iii]);
i++; ++iii;
assert(pair->collisionShape1ID != pair->collisionShape2ID);
// Get the two collision shapes of the pair // Get the two collision shapes of the pair
ProxyShape* shape1 = static_cast<ProxyShape*>(m_dynamicAABBTree.getNodeDataPointer(pair->collisionShape1ID)); ProxyShape* shape1 = static_cast<ProxyShape*>(m_dynamicAABBTree.getNodeDataPointer(pair.first));
ProxyShape* shape2 = static_cast<ProxyShape*>(m_dynamicAABBTree.getNodeDataPointer(pair->collisionShape2ID)); ProxyShape* shape2 = static_cast<ProxyShape*>(m_dynamicAABBTree.getNodeDataPointer(pair.second));
// Notify the collision detection about the overlapping pair // Notify the collision detection about the overlapping pair
m_collisionDetection.broadPhaseNotifyOverlappingPair(shape1, shape2); m_collisionDetection.broadPhaseNotifyOverlappingPair(shape1, shape2);
// Skip the duplicate overlapping pairs // Skip the duplicate overlapping pairs
while (i < m_numberPotentialPairs) { while (iii < m_potentialPairs.size()) {
// Get the next pair // Get the next pair
BroadPhasePair* nextPair = m_potentialPairs + i; const etk::Pair<int32_t,int32_t>& nextPair = m_potentialPairs[iii];
// If the next pair is different from the previous one, we stop skipping pairs // If the next pair is different from the previous one, we stop skipping pairs
if (nextPair->collisionShape1ID != pair->collisionShape1ID || if ( nextPair.first != pair.first
nextPair->collisionShape2ID != pair->collisionShape2ID) { || nextPair.second != pair.second) {
break; break;
} }
i++; ++iii;
} }
} }
// If the number of potential overlapping pairs is less than the quarter of allocated
// number of overlapping pairs
if (m_numberPotentialPairs < m_numberAllocatedPotentialPairs / 4 && m_numberPotentialPairs > 8) {
// Reduce the number of allocated potential overlapping pairs
BroadPhasePair* oldPairs = m_potentialPairs;
m_numberAllocatedPotentialPairs /= 2;
m_potentialPairs = (BroadPhasePair*) malloc(m_numberAllocatedPotentialPairs * sizeof(BroadPhasePair));
assert(m_potentialPairs);
memcpy(m_potentialPairs, oldPairs, m_numberPotentialPairs * sizeof(BroadPhasePair));
free(oldPairs);
}
} }
void BroadPhaseAlgorithm::notifyOverlappingNodes(int32_t node1ID, int32_t node2ID) { float BroadPhaseRaycastCallback::operator()(int32_t _nodeId, const Ray& _ray) {
// If both the nodes are the same, we do not create store the overlapping pair
if (node1ID == node2ID) return;
// If we need to allocate more memory for the array of potential overlapping pairs
if (m_numberPotentialPairs == m_numberAllocatedPotentialPairs) {
// Allocate more memory for the array of potential pairs
BroadPhasePair* oldPairs = m_potentialPairs;
m_numberAllocatedPotentialPairs *= 2;
m_potentialPairs = (BroadPhasePair*) malloc(m_numberAllocatedPotentialPairs * sizeof(BroadPhasePair));
assert(m_potentialPairs);
memcpy(m_potentialPairs, oldPairs, m_numberPotentialPairs * sizeof(BroadPhasePair));
free(oldPairs);
}
// Add the new potential pair int32_to the array of potential overlapping pairs
m_potentialPairs[m_numberPotentialPairs].collisionShape1ID = etk::min(node1ID, node2ID);
m_potentialPairs[m_numberPotentialPairs].collisionShape2ID = etk::max(node1ID, node2ID);
m_numberPotentialPairs++;
}
void AABBOverlapCallback::notifyOverlappingNode(int32_t nodeId) {
m_broadPhaseAlgorithm.notifyOverlappingNodes(m_referenceNodeId, nodeId);
}
float BroadPhaseRaycastCallback::raycastBroadPhaseShape(int32_t nodeId, const Ray& ray) {
float hitFraction = float(-1.0); float hitFraction = float(-1.0);
// Get the proxy shape from the node // Get the proxy shape from the node
ProxyShape* proxyShape = static_cast<ProxyShape*>(m_dynamicAABBTree.getNodeDataPointer(nodeId)); ProxyShape* proxyShape = static_cast<ProxyShape*>(m_dynamicAABBTree.getNodeDataPointer(_nodeId));
// Check if the raycast filtering mask allows raycast against this shape // Check if the raycast filtering mask allows raycast against this shape
if ((m_raycastWithCategoryMaskBits & proxyShape->getCollisionCategoryBits()) != 0) { if ((m_raycastWithCategoryMaskBits & proxyShape->getCollisionCategoryBits()) != 0) {
// Ask the collision detection to perform a ray cast test against // Ask the collision detection to perform a ray cast test against
// the proxy shape of this node because the ray is overlapping // the proxy shape of this node because the ray is overlapping
// with the shape in the broad-phase // with the shape in the broad-phase
hitFraction = m_raycastTest.raycastAgainstShape(proxyShape, ray); hitFraction = m_raycastTest.raycastAgainstShape(proxyShape, _ray);
} }
return hitFraction; return hitFraction;
} }
@ -259,7 +189,6 @@ bool BroadPhaseAlgorithm::testOverlappingShapes(const ProxyShape* _shape1,
// Get the two AABBs of the collision shapes // Get the two AABBs of the collision shapes
const AABB& aabb1 = m_dynamicAABBTree.getFatAABB(_shape1->m_broadPhaseID); const AABB& aabb1 = m_dynamicAABBTree.getFatAABB(_shape1->m_broadPhaseID);
const AABB& aabb2 = m_dynamicAABBTree.getFatAABB(_shape2->m_broadPhaseID); const AABB& aabb2 = m_dynamicAABBTree.getFatAABB(_shape2->m_broadPhaseID);
// Check if the two AABBs are overlapping // Check if the two AABBs are overlapping
return aabb1.testCollision(aabb2); return aabb1.testCollision(aabb2);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -16,49 +19,12 @@ namespace ephysics {
class CollisionDetection; class CollisionDetection;
class BroadPhaseAlgorithm; class BroadPhaseAlgorithm;
/** // TODO : remove this as callback ... DynamicAABBTreeOverlapCallback {
* @brief It represent a potential overlapping pair during the
* broad-phase collision detection.
*/
struct BroadPhasePair {
int32_t collisionShape1ID; //!< Broad-phase ID of the first collision shape
int32_t collisionShape2ID; //!< Broad-phase ID of the second collision shape
/**
* @brief Method used to compare two pairs for sorting algorithm
* @param[in] _pair1 first pair of element
* @param[in] _pair2 Second pair of element
* @return _pair1 is smaller than _pair2
*/
static bool smallerThan(const BroadPhasePair& _pair1, const BroadPhasePair& _pair2) {
if (_pair1.collisionShape1ID < _pair2.collisionShape1ID) return true;
if (_pair1.collisionShape1ID == _pair2.collisionShape1ID) {
return _pair1.collisionShape2ID < _pair2.collisionShape2ID;
}
return false;
}
};
class AABBOverlapCallback : public DynamicAABBTreeOverlapCallback {
private:
BroadPhaseAlgorithm& m_broadPhaseAlgorithm;
int32_t m_referenceNodeId;
public:
// Constructor
AABBOverlapCallback(BroadPhaseAlgorithm& _broadPhaseAlgo, int32_t _referenceNodeId):
m_broadPhaseAlgorithm(_broadPhaseAlgo),
m_referenceNodeId(_referenceNodeId) {
}
// Called when a overlapping node has been found during the call to
// DynamicAABBTree:reportAllShapesOverlappingWithAABB()
virtual void notifyOverlappingNode(int32_t nodeId);
};
/** /**
* Callback called when the AABB of a leaf node is hit by a ray the * Callback called when the AABB of a leaf node is hit by a ray the
* broad-phase Dynamic AABB Tree. * broad-phase Dynamic AABB Tree.
*/ */
class BroadPhaseRaycastCallback : public DynamicAABBTreeRaycastCallback { class BroadPhaseRaycastCallback {
private : private :
const DynamicAABBTree& m_dynamicAABBTree; const DynamicAABBTree& m_dynamicAABBTree;
unsigned short m_raycastWithCategoryMaskBits; unsigned short m_raycastWithCategoryMaskBits;
@ -74,7 +40,7 @@ namespace ephysics {
} }
// Called for a broad-phase shape that has to be tested for raycast // Called for a broad-phase shape that has to be tested for raycast
virtual float raycastBroadPhaseShape(int32_t _nodeId, const Ray& _ray); float operator()(int32_t _nodeId, const Ray& _ray);
}; };
/** /**
@ -87,18 +53,13 @@ namespace ephysics {
class BroadPhaseAlgorithm { class BroadPhaseAlgorithm {
protected : protected :
DynamicAABBTree m_dynamicAABBTree; //!< Dynamic AABB tree DynamicAABBTree m_dynamicAABBTree; //!< Dynamic AABB tree
int32_t* m_movedShapes; //!< Array with the broad-phase IDs of all collision shapes that have moved (or have been created) during the last simulation step. Those are the shapes that need to be tested for overlapping in the next simulation step. etk::Vector<int32_t> m_movedShapes; //!< Array with the broad-phase IDs of all collision shapes that have moved (or have been created) during the last simulation step. Those are the shapes that need to be tested for overlapping in the next simulation step.
uint32_t m_numberMovedShapes; //!< Number of collision shapes in the array of shapes that have moved during the last simulation step. etk::Vector<etk::Pair<int32_t,int32_t>> m_potentialPairs; //!< Temporary array of potential overlapping pairs (with potential duplicates)
uint32_t m_numberAllocatedMovedShapes; //!< Number of allocated elements for the array of shapes that have moved during the last simulation step.
uint32_t m_numberNonUsedMovedShapes; //!< Number of non-used elements in the array of shapes that have moved during the last simulation step.
BroadPhasePair* m_potentialPairs; //!< Temporary array of potential overlapping pairs (with potential duplicates)
uint32_t m_numberPotentialPairs; //!< Number of potential overlapping pairs
uint32_t m_numberAllocatedPotentialPairs; //!< Number of allocated elements for the array of potential overlapping pairs
CollisionDetection& m_collisionDetection; //!< Reference to the collision detection object CollisionDetection& m_collisionDetection; //!< Reference to the collision detection object
/// Private copy-constructor /// Private copy-constructor
BroadPhaseAlgorithm(const BroadPhaseAlgorithm& algorithm); BroadPhaseAlgorithm(const BroadPhaseAlgorithm& _obj);
/// Private assignment operator /// Private assignment operator
BroadPhaseAlgorithm& operator=(const BroadPhaseAlgorithm& algorithm); BroadPhaseAlgorithm& operator=(const BroadPhaseAlgorithm& _obj);
public : public :
/// Constructor /// Constructor
BroadPhaseAlgorithm(CollisionDetection& _collisionDetection); BroadPhaseAlgorithm(CollisionDetection& _collisionDetection);
@ -119,8 +80,6 @@ namespace ephysics {
/// Remove a collision shape from the array of shapes that have moved in the last simulation /// Remove a collision shape from the array of shapes that have moved in the last simulation
/// step and that need to be tested again for broad-phase overlapping. /// step and that need to be tested again for broad-phase overlapping.
void removeMovedCollisionShape(int32_t _broadPhaseID); void removeMovedCollisionShape(int32_t _broadPhaseID);
/// Notify the broad-phase about a potential overlapping pair in the dynamic AABB tree
void notifyOverlappingNodes(int32_t _broadPhaseId1, int32_t _broadPhaseId2);
/// Compute all the overlapping pairs of collision shapes /// Compute all the overlapping pairs of collision shapes
void computeOverlappingPairs(); void computeOverlappingPairs();
/// Return true if the two broad-phase collision shapes are overlapping /// Return true if the two broad-phase collision shapes are overlapping

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/broadphase/DynamicAABBTree.hpp> #include <ephysics/collision/broadphase/DynamicAABBTree.hpp>
#include <ephysics/collision/broadphase/BroadPhaseAlgorithm.hpp> #include <ephysics/collision/broadphase/BroadPhaseAlgorithm.hpp>
#include <ephysics/memory/Stack.hpp> #include <ephysics/memory/Stack.hpp>
@ -14,28 +16,24 @@ using namespace ephysics;
const int32_t TreeNode::NULL_TREE_NODE = -1; const int32_t TreeNode::NULL_TREE_NODE = -1;
DynamicAABBTree::DynamicAABBTree(float extraAABBGap) : m_extraAABBGap(extraAABBGap) { DynamicAABBTree::DynamicAABBTree(float _extraAABBGap):
m_extraAABBGap(_extraAABBGap) {
init(); init();
} }
DynamicAABBTree::~DynamicAABBTree() { DynamicAABBTree::~DynamicAABBTree() {
free(m_nodes); free(m_nodes);
} }
// Initialize the tree // Initialize the tree
void DynamicAABBTree::init() { void DynamicAABBTree::init() {
m_rootNodeID = TreeNode::NULL_TREE_NODE; m_rootNodeID = TreeNode::NULL_TREE_NODE;
m_numberNodes = 0; m_numberNodes = 0;
m_numberAllocatedNodes = 8; m_numberAllocatedNodes = 8;
// Allocate memory for the nodes of the tree // Allocate memory for the nodes of the tree
m_nodes = (TreeNode*) malloc(m_numberAllocatedNodes * sizeof(TreeNode)); m_nodes = (TreeNode*) malloc(m_numberAllocatedNodes * sizeof(TreeNode));
assert(m_nodes); assert(m_nodes);
memset(m_nodes, 0, m_numberAllocatedNodes * sizeof(TreeNode)); memset(m_nodes, 0, m_numberAllocatedNodes * sizeof(TreeNode));
// Initialize the allocated nodes // Initialize the allocated nodes
for (int32_t i=0; i<m_numberAllocatedNodes - 1; i++) { for (int32_t i=0; i<m_numberAllocatedNodes - 1; i++) {
m_nodes[i].nextNodeID = i + 1; m_nodes[i].nextNodeID = i + 1;
@ -48,10 +46,8 @@ void DynamicAABBTree::init() {
// Clear all the nodes and reset the tree // Clear all the nodes and reset the tree
void DynamicAABBTree::reset() { void DynamicAABBTree::reset() {
// Free the allocated memory for the nodes // Free the allocated memory for the nodes
free(m_nodes); free(m_nodes);
// Initialize the tree // Initialize the tree
init(); init();
} }
@ -88,7 +84,6 @@ int32_t DynamicAABBTree::allocateNode() {
// Release a node // Release a node
void DynamicAABBTree::releaseNode(int32_t _nodeID) { void DynamicAABBTree::releaseNode(int32_t _nodeID) {
assert(m_numberNodes > 0); assert(m_numberNodes > 0);
assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes); assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes);
assert(m_nodes[_nodeID].height >= 0); assert(m_nodes[_nodeID].height >= 0);
@ -100,34 +95,26 @@ void DynamicAABBTree::releaseNode(int32_t _nodeID) {
// Internally add an object int32_to the tree // Internally add an object int32_to the tree
int32_t DynamicAABBTree::addObjectInternal(const AABB& aabb) { int32_t DynamicAABBTree::addObjectInternal(const AABB& aabb) {
// Get the next available node (or allocate new ones if necessary) // Get the next available node (or allocate new ones if necessary)
int32_t _nodeID = allocateNode(); int32_t _nodeID = allocateNode();
// Create the fat aabb to use in the tree // Create the fat aabb to use in the tree
const vec3 gap(m_extraAABBGap, m_extraAABBGap, m_extraAABBGap); const vec3 gap(m_extraAABBGap, m_extraAABBGap, m_extraAABBGap);
m_nodes[_nodeID].aabb.setMin(aabb.getMin() - gap); m_nodes[_nodeID].aabb.setMin(aabb.getMin() - gap);
m_nodes[_nodeID].aabb.setMax(aabb.getMax() + gap); m_nodes[_nodeID].aabb.setMax(aabb.getMax() + gap);
// Set the height of the node in the tree // Set the height of the node in the tree
m_nodes[_nodeID].height = 0; m_nodes[_nodeID].height = 0;
// Insert the new leaf node in the tree // Insert the new leaf node in the tree
insertLeafNode(_nodeID); insertLeafNode(_nodeID);
assert(m_nodes[_nodeID].isLeaf()); assert(m_nodes[_nodeID].isLeaf());
assert(_nodeID >= 0); assert(_nodeID >= 0);
// Return the Id of the node // Return the Id of the node
return _nodeID; return _nodeID;
} }
// Remove an object from the tree // Remove an object from the tree
void DynamicAABBTree::removeObject(int32_t _nodeID) { void DynamicAABBTree::removeObject(int32_t _nodeID) {
assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes); assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes);
assert(m_nodes[_nodeID].isLeaf()); assert(m_nodes[_nodeID].isLeaf());
// Remove the node from the tree // Remove the node from the tree
removeLeafNode(_nodeID); removeLeafNode(_nodeID);
releaseNode(_nodeID); releaseNode(_nodeID);
@ -142,30 +129,24 @@ void DynamicAABBTree::removeObject(int32_t _nodeID) {
/// frames. If the "forceReinsert" parameter is true, we force a removal and reinsertion of the node /// frames. If the "forceReinsert" parameter is true, we force a removal and reinsertion of the node
/// (this can be useful if the shape AABB has become much smaller than the previous one for instance). /// (this can be useful if the shape AABB has become much smaller than the previous one for instance).
bool DynamicAABBTree::updateObject(int32_t _nodeID, const AABB& _newAABB, const vec3& _displacement, bool _forceReinsert) { bool DynamicAABBTree::updateObject(int32_t _nodeID, const AABB& _newAABB, const vec3& _displacement, bool _forceReinsert) {
PROFILE("DynamicAABBTree::updateObject()"); PROFILE("DynamicAABBTree::updateObject()");
assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes); assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes);
assert(m_nodes[_nodeID].isLeaf()); assert(m_nodes[_nodeID].isLeaf());
assert(m_nodes[_nodeID].height >= 0); assert(m_nodes[_nodeID].height >= 0);
EPHY_VERBOSE(" compare : " << m_nodes[_nodeID].aabb.m_minCoordinates << " " << m_nodes[_nodeID].aabb.m_maxCoordinates);
EPHY_INFO(" compare : " << m_nodes[_nodeID].aabb.m_minCoordinates << " " << m_nodes[_nodeID].aabb.m_maxCoordinates); EPHY_VERBOSE(" : " << _newAABB.m_minCoordinates << " " << _newAABB.m_maxCoordinates);
EPHY_INFO(" : " << _newAABB.m_minCoordinates << " " << _newAABB.m_maxCoordinates);
// If the new AABB is still inside the fat AABB of the node // If the new AABB is still inside the fat AABB of the node
if ( _forceReinsert == false if ( _forceReinsert == false
&& m_nodes[_nodeID].aabb.contains(_newAABB)) { && m_nodes[_nodeID].aabb.contains(_newAABB)) {
return false; return false;
} }
// If the new AABB is outside the fat AABB, we remove the corresponding node // If the new AABB is outside the fat AABB, we remove the corresponding node
removeLeafNode(_nodeID); removeLeafNode(_nodeID);
// Compute the fat AABB by inflating the AABB with a constant gap // Compute the fat AABB by inflating the AABB with a constant gap
m_nodes[_nodeID].aabb = _newAABB; m_nodes[_nodeID].aabb = _newAABB;
const vec3 gap(m_extraAABBGap, m_extraAABBGap, m_extraAABBGap); const vec3 gap(m_extraAABBGap, m_extraAABBGap, m_extraAABBGap);
m_nodes[_nodeID].aabb.m_minCoordinates -= gap; m_nodes[_nodeID].aabb.m_minCoordinates -= gap;
m_nodes[_nodeID].aabb.m_maxCoordinates += gap; m_nodes[_nodeID].aabb.m_maxCoordinates += gap;
// Inflate the fat AABB in direction of the linear motion of the AABB // Inflate the fat AABB in direction of the linear motion of the AABB
if (_displacement.x() < 0.0f) { if (_displacement.x() < 0.0f) {
m_nodes[_nodeID].aabb.m_minCoordinates.setX(m_nodes[_nodeID].aabb.m_minCoordinates.x() + DYNAMIC_TREE_AABB_LIN_GAP_MULTIPLIER *_displacement.x()); m_nodes[_nodeID].aabb.m_minCoordinates.setX(m_nodes[_nodeID].aabb.m_minCoordinates.x() + DYNAMIC_TREE_AABB_LIN_GAP_MULTIPLIER *_displacement.x());
@ -188,10 +169,8 @@ bool DynamicAABBTree::updateObject(int32_t _nodeID, const AABB& _newAABB, const
//EPHY_CRITICAL("ERROR"); //EPHY_CRITICAL("ERROR");
} }
assert(m_nodes[_nodeID].aabb.contains(_newAABB)); assert(m_nodes[_nodeID].aabb.contains(_newAABB));
// Reinsert the node int32_to the tree // Reinsert the node int32_to the tree
insertLeafNode(_nodeID); insertLeafNode(_nodeID);
return true; return true;
} }
@ -199,75 +178,61 @@ bool DynamicAABBTree::updateObject(int32_t _nodeID, const AABB& _newAABB, const
// in the dynamic tree is described in the book "Introduction to Game Physics // in the dynamic tree is described in the book "Introduction to Game Physics
// with Box2D" by Ian Parberry. // with Box2D" by Ian Parberry.
void DynamicAABBTree::insertLeafNode(int32_t _nodeID) { void DynamicAABBTree::insertLeafNode(int32_t _nodeID) {
// If the tree is empty // If the tree is empty
if (m_rootNodeID == TreeNode::NULL_TREE_NODE) { if (m_rootNodeID == TreeNode::NULL_TREE_NODE) {
m_rootNodeID = _nodeID; m_rootNodeID = _nodeID;
m_nodes[m_rootNodeID].parentID = TreeNode::NULL_TREE_NODE; m_nodes[m_rootNodeID].parentID = TreeNode::NULL_TREE_NODE;
return; return;
} }
assert(m_rootNodeID != TreeNode::NULL_TREE_NODE); assert(m_rootNodeID != TreeNode::NULL_TREE_NODE);
// Find the best sibling node for the new node // Find the best sibling node for the new node
AABB newNodeAABB = m_nodes[_nodeID].aabb; AABB newNodeAABB = m_nodes[_nodeID].aabb;
int32_t currentNodeID = m_rootNodeID; int32_t currentNodeID = m_rootNodeID;
while (!m_nodes[currentNodeID].isLeaf()) { while (!m_nodes[currentNodeID].isLeaf()) {
int32_t leftChild = m_nodes[currentNodeID].children[0]; int32_t leftChild = m_nodes[currentNodeID].children[0];
int32_t rightChild = m_nodes[currentNodeID].children[1]; int32_t rightChild = m_nodes[currentNodeID].children[1];
// Compute the merged AABB // Compute the merged AABB
float volumeAABB = m_nodes[currentNodeID].aabb.getVolume(); float volumeAABB = m_nodes[currentNodeID].aabb.getVolume();
AABB mergedAABBs; AABB mergedAABBs;
mergedAABBs.mergeTwoAABBs(m_nodes[currentNodeID].aabb, newNodeAABB); mergedAABBs.mergeTwoAABBs(m_nodes[currentNodeID].aabb, newNodeAABB);
float mergedVolume = mergedAABBs.getVolume(); float mergedVolume = mergedAABBs.getVolume();
// Compute the cost of making the current node the sibbling of the new node // Compute the cost of making the current node the sibbling of the new node
float costS = float(2.0) * mergedVolume; float costS = float(2.0) * mergedVolume;
// Compute the minimum cost of pushing the new node further down the tree (inheritance cost) // Compute the minimum cost of pushing the new node further down the tree (inheritance cost)
float costI = float(2.0) * (mergedVolume - volumeAABB); float costI = float(2.0) * (mergedVolume - volumeAABB);
// Compute the cost of descending int32_to the left child // Compute the cost of descending int32_to the left child
float costLeft; float costLeft;
AABB currentAndLeftAABB; AABB currentAndLeftAABB;
currentAndLeftAABB.mergeTwoAABBs(newNodeAABB, m_nodes[leftChild].aabb); currentAndLeftAABB.mergeTwoAABBs(newNodeAABB, m_nodes[leftChild].aabb);
if (m_nodes[leftChild].isLeaf()) { // If the left child is a leaf if (m_nodes[leftChild].isLeaf()) { // If the left child is a leaf
costLeft = currentAndLeftAABB.getVolume() + costI; costLeft = currentAndLeftAABB.getVolume() + costI;
} } else {
else {
float leftChildVolume = m_nodes[leftChild].aabb.getVolume(); float leftChildVolume = m_nodes[leftChild].aabb.getVolume();
costLeft = costI + currentAndLeftAABB.getVolume() - leftChildVolume; costLeft = costI + currentAndLeftAABB.getVolume() - leftChildVolume;
} }
// Compute the cost of descending int32_to the right child // Compute the cost of descending int32_to the right child
float costRight; float costRight;
AABB currentAndRightAABB; AABB currentAndRightAABB;
currentAndRightAABB.mergeTwoAABBs(newNodeAABB, m_nodes[rightChild].aabb); currentAndRightAABB.mergeTwoAABBs(newNodeAABB, m_nodes[rightChild].aabb);
if (m_nodes[rightChild].isLeaf()) { // If the right child is a leaf if (m_nodes[rightChild].isLeaf()) { // If the right child is a leaf
costRight = currentAndRightAABB.getVolume() + costI; costRight = currentAndRightAABB.getVolume() + costI;
} } else {
else {
float rightChildVolume = m_nodes[rightChild].aabb.getVolume(); float rightChildVolume = m_nodes[rightChild].aabb.getVolume();
costRight = costI + currentAndRightAABB.getVolume() - rightChildVolume; costRight = costI + currentAndRightAABB.getVolume() - rightChildVolume;
} }
// If the cost of making the current node a sibbling of the new node is smaller than // If the cost of making the current node a sibbling of the new node is smaller than
// the cost of going down int32_to the left or right child // the cost of going down int32_to the left or right child
if (costS < costLeft && costS < costRight) break; if (costS < costLeft && costS < costRight) {
break;
}
// It is cheaper to go down int32_to a child of the current node, choose the best child // It is cheaper to go down int32_to a child of the current node, choose the best child
if (costLeft < costRight) { if (costLeft < costRight) {
currentNodeID = leftChild; currentNodeID = leftChild;
} } else {
else {
currentNodeID = rightChild; currentNodeID = rightChild;
} }
} }
int32_t siblingNode = currentNodeID; int32_t siblingNode = currentNodeID;
// Create a new parent for the new node and the sibling node // Create a new parent for the new node and the sibling node
int32_t oldParentNode = m_nodes[siblingNode].parentID; int32_t oldParentNode = m_nodes[siblingNode].parentID;
int32_t newParentNode = allocateNode(); int32_t newParentNode = allocateNode();
@ -275,120 +240,96 @@ void DynamicAABBTree::insertLeafNode(int32_t _nodeID) {
m_nodes[newParentNode].aabb.mergeTwoAABBs(m_nodes[siblingNode].aabb, newNodeAABB); m_nodes[newParentNode].aabb.mergeTwoAABBs(m_nodes[siblingNode].aabb, newNodeAABB);
m_nodes[newParentNode].height = m_nodes[siblingNode].height + 1; m_nodes[newParentNode].height = m_nodes[siblingNode].height + 1;
assert(m_nodes[newParentNode].height > 0); assert(m_nodes[newParentNode].height > 0);
// If the sibling node was not the root node // If the sibling node was not the root node
if (oldParentNode != TreeNode::NULL_TREE_NODE) { if (oldParentNode != TreeNode::NULL_TREE_NODE) {
assert(!m_nodes[oldParentNode].isLeaf()); assert(!m_nodes[oldParentNode].isLeaf());
if (m_nodes[oldParentNode].children[0] == siblingNode) { if (m_nodes[oldParentNode].children[0] == siblingNode) {
m_nodes[oldParentNode].children[0] = newParentNode; m_nodes[oldParentNode].children[0] = newParentNode;
} } else {
else {
m_nodes[oldParentNode].children[1] = newParentNode; m_nodes[oldParentNode].children[1] = newParentNode;
} }
m_nodes[newParentNode].children[0] = siblingNode; m_nodes[newParentNode].children[0] = siblingNode;
m_nodes[newParentNode].children[1] = _nodeID; m_nodes[newParentNode].children[1] = _nodeID;
m_nodes[siblingNode].parentID = newParentNode; m_nodes[siblingNode].parentID = newParentNode;
m_nodes[_nodeID].parentID = newParentNode; m_nodes[_nodeID].parentID = newParentNode;
} } else {
else { // If the sibling node was the root node // If the sibling node was the root node
m_nodes[newParentNode].children[0] = siblingNode; m_nodes[newParentNode].children[0] = siblingNode;
m_nodes[newParentNode].children[1] = _nodeID; m_nodes[newParentNode].children[1] = _nodeID;
m_nodes[siblingNode].parentID = newParentNode; m_nodes[siblingNode].parentID = newParentNode;
m_nodes[_nodeID].parentID = newParentNode; m_nodes[_nodeID].parentID = newParentNode;
m_rootNodeID = newParentNode; m_rootNodeID = newParentNode;
} }
// Move up in the tree to change the AABBs that have changed // Move up in the tree to change the AABBs that have changed
currentNodeID = m_nodes[_nodeID].parentID; currentNodeID = m_nodes[_nodeID].parentID;
assert(!m_nodes[currentNodeID].isLeaf()); assert(!m_nodes[currentNodeID].isLeaf());
while (currentNodeID != TreeNode::NULL_TREE_NODE) { while (currentNodeID != TreeNode::NULL_TREE_NODE) {
// Balance the sub-tree of the current node if it is not balanced // Balance the sub-tree of the current node if it is not balanced
currentNodeID = balanceSubTreeAtNode(currentNodeID); currentNodeID = balanceSubTreeAtNode(currentNodeID);
assert(m_nodes[_nodeID].isLeaf()); assert(m_nodes[_nodeID].isLeaf());
assert(!m_nodes[currentNodeID].isLeaf()); assert(!m_nodes[currentNodeID].isLeaf());
int32_t leftChild = m_nodes[currentNodeID].children[0]; int32_t leftChild = m_nodes[currentNodeID].children[0];
int32_t rightChild = m_nodes[currentNodeID].children[1]; int32_t rightChild = m_nodes[currentNodeID].children[1];
assert(leftChild != TreeNode::NULL_TREE_NODE); assert(leftChild != TreeNode::NULL_TREE_NODE);
assert(rightChild != TreeNode::NULL_TREE_NODE); assert(rightChild != TreeNode::NULL_TREE_NODE);
// Recompute the height of the node in the tree // Recompute the height of the node in the tree
m_nodes[currentNodeID].height = etk::max(m_nodes[leftChild].height, m_nodes[currentNodeID].height = etk::max(m_nodes[leftChild].height,
m_nodes[rightChild].height) + 1; m_nodes[rightChild].height) + 1;
assert(m_nodes[currentNodeID].height > 0); assert(m_nodes[currentNodeID].height > 0);
// Recompute the AABB of the node // Recompute the AABB of the node
m_nodes[currentNodeID].aabb.mergeTwoAABBs(m_nodes[leftChild].aabb, m_nodes[rightChild].aabb); m_nodes[currentNodeID].aabb.mergeTwoAABBs(m_nodes[leftChild].aabb, m_nodes[rightChild].aabb);
currentNodeID = m_nodes[currentNodeID].parentID; currentNodeID = m_nodes[currentNodeID].parentID;
} }
assert(m_nodes[_nodeID].isLeaf()); assert(m_nodes[_nodeID].isLeaf());
} }
// Remove a leaf node from the tree // Remove a leaf node from the tree
void DynamicAABBTree::removeLeafNode(int32_t _nodeID) { void DynamicAABBTree::removeLeafNode(int32_t _nodeID) {
assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes); assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes);
assert(m_nodes[_nodeID].isLeaf()); assert(m_nodes[_nodeID].isLeaf());
// If we are removing the root node (root node is a leaf in this case) // If we are removing the root node (root node is a leaf in this case)
if (m_rootNodeID == _nodeID) { if (m_rootNodeID == _nodeID) {
m_rootNodeID = TreeNode::NULL_TREE_NODE; m_rootNodeID = TreeNode::NULL_TREE_NODE;
return; return;
} }
int32_t parentNodeID = m_nodes[_nodeID].parentID; int32_t parentNodeID = m_nodes[_nodeID].parentID;
int32_t grandParentNodeID = m_nodes[parentNodeID].parentID; int32_t grandParentNodeID = m_nodes[parentNodeID].parentID;
int32_t siblingNodeID; int32_t siblingNodeID;
if (m_nodes[parentNodeID].children[0] == _nodeID) { if (m_nodes[parentNodeID].children[0] == _nodeID) {
siblingNodeID = m_nodes[parentNodeID].children[1]; siblingNodeID = m_nodes[parentNodeID].children[1];
} } else {
else {
siblingNodeID = m_nodes[parentNodeID].children[0]; siblingNodeID = m_nodes[parentNodeID].children[0];
} }
// If the parent of the node to remove is not the root node // If the parent of the node to remove is not the root node
if (grandParentNodeID != TreeNode::NULL_TREE_NODE) { if (grandParentNodeID != TreeNode::NULL_TREE_NODE) {
// Destroy the parent node // Destroy the parent node
if (m_nodes[grandParentNodeID].children[0] == parentNodeID) { if (m_nodes[grandParentNodeID].children[0] == parentNodeID) {
m_nodes[grandParentNodeID].children[0] = siblingNodeID; m_nodes[grandParentNodeID].children[0] = siblingNodeID;
} } else {
else {
assert(m_nodes[grandParentNodeID].children[1] == parentNodeID); assert(m_nodes[grandParentNodeID].children[1] == parentNodeID);
m_nodes[grandParentNodeID].children[1] = siblingNodeID; m_nodes[grandParentNodeID].children[1] = siblingNodeID;
} }
m_nodes[siblingNodeID].parentID = grandParentNodeID; m_nodes[siblingNodeID].parentID = grandParentNodeID;
releaseNode(parentNodeID); releaseNode(parentNodeID);
// Now, we need to recompute the AABBs of the node on the path back to the root // Now, we need to recompute the AABBs of the node on the path back to the root
// and make sure that the tree is still balanced // and make sure that the tree is still balanced
int32_t currentNodeID = grandParentNodeID; int32_t currentNodeID = grandParentNodeID;
while(currentNodeID != TreeNode::NULL_TREE_NODE) { while(currentNodeID != TreeNode::NULL_TREE_NODE) {
// Balance the current sub-tree if necessary // Balance the current sub-tree if necessary
currentNodeID = balanceSubTreeAtNode(currentNodeID); currentNodeID = balanceSubTreeAtNode(currentNodeID);
assert(!m_nodes[currentNodeID].isLeaf()); assert(!m_nodes[currentNodeID].isLeaf());
// Get the two children of the current node // Get the two children of the current node
int32_t leftChildID = m_nodes[currentNodeID].children[0]; int32_t leftChildID = m_nodes[currentNodeID].children[0];
int32_t rightChildID = m_nodes[currentNodeID].children[1]; int32_t rightChildID = m_nodes[currentNodeID].children[1];
// Recompute the AABB and the height of the current node // Recompute the AABB and the height of the current node
m_nodes[currentNodeID].aabb.mergeTwoAABBs(m_nodes[leftChildID].aabb, m_nodes[currentNodeID].aabb.mergeTwoAABBs(m_nodes[leftChildID].aabb,
m_nodes[rightChildID].aabb); m_nodes[rightChildID].aabb);
m_nodes[currentNodeID].height = etk::max(m_nodes[leftChildID].height, m_nodes[currentNodeID].height = etk::max(m_nodes[leftChildID].height,
m_nodes[rightChildID].height) + 1; m_nodes[rightChildID].height) + 1;
assert(m_nodes[currentNodeID].height > 0); assert(m_nodes[currentNodeID].height > 0);
currentNodeID = m_nodes[currentNodeID].parentID; currentNodeID = m_nodes[currentNodeID].parentID;
} }
} } else { // If the parent of the node to remove is the root node
else { // If the parent of the node to remove is the root node
// The sibling node becomes the new root node // The sibling node becomes the new root node
m_rootNodeID = siblingNodeID; m_rootNodeID = siblingNodeID;
m_nodes[siblingNodeID].parentID = TreeNode::NULL_TREE_NODE; m_nodes[siblingNodeID].parentID = TreeNode::NULL_TREE_NODE;
@ -400,18 +341,13 @@ void DynamicAABBTree::removeLeafNode(int32_t _nodeID) {
/// The rotation schemes are described in the book "Introduction to Game Physics /// The rotation schemes are described in the book "Introduction to Game Physics
/// with Box2D" by Ian Parberry. This method returns the new root node ID. /// with Box2D" by Ian Parberry. This method returns the new root node ID.
int32_t DynamicAABBTree::balanceSubTreeAtNode(int32_t _nodeID) { int32_t DynamicAABBTree::balanceSubTreeAtNode(int32_t _nodeID) {
assert(_nodeID != TreeNode::NULL_TREE_NODE); assert(_nodeID != TreeNode::NULL_TREE_NODE);
TreeNode* nodeA = m_nodes + _nodeID; TreeNode* nodeA = m_nodes + _nodeID;
// If the node is a leaf or the height of A's sub-tree is less than 2 // If the node is a leaf or the height of A's sub-tree is less than 2
if (nodeA->isLeaf() || nodeA->height < 2) { if (nodeA->isLeaf() || nodeA->height < 2) {
// Do not perform any rotation // Do not perform any rotation
return _nodeID; return _nodeID;
} }
// Get the two children nodes // Get the two children nodes
int32_t nodeBID = nodeA->children[0]; int32_t nodeBID = nodeA->children[0];
int32_t nodeCID = nodeA->children[1]; int32_t nodeCID = nodeA->children[1];
@ -419,185 +355,147 @@ int32_t DynamicAABBTree::balanceSubTreeAtNode(int32_t _nodeID) {
assert(nodeCID >= 0 && nodeCID < m_numberAllocatedNodes); assert(nodeCID >= 0 && nodeCID < m_numberAllocatedNodes);
TreeNode* nodeB = m_nodes + nodeBID; TreeNode* nodeB = m_nodes + nodeBID;
TreeNode* nodeC = m_nodes + nodeCID; TreeNode* nodeC = m_nodes + nodeCID;
// Compute the factor of the left and right sub-trees // Compute the factor of the left and right sub-trees
int32_t balanceFactor = nodeC->height - nodeB->height; int32_t balanceFactor = nodeC->height - nodeB->height;
// If the right node C is 2 higher than left node B // If the right node C is 2 higher than left node B
if (balanceFactor > 1) { if (balanceFactor > 1) {
assert(!nodeC->isLeaf()); assert(!nodeC->isLeaf());
int32_t nodeFID = nodeC->children[0]; int32_t nodeFID = nodeC->children[0];
int32_t nodeGID = nodeC->children[1]; int32_t nodeGID = nodeC->children[1];
assert(nodeFID >= 0 && nodeFID < m_numberAllocatedNodes); assert(nodeFID >= 0 && nodeFID < m_numberAllocatedNodes);
assert(nodeGID >= 0 && nodeGID < m_numberAllocatedNodes); assert(nodeGID >= 0 && nodeGID < m_numberAllocatedNodes);
TreeNode* nodeF = m_nodes + nodeFID; TreeNode* nodeF = m_nodes + nodeFID;
TreeNode* nodeG = m_nodes + nodeGID; TreeNode* nodeG = m_nodes + nodeGID;
nodeC->children[0] = _nodeID; nodeC->children[0] = _nodeID;
nodeC->parentID = nodeA->parentID; nodeC->parentID = nodeA->parentID;
nodeA->parentID = nodeCID; nodeA->parentID = nodeCID;
if (nodeC->parentID != TreeNode::NULL_TREE_NODE) { if (nodeC->parentID != TreeNode::NULL_TREE_NODE) {
if (m_nodes[nodeC->parentID].children[0] == _nodeID) { if (m_nodes[nodeC->parentID].children[0] == _nodeID) {
m_nodes[nodeC->parentID].children[0] = nodeCID; m_nodes[nodeC->parentID].children[0] = nodeCID;
} } else {
else {
assert(m_nodes[nodeC->parentID].children[1] == _nodeID); assert(m_nodes[nodeC->parentID].children[1] == _nodeID);
m_nodes[nodeC->parentID].children[1] = nodeCID; m_nodes[nodeC->parentID].children[1] = nodeCID;
} }
} } else {
else {
m_rootNodeID = nodeCID; m_rootNodeID = nodeCID;
} }
assert(!nodeC->isLeaf()); assert(!nodeC->isLeaf());
assert(!nodeA->isLeaf()); assert(!nodeA->isLeaf());
// If the right node C was higher than left node B because of the F node // If the right node C was higher than left node B because of the F node
if (nodeF->height > nodeG->height) { if (nodeF->height > nodeG->height) {
nodeC->children[1] = nodeFID; nodeC->children[1] = nodeFID;
nodeA->children[1] = nodeGID; nodeA->children[1] = nodeGID;
nodeG->parentID = _nodeID; nodeG->parentID = _nodeID;
// Recompute the AABB of node A and C // Recompute the AABB of node A and C
nodeA->aabb.mergeTwoAABBs(nodeB->aabb, nodeG->aabb); nodeA->aabb.mergeTwoAABBs(nodeB->aabb, nodeG->aabb);
nodeC->aabb.mergeTwoAABBs(nodeA->aabb, nodeF->aabb); nodeC->aabb.mergeTwoAABBs(nodeA->aabb, nodeF->aabb);
// Recompute the height of node A and C // Recompute the height of node A and C
nodeA->height = etk::max(nodeB->height, nodeG->height) + 1; nodeA->height = etk::max(nodeB->height, nodeG->height) + 1;
nodeC->height = etk::max(nodeA->height, nodeF->height) + 1; nodeC->height = etk::max(nodeA->height, nodeF->height) + 1;
assert(nodeA->height > 0); assert(nodeA->height > 0);
assert(nodeC->height > 0); assert(nodeC->height > 0);
} } else {
else { // If the right node C was higher than left node B because of node G // If the right node C was higher than left node B because of node G
nodeC->children[1] = nodeGID; nodeC->children[1] = nodeGID;
nodeA->children[1] = nodeFID; nodeA->children[1] = nodeFID;
nodeF->parentID = _nodeID; nodeF->parentID = _nodeID;
// Recompute the AABB of node A and C // Recompute the AABB of node A and C
nodeA->aabb.mergeTwoAABBs(nodeB->aabb, nodeF->aabb); nodeA->aabb.mergeTwoAABBs(nodeB->aabb, nodeF->aabb);
nodeC->aabb.mergeTwoAABBs(nodeA->aabb, nodeG->aabb); nodeC->aabb.mergeTwoAABBs(nodeA->aabb, nodeG->aabb);
// Recompute the height of node A and C // Recompute the height of node A and C
nodeA->height = etk::max(nodeB->height, nodeF->height) + 1; nodeA->height = etk::max(nodeB->height, nodeF->height) + 1;
nodeC->height = etk::max(nodeA->height, nodeG->height) + 1; nodeC->height = etk::max(nodeA->height, nodeG->height) + 1;
assert(nodeA->height > 0); assert(nodeA->height > 0);
assert(nodeC->height > 0); assert(nodeC->height > 0);
} }
// Return the new root of the sub-tree // Return the new root of the sub-tree
return nodeCID; return nodeCID;
} }
// If the left node B is 2 higher than right node C // If the left node B is 2 higher than right node C
if (balanceFactor < -1) { if (balanceFactor < -1) {
assert(!nodeB->isLeaf()); assert(!nodeB->isLeaf());
int32_t nodeFID = nodeB->children[0]; int32_t nodeFID = nodeB->children[0];
int32_t nodeGID = nodeB->children[1]; int32_t nodeGID = nodeB->children[1];
assert(nodeFID >= 0 && nodeFID < m_numberAllocatedNodes); assert(nodeFID >= 0 && nodeFID < m_numberAllocatedNodes);
assert(nodeGID >= 0 && nodeGID < m_numberAllocatedNodes); assert(nodeGID >= 0 && nodeGID < m_numberAllocatedNodes);
TreeNode* nodeF = m_nodes + nodeFID; TreeNode* nodeF = m_nodes + nodeFID;
TreeNode* nodeG = m_nodes + nodeGID; TreeNode* nodeG = m_nodes + nodeGID;
nodeB->children[0] = _nodeID; nodeB->children[0] = _nodeID;
nodeB->parentID = nodeA->parentID; nodeB->parentID = nodeA->parentID;
nodeA->parentID = nodeBID; nodeA->parentID = nodeBID;
if (nodeB->parentID != TreeNode::NULL_TREE_NODE) { if (nodeB->parentID != TreeNode::NULL_TREE_NODE) {
if (m_nodes[nodeB->parentID].children[0] == _nodeID) { if (m_nodes[nodeB->parentID].children[0] == _nodeID) {
m_nodes[nodeB->parentID].children[0] = nodeBID; m_nodes[nodeB->parentID].children[0] = nodeBID;
} } else {
else {
assert(m_nodes[nodeB->parentID].children[1] == _nodeID); assert(m_nodes[nodeB->parentID].children[1] == _nodeID);
m_nodes[nodeB->parentID].children[1] = nodeBID; m_nodes[nodeB->parentID].children[1] = nodeBID;
} }
} } else {
else {
m_rootNodeID = nodeBID; m_rootNodeID = nodeBID;
} }
assert(!nodeB->isLeaf()); assert(!nodeB->isLeaf());
assert(!nodeA->isLeaf()); assert(!nodeA->isLeaf());
// If the left node B was higher than right node C because of the F node // If the left node B was higher than right node C because of the F node
if (nodeF->height > nodeG->height) { if (nodeF->height > nodeG->height) {
nodeB->children[1] = nodeFID; nodeB->children[1] = nodeFID;
nodeA->children[0] = nodeGID; nodeA->children[0] = nodeGID;
nodeG->parentID = _nodeID; nodeG->parentID = _nodeID;
// Recompute the AABB of node A and B // Recompute the AABB of node A and B
nodeA->aabb.mergeTwoAABBs(nodeC->aabb, nodeG->aabb); nodeA->aabb.mergeTwoAABBs(nodeC->aabb, nodeG->aabb);
nodeB->aabb.mergeTwoAABBs(nodeA->aabb, nodeF->aabb); nodeB->aabb.mergeTwoAABBs(nodeA->aabb, nodeF->aabb);
// Recompute the height of node A and B // Recompute the height of node A and B
nodeA->height = etk::max(nodeC->height, nodeG->height) + 1; nodeA->height = etk::max(nodeC->height, nodeG->height) + 1;
nodeB->height = etk::max(nodeA->height, nodeF->height) + 1; nodeB->height = etk::max(nodeA->height, nodeF->height) + 1;
assert(nodeA->height > 0); assert(nodeA->height > 0);
assert(nodeB->height > 0); assert(nodeB->height > 0);
} } else {
else { // If the left node B was higher than right node C because of node G // If the left node B was higher than right node C because of node G
nodeB->children[1] = nodeGID; nodeB->children[1] = nodeGID;
nodeA->children[0] = nodeFID; nodeA->children[0] = nodeFID;
nodeF->parentID = _nodeID; nodeF->parentID = _nodeID;
// Recompute the AABB of node A and B // Recompute the AABB of node A and B
nodeA->aabb.mergeTwoAABBs(nodeC->aabb, nodeF->aabb); nodeA->aabb.mergeTwoAABBs(nodeC->aabb, nodeF->aabb);
nodeB->aabb.mergeTwoAABBs(nodeA->aabb, nodeG->aabb); nodeB->aabb.mergeTwoAABBs(nodeA->aabb, nodeG->aabb);
// Recompute the height of node A and B // Recompute the height of node A and B
nodeA->height = etk::max(nodeC->height, nodeF->height) + 1; nodeA->height = etk::max(nodeC->height, nodeF->height) + 1;
nodeB->height = etk::max(nodeA->height, nodeG->height) + 1; nodeB->height = etk::max(nodeA->height, nodeG->height) + 1;
assert(nodeA->height > 0); assert(nodeA->height > 0);
assert(nodeB->height > 0); assert(nodeB->height > 0);
} }
// Return the new root of the sub-tree // Return the new root of the sub-tree
return nodeBID; return nodeBID;
} }
// If the sub-tree is balanced, return the current root node // If the sub-tree is balanced, return the current root node
return _nodeID; return _nodeID;
} }
/// Report all shapes overlapping with the AABB given in parameter. /// Report all shapes overlapping with the AABB given in parameter.
void DynamicAABBTree::reportAllShapesOverlappingWithAABB(const AABB& aabb, void DynamicAABBTree::reportAllShapesOverlappingWithAABB(const AABB& _aabb, etk::Function<void(int32_t nodeId)> _callback) const {
DynamicAABBTreeOverlapCallback& callback) const { if (_callback == null) {
EPHY_ERROR("call with null callback");
return;
}
// Create a stack with the nodes to visit // Create a stack with the nodes to visit
Stack<int32_t, 64> stack; Stack<int32_t, 64> stack;
stack.push(m_rootNodeID); stack.push(m_rootNodeID);
// While there are still nodes to visit // While there are still nodes to visit
while(stack.getNbElements() > 0) { while(stack.getNbElements() > 0) {
// Get the next node ID to visit // Get the next node ID to visit
int32_t nodeIDToVisit = stack.pop(); int32_t nodeIDToVisit = stack.pop();
// Skip it if it is a null node // Skip it if it is a null node
if (nodeIDToVisit == TreeNode::NULL_TREE_NODE) continue; if (nodeIDToVisit == TreeNode::NULL_TREE_NODE) {
continue;
}
// Get the corresponding node // Get the corresponding node
const TreeNode* nodeToVisit = m_nodes + nodeIDToVisit; const TreeNode* nodeToVisit = m_nodes + nodeIDToVisit;
// If the AABB in parameter overlaps with the AABB of the node to visit // If the AABB in parameter overlaps with the AABB of the node to visit
if (aabb.testCollision(nodeToVisit->aabb)) { if (_aabb.testCollision(nodeToVisit->aabb)) {
// If the node is a leaf // If the node is a leaf
if (nodeToVisit->isLeaf()) { if (nodeToVisit->isLeaf()) {
// Notify the broad-phase about a new potential overlapping pair // Notify the broad-phase about a new potential overlapping pair
callback.notifyOverlappingNode(nodeIDToVisit); _callback(nodeIDToVisit);
} } else {
else { // If the node is not a leaf // If the node is not a leaf
// We need to visit its children // We need to visit its children
stack.push(nodeToVisit->children[0]); stack.push(nodeToVisit->children[0]);
stack.push(nodeToVisit->children[1]); stack.push(nodeToVisit->children[1]);
@ -607,60 +505,51 @@ void DynamicAABBTree::reportAllShapesOverlappingWithAABB(const AABB& aabb,
} }
// Ray casting method // Ray casting method
void DynamicAABBTree::raycast(const Ray& ray, DynamicAABBTreeRaycastCallback &callback) const { void DynamicAABBTree::raycast(const ephysics::Ray& _ray, etk::Function<float(int32_t _nodeId, const ephysics::Ray& _ray)> _callback) const {
PROFILE("DynamicAABBTree::raycast()"); PROFILE("DynamicAABBTree::raycast()");
if (_callback == null) {
float maxFraction = ray.maxFraction; EPHY_ERROR("call with null callback");
return;
}
float maxFraction = _ray.maxFraction;
Stack<int32_t, 128> stack; Stack<int32_t, 128> stack;
stack.push(m_rootNodeID); stack.push(m_rootNodeID);
// Walk through the tree from the root looking for proxy shapes // Walk through the tree from the root looking for proxy shapes
// that overlap with the ray AABB // that overlap with the ray AABB
while (stack.getNbElements() > 0) { while (stack.getNbElements() > 0) {
// Get the next node in the stack // Get the next node in the stack
int32_t nodeID = stack.pop(); int32_t nodeID = stack.pop();
// If it is a null node, skip it // If it is a null node, skip it
if (nodeID == TreeNode::NULL_TREE_NODE) continue; if (nodeID == TreeNode::NULL_TREE_NODE) {
continue;
}
// Get the corresponding node // Get the corresponding node
const TreeNode* node = m_nodes + nodeID; const TreeNode* node = m_nodes + nodeID;
Ray rayTemp(_ray.point1, _ray.point2, maxFraction);
Ray rayTemp(ray.point1, ray.point2, maxFraction);
// Test if the ray int32_tersects with the current node AABB // Test if the ray int32_tersects with the current node AABB
if (!node->aabb.testRayIntersect(rayTemp)) continue; if (node->aabb.testRayIntersect(rayTemp) == false) {
continue;
}
// If the node is a leaf of the tree // If the node is a leaf of the tree
if (node->isLeaf()) { if (node->isLeaf()) {
// Call the callback that will raycast again the broad-phase shape // Call the callback that will raycast again the broad-phase shape
float hitFraction = callback.raycastBroadPhaseShape(nodeID, rayTemp); float hitFraction = _callback(nodeID, rayTemp);
// If the user returned a hitFraction of zero, it means that // If the user returned a hitFraction of zero, it means that
// the raycasting should stop here // the raycasting should stop here
if (hitFraction == 0.0f) { if (hitFraction == 0.0f) {
return; return;
} }
// If the user returned a positive fraction // If the user returned a positive fraction
if (hitFraction > 0.0f) { if (hitFraction > 0.0f) {
// We update the maxFraction value and the ray // We update the maxFraction value and the ray
// AABB using the new maximum fraction // AABB using the new maximum fraction
if (hitFraction < maxFraction) { if (hitFraction < maxFraction) {
maxFraction = hitFraction; maxFraction = hitFraction;
} }
} }
// If the user returned a negative fraction, we continue // If the user returned a negative fraction, we continue
// the raycasting as if the proxy shape did not exist // the raycasting as if the proxy shape did not exist
} } else { // If the node has children
else { // If the node has children
// Push its children in the stack of nodes to explore // Push its children in the stack of nodes to explore
stack.push(node->children[0]); stack.push(node->children[0]);
stack.push(node->children[1]); stack.push(node->children[1]);
@ -701,95 +590,75 @@ AABB DynamicAABBTree::getRootAABB() const {
// Add an object int32_to the tree. This method creates a new leaf node in the tree and // Add an object int32_to the tree. This method creates a new leaf node in the tree and
// returns the ID of the corresponding node. // returns the ID of the corresponding node.
int32_t DynamicAABBTree::addObject(const AABB& aabb, int32_t data1, int32_t data2) { int32_t DynamicAABBTree::addObject(const AABB& aabb, int32_t data1, int32_t data2) {
int32_t nodeId = addObjectInternal(aabb); int32_t nodeId = addObjectInternal(aabb);
m_nodes[nodeId].dataInt[0] = data1; m_nodes[nodeId].dataInt[0] = data1;
m_nodes[nodeId].dataInt[1] = data2; m_nodes[nodeId].dataInt[1] = data2;
return nodeId; return nodeId;
} }
// Add an object int32_to the tree. This method creates a new leaf node in the tree and // Add an object int32_to the tree. This method creates a new leaf node in the tree and
// returns the ID of the corresponding node. // returns the ID of the corresponding node.
int32_t DynamicAABBTree::addObject(const AABB& aabb, void* data) { int32_t DynamicAABBTree::addObject(const AABB& aabb, void* data) {
int32_t nodeId = addObjectInternal(aabb); int32_t nodeId = addObjectInternal(aabb);
m_nodes[nodeId].dataPointer = data; m_nodes[nodeId].dataPointer = data;
return nodeId; return nodeId;
} }
#ifndef NDEBUG #ifdef DEBUG
// Check if the tree structure is valid (for debugging purpose) // Check if the tree structure is valid (for debugging purpose)
void DynamicAABBTree::check() const { void DynamicAABBTree::check() const {
// Recursively check each node // Recursively check each node
checkNode(m_rootNodeID); checkNode(m_rootNodeID);
int32_t nbFreeNodes = 0; int32_t nbFreeNodes = 0;
int32_t freeNodeID = m_freeNodeID; int32_t freeNodeID = m_freeNodeID;
// Check the free nodes // Check the free nodes
while(freeNodeID != TreeNode::NULL_TREE_NODE) { while(freeNodeID != TreeNode::NULL_TREE_NODE) {
assert(0 <= freeNodeID && freeNodeID < m_numberAllocatedNodes); assert(0 <= freeNodeID && freeNodeID < m_numberAllocatedNodes);
freeNodeID = m_nodes[freeNodeID].nextNodeID; freeNodeID = m_nodes[freeNodeID].nextNodeID;
nbFreeNodes++; nbFreeNodes++;
} }
assert(m_numberNodes + nbFreeNodes == m_numberAllocatedNodes); assert(m_numberNodes + nbFreeNodes == m_numberAllocatedNodes);
} }
// Check if the node structure is valid (for debugging purpose) // Check if the node structure is valid (for debugging purpose)
void DynamicAABBTree::checkNode(int32_t _nodeID) const { void DynamicAABBTree::checkNode(int32_t _nodeID) const {
if (_nodeID == TreeNode::NULL_TREE_NODE) {
if (_nodeID == TreeNode::NULL_TREE_NODE) return; return;
}
// If it is the root // If it is the root
if (_nodeID == m_rootNodeID) { if (_nodeID == m_rootNodeID) {
assert(m_nodes[_nodeID].parentID == TreeNode::NULL_TREE_NODE); assert(m_nodes[_nodeID].parentID == TreeNode::NULL_TREE_NODE);
} }
// Get the children nodes // Get the children nodes
TreeNode* pNode = m_nodes + _nodeID; TreeNode* pNode = m_nodes + _nodeID;
assert(!pNode->isLeaf()); assert(!pNode->isLeaf());
int32_t leftChild = pNode->children[0]; int32_t leftChild = pNode->children[0];
int32_t rightChild = pNode->children[1]; int32_t rightChild = pNode->children[1];
assert(pNode->height >= 0); assert(pNode->height >= 0);
assert(pNode->aabb.getVolume() > 0); assert(pNode->aabb.getVolume() > 0);
// If the current node is a leaf // If the current node is a leaf
if (pNode->isLeaf()) { if (pNode->isLeaf()) {
// Check that there are no children // Check that there are no children
assert(leftChild == TreeNode::NULL_TREE_NODE); assert(leftChild == TreeNode::NULL_TREE_NODE);
assert(rightChild == TreeNode::NULL_TREE_NODE); assert(rightChild == TreeNode::NULL_TREE_NODE);
assert(pNode->height == 0); assert(pNode->height == 0);
} } else {
else {
// Check that the children node IDs are valid // Check that the children node IDs are valid
assert(0 <= leftChild && leftChild < m_numberAllocatedNodes); assert(0 <= leftChild && leftChild < m_numberAllocatedNodes);
assert(0 <= rightChild && rightChild < m_numberAllocatedNodes); assert(0 <= rightChild && rightChild < m_numberAllocatedNodes);
// Check that the children nodes have the correct parent node // Check that the children nodes have the correct parent node
assert(m_nodes[leftChild].parentID == _nodeID); assert(m_nodes[leftChild].parentID == _nodeID);
assert(m_nodes[rightChild].parentID == _nodeID); assert(m_nodes[rightChild].parentID == _nodeID);
// Check the height of node // Check the height of node
int32_t height = 1 + etk::max(m_nodes[leftChild].height, m_nodes[rightChild].height); int32_t height = 1 + etk::max(m_nodes[leftChild].height, m_nodes[rightChild].height);
assert(m_nodes[_nodeID].height == height); assert(m_nodes[_nodeID].height == height);
// Check the AABB of the node // Check the AABB of the node
AABB aabb; AABB aabb;
aabb.mergeTwoAABBs(m_nodes[leftChild].aabb, m_nodes[rightChild].aabb); aabb.mergeTwoAABBs(m_nodes[leftChild].aabb, m_nodes[rightChild].aabb);
assert(aabb.getMin() == m_nodes[_nodeID].aabb.getMin()); assert(aabb.getMin() == m_nodes[_nodeID].aabb.getMin());
assert(aabb.getMax() == m_nodes[_nodeID].aabb.getMax()); assert(aabb.getMax() == m_nodes[_nodeID].aabb.getMax());
// Recursively check the children nodes // Recursively check the children nodes
checkNode(leftChild); checkNode(leftChild);
checkNode(rightChild); checkNode(rightChild);
@ -798,23 +667,20 @@ void DynamicAABBTree::checkNode(int32_t _nodeID) const {
// Compute the height of the tree // Compute the height of the tree
int32_t DynamicAABBTree::computeHeight() { int32_t DynamicAABBTree::computeHeight() {
return computeHeight(m_rootNodeID); return computeHeight(m_rootNodeID);
} }
// Compute the height of a given node in the tree // Compute the height of a given node in the tree
int32_t DynamicAABBTree::computeHeight(int32_t _nodeID) { int32_t DynamicAABBTree::computeHeight(int32_t _nodeID) {
assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes); assert(_nodeID >= 0 && _nodeID < m_numberAllocatedNodes);
TreeNode* node = m_nodes + _nodeID; TreeNode* node = m_nodes + _nodeID;
// If the node is a leaf, its height is zero // If the node is a leaf, its height is zero
if (node->isLeaf()) { if (node->isLeaf()) {
return 0; return 0;
} }
// Compute the height of the left and right sub-tree // Compute the height of the left and right sub-tree
int32_t leftHeight = computeHeight(node->children[0]); int32_t leftHeight = computeHeight(node->children[0]);
int32_t rightHeight = computeHeight(node->children[1]); int32_t rightHeight = computeHeight(node->children[1]);
// Return the height of the node // Return the height of the node
return 1 + etk::max(leftHeight, rightHeight); return 1 + etk::max(leftHeight, rightHeight);
} }

View File

@ -1,19 +1,20 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
#include <ephysics/collision/shapes/AABB.hpp> #include <ephysics/collision/shapes/AABB.hpp>
#include <ephysics/body/CollisionBody.hpp> #include <ephysics/body/CollisionBody.hpp>
#include <etk/Function.hpp>
namespace ephysics { namespace ephysics {
class BroadPhaseAlgorithm; // TODO: to replace this, create a Tree<T> template (multiple child) or TreeRedBlack<T>
class BroadPhaseRaycastTestCallback;
class DynamicAABBTreeOverlapCallback;
struct RaycastTest;
/** /**
* @brief It represents a node of the dynamic AABB tree. * @brief It represents a node of the dynamic AABB tree.
*/ */
@ -43,29 +44,6 @@ namespace ephysics {
bool isLeaf() const; bool isLeaf() const;
}; };
/**
* @brief Overlapping callback method that has to be used as parameter of the
* reportAllShapesOverlappingWithNode() method.
*/
class DynamicAABBTreeOverlapCallback {
public :
virtual ~DynamicAABBTreeOverlapCallback() = default;
// Called when a overlapping node has been found during the call to
// DynamicAABBTree:reportAllShapesOverlappingWithAABB()
virtual void notifyOverlappingNode(int32_t nodeId)=0;
};
/**
* @brief Raycast callback in the Dynamic AABB Tree called when the AABB of a leaf
* node is hit by the ray.
*/
class DynamicAABBTreeRaycastCallback {
public:
virtual ~DynamicAABBTreeRaycastCallback() = default;
// Called when the AABB of a leaf node is hit by a ray
virtual float raycastBroadPhaseShape(int32_t nodeId, const Ray& ray)=0;
};
/** /**
* @brief It implements a dynamic AABB tree that is used for broad-phase * @brief It implements a dynamic AABB tree that is used for broad-phase
* collision detection. This data structure is inspired by Nathanael Presson's * collision detection. This data structure is inspired by Nathanael Presson's
@ -123,10 +101,9 @@ namespace ephysics {
/// Return the data pointer of a given leaf node of the tree /// Return the data pointer of a given leaf node of the tree
void* getNodeDataPointer(int32_t _nodeID) const; void* getNodeDataPointer(int32_t _nodeID) const;
/// Report all shapes overlapping with the AABB given in parameter. /// Report all shapes overlapping with the AABB given in parameter.
void reportAllShapesOverlappingWithAABB(const AABB& _aabb, void reportAllShapesOverlappingWithAABB(const AABB& _aabb, etk::Function<void(int32_t _nodeId)> _callback) const;
DynamicAABBTreeOverlapCallback& _callback) const;
/// Ray casting method /// Ray casting method
void raycast(const Ray& _ray, DynamicAABBTreeRaycastCallback& _callback) const; void raycast(const Ray& _ray, etk::Function<float(int32_t _nodeId, const ephysics::Ray& _ray)> _callback) const;
/// Compute the height of the tree /// Compute the height of the tree
int32_t computeHeight(); int32_t computeHeight();
/// Return the root AABB of the tree /// Return the root AABB of the tree

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,14 +1,17 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/shapes/ConcaveShape.hpp> #include <ephysics/collision/shapes/ConcaveShape.hpp>
#include <ephysics/collision/shapes/TriangleShape.hpp> #include <ephysics/collision/shapes/TriangleShape.hpp>
#include <ephysics/collision/narrowphase/ConcaveVsConvexAlgorithm.hpp> #include <ephysics/collision/narrowphase/ConcaveVsConvexAlgorithm.hpp>
#include <ephysics/collision/CollisionDetection.hpp> #include <ephysics/collision/CollisionDetection.hpp>
#include <ephysics/engine/CollisionWorld.hpp> #include <ephysics/engine/CollisionWorld.hpp>
#include <etk/algorithm.hpp>
using namespace ephysics; using namespace ephysics;
@ -16,29 +19,25 @@ ConcaveVsConvexAlgorithm::ConcaveVsConvexAlgorithm() {
} }
ConcaveVsConvexAlgorithm::~ConcaveVsConvexAlgorithm() { void ConcaveVsConvexAlgorithm::testCollision(const CollisionShapeInfo& _shape1Info,
const CollisionShapeInfo& _shape2Info,
} NarrowPhaseCallback* _callback) {
void ConcaveVsConvexAlgorithm::testCollision(const CollisionShapeInfo& shape1Info,
const CollisionShapeInfo& shape2Info,
NarrowPhaseCallback* narrowPhaseCallback) {
ProxyShape* convexProxyShape; ProxyShape* convexProxyShape;
ProxyShape* concaveProxyShape; ProxyShape* concaveProxyShape;
const ConvexShape* convexShape; const ConvexShape* convexShape;
const ConcaveShape* concaveShape; const ConcaveShape* concaveShape;
// Collision shape 1 is convex, collision shape 2 is concave // Collision shape 1 is convex, collision shape 2 is concave
if (shape1Info.collisionShape->isConvex()) { if (_shape1Info.collisionShape->isConvex()) {
convexProxyShape = shape1Info.proxyShape; convexProxyShape = _shape1Info.proxyShape;
convexShape = static_cast<const ConvexShape*>(shape1Info.collisionShape); convexShape = static_cast<const ConvexShape*>(_shape1Info.collisionShape);
concaveProxyShape = shape2Info.proxyShape; concaveProxyShape = _shape2Info.proxyShape;
concaveShape = static_cast<const ConcaveShape*>(shape2Info.collisionShape); concaveShape = static_cast<const ConcaveShape*>(_shape2Info.collisionShape);
} else { } else {
// Collision shape 2 is convex, collision shape 1 is concave // Collision shape 2 is convex, collision shape 1 is concave
convexProxyShape = shape2Info.proxyShape; convexProxyShape = _shape2Info.proxyShape;
convexShape = static_cast<const ConvexShape*>(shape2Info.collisionShape); convexShape = static_cast<const ConvexShape*>(_shape2Info.collisionShape);
concaveProxyShape = shape1Info.proxyShape; concaveProxyShape = _shape1Info.proxyShape;
concaveShape = static_cast<const ConcaveShape*>(shape1Info.collisionShape); concaveShape = static_cast<const ConcaveShape*>(_shape1Info.collisionShape);
} }
// Set the parameters of the callback object // Set the parameters of the callback object
ConvexVsTriangleCallback convexVsTriangleCallback; ConvexVsTriangleCallback convexVsTriangleCallback;
@ -46,7 +45,7 @@ void ConcaveVsConvexAlgorithm::testCollision(const CollisionShapeInfo& shape1Inf
convexVsTriangleCallback.setConvexShape(convexShape); convexVsTriangleCallback.setConvexShape(convexShape);
convexVsTriangleCallback.setConcaveShape(concaveShape); convexVsTriangleCallback.setConcaveShape(concaveShape);
convexVsTriangleCallback.setProxyShapes(convexProxyShape, concaveProxyShape); convexVsTriangleCallback.setProxyShapes(convexProxyShape, concaveProxyShape);
convexVsTriangleCallback.setOverlappingPair(shape1Info.overlappingPair); convexVsTriangleCallback.setOverlappingPair(_shape1Info.overlappingPair);
// Compute the convex shape AABB in the local-space of the convex shape // Compute the convex shape AABB in the local-space of the convex shape
AABB aabb; AABB aabb;
convexShape->computeAABB(aabb, convexProxyShape->getLocalToWorldTransform()); convexShape->computeAABB(aabb, convexProxyShape->getLocalToWorldTransform());
@ -58,51 +57,55 @@ void ConcaveVsConvexAlgorithm::testCollision(const CollisionShapeInfo& shape1Inf
// Call the convex vs triangle callback for each triangle of the concave shape // Call the convex vs triangle callback for each triangle of the concave shape
concaveShape->testAllTriangles(convexVsTriangleCallback, aabb); concaveShape->testAllTriangles(convexVsTriangleCallback, aabb);
// Run the smooth mesh collision algorithm // Run the smooth mesh collision algorithm
processSmoothMeshCollision(shape1Info.overlappingPair, contactPoints, narrowPhaseCallback); processSmoothMeshCollision(_shape1Info.overlappingPair, contactPoints, _callback);
} else { } else {
convexVsTriangleCallback.setNarrowPhaseCallback(narrowPhaseCallback); convexVsTriangleCallback.setNarrowPhaseCallback(_callback);
// Call the convex vs triangle callback for each triangle of the concave shape // Call the convex vs triangle callback for each triangle of the concave shape
concaveShape->testAllTriangles(convexVsTriangleCallback, aabb); concaveShape->testAllTriangles(convexVsTriangleCallback, aabb);
} }
} }
void ConvexVsTriangleCallback::testTriangle(const vec3* trianglePoints) { void ConvexVsTriangleCallback::testTriangle(const vec3* _trianglePoints) {
// Create a triangle collision shape // Create a triangle collision shape
float margin = m_concaveShape->getTriangleMargin(); float margin = m_concaveShape->getTriangleMargin();
TriangleShape triangleShape(trianglePoints[0], trianglePoints[1], trianglePoints[2], margin); TriangleShape triangleShape(_trianglePoints[0], _trianglePoints[1], _trianglePoints[2], margin);
// Select the collision algorithm to use between the triangle and the convex shape // Select the collision algorithm to use between the triangle and the convex shape
NarrowPhaseAlgorithm* algo = m_collisionDetection->getCollisionAlgorithm(triangleShape.getType(), NarrowPhaseAlgorithm* algo = m_collisionDetection->getCollisionAlgorithm(triangleShape.getType(), m_convexShape->getType());
m_convexShape->getType());
// If there is no collision algorithm between those two kinds of shapes // If there is no collision algorithm between those two kinds of shapes
if (algo == nullptr) { if (algo == null) {
return; return;
} }
// Notify the narrow-phase algorithm about the overlapping pair we are going to test // Notify the narrow-phase algorithm about the overlapping pair we are going to test
algo->setCurrentOverlappingPair(m_overlappingPair); algo->setCurrentOverlappingPair(m_overlappingPair);
// Create the CollisionShapeInfo objects // Create the CollisionShapeInfo objects
CollisionShapeInfo shapeConvexInfo(m_convexProxyShape, m_convexShape, m_convexProxyShape->getLocalToWorldTransform(), CollisionShapeInfo shapeConvexInfo(m_convexProxyShape,
m_overlappingPair, m_convexProxyShape->getCachedCollisionData()); m_convexShape,
CollisionShapeInfo shapeConcaveInfo(m_concaveProxyShape, &triangleShape, m_convexProxyShape->getLocalToWorldTransform(),
m_concaveProxyShape->getLocalToWorldTransform(), m_overlappingPair,
m_overlappingPair, m_concaveProxyShape->getCachedCollisionData()); m_convexProxyShape->getCachedCollisionData());
CollisionShapeInfo shapeConcaveInfo(m_concaveProxyShape,
&triangleShape,
m_concaveProxyShape->getLocalToWorldTransform(),
m_overlappingPair,
m_concaveProxyShape->getCachedCollisionData());
// Use the collision algorithm to test collision between the triangle and the other convex shape // Use the collision algorithm to test collision between the triangle and the other convex shape
algo->testCollision(shapeConvexInfo, shapeConcaveInfo, m_narrowPhaseCallback); algo->testCollision(shapeConvexInfo, shapeConcaveInfo, m_narrowPhaseCallback);
} }
void ConcaveVsConvexAlgorithm::processSmoothMeshCollision(OverlappingPair* overlappingPair, static bool sortFunction(const SmoothMeshContactInfo& _contact1, const SmoothMeshContactInfo& _contact2) {
etk::Vector<SmoothMeshContactInfo> contactPoints, return _contact1.contactInfo.penetrationDepth <= _contact2.contactInfo.penetrationDepth;
NarrowPhaseCallback* narrowPhaseCallback) { }
void ConcaveVsConvexAlgorithm::processSmoothMeshCollision(OverlappingPair* _overlappingPair,
etk::Vector<SmoothMeshContactInfo> _contactPoints,
NarrowPhaseCallback* _callback) {
// Set with the triangle vertices already processed to void further contacts with same triangle // Set with the triangle vertices already processed to void further contacts with same triangle
etk::Vector<etk::Pair<int32_t, vec3>> processTriangleVertices; etk::Vector<etk::Pair<int32_t, vec3>> processTriangleVertices;
// Sort the list of narrow-phase contacts according to their penetration depth // Sort the list of narrow-phase contacts according to their penetration depth
contactPoints.sort(0, etk::algorithm::quickSort(_contactPoints, sortFunction);
contactPoints.size()-1,
[](const SmoothMeshContactInfo& _contact1, const SmoothMeshContactInfo& _contact2) {
return _contact1.contactInfo.penetrationDepth < _contact2.contactInfo.penetrationDepth;
});
// For each contact point (from smaller penetration depth to larger) // For each contact point (from smaller penetration depth to larger)
etk::Vector<SmoothMeshContactInfo>::Iterator it; etk::Vector<SmoothMeshContactInfo>::Iterator it;
for (it = contactPoints.begin(); it != contactPoints.end(); ++it) { for (it = _contactPoints.begin(); it != _contactPoints.end(); ++it) {
const SmoothMeshContactInfo info = *it; const SmoothMeshContactInfo info = *it;
const vec3& contactPoint = info.isFirstShapeTriangle ? info.contactInfo.localPoint1 : info.contactInfo.localPoint2; const vec3& contactPoint = info.isFirstShapeTriangle ? info.contactInfo.localPoint1 : info.contactInfo.localPoint2;
// Compute the barycentric coordinates of the point in the triangle // Compute the barycentric coordinates of the point in the triangle
@ -130,7 +133,7 @@ void ConcaveVsConvexAlgorithm::processSmoothMeshCollision(OverlappingPair* overl
// Check that this triangle vertex has not been processed yet // Check that this triangle vertex has not been processed yet
if (!hasVertexBeenProcessed(processTriangleVertices, contactVertex)) { if (!hasVertexBeenProcessed(processTriangleVertices, contactVertex)) {
// Keep the contact as it is and report it // Keep the contact as it is and report it
narrowPhaseCallback->notifyContact(overlappingPair, info.contactInfo); _callback->notifyContact(_overlappingPair, info.contactInfo);
} }
} else if (nbZeros == 1) { } else if (nbZeros == 1) {
// If it is an edge contact // If it is an edge contact
@ -140,7 +143,7 @@ void ConcaveVsConvexAlgorithm::processSmoothMeshCollision(OverlappingPair* overl
if (!hasVertexBeenProcessed(processTriangleVertices, contactVertex1) && if (!hasVertexBeenProcessed(processTriangleVertices, contactVertex1) &&
!hasVertexBeenProcessed(processTriangleVertices, contactVertex2)) { !hasVertexBeenProcessed(processTriangleVertices, contactVertex2)) {
// Keep the contact as it is and report it // Keep the contact as it is and report it
narrowPhaseCallback->notifyContact(overlappingPair, info.contactInfo); _callback->notifyContact(_overlappingPair, info.contactInfo);
} }
} else { } else {
// If it is a face contact // If it is a face contact
@ -148,11 +151,11 @@ void ConcaveVsConvexAlgorithm::processSmoothMeshCollision(OverlappingPair* overl
ProxyShape* firstShape; ProxyShape* firstShape;
ProxyShape* secondShape; ProxyShape* secondShape;
if (info.isFirstShapeTriangle) { if (info.isFirstShapeTriangle) {
firstShape = overlappingPair->getShape1(); firstShape = _overlappingPair->getShape1();
secondShape = overlappingPair->getShape2(); secondShape = _overlappingPair->getShape2();
} else { } else {
firstShape = overlappingPair->getShape2(); firstShape = _overlappingPair->getShape2();
secondShape = overlappingPair->getShape1(); secondShape = _overlappingPair->getShape1();
} }
// We use the triangle normal as the contact normal // We use the triangle normal as the contact normal
vec3 a = info.triangleVertices[1] - info.triangleVertices[0]; vec3 a = info.triangleVertices[1] - info.triangleVertices[0];
@ -177,7 +180,7 @@ void ConcaveVsConvexAlgorithm::processSmoothMeshCollision(OverlappingPair* overl
newContactInfo.localPoint1 = worldToLocalSecondPoint * newSecondWorldPoint; newContactInfo.localPoint1 = worldToLocalSecondPoint * newSecondWorldPoint;
} }
// Report the contact // Report the contact
narrowPhaseCallback->notifyContact(overlappingPair, newContactInfo); _callback->notifyContact(_overlappingPair, newContactInfo);
} }
// Add the three vertices of the triangle to the set of processed // Add the three vertices of the triangle to the set of processed
// triangle vertices // triangle vertices
@ -212,7 +215,7 @@ bool ConcaveVsConvexAlgorithm::hasVertexBeenProcessed(const etk::Vector<etk::Pai
} }
void SmoothCollisionNarrowPhaseCallback::notifyContact(OverlappingPair* _overlappingPair, void SmoothCollisionNarrowPhaseCallback::notifyContact(OverlappingPair* _overlappingPair,
const ContactPointInfo& _contactInfo) { const ContactPointInfo& _contactInfo) {
vec3 triangleVertices[3]; vec3 triangleVertices[3];
bool isFirstShapeTriangle; bool isFirstShapeTriangle;
// If the collision shape 1 is the triangle // If the collision shape 1 is the triangle

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -135,8 +138,6 @@ namespace ephysics {
public : public :
/// Constructor /// Constructor
ConcaveVsConvexAlgorithm(); ConcaveVsConvexAlgorithm();
/// Destructor
virtual ~ConcaveVsConvexAlgorithm();
/// Compute a contact info if the two bounding volume collide /// Compute a contact info if the two bounding volume collide
virtual void testCollision(const CollisionShapeInfo& _shape1Info, virtual void testCollision(const CollisionShapeInfo& _shape1Info,
const CollisionShapeInfo& _shape2Info, const CollisionShapeInfo& _shape2Info,

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries // Libraries
#include <ephysics/collision/narrowphase/DefaultCollisionDispatch.hpp> #include <ephysics/collision/narrowphase/DefaultCollisionDispatch.hpp>
#include <ephysics/collision/shapes/CollisionShape.hpp> #include <ephysics/collision/shapes/CollisionShape.hpp>
@ -40,6 +42,6 @@ NarrowPhaseAlgorithm* DefaultCollisionDispatch::selectAlgorithm(int32_t _type1,
// Convex vs Convex algorithm (GJK algorithm) // Convex vs Convex algorithm (GJK algorithm)
return &m_GJKAlgorithm; return &m_GJKAlgorithm;
} else { } else {
return nullptr; return null;
} }
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/narrowphase/EPA/EPAAlgorithm.hpp> #include <ephysics/collision/narrowphase/EPA/EPAAlgorithm.hpp>
#include <ephysics/engine/Profiler.hpp> #include <ephysics/engine/Profiler.hpp>
@ -18,64 +21,63 @@ EPAAlgorithm::~EPAAlgorithm() {
} }
int32_t EPAAlgorithm::isOriginInTetrahedron(const vec3& p1, const vec3& p2, const vec3& p3, const vec3& p4) const { int32_t EPAAlgorithm::isOriginInTetrahedron(const vec3& _p1, const vec3& _p2, const vec3& _p3, const vec3& _p4) const {
// Check vertex 1 // Check vertex 1
vec3 normal1 = (p2-p1).cross(p3-p1); vec3 normal1 = (_p2-_p1).cross(_p3-_p1);
if ((normal1.dot(p1) > 0.0) == (normal1.dot(p4) > 0.0)) { if ((normal1.dot(_p1) > 0.0) == (normal1.dot(_p4) > 0.0)) {
return 4; return 4;
} }
// Check vertex 2 // Check vertex 2
vec3 normal2 = (p4-p2).cross(p3-p2); vec3 normal2 = (_p4-_p2).cross(_p3-_p2);
if ((normal2.dot(p2) > 0.0) == (normal2.dot(p1) > 0.0)) { if ((normal2.dot(_p2) > 0.0) == (normal2.dot(_p1) > 0.0)) {
return 1; return 1;
} }
// Check vertex 3 // Check vertex 3
vec3 normal3 = (p4-p3).cross(p1-p3); vec3 normal3 = (_p4-_p3).cross(_p1-_p3);
if ((normal3.dot(p3) > 0.0) == (normal3.dot(p2) > 0.0)) { if ((normal3.dot(_p3) > 0.0) == (normal3.dot(_p2) > 0.0)) {
return 2; return 2;
} }
// Check vertex 4 // Check vertex 4
vec3 normal4 = (p2-p4).cross(p1-p4); vec3 normal4 = (_p2-_p4).cross(_p1-_p4);
if ((normal4.dot(p4) > 0.0) == (normal4.dot(p3) > 0.0)) { if ((normal4.dot(_p4) > 0.0) == (normal4.dot(_p3) > 0.0)) {
return 3; return 3;
} }
// The origin is in the tetrahedron, we return 0 // The origin is in the tetrahedron, we return 0
return 0; return 0;
} }
void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simplex, void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& _simplex,
CollisionShapeInfo shape1Info, CollisionShapeInfo _shape1Info,
const etk::Transform3D& transform1, const etk::Transform3D& _transform1,
CollisionShapeInfo shape2Info, CollisionShapeInfo _shape2Info,
const etk::Transform3D& transform2, const etk::Transform3D& _transform2,
vec3& v, vec3& _vector,
NarrowPhaseCallback* narrowPhaseCallback) { NarrowPhaseCallback* narrowPhaseCallback) {
PROFILE("EPAAlgorithm::computePenetrationDepthAndContactPoints()"); PROFILE("EPAAlgorithm::computePenetrationDepthAndContactPoints()");
assert(shape1Info.collisionShape->isConvex()); assert(_shape1Info.collisionShape->isConvex());
assert(shape2Info.collisionShape->isConvex()); assert(_shape2Info.collisionShape->isConvex());
const ConvexShape* shape1 = static_cast<const ConvexShape*>(shape1Info.collisionShape); const ConvexShape* shape1 = static_cast<const ConvexShape*>(_shape1Info.collisionShape);
const ConvexShape* shape2 = static_cast<const ConvexShape*>(shape2Info.collisionShape); const ConvexShape* shape2 = static_cast<const ConvexShape*>(_shape2Info.collisionShape);
void** shape1CachedCollisionData = shape1Info.cachedCollisionData; void** shape1CachedCollisionData = _shape1Info.cachedCollisionData;
void** shape2CachedCollisionData = shape2Info.cachedCollisionData; void** shape2CachedCollisionData = _shape2Info.cachedCollisionData;
vec3 suppPointsA[MAX_SUPPORT_POINTS]; // Support points of object A in local coordinates vec3 suppPointsA[MAX_SUPPORT_POINTS]; // Support points of object A in local coordinates
vec3 suppPointsB[MAX_SUPPORT_POINTS]; // Support points of object B in local coordinates vec3 suppPointsB[MAX_SUPPORT_POINTS]; // Support points of object B in local coordinates
vec3 points[MAX_SUPPORT_POINTS]; // Current points vec3 points[MAX_SUPPORT_POINTS]; // Current points
TrianglesStore triangleStore; // Store the triangles TrianglesStore triangleStore; // Store the triangles
TriangleEPA* triangleHeap[MAX_FACETS]; // Heap that contains the face etk::Set<TriangleEPA*> triangleHeap; // list of face candidate of the EPA algorithm sorted lower square dist to upper square dist
// candidate of the EPA algorithm triangleHeap.setComparator([](TriangleEPA * const & _face1, TriangleEPA * const & _face2) {
return (_face1->getDistSquare() < _face2->getDistSquare());
});
// etk::Transform3D a point from local space of body 2 to local // etk::Transform3D a point from local space of body 2 to local
// space of body 1 (the GJK algorithm is done in local space of body 1) // space of body 1 (the GJK algorithm is done in local space of body 1)
etk::Transform3D body2Tobody1 = transform1.getInverse() * transform2; etk::Transform3D body2Tobody1 = _transform1.getInverse() * _transform2;
// Matrix that transform a direction from local // Matrix that transform a direction from local
// space of body 1 int32_to local space of body 2 // space of body 1 int32_to local space of body 2
etk::Quaternion rotateToBody2 = transform2.getOrientation().getInverse() * etk::Quaternion rotateToBody2 = _transform2.getOrientation().getInverse() * _transform1.getOrientation();
transform1.getOrientation();
// Get the simplex computed previously by the GJK algorithm // Get the simplex computed previously by the GJK algorithm
uint32_t nbVertices = simplex.getSimplex(suppPointsA, suppPointsB, points); uint32_t nbVertices = _simplex.getSimplex(suppPointsA, suppPointsB, points);
// Compute the tolerance // Compute the tolerance
float tolerance = FLT_EPSILON * simplex.getMaxLengthSquareOfAPoint(); float tolerance = FLT_EPSILON * _simplex.getMaxLengthSquareOfAPoint();
// Number of triangles in the polytope
uint32_t nbTriangles = 0;
// Clear the storing of triangles // Clear the storing of triangles
triangleStore.clear(); triangleStore.clear();
// Select an action according to the number of points in the simplex // Select an action according to the number of points in the simplex
@ -177,10 +179,10 @@ void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simple
link(EdgeEPA(face1, 1), EdgeEPA(face3, 0)); link(EdgeEPA(face1, 1), EdgeEPA(face3, 0));
link(EdgeEPA(face2, 1), EdgeEPA(face3, 1)); link(EdgeEPA(face2, 1), EdgeEPA(face3, 1));
// Add the triangle faces in the candidate heap // Add the triangle faces in the candidate heap
addFaceCandidate(face0, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face0, triangleHeap, FLT_MAX);
addFaceCandidate(face1, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face1, triangleHeap, FLT_MAX);
addFaceCandidate(face2, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face2, triangleHeap, FLT_MAX);
addFaceCandidate(face3, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face3, triangleHeap, FLT_MAX);
break; break;
} }
// The tetrahedron contains a wrong vertex (the origin is not inside the tetrahedron) // The tetrahedron contains a wrong vertex (the origin is not inside the tetrahedron)
@ -213,10 +215,10 @@ void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simple
suppPointsB[4] = body2Tobody1 * suppPointsB[4] = body2Tobody1 *
shape2->getLocalSupportPointWithMargin(rotateToBody2 * n, shape2CachedCollisionData); shape2->getLocalSupportPointWithMargin(rotateToBody2 * n, shape2CachedCollisionData);
points[4] = suppPointsA[4] - suppPointsB[4]; points[4] = suppPointsA[4] - suppPointsB[4];
TriangleEPA* face0 = NULL; TriangleEPA* face0 = null;
TriangleEPA* face1 = NULL; TriangleEPA* face1 = null;
TriangleEPA* face2 = NULL; TriangleEPA* face2 = null;
TriangleEPA* face3 = NULL; TriangleEPA* face3 = null;
// If the origin is in the first tetrahedron // If the origin is in the first tetrahedron
if (isOriginInTetrahedron(points[0], points[1], if (isOriginInTetrahedron(points[0], points[1],
points[2], points[3]) == 0) { points[2], points[3]) == 0) {
@ -242,9 +244,14 @@ void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simple
return; return;
} }
// If the constructed tetrahedron is not correct // If the constructed tetrahedron is not correct
if (!((face0 != NULL) && (face1 != NULL) && (face2 != NULL) && (face3 != NULL) if (!( face0 != null
&& face0->getDistSquare() > 0.0 && face1->getDistSquare() > 0.0 && face1 != null
&& face2->getDistSquare() > 0.0 && face3->getDistSquare() > 0.0)) { && face2 != null
&& face3 != null
&& face0->getDistSquare() > 0.0
&& face1->getDistSquare() > 0.0
&& face2->getDistSquare() > 0.0
&& face3->getDistSquare() > 0.0) ) {
return; return;
} }
// Associate the edges of neighbouring triangle faces // Associate the edges of neighbouring triangle faces
@ -255,26 +262,28 @@ void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simple
link(EdgeEPA(face1, 1), EdgeEPA(face3, 0)); link(EdgeEPA(face1, 1), EdgeEPA(face3, 0));
link(EdgeEPA(face2, 1), EdgeEPA(face3, 1)); link(EdgeEPA(face2, 1), EdgeEPA(face3, 1));
// Add the triangle faces in the candidate heap // Add the triangle faces in the candidate heap
addFaceCandidate(face0, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face0, triangleHeap, FLT_MAX);
addFaceCandidate(face1, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face1, triangleHeap, FLT_MAX);
addFaceCandidate(face2, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face2, triangleHeap, FLT_MAX);
addFaceCandidate(face3, triangleHeap, nbTriangles, FLT_MAX); addFaceCandidate(face3, triangleHeap, FLT_MAX);
nbVertices = 4; nbVertices = 4;
} }
break; break;
} }
// At this point, we have a polytope that contains the origin. Therefore, we // At this point, we have a polytope that contains the origin. Therefore, we
// can run the EPA algorithm. // can run the EPA algorithm.
if (nbTriangles == 0) { if (triangleHeap.size() == 0) {
return; return;
} }
TriangleEPA* triangle = 0; TriangleEPA* triangle = 0;
float upperBoundSquarePenDepth = FLT_MAX; float upperBoundSquarePenDepth = FLT_MAX;
do { do {
triangle = triangleHeap[0]; triangle = triangleHeap[0];
// Get the next candidate face (the face closest to the origin) triangleHeap.popFront();
std::pop_heap(&triangleHeap[0], &triangleHeap[nbTriangles], m_triangleComparison); EPHY_INFO("rm from heap:");
nbTriangles--; for (size_t iii=0; iii<triangleHeap.size(); ++iii) {
EPHY_INFO(" [" << iii << "] " << triangleHeap[iii]->getDistSquare());
}
// If the candidate face in the heap is not obsolete // If the candidate face in the heap is not obsolete
if (!triangle->getIsObsolete()) { if (!triangle->getIsObsolete()) {
// If we have reached the maximum number of support points // If we have reached the maximum number of support points
@ -284,17 +293,21 @@ void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simple
} }
// Compute the support point of the Minkowski // Compute the support point of the Minkowski
// difference (A-B) in the closest point direction // difference (A-B) in the closest point direction
suppPointsA[nbVertices] = shape1->getLocalSupportPointWithMargin( suppPointsA[nbVertices] = shape1->getLocalSupportPointWithMargin(triangle->getClosestPoint(), shape1CachedCollisionData);
triangle->getClosestPoint(), shape1CachedCollisionData); suppPointsB[nbVertices] = body2Tobody1 * shape2->getLocalSupportPointWithMargin(rotateToBody2 * (-triangle->getClosestPoint()), shape2CachedCollisionData);
suppPointsB[nbVertices] = body2Tobody1 *
shape2->getLocalSupportPointWithMargin(rotateToBody2 *
(-triangle->getClosestPoint()), shape2CachedCollisionData);
points[nbVertices] = suppPointsA[nbVertices] - suppPointsB[nbVertices]; points[nbVertices] = suppPointsA[nbVertices] - suppPointsB[nbVertices];
int32_t indexNewVertex = nbVertices; int32_t indexNewVertex = nbVertices;
nbVertices++; nbVertices++;
// Update the upper bound of the penetration depth // Update the upper bound of the penetration depth
float wDotv = points[indexNewVertex].dot(triangle->getClosestPoint()); float wDotv = points[indexNewVertex].dot(triangle->getClosestPoint());
assert(wDotv > 0.0); EPHY_INFO(" point=" << points[indexNewVertex]);
EPHY_INFO("close point=" << triangle->getClosestPoint());
EPHY_INFO(" ==>" << wDotv);
if (wDotv < 0.0) {
EPHY_ERROR("depth penetration error " << wDotv);
continue;
}
EPHY_ASSERT(wDotv >= 0.0, "depth penetration error " << wDotv);
float wDotVSquare = wDotv * wDotv / triangle->getDistSquare(); float wDotVSquare = wDotv * wDotv / triangle->getDistSquare();
if (wDotVSquare < upperBoundSquarePenDepth) { if (wDotVSquare < upperBoundSquarePenDepth) {
upperBoundSquarePenDepth = wDotVSquare; upperBoundSquarePenDepth = wDotVSquare;
@ -310,7 +323,7 @@ void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simple
// Now, we compute the silhouette cast by the new vertex. The current triangle // Now, we compute the silhouette cast by the new vertex. The current triangle
// face will not be in the convex hull. We start the local recursive silhouette // face will not be in the convex hull. We start the local recursive silhouette
// algorithm from the current triangle face. // algorithm from the current triangle face.
int32_t i = triangleStore.getNbTriangles(); size_t i = triangleStore.getNbTriangles();
if (!triangle->computeSilhouette(points, indexNewVertex, triangleStore)) { if (!triangle->computeSilhouette(points, indexNewVertex, triangleStore)) {
break; break;
} }
@ -318,22 +331,23 @@ void EPAAlgorithm::computePenetrationDepthAndContactPoints(const Simplex& simple
// to the candidates list of faces of the current polytope // to the candidates list of faces of the current polytope
while(i != triangleStore.getNbTriangles()) { while(i != triangleStore.getNbTriangles()) {
TriangleEPA* newTriangle = &triangleStore[i]; TriangleEPA* newTriangle = &triangleStore[i];
addFaceCandidate(newTriangle, triangleHeap, nbTriangles, upperBoundSquarePenDepth); addFaceCandidate(newTriangle, triangleHeap, upperBoundSquarePenDepth);
i++; i++;
} }
} }
} while(nbTriangles > 0 && triangleHeap[0]->getDistSquare() <= upperBoundSquarePenDepth); } while( triangleHeap.size() > 0
&& triangleHeap[0]->getDistSquare() <= upperBoundSquarePenDepth);
// Compute the contact info // Compute the contact info
v = transform1.getOrientation() * triangle->getClosestPoint(); _vector = _transform1.getOrientation() * triangle->getClosestPoint();
vec3 pALocal = triangle->computeClosestPointOfObject(suppPointsA); vec3 pALocal = triangle->computeClosestPointOfObject(suppPointsA);
vec3 pBLocal = body2Tobody1.getInverse() * triangle->computeClosestPointOfObject(suppPointsB); vec3 pBLocal = body2Tobody1.getInverse() * triangle->computeClosestPointOfObject(suppPointsB);
vec3 normal = v.safeNormalized(); vec3 normal = _vector.safeNormalized();
float penetrationDepth = v.length(); float penetrationDepth = _vector.length();
assert(penetrationDepth > 0.0); EPHY_ASSERT(penetrationDepth >= 0.0, "penetration depth <0");
if (normal.length2() < FLT_EPSILON) { if (normal.length2() < FLT_EPSILON) {
return; return;
} }
// Create the contact info object // Create the contact info object
ContactPointInfo contactInfo(shape1Info.proxyShape, shape2Info.proxyShape, shape1Info.collisionShape, shape2Info.collisionShape, normal, penetrationDepth, pALocal, pBLocal); ContactPointInfo contactInfo(_shape1Info.proxyShape, _shape2Info.proxyShape, _shape1Info.collisionShape, _shape2Info.collisionShape, normal, penetrationDepth, pALocal, pBLocal);
narrowPhaseCallback->notifyContact(shape1Info.overlappingPair, contactInfo); narrowPhaseCallback->notifyContact(_shape1Info.overlappingPair, contactInfo);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
#include <ephysics/collision/narrowphase/GJK/Simplex.hpp> #include <ephysics/collision/narrowphase/GJK/Simplex.hpp>
@ -11,29 +14,14 @@
#include <ephysics/collision/narrowphase/NarrowPhaseAlgorithm.hpp> #include <ephysics/collision/narrowphase/NarrowPhaseAlgorithm.hpp>
#include <ephysics/mathematics/mathematics.hpp> #include <ephysics/mathematics/mathematics.hpp>
#include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp> #include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp>
#include <algorithm> #include <ephysics/debug.hpp>
#include <etk/Set.hpp>
namespace ephysics { namespace ephysics {
/// Maximum number of support points of the polytope /// Maximum number of support points of the polytope
const uint32_t MAX_SUPPORT_POINTS = 100; const uint32_t MAX_SUPPORT_POINTS = 100;
/// Maximum number of facets of the polytope /// Maximum number of facets of the polytope
const uint32_t MAX_FACETS = 200; const uint32_t MAX_FACETS = 200;
/**
* @brief Class TriangleComparison
* This class allows the comparison of two triangles in the heap
* The comparison between two triangles is made using their square distance to the closest
* point to the origin. The goal is that in the heap, the first triangle is the one with the
* smallest square distance.
*/
class TriangleComparison {
public:
/**
* @brief Comparison operator
*/
bool operator()(const TriangleEPA* face1, const TriangleEPA* face2) {
return (face1->getDistSquare() > face2->getDistSquare());
}
};
/** /**
* @brief Class EPAAlgorithm * @brief Class EPAAlgorithm
* This class is the implementation of the Expanding Polytope Algorithm (EPA). * This class is the implementation of the Expanding Polytope Algorithm (EPA).
@ -50,15 +38,13 @@ namespace ephysics {
*/ */
class EPAAlgorithm { class EPAAlgorithm {
private: private:
TriangleComparison m_triangleComparison; //!< Triangle comparison operator
/// Private copy-constructor /// Private copy-constructor
EPAAlgorithm(const EPAAlgorithm& _algorithm); EPAAlgorithm(const EPAAlgorithm& _algorithm);
/// Private assignment operator /// Private assignment operator
EPAAlgorithm& operator=(const EPAAlgorithm& _algorithm); EPAAlgorithm& operator=(const EPAAlgorithm& _algorithm);
/// Add a triangle face in the candidate triangle heap /// Add a triangle face in the candidate triangle heap
void addFaceCandidate(TriangleEPA* _triangle, void addFaceCandidate(TriangleEPA* _triangle,
TriangleEPA** _heap, etk::Set<TriangleEPA*>& _heap,
uint32_t& _nbTriangles,
float _upperBoundSquarePenDepth) { float _upperBoundSquarePenDepth) {
// If the closest point of the affine hull of triangle // If the closest point of the affine hull of triangle
// points is int32_ternal to the triangle and if the distance // points is int32_ternal to the triangle and if the distance
@ -67,9 +53,11 @@ namespace ephysics {
if ( _triangle->isClosestPointInternalToTriangle() if ( _triangle->isClosestPointInternalToTriangle()
&& _triangle->getDistSquare() <= _upperBoundSquarePenDepth) { && _triangle->getDistSquare() <= _upperBoundSquarePenDepth) {
// Add the triangle face to the list of candidates // Add the triangle face to the list of candidates
_heap[_nbTriangles] = _triangle; _heap.add(_triangle);
_nbTriangles++; EPHY_INFO("add in heap:");
std::push_heap(&_heap[0], &_heap[_nbTriangles], m_triangleComparison); for (size_t iii=0; iii<_heap.size(); ++iii) {
EPHY_INFO(" [" << iii << "] " << _heap[iii]->getDistSquare());
}
} }
} }
// Decide if the origin is in the tetrahedron. // Decide if the origin is in the tetrahedron.

View File

@ -1,11 +1,15 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/narrowphase/EPA/EdgeEPA.hpp> #include <ephysics/collision/narrowphase/EPA/EdgeEPA.hpp>
#include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp> #include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp>
#include <ephysics/collision/narrowphase/EPA/TrianglesStore.hpp> #include <ephysics/collision/narrowphase/EPA/TrianglesStore.hpp>
#include <etk/types.hpp>
using namespace ephysics; using namespace ephysics;
@ -14,18 +18,22 @@ EdgeEPA::EdgeEPA() {
} }
EdgeEPA::EdgeEPA(TriangleEPA* ownerTriangle, int32_t index) EdgeEPA::EdgeEPA(TriangleEPA* ownerTriangle, int32_t index):
: m_ownerTriangle(ownerTriangle), m_index(index) { m_ownerTriangle(ownerTriangle),
m_index(index) {
assert(index >= 0 && index < 3); assert(index >= 0 && index < 3);
} }
EdgeEPA::EdgeEPA(const EdgeEPA& edge) { EdgeEPA::EdgeEPA(const EdgeEPA& _obj):
m_ownerTriangle = edge.m_ownerTriangle; m_ownerTriangle(_obj.m_ownerTriangle),
m_index = edge.m_index; m_index(_obj.m_index) {
} }
EdgeEPA::~EdgeEPA() { EdgeEPA::EdgeEPA(EdgeEPA&& _obj):
m_ownerTriangle(null) {
etk::swap(m_ownerTriangle, _obj.m_ownerTriangle);
etk::swap(m_index, _obj.m_index);
} }
uint32_t EdgeEPA::getSourceVertexIndex() const { uint32_t EdgeEPA::getSourceVertexIndex() const {
@ -36,17 +44,17 @@ uint32_t EdgeEPA::getTargetVertexIndex() const {
return (*m_ownerTriangle)[indexOfNextCounterClockwiseEdge(m_index)]; return (*m_ownerTriangle)[indexOfNextCounterClockwiseEdge(m_index)];
} }
bool EdgeEPA::computeSilhouette(const vec3* vertices, uint32_t indexNewVertex, bool EdgeEPA::computeSilhouette(const vec3* _vertices, uint32_t _indexNewVertex,
TrianglesStore& triangleStore) { TrianglesStore& _triangleStore) {
// If the edge has not already been visited // If the edge has not already been visited
if (!m_ownerTriangle->getIsObsolete()) { if (!m_ownerTriangle->getIsObsolete()) {
// If the triangle of this edge is not visible from the given point // If the triangle of this edge is not visible from the given point
if (!m_ownerTriangle->isVisibleFromVertex(vertices, indexNewVertex)) { if (!m_ownerTriangle->isVisibleFromVertex(_vertices, _indexNewVertex)) {
TriangleEPA* triangle = triangleStore.newTriangle(vertices, indexNewVertex, TriangleEPA* triangle = _triangleStore.newTriangle(_vertices, _indexNewVertex,
getTargetVertexIndex(), getTargetVertexIndex(),
getSourceVertexIndex()); getSourceVertexIndex());
// If the triangle has been created // If the triangle has been created
if (triangle != nullptr) { if (triangle != null) {
halfLink(EdgeEPA(triangle, 1), *this); halfLink(EdgeEPA(triangle, 1), *this);
return true; return true;
} }
@ -54,28 +62,26 @@ bool EdgeEPA::computeSilhouette(const vec3* vertices, uint32_t indexNewVertex,
} else { } else {
// The current triangle is visible and therefore obsolete // The current triangle is visible and therefore obsolete
m_ownerTriangle->setIsObsolete(true); m_ownerTriangle->setIsObsolete(true);
int32_t backup = triangleStore.getNbTriangles(); int32_t backup = _triangleStore.getNbTriangles();
if(!m_ownerTriangle->getAdjacentEdge(indexOfNextCounterClockwiseEdge( if(!m_ownerTriangle->getAdjacentEdge(indexOfNextCounterClockwiseEdge(this->m_index)).computeSilhouette(_vertices,
this->m_index)).computeSilhouette(vertices, _indexNewVertex,
indexNewVertex, _triangleStore)) {
triangleStore)) {
m_ownerTriangle->setIsObsolete(false); m_ownerTriangle->setIsObsolete(false);
TriangleEPA* triangle = triangleStore.newTriangle(vertices, indexNewVertex, TriangleEPA* triangle = _triangleStore.newTriangle(_vertices, _indexNewVertex,
getTargetVertexIndex(), getTargetVertexIndex(),
getSourceVertexIndex()); getSourceVertexIndex());
// If the triangle has been created // If the triangle has been created
if (triangle != nullptr) { if (triangle != null) {
halfLink(EdgeEPA(triangle, 1), *this); halfLink(EdgeEPA(triangle, 1), *this);
return true; return true;
} }
return false; return false;
} else if (!m_ownerTriangle->getAdjacentEdge(indexOfPreviousCounterClockwiseEdge( } else if (!m_ownerTriangle->getAdjacentEdge(indexOfPreviousCounterClockwiseEdge(this->m_index)).computeSilhouette(_vertices,
this->m_index)).computeSilhouette(vertices, _indexNewVertex,
indexNewVertex, _triangleStore)) {
triangleStore)) {
m_ownerTriangle->setIsObsolete(false); m_ownerTriangle->setIsObsolete(false);
triangleStore.setNbTriangles(backup); _triangleStore.resize(backup);
TriangleEPA* triangle = triangleStore.newTriangle(vertices, indexNewVertex, TriangleEPA* triangle = _triangleStore.newTriangle(_vertices, _indexNewVertex,
getTargetVertexIndex(), getTargetVertexIndex(),
getSourceVertexIndex()); getSourceVertexIndex());
if (triangle != NULL) { if (triangle != NULL) {

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
#include <ephysics/mathematics/mathematics.hpp> #include <ephysics/mathematics/mathematics.hpp>
@ -26,9 +29,9 @@ class EdgeEPA {
/// Constructor /// Constructor
EdgeEPA(TriangleEPA* ownerTriangle, int32_t index); EdgeEPA(TriangleEPA* ownerTriangle, int32_t index);
/// Copy-constructor /// Copy-constructor
EdgeEPA(const EdgeEPA& edge); EdgeEPA(const EdgeEPA& _obj);
/// Destructor /// Move-constructor
~EdgeEPA(); EdgeEPA(EdgeEPA&& _obj);
/// Return the pointer to the owner triangle /// Return the pointer to the owner triangle
TriangleEPA* getOwnerTriangle() const { TriangleEPA* getOwnerTriangle() const {
return m_ownerTriangle; return m_ownerTriangle;
@ -44,9 +47,15 @@ class EdgeEPA {
/// Execute the recursive silhouette algorithm from this edge /// Execute the recursive silhouette algorithm from this edge
bool computeSilhouette(const vec3* vertices, uint32_t index, TrianglesStore& triangleStore); bool computeSilhouette(const vec3* vertices, uint32_t index, TrianglesStore& triangleStore);
/// Assignment operator /// Assignment operator
EdgeEPA& operator=(const EdgeEPA& edge) { EdgeEPA& operator=(const EdgeEPA& _obj) {
m_ownerTriangle = edge.m_ownerTriangle; m_ownerTriangle = _obj.m_ownerTriangle;
m_index = edge.m_index; m_index = _obj.m_index;
return *this;
}
/// Move operator
EdgeEPA& operator=(EdgeEPA&& _obj) {
etk::swap(m_ownerTriangle, _obj.m_ownerTriangle);
etk::swap(m_index, _obj.m_index);
return *this; return *this;
} }
}; };

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp> #include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp>
#include <ephysics/collision/narrowphase/EPA/EdgeEPA.hpp> #include <ephysics/collision/narrowphase/EPA/EdgeEPA.hpp>
#include <ephysics/collision/narrowphase/EPA/TrianglesStore.hpp> #include <ephysics/collision/narrowphase/EPA/TrianglesStore.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
#include <ephysics/mathematics/mathematics.hpp> #include <ephysics/mathematics/mathematics.hpp>
@ -24,11 +27,38 @@ namespace ephysics {
float m_lambda1; //!< Lambda1 value such that v = lambda0 * y_0 + lambda1 * y_1 + lambda2 * y_2 float m_lambda1; //!< Lambda1 value such that v = lambda0 * y_0 + lambda1 * y_1 + lambda2 * y_2
float m_lambda2; //!< Lambda1 value such that v = lambda0 * y_0 + lambda1 * y_1 + lambda2 * y_2 float m_lambda2; //!< Lambda1 value such that v = lambda0 * y_0 + lambda1 * y_1 + lambda2 * y_2
float m_distSquare; //!< Square distance of the point closest point v to the origin float m_distSquare; //!< Square distance of the point closest point v to the origin
/// Private copy-constructor
TriangleEPA(const TriangleEPA& _triangle);
/// Private assignment operator
TriangleEPA& operator=(const TriangleEPA& _triangle);
public: public:
/// Private copy-constructor
TriangleEPA(const TriangleEPA& _triangle) {
m_indicesVertices[0] = _triangle.m_indicesVertices[0];
m_indicesVertices[1] = _triangle.m_indicesVertices[1];
m_indicesVertices[2] = _triangle.m_indicesVertices[2];
m_adjacentEdges[0] = _triangle.m_adjacentEdges[0];
m_adjacentEdges[1] = _triangle.m_adjacentEdges[1];
m_adjacentEdges[2] = _triangle.m_adjacentEdges[2];
m_isObsolete = _triangle.m_isObsolete;
m_determinant = _triangle.m_determinant;
m_closestPoint = _triangle.m_closestPoint;
m_lambda1 = _triangle.m_lambda1;
m_lambda2 = _triangle.m_lambda2;
m_distSquare = _triangle.m_distSquare;
}
/// Private assignment operator
TriangleEPA& operator=(const TriangleEPA& _triangle) {
m_indicesVertices[0] = _triangle.m_indicesVertices[0];
m_indicesVertices[1] = _triangle.m_indicesVertices[1];
m_indicesVertices[2] = _triangle.m_indicesVertices[2];
m_adjacentEdges[0] = _triangle.m_adjacentEdges[0];
m_adjacentEdges[1] = _triangle.m_adjacentEdges[1];
m_adjacentEdges[2] = _triangle.m_adjacentEdges[2];
m_isObsolete = _triangle.m_isObsolete;
m_determinant = _triangle.m_determinant;
m_closestPoint = _triangle.m_closestPoint;
m_lambda1 = _triangle.m_lambda1;
m_lambda2 = _triangle.m_lambda2;
m_distSquare = _triangle.m_distSquare;
return *this;
}
/// Constructor /// Constructor
TriangleEPA(); TriangleEPA();
/// Constructor /// Constructor

View File

@ -1,15 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/narrowphase/EPA/TrianglesStore.hpp> #include <ephysics/collision/narrowphase/EPA/TrianglesStore.hpp>
ephysics::TrianglesStore::TrianglesStore() : m_numberTriangles(0) {
}
ephysics::TrianglesStore::~TrianglesStore() {
}

View File

@ -1,10 +1,16 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
#include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp> #include <ephysics/collision/narrowphase/EPA/TriangleEPA.hpp>
#include <ephysics/debug.hpp>
#include <etk/Array.hpp>
namespace ephysics { namespace ephysics {
const uint32_t MAX_TRIANGLES = 200; // Maximum number of triangles const uint32_t MAX_TRIANGLES = 200; // Maximum number of triangles
/** /**
@ -12,48 +18,46 @@ namespace ephysics {
*/ */
class TrianglesStore { class TrianglesStore {
private: private:
TriangleEPA m_triangles[MAX_TRIANGLES]; //!< Triangles etk::Array<TriangleEPA, MAX_TRIANGLES> m_triangles; //!< Triangles
int32_t m_numberTriangles; //!< Number of triangles
/// Private copy-constructor /// Private copy-constructor
TrianglesStore(const TrianglesStore& triangleStore); TrianglesStore(const TrianglesStore& triangleStore) = delete;
/// Private assignment operator /// Private assignment operator
TrianglesStore& operator=(const TrianglesStore& triangleStore); TrianglesStore& operator=(const TrianglesStore& triangleStore) = delete;
public: public:
/// Constructor /// Constructor
TrianglesStore(); TrianglesStore() = default;
/// Destructor
~TrianglesStore();
/// Clear all the storage /// Clear all the storage
void clear() { void clear() {
m_numberTriangles = 0; m_triangles.clear();
} }
/// Return the number of triangles /// Return the number of triangles
int32_t getNbTriangles() const { size_t getNbTriangles() const {
return m_numberTriangles; return m_triangles.size();
} }
/// Set the number of triangles /// Set the number of triangles
void setNbTriangles(int32_t _backup) { void resize(int32_t _backup) {
m_numberTriangles = _backup; if (_backup > m_triangles.size()) {
EPHY_ERROR("RESIZE BIGGER : " << _backup << " > " << m_triangles.size());
}
m_triangles.resize(_backup);
} }
/// Return the last triangle /// Return the last triangle
TriangleEPA& last() { TriangleEPA& last() {
assert(m_numberTriangles > 0); return m_triangles.back();
return m_triangles[m_numberTriangles - 1];
} }
/// Create a new triangle /// Create a new triangle
TriangleEPA* newTriangle(const vec3* _vertices, uint32_t _v0, uint32_t _v1, uint32_t _v2) { TriangleEPA* newTriangle(const vec3* _vertices, uint32_t _v0, uint32_t _v1, uint32_t _v2) {
TriangleEPA* newTriangle = nullptr;
// If we have not reached the maximum number of triangles // If we have not reached the maximum number of triangles
if (m_numberTriangles != MAX_TRIANGLES) { if (m_triangles.size() < MAX_TRIANGLES) {
newTriangle = &m_triangles[m_numberTriangles++]; TriangleEPA tmp(_v0, _v1, _v2);
newTriangle->set(_v0, _v1, _v2); if (!tmp.computeClosestPoint(_vertices)) {
if (!newTriangle->computeClosestPoint(_vertices)) { return null;
m_numberTriangles--;
newTriangle = nullptr;
} }
m_triangles.pushBack(etk::move(tmp));
return &m_triangles.back();
} }
// Return the new triangle // We are at the limit (internal)
return newTriangle; return null;
} }
/// Access operator /// Access operator
TriangleEPA& operator[](int32_t _id) { TriangleEPA& operator[](int32_t _id) {

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/narrowphase/GJK/GJKAlgorithm.hpp> #include <ephysics/collision/narrowphase/GJK/GJKAlgorithm.hpp>
#include <ephysics/collision/narrowphase/GJK/Simplex.hpp> #include <ephysics/collision/narrowphase/GJK/Simplex.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,25 +1,19 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/narrowphase/GJK/Simplex.hpp> #include <ephysics/collision/narrowphase/GJK/Simplex.hpp>
// We want to use the ReactPhysics3D namespace
using namespace ephysics; using namespace ephysics;
// Constructor
Simplex::Simplex() : mBitsCurrentSimplex(0x0), mAllBits(0x0) { Simplex::Simplex() : mBitsCurrentSimplex(0x0), mAllBits(0x0) {
} }
// Destructor
Simplex::~Simplex() {
}
// Add a new support point of (A-B) int32_to the simplex // Add a new support point of (A-B) int32_to the simplex
/// suppPointA : support point of object A in a direction -v /// suppPointA : support point of object A in a direction -v
/// suppPointB : support point of object B in a direction v /// suppPointB : support point of object B in a direction v

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -103,9 +106,6 @@ class Simplex {
/// Constructor /// Constructor
Simplex(); Simplex();
/// Destructor
~Simplex();
/// Return true if the simplex contains 4 points /// Return true if the simplex contains 4 points
bool isFull() const; bool isFull() const;

View File

@ -1,15 +1,17 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/narrowphase/NarrowPhaseAlgorithm.hpp> #include <ephysics/collision/narrowphase/NarrowPhaseAlgorithm.hpp>
using namespace ephysics; using namespace ephysics;
NarrowPhaseAlgorithm::NarrowPhaseAlgorithm(): NarrowPhaseAlgorithm::NarrowPhaseAlgorithm():
m_currentOverlappingPair(nullptr) { m_currentOverlappingPair(null) {
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/narrowphase/SphereVsSphereAlgorithm.hpp> #include <ephysics/collision/narrowphase/SphereVsSphereAlgorithm.hpp>
#include <ephysics/collision/shapes/SphereShape.hpp> #include <ephysics/collision/shapes/SphereShape.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,10 +1,12 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries // Libraries
#include <ephysics/collision/shapes/AABB.hpp> #include <ephysics/collision/shapes/AABB.hpp>
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
@ -149,13 +151,19 @@ void AABB::inflate(float _dx, float _dy, float _dz) {
m_minCoordinates -= vec3(_dx, _dy, _dz); m_minCoordinates -= vec3(_dx, _dy, _dz);
} }
bool AABB::testCollision(const AABB& aabb) const { bool AABB::testCollision(const AABB& _aabb) const {
if (m_maxCoordinates.x() < aabb.m_minCoordinates.x() || if ( m_maxCoordinates.x() < _aabb.m_minCoordinates.x()
aabb.m_maxCoordinates.x() < m_minCoordinates.x()) return false; || _aabb.m_maxCoordinates.x() < m_minCoordinates.x()) {
if (m_maxCoordinates.y() < aabb.m_minCoordinates.y() || return false;
aabb.m_maxCoordinates.y() < m_minCoordinates.y()) return false; }
if (m_maxCoordinates.z() < aabb.m_minCoordinates.z()|| if ( m_maxCoordinates.y() < _aabb.m_minCoordinates.y()
aabb.m_maxCoordinates.z() < m_minCoordinates.z()) return false; || _aabb.m_maxCoordinates.y() < m_minCoordinates.y()) {
return false;
}
if ( m_maxCoordinates.z() < _aabb.m_minCoordinates.z()
|| _aabb.m_maxCoordinates.z() < m_minCoordinates.z()) {
return false;
}
return true; return true;
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
#include <ephysics/mathematics/mathematics.hpp> #include <ephysics/mathematics/mathematics.hpp>

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries // Libraries
#include <ephysics/collision/shapes/BoxShape.hpp> #include <ephysics/collision/shapes/BoxShape.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
@ -12,11 +14,6 @@
using namespace ephysics; using namespace ephysics;
// Constructor
/**
* @param extent The vector with the three extents of the box (in meters)
* @param margin The collision margin (in meters) around the collision shape
*/
BoxShape::BoxShape(const vec3& _extent, float _margin): BoxShape::BoxShape(const vec3& _extent, float _margin):
ConvexShape(BOX, _margin), ConvexShape(BOX, _margin),
m_extent(_extent - vec3(_margin, _margin, _margin)) { m_extent(_extent - vec3(_margin, _margin, _margin)) {
@ -25,27 +22,19 @@ BoxShape::BoxShape(const vec3& _extent, float _margin):
assert(_extent.z() > 0.0f && _extent.z() > _margin); assert(_extent.z() > 0.0f && _extent.z() > _margin);
} }
// Return the local inertia tensor of the collision shape void BoxShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const {
/** float factor = (1.0f / float(3.0)) * _mass;
* @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
*/
void BoxShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const {
float factor = (1.0f / float(3.0)) * mass;
vec3 realExtent = m_extent + vec3(m_margin, m_margin, m_margin); vec3 realExtent = m_extent + vec3(m_margin, m_margin, m_margin);
float xSquare = realExtent.x() * realExtent.x(); float xSquare = realExtent.x() * realExtent.x();
float ySquare = realExtent.y() * realExtent.y(); float ySquare = realExtent.y() * realExtent.y();
float zSquare = realExtent.z() * realExtent.z(); float zSquare = realExtent.z() * realExtent.z();
tensor.setValue(factor * (ySquare + zSquare), 0.0, 0.0, _tensor.setValue(factor * (ySquare + zSquare), 0.0, 0.0,
0.0, factor * (xSquare + zSquare), 0.0, 0.0, factor * (xSquare + zSquare), 0.0,
0.0, 0.0, factor * (xSquare + ySquare)); 0.0, 0.0, factor * (xSquare + ySquare));
} }
// Raycast method with feedback information bool BoxShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
bool BoxShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const { vec3 rayDirection = _ray.point2 - _ray.point1;
vec3 rayDirection = ray.point2 - ray.point1;
float tMin = FLT_MIN; float tMin = FLT_MIN;
float tMax = FLT_MAX; float tMax = FLT_MAX;
vec3 normalDirection(0,0,0); vec3 normalDirection(0,0,0);
@ -55,14 +44,14 @@ bool BoxShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* pro
// If ray is parallel to the slab // If ray is parallel to the slab
if (etk::abs(rayDirection[iii]) < FLT_EPSILON) { if (etk::abs(rayDirection[iii]) < FLT_EPSILON) {
// If the ray's origin is not inside the slab, there is no hit // If the ray's origin is not inside the slab, there is no hit
if (ray.point1[iii] > m_extent[iii] || ray.point1[iii] < -m_extent[iii]) { if (_ray.point1[iii] > m_extent[iii] || _ray.point1[iii] < -m_extent[iii]) {
return false; return false;
} }
} else { } else {
// Compute the intersection of the ray with the near and far plane of the slab // Compute the intersection of the ray with the near and far plane of the slab
float oneOverD = 1.0f / rayDirection[iii]; float oneOverD = 1.0f / rayDirection[iii];
float t1 = (-m_extent[iii] - ray.point1[iii]) * oneOverD; float t1 = (-m_extent[iii] - _ray.point1[iii]) * oneOverD;
float t2 = (m_extent[iii] - ray.point1[iii]) * oneOverD; float t2 = (m_extent[iii] - _ray.point1[iii]) * oneOverD;
currentNormal[0] = (iii == 0) ? -m_extent[iii] : 0.0f; currentNormal[0] = (iii == 0) ? -m_extent[iii] : 0.0f;
currentNormal[1] = (iii == 1) ? -m_extent[iii] : 0.0f; currentNormal[1] = (iii == 1) ? -m_extent[iii] : 0.0f;
currentNormal[2] = (iii == 2) ? -m_extent[iii] : 0.0f; currentNormal[2] = (iii == 2) ? -m_extent[iii] : 0.0f;
@ -79,7 +68,7 @@ bool BoxShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* pro
} }
tMax = etk::min(tMax, t2); tMax = etk::min(tMax, t2);
// If tMin is larger than the maximum raycasting fraction, we return no hit // If tMin is larger than the maximum raycasting fraction, we return no hit
if (tMin > ray.maxFraction) { if (tMin > _ray.maxFraction) {
return false; return false;
} }
// If the slabs intersection is empty, there is no hit // If the slabs intersection is empty, there is no hit
@ -90,71 +79,55 @@ bool BoxShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* pro
} }
// If tMin is negative, we return no hit // If tMin is negative, we return no hit
if ( tMin < 0.0f if ( tMin < 0.0f
|| tMin > ray.maxFraction) { || tMin > _ray.maxFraction) {
return false; return false;
} }
if (normalDirection == vec3(0,0,0)) { if (normalDirection == vec3(0,0,0)) {
return false; return false;
} }
// The ray int32_tersects the three slabs, we compute the hit point // The ray int32_tersects the three slabs, we compute the hit point
vec3 localHitPoint = ray.point1 + tMin * rayDirection; vec3 localHitPoint = _ray.point1 + tMin * rayDirection;
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = tMin; _raycastInfo.hitFraction = tMin;
raycastInfo.worldPoint = localHitPoint; _raycastInfo.worldPoint = localHitPoint;
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
// Return the extents of the box
/**
* @return The vector with the three extents of the box shape (in meters)
*/
vec3 BoxShape::getExtent() const { vec3 BoxShape::getExtent() const {
return m_extent + vec3(m_margin, m_margin, m_margin); return m_extent + vec3(m_margin, m_margin, m_margin);
} }
// Set the scaling vector of the collision shape void BoxShape::setLocalScaling(const vec3& _scaling) {
void BoxShape::setLocalScaling(const vec3& scaling) { m_extent = (m_extent / m_scaling) * _scaling;
CollisionShape::setLocalScaling(_scaling);
m_extent = (m_extent / m_scaling) * scaling;
CollisionShape::setLocalScaling(scaling);
} }
// Return the local bounds of the shape in x, y and z directions
/// This method is used to compute the AABB of the box
/**
* @param min The minimum bounds of the shape in local-space coordinates
* @param max The maximum bounds of the shape in local-space coordinates
*/
void BoxShape::getLocalBounds(vec3& _min, vec3& _max) const { void BoxShape::getLocalBounds(vec3& _min, vec3& _max) const {
// Maximum bounds // Maximum bounds
_max = m_extent + vec3(m_margin, m_margin, m_margin); _max = m_extent + vec3(m_margin, m_margin, m_margin);
// Minimum bounds // Minimum bounds
_min = -_max; _min = -_max;
} }
// Return the number of bytes used by the collision shape
size_t BoxShape::getSizeInBytes() const { size_t BoxShape::getSizeInBytes() const {
return sizeof(BoxShape); return sizeof(BoxShape);
} }
// Return a local support point in a given direction without the objec margin vec3 BoxShape::getLocalSupportPointWithoutMargin(const vec3& _direction,
vec3 BoxShape::getLocalSupportPointWithoutMargin(const vec3& direction, void** _cachedCollisionData) const {
void** cachedCollisionData) const { return vec3(_direction.x() < 0.0 ? -m_extent.x() : m_extent.x(),
_direction.y() < 0.0 ? -m_extent.y() : m_extent.y(),
return vec3(direction.x() < 0.0 ? -m_extent.x() : m_extent.x(), _direction.z() < 0.0 ? -m_extent.z() : m_extent.z());
direction.y() < 0.0 ? -m_extent.y() : m_extent.y(),
direction.z() < 0.0 ? -m_extent.z() : m_extent.z());
} }
// Return true if a point is inside the collision shape bool BoxShape::testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const {
bool BoxShape::testPointInside(const vec3& localPoint, ProxyShape* proxyShape) const { return ( _localPoint.x() < m_extent[0]
return (localPoint.x() < m_extent[0] && localPoint.x() > -m_extent[0] && && _localPoint.x() > -m_extent[0]
localPoint.y() < m_extent[1] && localPoint.y() > -m_extent[1] && && _localPoint.y() < m_extent[1]
localPoint.z() < m_extent[2] && localPoint.z() > -m_extent[2]); && _localPoint.y() > -m_extent[1]
&& _localPoint.z() < m_extent[2]
&& _localPoint.z() > -m_extent[2]);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -25,24 +28,31 @@ namespace ephysics {
* default margin distance by not using the "margin" parameter in the constructor. * default margin distance by not using the "margin" parameter in the constructor.
*/ */
class BoxShape : public ConvexShape { class BoxShape : public ConvexShape {
protected : public:
vec3 m_extent; //!< Extent sizes of the box in the x, y and z direction /**
/// Private copy-constructor * @brief Constructor
BoxShape(const BoxShape& _shape) = delete; * @param extent The vector with the three extents of the box (in meters)
/// Private assignment operator * @param margin The collision margin (in meters) around the collision shape
BoxShape& operator=(const BoxShape& _shape) = delete; */
vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override;
bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
size_t getSizeInBytes() const override;
public :
/// Constructor
BoxShape(const vec3& _extent, float _margin = OBJECT_MARGIN); BoxShape(const vec3& _extent, float _margin = OBJECT_MARGIN);
/// Return the extents of the box /// DELETE copy-constructor
BoxShape(const BoxShape& _shape) = delete;
/// DELETE assignment operator
BoxShape& operator=(const BoxShape& _shape) = delete;
/**
* @brief Return the extents of the box
* @return The vector with the three extents of the box shape (in meters)
*/
vec3 getExtent() const; vec3 getExtent() const;
void setLocalScaling(const vec3& _scaling) override; void setLocalScaling(const vec3& _scaling) override;
void getLocalBounds(vec3& _min, vec3& _max) const override; void getLocalBounds(vec3& _min, vec3& _max) const override;
void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override; void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override;
protected:
vec3 m_extent; //!< Extent sizes of the box in the x, y and z direction
vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override;
bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
size_t getSizeInBytes() const override;
}; };
} }

View File

@ -1,21 +1,17 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/shapes/CapsuleShape.hpp> #include <ephysics/collision/shapes/CapsuleShape.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
using namespace ephysics; using namespace ephysics;
// Constructor
/**
* @param _radius The radius of the capsule (in meters)
* @param _height The height of the capsule (in meters)
*/
CapsuleShape::CapsuleShape(float _radius, float _height): CapsuleShape::CapsuleShape(float _radius, float _height):
ConvexShape(CAPSULE, _radius), ConvexShape(CAPSULE, _radius),
m_halfHeight(_height * 0.5f) { m_halfHeight(_height * 0.5f) {
@ -23,21 +19,8 @@ CapsuleShape::CapsuleShape(float _radius, float _height):
assert(_height > 0.0f); assert(_height > 0.0f);
} }
// Destructor void CapsuleShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const {
CapsuleShape::~CapsuleShape() {
}
// Return the local inertia tensor of the capsule
/**
* @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
*/
void CapsuleShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const {
// The inertia tensor formula for a capsule can be found in : Game Engine Gems, Volume 1 // The inertia tensor formula for a capsule can be found in : Game Engine Gems, Volume 1
float height = m_halfHeight + m_halfHeight; float height = m_halfHeight + m_halfHeight;
float radiusSquare = m_margin * m_margin; float radiusSquare = m_margin * m_margin;
float heightSquare = height * height; float heightSquare = height * height;
@ -47,292 +30,231 @@ void CapsuleShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass)
float sum1 = float(0.4) * radiusSquareDouble; float sum1 = float(0.4) * radiusSquareDouble;
float sum2 = float(0.75) * height * m_margin + 0.5f * heightSquare; float sum2 = float(0.75) * height * m_margin + 0.5f * heightSquare;
float sum3 = float(0.25) * radiusSquare + float(1.0 / 12.0) * heightSquare; float sum3 = float(0.25) * radiusSquare + float(1.0 / 12.0) * heightSquare;
float IxxAndzz = factor1 * mass * (sum1 + sum2) + factor2 * mass * sum3; float IxxAndzz = factor1 * _mass * (sum1 + sum2) + factor2 * _mass * sum3;
float Iyy = factor1 * mass * sum1 + factor2 * mass * float(0.25) * radiusSquareDouble; float Iyy = factor1 * _mass * sum1 + factor2 * _mass * float(0.25) * radiusSquareDouble;
tensor.setValue(IxxAndzz, 0.0, 0.0, _tensor.setValue(IxxAndzz, 0.0, 0.0,
0.0, Iyy, 0.0, 0.0, Iyy, 0.0,
0.0, 0.0, IxxAndzz); 0.0, 0.0, IxxAndzz);
} }
// Return true if a point is inside the collision shape bool CapsuleShape::testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const {
bool CapsuleShape::testPointInside(const vec3& localPoint, ProxyShape* proxyShape) const { const float diffYCenterSphere1 = _localPoint.y() - m_halfHeight;
const float diffYCenterSphere2 = _localPoint.y() + m_halfHeight;
const float diffYCenterSphere1 = localPoint.y() - m_halfHeight; const float xSquare = _localPoint.x() * _localPoint.x();
const float diffYCenterSphere2 = localPoint.y() + m_halfHeight; const float zSquare = _localPoint.z() * _localPoint.z();
const float xSquare = localPoint.x() * localPoint.x();
const float zSquare = localPoint.z() * localPoint.z();
const float squareRadius = m_margin * m_margin; const float squareRadius = m_margin * m_margin;
// Return true if the point is inside the cylinder or one of the two spheres of the capsule // Return true if the point is inside the cylinder or one of the two spheres of the capsule
return ((xSquare + zSquare) < squareRadius && return ((xSquare + zSquare) < squareRadius &&
localPoint.y() < m_halfHeight && localPoint.y() > -m_halfHeight) || _localPoint.y() < m_halfHeight && _localPoint.y() > -m_halfHeight) ||
(xSquare + zSquare + diffYCenterSphere1 * diffYCenterSphere1) < squareRadius || (xSquare + zSquare + diffYCenterSphere1 * diffYCenterSphere1) < squareRadius ||
(xSquare + zSquare + diffYCenterSphere2 * diffYCenterSphere2) < squareRadius; (xSquare + zSquare + diffYCenterSphere2 * diffYCenterSphere2) < squareRadius;
} }
// Raycast method with feedback information bool CapsuleShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
bool CapsuleShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const { const vec3 n = _ray.point2 - _ray.point1;
const vec3 n = ray.point2 - ray.point1;
const float epsilon = float(0.01); const float epsilon = float(0.01);
vec3 p(float(0), -m_halfHeight, float(0)); vec3 p(float(0), -m_halfHeight, float(0));
vec3 q(float(0), m_halfHeight, float(0)); vec3 q(float(0), m_halfHeight, float(0));
vec3 d = q - p; vec3 d = q - p;
vec3 m = ray.point1 - p; vec3 m = _ray.point1 - p;
float t; float t;
float mDotD = m.dot(d); float mDotD = m.dot(d);
float nDotD = n.dot(d); float nDotD = n.dot(d);
float dDotD = d.dot(d); float dDotD = d.dot(d);
// Test if the segment is outside the cylinder // Test if the segment is outside the cylinder
float vec1DotD = (ray.point1 - vec3(0.0f, -m_halfHeight - m_margin, float(0.0))).dot(d); float vec1DotD = (_ray.point1 - vec3(0.0f, -m_halfHeight - m_margin, float(0.0))).dot(d);
if (vec1DotD < 0.0f && vec1DotD + nDotD < float(0.0)) return false; if ( vec1DotD < 0.0f
&& vec1DotD + nDotD < float(0.0)) {
return false;
}
float ddotDExtraCaps = float(2.0) * m_margin * d.y(); float ddotDExtraCaps = float(2.0) * m_margin * d.y();
if (vec1DotD > dDotD + ddotDExtraCaps && vec1DotD + nDotD > dDotD + ddotDExtraCaps) return false; if ( vec1DotD > dDotD + ddotDExtraCaps
&& vec1DotD + nDotD > dDotD + ddotDExtraCaps) {
return false;
}
float nDotN = n.dot(n); float nDotN = n.dot(n);
float mDotN = m.dot(n); float mDotN = m.dot(n);
float a = dDotD * nDotN - nDotD * nDotD; float a = dDotD * nDotN - nDotD * nDotD;
float k = m.dot(m) - m_margin * m_margin; float k = m.dot(m) - m_margin * m_margin;
float c = dDotD * k - mDotD * mDotD; float c = dDotD * k - mDotD * mDotD;
// If the ray is parallel to the capsule axis // If the ray is parallel to the capsule axis
if (etk::abs(a) < epsilon) { if (etk::abs(a) < epsilon) {
// If the origin is outside the surface of the capusle's cylinder, we return no hit // If the origin is outside the surface of the capusle's cylinder, we return no hit
if (c > 0.0f) return false; if (c > 0.0f) {
return false;
}
// Here we know that the segment int32_tersect an endcap of the capsule // Here we know that the segment int32_tersect an endcap of the capsule
// If the ray int32_tersects with the "p" endcap of the capsule // If the ray int32_tersects with the "p" endcap of the capsule
if (mDotD < 0.0f) { if (mDotD < 0.0f) {
// Check int32_tersection between the ray and the "p" sphere endcap of the capsule // Check int32_tersection between the ray and the "p" sphere endcap of the capsule
vec3 hitLocalPoint; vec3 hitLocalPoint;
float hitFraction; float hitFraction;
if (raycastWithSphereEndCap(ray.point1, ray.point2, p, ray.maxFraction, hitLocalPoint, hitFraction)) { if (raycastWithSphereEndCap(_ray.point1, _ray.point2, p, _ray.maxFraction, hitLocalPoint, hitFraction)) {
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = hitFraction; _raycastInfo.hitFraction = hitFraction;
raycastInfo.worldPoint = hitLocalPoint; _raycastInfo.worldPoint = hitLocalPoint;
vec3 normalDirection = hitLocalPoint - p; vec3 normalDirection = hitLocalPoint - p;
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
return false; return false;
} } else if (mDotD > dDotD) { // If the ray int32_tersects with the "q" endcap of the cylinder
else if (mDotD > dDotD) { // If the ray int32_tersects with the "q" endcap of the cylinder
// Check int32_tersection between the ray and the "q" sphere endcap of the capsule // Check int32_tersection between the ray and the "q" sphere endcap of the capsule
vec3 hitLocalPoint; vec3 hitLocalPoint;
float hitFraction; float hitFraction;
if (raycastWithSphereEndCap(ray.point1, ray.point2, q, ray.maxFraction, hitLocalPoint, hitFraction)) { if (raycastWithSphereEndCap(_ray.point1, _ray.point2, q, _ray.maxFraction, hitLocalPoint, hitFraction)) {
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = hitFraction; _raycastInfo.hitFraction = hitFraction;
raycastInfo.worldPoint = hitLocalPoint; _raycastInfo.worldPoint = hitLocalPoint;
vec3 normalDirection = hitLocalPoint - q; vec3 normalDirection = hitLocalPoint - q;
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
return false; return false;
} } else {
else { // If the origin is inside the cylinder, we return no hit // If the origin is inside the cylinder, we return no hit
return false; return false;
} }
} }
float b = dDotD * mDotN - nDotD * mDotD; float b = dDotD * mDotN - nDotD * mDotD;
float discriminant = b * b - a * c; float discriminant = b * b - a * c;
// If the discriminant is negative, no real roots and therfore, no hit // If the discriminant is negative, no real roots and therfore, no hit
if (discriminant < 0.0f) return false; if (discriminant < 0.0f) {
return false;
}
// Compute the smallest root (first int32_tersection along the ray) // Compute the smallest root (first int32_tersection along the ray)
float t0 = t = (-b - etk::sqrt(discriminant)) / a; float t0 = t = (-b - etk::sqrt(discriminant)) / a;
// If the int32_tersection is outside the finite cylinder of the capsule on "p" endcap side // If the int32_tersection is outside the finite cylinder of the capsule on "p" endcap side
float value = mDotD + t * nDotD; float value = mDotD + t * nDotD;
if (value < 0.0f) { if (value < 0.0f) {
// Check int32_tersection between the ray and the "p" sphere endcap of the capsule // Check int32_tersection between the ray and the "p" sphere endcap of the capsule
vec3 hitLocalPoint; vec3 hitLocalPoint;
float hitFraction; float hitFraction;
if (raycastWithSphereEndCap(ray.point1, ray.point2, p, ray.maxFraction, hitLocalPoint, hitFraction)) { if (raycastWithSphereEndCap(_ray.point1, _ray.point2, p, _ray.maxFraction, hitLocalPoint, hitFraction)) {
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = hitFraction; _raycastInfo.hitFraction = hitFraction;
raycastInfo.worldPoint = hitLocalPoint; _raycastInfo.worldPoint = hitLocalPoint;
vec3 normalDirection = hitLocalPoint - p; vec3 normalDirection = hitLocalPoint - p;
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
return false; return false;
} } else if (value > dDotD) { // If the int32_tersection is outside the finite cylinder on the "q" side
else if (value > dDotD) { // If the int32_tersection is outside the finite cylinder on the "q" side
// Check int32_tersection between the ray and the "q" sphere endcap of the capsule // Check int32_tersection between the ray and the "q" sphere endcap of the capsule
vec3 hitLocalPoint; vec3 hitLocalPoint;
float hitFraction; float hitFraction;
if (raycastWithSphereEndCap(ray.point1, ray.point2, q, ray.maxFraction, hitLocalPoint, hitFraction)) { if (raycastWithSphereEndCap(_ray.point1, _ray.point2, q, _ray.maxFraction, hitLocalPoint, hitFraction)) {
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = hitFraction; _raycastInfo.hitFraction = hitFraction;
raycastInfo.worldPoint = hitLocalPoint; _raycastInfo.worldPoint = hitLocalPoint;
vec3 normalDirection = hitLocalPoint - q; vec3 normalDirection = hitLocalPoint - q;
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
return false; return false;
} }
t = t0; t = t0;
// If the int32_tersection is behind the origin of the ray or beyond the maximum // If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit // raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) return false; if (t < 0.0f || t > _ray.maxFraction) {
return false;
}
// Compute the hit information // Compute the hit information
vec3 localHitPoint = ray.point1 + t * n; vec3 localHitPoint = _ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = t; _raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint; _raycastInfo.worldPoint = localHitPoint;
vec3 v = localHitPoint - p; vec3 v = localHitPoint - p;
vec3 w = (v.dot(d) / d.length2()) * d; vec3 w = (v.dot(d) / d.length2()) * d;
vec3 normalDirection = (localHitPoint - (p + w)).safeNormalized(); vec3 normalDirection = (localHitPoint - (p + w)).safeNormalized();
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
// Raycasting method between a ray one of the two spheres end cap of the capsule bool CapsuleShape::raycastWithSphereEndCap(const vec3& _point1,
bool CapsuleShape::raycastWithSphereEndCap(const vec3& point1, const vec3& point2, const vec3& _point2,
const vec3& sphereCenter, float maxFraction, const vec3& _sphereCenter,
vec3& hitLocalPoint, float& hitFraction) const { float _maxFraction,
vec3& _hitLocalPoint,
const vec3 m = point1 - sphereCenter; float& _hitFraction) const {
const vec3 m = _point1 - _sphereCenter;
float c = m.dot(m) - m_margin * m_margin; float c = m.dot(m) - m_margin * m_margin;
// If the origin of the ray is inside the sphere, we return no int32_tersection // If the origin of the ray is inside the sphere, we return no int32_tersection
if (c < 0.0f) return false; if (c < 0.0f) {
return false;
const vec3 rayDirection = point2 - point1; }
const vec3 rayDirection = _point2 - _point1;
float b = m.dot(rayDirection); float b = m.dot(rayDirection);
// If the origin of the ray is outside the sphere and the ray // If the origin of the ray is outside the sphere and the ray
// is pointing away from the sphere, there is no int32_tersection // is pointing away from the sphere, there is no int32_tersection
if (b > 0.0f) return false; if (b > 0.0f) {
return false;
}
float raySquareLength = rayDirection.length2(); float raySquareLength = rayDirection.length2();
// Compute the discriminant of the quadratic equation // Compute the discriminant of the quadratic equation
float discriminant = b * b - raySquareLength * c; float discriminant = b * b - raySquareLength * c;
// If the discriminant is negative or the ray length is very small, there is no int32_tersection // If the discriminant is negative or the ray length is very small, there is no int32_tersection
if (discriminant < 0.0f || raySquareLength < FLT_EPSILON) return false; if ( discriminant < 0.0f
|| raySquareLength < FLT_EPSILON) {
return false;
}
// Compute the solution "t" closest to the origin // Compute the solution "t" closest to the origin
float t = -b - etk::sqrt(discriminant); float t = -b - etk::sqrt(discriminant);
assert(t >= 0.0f); assert(t >= 0.0f);
// If the hit point is withing the segment ray fraction // If the hit point is withing the segment ray fraction
if (t < maxFraction * raySquareLength) { if (t < _maxFraction * raySquareLength) {
// Compute the int32_tersection information // Compute the int32_tersection information
t /= raySquareLength; t /= raySquareLength;
hitFraction = t; _hitFraction = t;
hitLocalPoint = point1 + t * rayDirection; _hitLocalPoint = _point1 + t * rayDirection;
return true; return true;
} }
return false; return false;
} }
// Get the radius of the capsule
/**
* @return The radius of the capsule shape (in meters)
*/
float CapsuleShape::getRadius() const { float CapsuleShape::getRadius() const {
return m_margin; return m_margin;
} }
// Return the height of the capsule
/**
* @return The height of the capsule shape (in meters)
*/
float CapsuleShape::getHeight() const { float CapsuleShape::getHeight() const {
return m_halfHeight + m_halfHeight; return m_halfHeight + m_halfHeight;
} }
// Set the scaling vector of the collision shape void CapsuleShape::setLocalScaling(const vec3& _scaling) {
void CapsuleShape::setLocalScaling(const vec3& scaling) { m_halfHeight = (m_halfHeight / m_scaling.y()) * _scaling.y();
m_margin = (m_margin / m_scaling.x()) * _scaling.x();
m_halfHeight = (m_halfHeight / m_scaling.y()) * scaling.y(); CollisionShape::setLocalScaling(_scaling);
m_margin = (m_margin / m_scaling.x()) * scaling.x();
CollisionShape::setLocalScaling(scaling);
} }
// Return the number of bytes used by the collision shape
size_t CapsuleShape::getSizeInBytes() const { size_t CapsuleShape::getSizeInBytes() const {
return sizeof(CapsuleShape); return sizeof(CapsuleShape);
} }
// Return the local bounds of the shape in x, y and z directions void CapsuleShape::getLocalBounds(vec3& _min, vec3& _max) const {
// This method is used to compute the AABB of the box
/**
* @param min The minimum bounds of the shape in local-space coordinates
* @param max The maximum bounds of the shape in local-space coordinates
*/
void CapsuleShape::getLocalBounds(vec3& min, vec3& max) const {
// Maximum bounds // Maximum bounds
max.setX(m_margin); _max.setX(m_margin);
max.setY(m_halfHeight + m_margin); _max.setY(m_halfHeight + m_margin);
max.setZ(m_margin); _max.setZ(m_margin);
// Minimum bounds // Minimum bounds
min.setX(-m_margin); _min.setX(-m_margin);
min.setY(-max.y()); _min.setY(-_max.y());
min.setZ(min.x()); _min.setZ(_min.x());
} }
// Return a local support point in a given direction without the object margin. vec3 CapsuleShape::getLocalSupportPointWithoutMargin(const vec3& _direction,
/// A capsule is the convex hull of two spheres S1 and S2. The support point in the direction "d" void** _cachedCollisionData) const {
/// of the convex hull of a set of convex objects is the support point "p" in the set of all
/// support points from all the convex objects with the maximum dot product with the direction "d".
/// Therefore, in this method, we compute the support points of both top and bottom spheres of
/// the capsule and return the point with the maximum dot product with the direction vector. Note
/// that the object margin is implicitly the radius and height of the capsule.
vec3 CapsuleShape::getLocalSupportPointWithoutMargin(const vec3& direction,
void** cachedCollisionData) const {
// Support point top sphere // Support point top sphere
float dotProductTop = m_halfHeight * direction.y(); float dotProductTop = m_halfHeight * _direction.y();
// Support point bottom sphere // Support point bottom sphere
float dotProductBottom = -m_halfHeight * direction.y(); float dotProductBottom = -m_halfHeight * _direction.y();
// Return the point with the maximum dot product // Return the point with the maximum dot product
if (dotProductTop > dotProductBottom) { if (dotProductTop > dotProductBottom) {
return vec3(0, m_halfHeight, 0); return vec3(0, m_halfHeight, 0);
} }
else { return vec3(0, -m_halfHeight, 0);
return vec3(0, -m_halfHeight, 0);
}
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -20,31 +23,44 @@ namespace ephysics {
* capsule shape. * capsule shape.
*/ */
class CapsuleShape : public ConvexShape { class CapsuleShape : public ConvexShape {
protected:
float m_halfHeight; //!< Half height of the capsule (height = distance between the centers of the two spheres)
/// Private copy-constructor
CapsuleShape(const CapsuleShape& _shape);
/// Private assignment operator
CapsuleShape& operator=(const CapsuleShape& _shape);
vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override;
bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
/// Raycasting method between a ray one of the two spheres end cap of the capsule
bool raycastWithSphereEndCap(const vec3& _point1, const vec3& _point2,
const vec3& _sphereCenter, float _maxFraction,
vec3& _hitLocalPoint, float& _hitFraction) const;
size_t getSizeInBytes() const override;
public : public :
/// Constructor /**
* @brief Constructor
* @param _radius The radius of the capsule (in meters)
* @param _height The height of the capsule (in meters)
*/
CapsuleShape(float _radius, float _height); CapsuleShape(float _radius, float _height);
/// Destructor /// DELETE copy-constructor
virtual ~CapsuleShape(); CapsuleShape(const CapsuleShape& _shape) = delete;
/// Return the radius of the capsule /// DELETE assignment operator
CapsuleShape& operator=(const CapsuleShape& _shape) = delete;
/**
* Get the radius of the capsule
* @return The radius of the capsule shape (in meters)
*/
float getRadius() const; float getRadius() const;
/// Return the height of the capsule /**
* @brief Return the height of the capsule
* @return The height of the capsule shape (in meters)
*/
float getHeight() const; float getHeight() const;
void setLocalScaling(const vec3& _scaling) override; void setLocalScaling(const vec3& _scaling) override;
void getLocalBounds(vec3& _min, vec3& _max) const override; void getLocalBounds(vec3& _min, vec3& _max) const override;
void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override; void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override;
protected:
float m_halfHeight; //!< Half height of the capsule (height = distance between the centers of the two spheres)
vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override;
bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
/**
* @brief Raycasting method between a ray one of the two spheres end cap of the capsule
*/
bool raycastWithSphereEndCap(const vec3& _point1,
const vec3& _point2,
const vec3& _sphereCenter,
float _maxFraction,
vec3& _hitLocalPoint,
float& _hitFraction) const;
size_t getSizeInBytes() const override;
}; };
} }

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/shapes/CollisionShape.hpp> #include <ephysics/collision/shapes/CollisionShape.hpp>
#include <ephysics/engine/Profiler.hpp> #include <ephysics/engine/Profiler.hpp>
#include <ephysics/body/CollisionBody.hpp> #include <ephysics/body/CollisionBody.hpp>
@ -18,50 +19,35 @@ CollisionShape::CollisionShape(CollisionShapeType type) :
} }
CollisionShape::~CollisionShape() { void CollisionShape::computeAABB(AABB& _aabb, const etk::Transform3D& _transform) const {
}
// Compute the world-space AABB of the collision shape given a transform
/**
* @param[out] aabb The axis-aligned bounding box (AABB) of the collision shape
* computed in world-space coordinates
* @param transform etk::Transform3D used to compute the AABB of the collision shape
*/
void CollisionShape::computeAABB(AABB& aabb, const etk::Transform3D& transform) const {
PROFILE("CollisionShape::computeAABB()"); PROFILE("CollisionShape::computeAABB()");
// Get the local bounds in x,y and z direction // Get the local bounds in x,y and z direction
vec3 minBounds(0,0,0); vec3 minBounds(0,0,0);
vec3 maxBounds(0,0,0); vec3 maxBounds(0,0,0);
getLocalBounds(minBounds, maxBounds); getLocalBounds(minBounds, maxBounds);
// Rotate the local bounds according to the orientation of the body // Rotate the local bounds according to the orientation of the body
etk::Matrix3x3 worldAxis = transform.getOrientation().getMatrix().getAbsolute(); etk::Matrix3x3 worldAxis = _transform.getOrientation().getMatrix().getAbsolute();
vec3 worldMinBounds(worldAxis.getColumn(0).dot(minBounds), vec3 worldMinBounds(worldAxis.getColumn(0).dot(minBounds),
worldAxis.getColumn(1).dot(minBounds), worldAxis.getColumn(1).dot(minBounds),
worldAxis.getColumn(2).dot(minBounds)); worldAxis.getColumn(2).dot(minBounds));
vec3 worldMaxBounds(worldAxis.getColumn(0).dot(maxBounds), vec3 worldMaxBounds(worldAxis.getColumn(0).dot(maxBounds),
worldAxis.getColumn(1).dot(maxBounds), worldAxis.getColumn(1).dot(maxBounds),
worldAxis.getColumn(2).dot(maxBounds)); worldAxis.getColumn(2).dot(maxBounds));
// Compute the minimum and maximum coordinates of the rotated extents // Compute the minimum and maximum coordinates of the rotated extents
vec3 minCoordinates = transform.getPosition() + worldMinBounds; vec3 minCoordinates = _transform.getPosition() + worldMinBounds;
vec3 maxCoordinates = transform.getPosition() + worldMaxBounds; vec3 maxCoordinates = _transform.getPosition() + worldMaxBounds;
// Update the AABB with the new minimum and maximum coordinates // Update the AABB with the new minimum and maximum coordinates
aabb.setMin(minCoordinates); _aabb.setMin(minCoordinates);
aabb.setMax(maxCoordinates); _aabb.setMax(maxCoordinates);
} }
int32_t CollisionShape::computeNbMaxContactManifolds(CollisionShapeType shapeType1, int32_t CollisionShape::computeNbMaxContactManifolds(CollisionShapeType _shapeType1,
CollisionShapeType shapeType2) { CollisionShapeType _shapeType2) {
// If both shapes are convex // If both shapes are convex
if (isConvex(shapeType1) && isConvex(shapeType2)) { if (isConvex(_shapeType1) && isConvex(_shapeType2)) {
return NB_MAX_CONTACT_MANIFOLDS_CONVEX_SHAPE; return NB_MAX_CONTACT_MANIFOLDS_CONVEX_SHAPE;
} // If there is at least one concave shape
else {
return NB_MAX_CONTACT_MANIFOLDS_CONCAVE_SHAPE;
} }
// If there is at least one concave shape
return NB_MAX_CONTACT_MANIFOLDS_CONCAVE_SHAPE;
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -25,24 +28,15 @@ class CollisionBody;
* body that is used during the narrow-phase collision detection. * body that is used during the narrow-phase collision detection.
*/ */
class CollisionShape { class CollisionShape {
protected :
CollisionShapeType m_type; //!< Type of the collision shape
vec3 m_scaling; //!< Scaling vector of the collision shape
/// Private copy-constructor
CollisionShape(const CollisionShape& shape) = delete;
/// Private assignment operator
CollisionShape& operator=(const CollisionShape& shape) = delete;
/// Return true if a point is inside the collision shape
virtual bool testPointInside(const vec3& worldPoint, ProxyShape* proxyShape) const=0;
/// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const=0;
/// Return the number of bytes used by the collision shape
virtual size_t getSizeInBytes() const = 0;
public : public :
/// Constructor /// Constructor
CollisionShape(CollisionShapeType _type); CollisionShape(CollisionShapeType _type);
/// Destructor /// DELETE copy-constructor
virtual ~CollisionShape(); CollisionShape(const CollisionShape& shape) = delete;
/// DELETE assignment operator
CollisionShape& operator=(const CollisionShape& shape) = delete;
/// Virtualize destructor
virtual ~CollisionShape() {};
/** /**
* @brief Get the type of the collision shapes * @brief Get the type of the collision shapes
* @return The type of the collision shape (box, sphere, cylinder, ...) * @return The type of the collision shape (box, sphere, cylinder, ...)
@ -103,6 +97,15 @@ class CollisionShape {
CollisionShapeType _shapeType2); CollisionShapeType _shapeType2);
friend class ProxyShape; friend class ProxyShape;
friend class CollisionWorld; friend class CollisionWorld;
protected :
CollisionShapeType m_type; //!< Type of the collision shape
vec3 m_scaling; //!< Scaling vector of the collision shape
/// Return true if a point is inside the collision shape
virtual bool testPointInside(const vec3& worldPoint, ProxyShape* proxyShape) const = 0;
/// Raycast method with feedback information
virtual bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const = 0;
/// Return the number of bytes used by the collision shape
virtual size_t getSizeInBytes() const = 0;
}; };

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/shapes/ConcaveMeshShape.hpp> #include <ephysics/collision/shapes/ConcaveMeshShape.hpp>
#include <ephysics/debug.hpp> #include <ephysics/debug.hpp>
@ -39,12 +41,11 @@ void ConcaveMeshShape::initBVHTree() {
} }
void ConcaveMeshShape::getTriangleVerticesWithIndexPointer(int32_t _subPart, int32_t _triangleIndex, vec3* _outTriangleVertices) const { void ConcaveMeshShape::getTriangleVerticesWithIndexPointer(int32_t _subPart, int32_t _triangleIndex, vec3* _outTriangleVertices) const {
EPHY_ASSERT(_outTriangleVertices != nullptr, "Input check error"); EPHY_ASSERT(_outTriangleVertices != null, "Input check error");
// Get the triangle vertex array of the current sub-part // Get the triangle vertex array of the current sub-part
TriangleVertexArray* triangleVertexArray = m_triangleMesh->getSubpart(_subPart); TriangleVertexArray* triangleVertexArray = m_triangleMesh->getSubpart(_subPart);
if (triangleVertexArray == nullptr) { if (triangleVertexArray == null) {
EPHY_ERROR("get nullptr ..."); EPHY_ERROR("get null ...");
} }
ephysics::Triangle trianglePoints = triangleVertexArray->getTriangle(_triangleIndex); ephysics::Triangle trianglePoints = triangleVertexArray->getTriangle(_triangleIndex);
_outTriangleVertices[0] = trianglePoints[0] * m_scaling; _outTriangleVertices[0] = trianglePoints[0] * m_scaling;
@ -53,10 +54,17 @@ void ConcaveMeshShape::getTriangleVerticesWithIndexPointer(int32_t _subPart, int
} }
void ConcaveMeshShape::testAllTriangles(TriangleCallback& _callback, const AABB& _localAABB) const { void ConcaveMeshShape::testAllTriangles(TriangleCallback& _callback, const AABB& _localAABB) const {
ConvexTriangleAABBOverlapCallback overlapCallback(_callback, *this, m_dynamicAABBTree);
// Ask the Dynamic AABB Tree to report all the triangles that are overlapping // Ask the Dynamic AABB Tree to report all the triangles that are overlapping
// with the AABB of the convex shape. // with the AABB of the convex shape.
m_dynamicAABBTree.reportAllShapesOverlappingWithAABB(_localAABB, overlapCallback); m_dynamicAABBTree.reportAllShapesOverlappingWithAABB(_localAABB, [&](int32_t _nodeId) {
// Get the node data (triangle index and mesh subpart index)
int32_t* data = m_dynamicAABBTree.getNodeDataInt(_nodeId);
// Get the triangle vertices for this node from the concave mesh shape
vec3 trianglePoints[3];
getTriangleVerticesWithIndexPointer(data[0], data[1], trianglePoints);
// Call the callback to test narrow-phase collision with this triangle
_callback.testTriangle(trianglePoints);
});
} }
bool ConcaveMeshShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const { bool ConcaveMeshShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
@ -66,12 +74,12 @@ bool ConcaveMeshShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, Proxy
// Ask the Dynamic AABB Tree to report all AABB nodes that are hit by the ray. // 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 // The raycastCallback object will then compute ray casting against the triangles
// in the hit AABBs. // in the hit AABBs.
m_dynamicAABBTree.raycast(_ray, raycastCallback); m_dynamicAABBTree.raycast(_ray, [&](int32_t _nodeId, const ephysics::Ray& _ray) mutable { return raycastCallback(_nodeId, _ray);});
raycastCallback.raycastTriangles(); raycastCallback.raycastTriangles();
return raycastCallback.getIsHit(); return raycastCallback.getIsHit();
} }
float ConcaveMeshRaycastCallback::raycastBroadPhaseShape(int32_t _nodeId, const Ray& _ray) { float ConcaveMeshRaycastCallback::operator()(int32_t _nodeId, const Ray& _ray) {
// Add the id of the hit AABB node int32_to // Add the id of the hit AABB node int32_to
m_hitAABBNodes.pushBack(_nodeId); m_hitAABBNodes.pushBack(_nodeId);
return _ray.maxFraction; return _ray.maxFraction;
@ -104,7 +112,7 @@ void ConcaveMeshRaycastCallback::raycastTriangles() {
m_raycastInfo.meshSubpart = data[0]; m_raycastInfo.meshSubpart = data[0];
m_raycastInfo.triangleIndex = data[1]; m_raycastInfo.triangleIndex = data[1];
smallestHitFraction = raycastInfo.hitFraction; smallestHitFraction = raycastInfo.hitFraction;
mIsHit = true; m_isHit = true;
} }
} }
} }
@ -136,17 +144,4 @@ void ConcaveMeshShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float
0, 0, _mass); 0, 0, _mass);
} }
void ConvexTriangleAABBOverlapCallback::notifyOverlappingNode(int32_t _nodeId) {
// Get the node data (triangle index and mesh subpart index)
int32_t* data = m_dynamicAABBTree.getNodeDataInt(_nodeId);
// Get the triangle vertices for this node from the concave mesh shape
vec3 trianglePoints[3];
m_concaveMeshShape.getTriangleVerticesWithIndexPointer(data[0], data[1], trianglePoints);
// Call the callback to test narrow-phase collision with this triangle
m_triangleTestCallback.testTriangle(trianglePoints);
}

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -13,35 +16,15 @@
namespace ephysics { namespace ephysics {
class ConcaveMeshShape; class ConcaveMeshShape;
class ConvexTriangleAABBOverlapCallback : public DynamicAABBTreeOverlapCallback { class ConcaveMeshRaycastCallback {
private: private:
TriangleCallback& m_triangleTestCallback; //!<
const ConcaveMeshShape& m_concaveMeshShape; //!< Reference to the concave mesh shape
const DynamicAABBTree& m_dynamicAABBTree; //!< Reference to the Dynamic AABB tree
public:
// Constructor
ConvexTriangleAABBOverlapCallback(TriangleCallback& _triangleCallback,
const ConcaveMeshShape& _concaveShape,
const DynamicAABBTree& _dynamicAABBTree):
m_triangleTestCallback(_triangleCallback),
m_concaveMeshShape(_concaveShape),
m_dynamicAABBTree(_dynamicAABBTree) {
}
// Called when a overlapping node has been found during the call to
// DynamicAABBTree:reportAllShapesOverlappingWithAABB()
virtual void notifyOverlappingNode(int32_t _nodeId);
};
class ConcaveMeshRaycastCallback : public DynamicAABBTreeRaycastCallback {
private :
etk::Vector<int32_t> m_hitAABBNodes; etk::Vector<int32_t> m_hitAABBNodes;
const DynamicAABBTree& m_dynamicAABBTree; const DynamicAABBTree& m_dynamicAABBTree;
const ConcaveMeshShape& m_concaveMeshShape; const ConcaveMeshShape& m_concaveMeshShape;
ProxyShape* m_proxyShape; ProxyShape* m_proxyShape;
RaycastInfo& m_raycastInfo; RaycastInfo& m_raycastInfo;
const Ray& m_ray; const Ray& m_ray;
bool mIsHit; bool m_isHit;
public: public:
// Constructor // Constructor
ConcaveMeshRaycastCallback(const DynamicAABBTree& _dynamicAABBTree, ConcaveMeshRaycastCallback(const DynamicAABBTree& _dynamicAABBTree,
@ -54,16 +37,16 @@ namespace ephysics {
m_proxyShape(_proxyShape), m_proxyShape(_proxyShape),
m_raycastInfo(_raycastInfo), m_raycastInfo(_raycastInfo),
m_ray(_ray), m_ray(_ray),
mIsHit(false) { m_isHit(false) {
} }
/// Collect all the AABB nodes that are hit by the ray in the Dynamic AABB Tree /// Collect all the AABB nodes that are hit by the ray in the Dynamic AABB Tree
virtual float raycastBroadPhaseShape(int32_t _nodeId, const Ray& _ray); float operator()(int32_t _nodeId, const ephysics::Ray& _ray);
/// Raycast all collision shapes that have been collected /// Raycast all collision shapes that have been collected
void raycastTriangles(); void raycastTriangles();
/// Return true if a raycast hit has been found /// Return true if a raycast hit has been found
bool getIsHit() const { bool getIsHit() const {
return mIsHit; return m_isHit;
} }
}; };
/** /**
@ -72,13 +55,22 @@ namespace ephysics {
* this shape for a static mesh. * this shape for a static mesh.
*/ */
class ConcaveMeshShape : public ConcaveShape { class ConcaveMeshShape : public ConcaveShape {
public:
/// Constructor
ConcaveMeshShape(TriangleMesh* _triangleMesh);
/// DELETE copy-constructor
ConcaveMeshShape(const ConcaveMeshShape& _shape) = delete;
/// DELETE assignment operator
ConcaveMeshShape& operator=(const ConcaveMeshShape& _shape) = delete;
virtual void getLocalBounds(vec3& _min, vec3& _max) const override;
virtual void setLocalScaling(const vec3& _scaling) override;
virtual void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override;
virtual void testAllTriangles(TriangleCallback& _callback, const AABB& _localAABB) const override;
friend class ConvexTriangleAABBOverlapCallback;
friend class ConcaveMeshRaycastCallback;
protected: protected:
TriangleMesh* m_triangleMesh; //!< Triangle mesh TriangleMesh* m_triangleMesh; //!< Triangle mesh
DynamicAABBTree m_dynamicAABBTree; //!< Dynamic AABB tree to accelerate collision with the triangles DynamicAABBTree m_dynamicAABBTree; //!< Dynamic AABB tree to accelerate collision with the triangles
/// Private copy-constructor
ConcaveMeshShape(const ConcaveMeshShape& _shape) = delete;
/// Private assignment operator
ConcaveMeshShape& operator=(const ConcaveMeshShape& _shape) = delete;
virtual bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override; virtual bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
virtual size_t getSizeInBytes() const override; virtual size_t getSizeInBytes() const override;
/// Insert all the triangles int32_to the dynamic AABB tree /// Insert all the triangles int32_to the dynamic AABB tree
@ -88,16 +80,6 @@ namespace ephysics {
void getTriangleVerticesWithIndexPointer(int32_t _subPart, void getTriangleVerticesWithIndexPointer(int32_t _subPart,
int32_t _triangleIndex, int32_t _triangleIndex,
vec3* _outTriangleVertices) const; vec3* _outTriangleVertices) const;
public:
/// Constructor
ConcaveMeshShape(TriangleMesh* triangleMesh);
virtual void getLocalBounds(vec3& min, vec3& max) const override;
virtual void setLocalScaling(const vec3& scaling) override;
virtual void computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const override;
/// Use a callback method on all triangles of the concave shape inside a given AABB
virtual void testAllTriangles(TriangleCallback& callback, const AABB& localAABB) const override;
friend class ConvexTriangleAABBOverlapCallback;
friend class ConcaveMeshRaycastCallback;
}; };
} }

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/shapes/ConcaveShape.hpp> #include <ephysics/collision/shapes/ConcaveShape.hpp>
@ -12,54 +13,38 @@
using namespace ephysics; using namespace ephysics;
// Constructor // Constructor
ConcaveShape::ConcaveShape(CollisionShapeType type) ConcaveShape::ConcaveShape(CollisionShapeType _type):
: CollisionShape(type), m_isSmoothMeshCollisionEnabled(false), CollisionShape(_type),
m_triangleMargin(0), m_raycastTestType(FRONT) { m_isSmoothMeshCollisionEnabled(false),
m_triangleMargin(0),
m_raycastTestType(FRONT) {
} }
// Destructor
ConcaveShape::~ConcaveShape() {
}
// Return the triangle margin
float ConcaveShape::getTriangleMargin() const { float ConcaveShape::getTriangleMargin() const {
return m_triangleMargin; return m_triangleMargin;
} }
/// Return true if the collision shape is convex, false if it is concave
bool ConcaveShape::isConvex() const { bool ConcaveShape::isConvex() const {
return false; return false;
} }
// Return true if a point is inside the collision shape bool ConcaveShape::testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const {
bool ConcaveShape::testPointInside(const vec3& localPoint, ProxyShape* proxyShape) const {
return false; return false;
} }
// Return true if the smooth mesh collision is enabled
bool ConcaveShape::getIsSmoothMeshCollisionEnabled() const { bool ConcaveShape::getIsSmoothMeshCollisionEnabled() const {
return m_isSmoothMeshCollisionEnabled; return m_isSmoothMeshCollisionEnabled;
} }
// Enable/disable the smooth mesh collision algorithm void ConcaveShape::setIsSmoothMeshCollisionEnabled(bool _isEnabled) {
/// Smooth mesh collision is used to avoid collisions against some int32_ternal edges m_isSmoothMeshCollisionEnabled = _isEnabled;
/// of the triangle mesh. If it is enabled, collsions with the mesh will be smoother
/// but collisions computation is a bit more expensive.
void ConcaveShape::setIsSmoothMeshCollisionEnabled(bool isEnabled) {
m_isSmoothMeshCollisionEnabled = isEnabled;
} }
// Return the raycast test type (front, back, front-back)
TriangleRaycastSide ConcaveShape::getRaycastTestType() const { TriangleRaycastSide ConcaveShape::getRaycastTestType() const {
return m_raycastTestType; return m_raycastTestType;
} }
// Set the raycast test type (front, back, front-back) void ConcaveShape::setRaycastTestType(TriangleRaycastSide _testType) {
/** m_raycastTestType = _testType;
* @param testType Raycast test type for the triangle (front, back, front-back)
*/
void ConcaveShape::setRaycastTestType(TriangleRaycastSide testType) {
m_raycastTestType = testType;
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -25,25 +28,27 @@ namespace ephysics {
* body that is used during the narrow-phase collision detection. * body that is used during the narrow-phase collision detection.
*/ */
class ConcaveShape : public CollisionShape { class ConcaveShape : public CollisionShape {
public :
/// Constructor
ConcaveShape(CollisionShapeType _type);
/// DELETE copy-constructor
ConcaveShape(const ConcaveShape& _shape) = delete;
/// DELETE assignment operator
ConcaveShape& operator=(const ConcaveShape& _shape) = delete;
protected : protected :
bool m_isSmoothMeshCollisionEnabled; //!< True if the smooth mesh collision algorithm is enabled bool m_isSmoothMeshCollisionEnabled; //!< True if the smooth mesh collision algorithm is enabled
float m_triangleMargin; //!< Margin use for collision detection for each triangle float m_triangleMargin; //!< Margin use for collision detection for each triangle
TriangleRaycastSide m_raycastTestType; //!< Raycast test type for the triangle (front, back, front-back) TriangleRaycastSide m_raycastTestType; //!< Raycast test type for the triangle (front, back, front-back)
/// Private copy-constructor bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
ConcaveShape(const ConcaveShape& _shape) = delete; public:
/// Private assignment operator
ConcaveShape& operator=(const ConcaveShape& _shape) = delete;
virtual bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
public :
/// Constructor
ConcaveShape(CollisionShapeType _type);
/// Destructor
virtual ~ConcaveShape();
/// Return the triangle margin /// Return the triangle margin
float getTriangleMargin() const; float getTriangleMargin() const;
/// Return the raycast test type (front, back, front-back) /// Return the raycast test type (front, back, front-back)
TriangleRaycastSide getRaycastTestType() const; TriangleRaycastSide getRaycastTestType() const;
// Set the raycast test type (front, back, front-back) /**
* @brief Set the raycast test type (front, back, front-back)
* @param testType Raycast test type for the triangle (front, back, front-back)
*/
void setRaycastTestType(TriangleRaycastSide _testType); void setRaycastTestType(TriangleRaycastSide _testType);
/// Return true if the collision shape is convex, false if it is concave /// Return true if the collision shape is convex, false if it is concave
virtual bool isConvex() const override; virtual bool isConvex() const override;
@ -51,7 +56,12 @@ namespace ephysics {
virtual void testAllTriangles(TriangleCallback& _callback, const AABB& _localAABB) const=0; virtual void testAllTriangles(TriangleCallback& _callback, const AABB& _localAABB) const=0;
/// Return true if the smooth mesh collision is enabled /// Return true if the smooth mesh collision is enabled
bool getIsSmoothMeshCollisionEnabled() const; bool getIsSmoothMeshCollisionEnabled() const;
/// Enable/disable the smooth mesh collision algorithm /**
* @brief Enable/disable the smooth mesh collision algorithm
*
* Smooth mesh collision is used to avoid collisions against some int32_ternal edges of the triangle mesh.
* If it is enabled, collsions with the mesh will be smoother but collisions computation is a bit more expensive.
*/
void setIsSmoothMeshCollisionEnabled(bool _isEnabled); void setIsSmoothMeshCollisionEnabled(bool _isEnabled);
}; };

View File

@ -1,66 +1,47 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
#include <ephysics/collision/shapes/ConeShape.hpp> #include <ephysics/collision/shapes/ConeShape.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
using namespace ephysics; using namespace ephysics;
// Constructor ConeShape::ConeShape(float _radius, float _height, float _margin):
/** ConvexShape(CONE, _margin),
* @param radius Radius of the cone (in meters) m_radius(_radius),
* @param height Height of the cone (in meters) m_halfHeight(_height * 0.5f) {
* @param margin Collision margin (in meters) around the collision shape
*/
ConeShape::ConeShape(float radius, float height, float margin):
ConvexShape(CONE, margin),
m_radius(radius),
m_halfHeight(height * 0.5f) {
assert(m_radius > 0.0f); assert(m_radius > 0.0f);
assert(m_halfHeight > 0.0f); assert(m_halfHeight > 0.0f);
// Compute the sine of the semi-angle at the apex point // Compute the sine of the semi-angle at the apex point
m_sinTheta = m_radius / (sqrt(m_radius * m_radius + height * height)); m_sinTheta = m_radius / (sqrt(m_radius * m_radius + _height * _height));
} }
// Return a local support point in a given direction without the object margin vec3 ConeShape::getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const {
vec3 ConeShape::getLocalSupportPointWithoutMargin(const vec3& direction, const vec3& v = _direction;
void** cachedCollisionData) const {
const vec3& v = direction;
float sinThetaTimesLengthV = m_sinTheta * v.length(); float sinThetaTimesLengthV = m_sinTheta * v.length();
vec3 supportPoint; vec3 supportPoint;
if (v.y() > sinThetaTimesLengthV) { if (v.y() > sinThetaTimesLengthV) {
supportPoint = vec3(0.0, m_halfHeight, 0.0); supportPoint = vec3(0.0, m_halfHeight, 0.0);
} } else {
else {
float projectedLength = sqrt(v.x() * v.x() + v.z() * v.z()); float projectedLength = sqrt(v.x() * v.x() + v.z() * v.z());
if (projectedLength > FLT_EPSILON) { if (projectedLength > FLT_EPSILON) {
float d = m_radius / projectedLength; float d = m_radius / projectedLength;
supportPoint = vec3(v.x() * d, -m_halfHeight, v.z() * d); supportPoint = vec3(v.x() * d, -m_halfHeight, v.z() * d);
} } else {
else {
supportPoint = vec3(0.0, -m_halfHeight, 0.0); supportPoint = vec3(0.0, -m_halfHeight, 0.0);
} }
} }
return supportPoint; return supportPoint;
} }
// Raycast method with feedback information bool ConeShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
// This implementation is based on the technique described by David Eberly in the article const vec3 r = _ray.point2 - _ray.point1;
// "Intersection of a Line and a Cone" that can be found at
// http://www.geometrictools.com/Documentation/IntersectionLineCone.pdf
bool ConeShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const {
const vec3 r = ray.point2 - ray.point1;
const float epsilon = float(0.00001); const float epsilon = float(0.00001);
vec3 V(0, m_halfHeight, 0); vec3 V(0, m_halfHeight, 0);
vec3 centerBase(0, -m_halfHeight, 0); vec3 centerBase(0, -m_halfHeight, 0);
@ -68,57 +49,47 @@ bool ConeShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* pr
float heightSquare = float(4.0) * m_halfHeight * m_halfHeight; float heightSquare = float(4.0) * m_halfHeight * m_halfHeight;
float cosThetaSquare = heightSquare / (heightSquare + m_radius * m_radius); float cosThetaSquare = heightSquare / (heightSquare + m_radius * m_radius);
float factor = 1.0f - cosThetaSquare; float factor = 1.0f - cosThetaSquare;
vec3 delta = ray.point1 - V; vec3 delta = _ray.point1 - V;
float c0 = -cosThetaSquare * delta.x() * delta.x() + factor * delta.y() * delta.y() - float c0 = -cosThetaSquare * delta.x() * delta.x() + factor * delta.y() * delta.y() - cosThetaSquare * delta.z() * delta.z();
cosThetaSquare * delta.z() * delta.z();
float c1 = -cosThetaSquare * delta.x() * r.x() + factor * delta.y() * r.y() - cosThetaSquare * delta.z() * r.z(); float c1 = -cosThetaSquare * delta.x() * r.x() + factor * delta.y() * r.y() - cosThetaSquare * delta.z() * r.z();
float c2 = -cosThetaSquare * r.x() * r.x() + factor * r.y() * r.y() - cosThetaSquare * r.z() * r.z(); float c2 = -cosThetaSquare * r.x() * r.x() + factor * r.y() * r.y() - cosThetaSquare * r.z() * r.z();
float tHit[] = {float(-1.0), float(-1.0), float(-1.0)}; float tHit[] = {float(-1.0), float(-1.0), float(-1.0)};
vec3 localHitPoint[3]; vec3 localHitPoint[3];
vec3 localNormal[3]; vec3 localNormal[3];
// If c2 is different from zero // If c2 is different from zero
if (etk::abs(c2) > FLT_EPSILON) { if (etk::abs(c2) > FLT_EPSILON) {
float gamma = c1 * c1 - c0 * c2; float gamma = c1 * c1 - c0 * c2;
// If there is no real roots in the quadratic equation // If there is no real roots in the quadratic equation
if (gamma < 0.0f) { if (gamma < 0.0f) {
return false; return false;
} } else if (gamma > 0.0f) { // The equation has two real roots
else if (gamma > 0.0f) { // The equation has two real roots
// Compute two int32_tersections // Compute two int32_tersections
float sqrRoot = etk::sqrt(gamma); float sqrRoot = etk::sqrt(gamma);
tHit[0] = (-c1 - sqrRoot) / c2; tHit[0] = (-c1 - sqrRoot) / c2;
tHit[1] = (-c1 + sqrRoot) / c2; tHit[1] = (-c1 + sqrRoot) / c2;
} } else { // If the equation has a single real root
else { // If the equation has a single real root
// Compute the int32_tersection // Compute the int32_tersection
tHit[0] = -c1 / c2; tHit[0] = -c1 / c2;
} }
} } else {
else { // If c2 == 0 // If c2 == 0
// If c2 = 0 and c1 != 0
if (etk::abs(c1) > FLT_EPSILON) { if (etk::abs(c1) > FLT_EPSILON) {
// If c2 = 0 and c1 != 0
tHit[0] = -c0 / (float(2.0) * c1); tHit[0] = -c0 / (float(2.0) * c1);
} } else {
else { // If c2 = c1 = 0 // If c2 = c1 = 0
// If c0 is different from zero, no solution and if c0 = 0, we have a // If c0 is different from zero, no solution and if c0 = 0, we have a
// degenerate case, the whole ray is contained in the cone side // degenerate case, the whole ray is contained in the cone side
// but we return no hit in this case // but we return no hit in this case
return false; return false;
} }
} }
// If the origin of the ray is inside the cone, we return no hit // If the origin of the ray is inside the cone, we return no hit
if (testPointInside(ray.point1, NULL)) return false; if (testPointInside(_ray.point1, NULL)) {
return false;
localHitPoint[0] = ray.point1 + tHit[0] * r; }
localHitPoint[1] = ray.point1 + tHit[1] * r; localHitPoint[0] = _ray.point1 + tHit[0] * r;
localHitPoint[1] = _ray.point1 + tHit[1] * r;
// Only keep hit points in one side of the double cone (the cone we are int32_terested in) // Only keep hit points in one side of the double cone (the cone we are int32_terested in)
if (axis.dot(localHitPoint[0] - V) < 0.0f) { if (axis.dot(localHitPoint[0] - V) < 0.0f) {
tHit[0] = float(-1.0); tHit[0] = float(-1.0);
@ -126,7 +97,6 @@ bool ConeShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* pr
if (axis.dot(localHitPoint[1] - V) < 0.0f) { if (axis.dot(localHitPoint[1] - V) < 0.0f) {
tHit[1] = float(-1.0); tHit[1] = float(-1.0);
} }
// Only keep hit points that are within the correct height of the cone // Only keep hit points that are within the correct height of the cone
if (localHitPoint[0].y() < float(-m_halfHeight)) { if (localHitPoint[0].y() < float(-m_halfHeight)) {
tHit[0] = float(-1.0); tHit[0] = float(-1.0);
@ -134,43 +104,40 @@ bool ConeShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* pr
if (localHitPoint[1].y() < float(-m_halfHeight)) { if (localHitPoint[1].y() < float(-m_halfHeight)) {
tHit[1] = float(-1.0); tHit[1] = float(-1.0);
} }
// If the ray is in direction of the base plane of the cone // If the ray is in direction of the base plane of the cone
if (r.y() > epsilon) { if (r.y() > epsilon) {
// Compute the int32_tersection with the base plane of the cone // Compute the int32_tersection with the base plane of the cone
tHit[2] = (-ray.point1.y() - m_halfHeight) / (r.y()); tHit[2] = (-_ray.point1.y() - m_halfHeight) / (r.y());
// Only keep this int32_tersection if it is inside the cone radius // Only keep this int32_tersection if it is inside the cone radius
localHitPoint[2] = ray.point1 + tHit[2] * r; localHitPoint[2] = _ray.point1 + tHit[2] * r;
if ((localHitPoint[2] - centerBase).length2() > m_radius * m_radius) { if ((localHitPoint[2] - centerBase).length2() > m_radius * m_radius) {
tHit[2] = float(-1.0); tHit[2] = float(-1.0);
} }
// Compute the normal direction // Compute the normal direction
localNormal[2] = axis; localNormal[2] = axis;
} }
// Find the smallest positive t value // Find the smallest positive t value
int32_t hitIndex = -1; int32_t hitIndex = -1;
float t = FLT_MAX; float t = FLT_MAX;
for (int32_t i=0; i<3; i++) { for (int32_t i=0; i<3; i++) {
if (tHit[i] < 0.0f) continue; if (tHit[i] < 0.0f) {
continue;
}
if (tHit[i] < t) { if (tHit[i] < t) {
hitIndex = i; hitIndex = i;
t = tHit[hitIndex]; t = tHit[hitIndex];
} }
} }
if (hitIndex < 0) {
if (hitIndex < 0) return false; return false;
if (t > ray.maxFraction) return false; }
if (t > _ray.maxFraction) {
return false;
}
// Compute the normal direction for hit against side of the cone // Compute the normal direction for hit against side of the cone
if (hitIndex != 2) { if (hitIndex != 2) {
float h = float(2.0) * m_halfHeight; float h = float(2.0) * m_halfHeight;
float value1 = (localHitPoint[hitIndex].x() * localHitPoint[hitIndex].x() + float value1 = (localHitPoint[hitIndex].x() * localHitPoint[hitIndex].x() + localHitPoint[hitIndex].z() * localHitPoint[hitIndex].z());
localHitPoint[hitIndex].z() * localHitPoint[hitIndex].z());
float rOverH = m_radius / h; float rOverH = m_radius / h;
float value2 = 1.0f + rOverH * rOverH; float value2 = 1.0f + rOverH * rOverH;
float factor = 1.0f / etk::sqrt(value1 * value2); float factor = 1.0f / etk::sqrt(value1 * value2);
@ -180,82 +147,56 @@ bool ConeShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* pr
localNormal[hitIndex].setY(etk::sqrt(x * x + z * z) * rOverH); localNormal[hitIndex].setY(etk::sqrt(x * x + z * z) * rOverH);
localNormal[hitIndex].setZ(z); localNormal[hitIndex].setZ(z);
} }
_raycastInfo.body = _proxyShape->getBody();
raycastInfo.body = proxyShape->getBody(); _raycastInfo.proxyShape = _proxyShape;
raycastInfo.proxyShape = proxyShape; _raycastInfo.hitFraction = t;
raycastInfo.hitFraction = t; _raycastInfo.worldPoint = localHitPoint[hitIndex];
raycastInfo.worldPoint = localHitPoint[hitIndex]; _raycastInfo.worldNormal = localNormal[hitIndex];
raycastInfo.worldNormal = localNormal[hitIndex];
return true; return true;
} }
// Return the radius
/**
* @return Radius of the cone (in meters)
*/
float ConeShape::getRadius() const { float ConeShape::getRadius() const {
return m_radius; return m_radius;
} }
// Return the height
/**
* @return Height of the cone (in meters)
*/
float ConeShape::getHeight() const { float ConeShape::getHeight() const {
return float(2.0) * m_halfHeight; return float(2.0) * m_halfHeight;
} }
// Set the scaling vector of the collision shape void ConeShape::setLocalScaling(const vec3& _scaling) {
void ConeShape::setLocalScaling(const vec3& scaling) { m_halfHeight = (m_halfHeight / m_scaling.y()) * _scaling.y();
m_radius = (m_radius / m_scaling.x()) * _scaling.x();
m_halfHeight = (m_halfHeight / m_scaling.y()) * scaling.y(); CollisionShape::setLocalScaling(_scaling);
m_radius = (m_radius / m_scaling.x()) * scaling.x();
CollisionShape::setLocalScaling(scaling);
} }
// Return the number of bytes used by the collision shape
size_t ConeShape::getSizeInBytes() const { size_t ConeShape::getSizeInBytes() const {
return sizeof(ConeShape); return sizeof(ConeShape);
} }
// Return the local bounds of the shape in x, y and z directions void ConeShape::getLocalBounds(vec3& _min, vec3& _max) const {
/**
* @param min The minimum bounds of the shape in local-space coordinates
* @param max The maximum bounds of the shape in local-space coordinates
*/
void ConeShape::getLocalBounds(vec3& min, vec3& max) const {
// Maximum bounds // Maximum bounds
max.setX(m_radius + m_margin); _max.setX(m_radius + m_margin);
max.setY(m_halfHeight + m_margin); _max.setY(m_halfHeight + m_margin);
max.setZ(max.x()); _max.setZ(_max.x());
// Minimum bounds // Minimum bounds
min.setX(-max.x()); _min.setX(-_max.x());
min.setY(-max.y()); _min.setY(-_max.y());
min.setZ(min.x()); _min.setZ(_min.x());
} }
// Return the local inertia tensor of the collision shape void ConeShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const {
/**
* @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
*/
void ConeShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const {
float rSquare = m_radius * m_radius; float rSquare = m_radius * m_radius;
float diagXZ = float(0.15) * mass * (rSquare + m_halfHeight); float diagXZ = float(0.15) * _mass * (rSquare + m_halfHeight);
tensor.setValue(diagXZ, 0.0, 0.0, _tensor.setValue(diagXZ, 0.0, 0.0,
0.0, float(0.3) * mass * rSquare, 0.0, float(0.3) * _mass * rSquare,
0.0, 0.0, 0.0, diagXZ); 0.0, 0.0, 0.0, diagXZ);
} }
// Return true if a point is inside the collision shape bool ConeShape::testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const {
bool ConeShape::testPointInside(const vec3& localPoint, ProxyShape* proxyShape) const { const float radiusHeight = m_radius
const float radiusHeight = m_radius * (-localPoint.y() + m_halfHeight) / * (-_localPoint.y() + m_halfHeight)
(m_halfHeight * float(2.0)); / (m_halfHeight * float(2.0));
return (localPoint.y() < m_halfHeight && localPoint.y() > -m_halfHeight) && return ( _localPoint.y() < m_halfHeight
(localPoint.x() * localPoint.x() + localPoint.z() * localPoint.z() < radiusHeight *radiusHeight); && _localPoint.y() > -m_halfHeight)
&& (_localPoint.x() * _localPoint.x() + _localPoint.z() * _localPoint.z() < radiusHeight *radiusHeight);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -25,24 +28,36 @@ namespace ephysics {
* default margin distance by not using the "margin" parameter in the constructor. * default margin distance by not using the "margin" parameter in the constructor.
*/ */
class ConeShape : public ConvexShape { class ConeShape : public ConvexShape {
public :
/**
* @brief Constructor
* @param _radius Radius of the cone (in meters)
* @param _height Height of the cone (in meters)
* @param _margin Collision margin (in meters) around the collision shape
*/
ConeShape(float _radius, float _height, float _margin = OBJECT_MARGIN);
/// DELETE copy-constructor
ConeShape(const ConeShape& _shape) = delete;
/// DELETE assignment operator
ConeShape& operator=(const ConeShape& _shape) = delete;
protected : protected :
float m_radius; //!< Radius of the base float m_radius; //!< Radius of the base
float m_halfHeight; //!< Half height of the cone float m_halfHeight; //!< Half height of the cone
float m_sinTheta; //!< sine of the semi angle at the apex point float m_sinTheta; //!< sine of the semi angle at the apex point
/// Private copy-constructor
ConeShape(const ConeShape& _shape) = delete;
/// Private assignment operator
ConeShape& operator=(const ConeShape& _shape) = delete;
virtual vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override; virtual vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override;
bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override; bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override; bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
size_t getSizeInBytes() const override; size_t getSizeInBytes() const override;
public : public:
/// Constructor /**
ConeShape(float _radius, float _height, float _margin = OBJECT_MARGIN); * @brief Return the radius
/// Return the radius * @return Radius of the cone (in meters)
*/
float getRadius() const; float getRadius() const;
/// Return the height /**
* @brief Return the height
* @return Height of the cone (in meters)
*/
float getHeight() const; float getHeight() const;
void setLocalScaling(const vec3& _scaling) override; void setLocalScaling(const vec3& _scaling) override;

View File

@ -1,31 +1,41 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
#include <ephysics/collision/shapes/ConvexMeshShape.hpp> #include <ephysics/collision/shapes/ConvexMeshShape.hpp>
using namespace ephysics; using namespace ephysics;
ConvexMeshShape::ConvexMeshShape(const float* arrayVertices, uint32_t nbVertices, int32_t stride, float margin) ConvexMeshShape::ConvexMeshShape(const float* _arrayVertices,
: ConvexShape(CONVEX_MESH, margin), m_numberVertices(nbVertices), m_minBounds(0, 0, 0), uint32_t _nbVertices,
m_maxBounds(0, 0, 0), m_isEdgesInformationUsed(false) { int32_t _stride,
assert(nbVertices > 0); float _margin):
assert(stride > 0); ConvexShape(CONVEX_MESH, _margin),
const unsigned char* vertexPointer = (const unsigned char*) arrayVertices; m_numberVertices(_nbVertices),
m_minBounds(0, 0, 0),
m_maxBounds(0, 0, 0),
m_isEdgesInformationUsed(false) {
assert(_nbVertices > 0);
assert(_stride > 0);
const unsigned char* vertexPointer = (const unsigned char*) _arrayVertices;
// Copy all the vertices int32_to the int32_ternal array // Copy all the vertices int32_to the int32_ternal array
for (uint32_t i=0; i<m_numberVertices; i++) { for (uint32_t iii=0; iii<m_numberVertices; iii++) {
const float* newPoint = (const float*) vertexPointer; const float* newPoint = (const float*) vertexPointer;
m_vertices.pushBack(vec3(newPoint[0], newPoint[1], newPoint[2])); m_vertices.pushBack(vec3(newPoint[0], newPoint[1], newPoint[2]));
vertexPointer += stride; vertexPointer += _stride;
} }
// Recalculate the bounds of the mesh // Recalculate the bounds of the mesh
recalculateBounds(); recalculateBounds();
} }
ConvexMeshShape::ConvexMeshShape(TriangleVertexArray* _triangleVertexArray, bool _isEdgesInformationUsed, float _margin): ConvexMeshShape::ConvexMeshShape(TriangleVertexArray* _triangleVertexArray,
bool _isEdgesInformationUsed,
float _margin):
ConvexShape(CONVEX_MESH, _margin), ConvexShape(CONVEX_MESH, _margin),
m_minBounds(0, 0, 0), m_minBounds(0, 0, 0),
m_maxBounds(0, 0, 0), m_maxBounds(0, 0, 0),
@ -52,9 +62,6 @@ ConvexMeshShape::ConvexMeshShape(TriangleVertexArray* _triangleVertexArray, bool
recalculateBounds(); recalculateBounds();
} }
// Constructor.
/// If you use this constructor, you will need to set the vertices manually one by one using
/// the addVertex() method.
ConvexMeshShape::ConvexMeshShape(float _margin): ConvexMeshShape::ConvexMeshShape(float _margin):
ConvexShape(CONVEX_MESH, _margin), ConvexShape(CONVEX_MESH, _margin),
m_numberVertices(0), m_numberVertices(0),
@ -64,11 +71,12 @@ ConvexMeshShape::ConvexMeshShape(float _margin):
} }
vec3 ConvexMeshShape::getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const { vec3 ConvexMeshShape::getLocalSupportPointWithoutMargin(const vec3& _direction,
void** _cachedCollisionData) const {
assert(m_numberVertices == m_vertices.size()); assert(m_numberVertices == m_vertices.size());
assert(_cachedCollisionData != nullptr); assert(_cachedCollisionData != null);
// Allocate memory for the cached collision data if not allocated yet // Allocate memory for the cached collision data if not allocated yet
if ((*_cachedCollisionData) == nullptr) { if ((*_cachedCollisionData) == null) {
*_cachedCollisionData = (int32_t*) malloc(sizeof(int32_t)); *_cachedCollisionData = (int32_t*) malloc(sizeof(int32_t));
*((int32_t*)(*_cachedCollisionData)) = 0; *((int32_t*)(*_cachedCollisionData)) = 0;
} }
@ -156,12 +164,12 @@ void ConvexMeshShape::recalculateBounds() {
m_minBounds -= vec3(m_margin, m_margin, m_margin); m_minBounds -= vec3(m_margin, m_margin, m_margin);
} }
bool ConvexMeshShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const { bool ConvexMeshShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
return proxyShape->m_body->m_world.m_collisionDetection.m_narrowPhaseGJKAlgorithm.raycast(ray, proxyShape, raycastInfo); return _proxyShape->m_body->m_world.m_collisionDetection.m_narrowPhaseGJKAlgorithm.raycast(_ray, _proxyShape, _raycastInfo);
} }
void ConvexMeshShape::setLocalScaling(const vec3& scaling) { void ConvexMeshShape::setLocalScaling(const vec3& _scaling) {
ConvexShape::setLocalScaling(scaling); ConvexShape::setLocalScaling(_scaling);
recalculateBounds(); recalculateBounds();
} }
@ -169,73 +177,72 @@ size_t ConvexMeshShape::getSizeInBytes() const {
return sizeof(ConvexMeshShape); return sizeof(ConvexMeshShape);
} }
void ConvexMeshShape::getLocalBounds(vec3& min, vec3& max) const { void ConvexMeshShape::getLocalBounds(vec3& _min, vec3& _max) const {
min = m_minBounds; _min = m_minBounds;
max = m_maxBounds; _max = m_maxBounds;
} }
void ConvexMeshShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const { void ConvexMeshShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const {
float factor = (1.0f / float(3.0)) * mass; float factor = (1.0f / float(3.0)) * _mass;
vec3 realExtent = 0.5f * (m_maxBounds - m_minBounds); vec3 realExtent = 0.5f * (m_maxBounds - m_minBounds);
assert(realExtent.x() > 0 && realExtent.y() > 0 && realExtent.z() > 0); assert(realExtent.x() > 0 && realExtent.y() > 0 && realExtent.z() > 0);
float xSquare = realExtent.x() * realExtent.x(); float xSquare = realExtent.x() * realExtent.x();
float ySquare = realExtent.y() * realExtent.y(); float ySquare = realExtent.y() * realExtent.y();
float zSquare = realExtent.z() * realExtent.z(); float zSquare = realExtent.z() * realExtent.z();
tensor.setValue(factor * (ySquare + zSquare), 0.0, 0.0, _tensor.setValue(factor * (ySquare + zSquare), 0.0, 0.0,
0.0, factor * (xSquare + zSquare), 0.0, 0.0, factor * (xSquare + zSquare), 0.0,
0.0, 0.0, factor * (xSquare + ySquare)); 0.0, 0.0, factor * (xSquare + ySquare));
} }
void ConvexMeshShape::addVertex(const vec3& vertex) { void ConvexMeshShape::addVertex(const vec3& _vertex) {
// Add the vertex in to vertices array // Add the vertex in to vertices array
m_vertices.pushBack(vertex); m_vertices.pushBack(_vertex);
m_numberVertices++; m_numberVertices++;
// Update the bounds of the mesh // Update the bounds of the mesh
if (vertex.x() * m_scaling.x() > m_maxBounds.x()) { if (_vertex.x() * m_scaling.x() > m_maxBounds.x()) {
m_maxBounds.setX(vertex.x() * m_scaling.x()); m_maxBounds.setX(_vertex.x() * m_scaling.x());
} }
if (vertex.x() * m_scaling.x() < m_minBounds.x()) { if (_vertex.x() * m_scaling.x() < m_minBounds.x()) {
m_minBounds.setX(vertex.x() * m_scaling.x()); m_minBounds.setX(_vertex.x() * m_scaling.x());
} }
if (vertex.y() * m_scaling.y() > m_maxBounds.y()) { if (_vertex.y() * m_scaling.y() > m_maxBounds.y()) {
m_maxBounds.setY(vertex.y() * m_scaling.y()); m_maxBounds.setY(_vertex.y() * m_scaling.y());
} }
if (vertex.y() * m_scaling.y() < m_minBounds.y()) { if (_vertex.y() * m_scaling.y() < m_minBounds.y()) {
m_minBounds.setY(vertex.y() * m_scaling.y()); m_minBounds.setY(_vertex.y() * m_scaling.y());
} }
if (vertex.z() * m_scaling.z() > m_maxBounds.z()) { if (_vertex.z() * m_scaling.z() > m_maxBounds.z()) {
m_maxBounds.setZ(vertex.z() * m_scaling.z()); m_maxBounds.setZ(_vertex.z() * m_scaling.z());
} }
if (vertex.z() * m_scaling.z() < m_minBounds.z()) { if (_vertex.z() * m_scaling.z() < m_minBounds.z()) {
m_minBounds.setZ(vertex.z() * m_scaling.z()); m_minBounds.setZ(_vertex.z() * m_scaling.z());
} }
} }
void ConvexMeshShape::addEdge(uint32_t v1, uint32_t v2) { void ConvexMeshShape::addEdge(uint32_t _v1, uint32_t _v2) {
// If the entry for vertex v1 does not exist in the adjacency list // If the entry for vertex v1 does not exist in the adjacency list
if (m_edgesAdjacencyList.count(v1) == 0) { if (m_edgesAdjacencyList.count(_v1) == 0) {
m_edgesAdjacencyList.add(v1, etk::Set<uint32_t>()); m_edgesAdjacencyList.add(_v1, etk::Set<uint32_t>());
} }
// If the entry for vertex v2 does not exist in the adjacency list // If the entry for vertex v2 does not exist in the adjacency list
if (m_edgesAdjacencyList.count(v2) == 0) { if (m_edgesAdjacencyList.count(_v2) == 0) {
m_edgesAdjacencyList.add(v2, etk::Set<uint32_t>()); m_edgesAdjacencyList.add(_v2, etk::Set<uint32_t>());
} }
// Add the edge in the adjacency list // Add the edge in the adjacency list
m_edgesAdjacencyList[v1].add(v2); m_edgesAdjacencyList[_v1].add(_v2);
m_edgesAdjacencyList[v2].add(v1); m_edgesAdjacencyList[_v2].add(_v1);
} }
bool ConvexMeshShape::isEdgesInformationUsed() const { bool ConvexMeshShape::isEdgesInformationUsed() const {
return m_isEdgesInformationUsed; return m_isEdgesInformationUsed;
} }
void ConvexMeshShape::setIsEdgesInformationUsed(bool isEdgesUsed) { void ConvexMeshShape::setIsEdgesInformationUsed(bool _isEdgesUsed) {
m_isEdgesInformationUsed = isEdgesUsed; m_isEdgesInformationUsed = _isEdgesUsed;
} }
bool ConvexMeshShape::testPointInside(const vec3& localPoint, bool ConvexMeshShape::testPointInside(const vec3& _localPoint,
ProxyShape* proxyShape) const { ProxyShape* _proxyShape) const {
// Use the GJK algorithm to test if the point is inside the convex mesh // Use the GJK algorithm to test if the point is inside the convex mesh
return proxyShape->m_body->m_world.m_collisionDetection. return _proxyShape->m_body->m_world.m_collisionDetection.m_narrowPhaseGJKAlgorithm.testPointInside(_localPoint, _proxyShape);
m_narrowPhaseGJKAlgorithm.testPointInside(localPoint, proxyShape);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -51,7 +54,7 @@ namespace ephysics {
size_t getSizeInBytes() const override; size_t getSizeInBytes() const override;
public : public :
/** /**
* @brief Constructor to initialize with an array of 3D vertices. * @brief Constructor to initialize with an array of 3D vertices.
* This method creates an int32_ternal copy of the input vertices. * This method creates an int32_ternal copy of the input vertices.
* @param[in] _arrayVertices Array with the vertices of the convex mesh * @param[in] _arrayVertices Array with the vertices of the convex mesh
* @param[in] _nbVertices Number of vertices in the convex mesh * @param[in] _nbVertices Number of vertices in the convex mesh
@ -72,8 +75,12 @@ namespace ephysics {
ConvexMeshShape(TriangleVertexArray* _triangleVertexArray, ConvexMeshShape(TriangleVertexArray* _triangleVertexArray,
bool _isEdgesInformationUsed = true, bool _isEdgesInformationUsed = true,
float _margin = OBJECT_MARGIN); float _margin = OBJECT_MARGIN);
/// Constructor. /**
* @brief Constructor.
* If you use this constructor, you will need to set the vertices manually one by one using the addVertex() method.
*/
ConvexMeshShape(float _margin = OBJECT_MARGIN); ConvexMeshShape(float _margin = OBJECT_MARGIN);
public:
void getLocalBounds(vec3& _min, vec3& _max) const override; void getLocalBounds(vec3& _min, vec3& _max) const override;
void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override; void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override;
/** /**
@ -83,7 +90,7 @@ namespace ephysics {
void addVertex(const vec3& _vertex); void addVertex(const vec3& _vertex);
/** /**
* @brief Add an edge int32_to the convex mesh by specifying the two vertex indices of the edge. * @brief Add an edge int32_to the convex mesh by specifying the two vertex indices of the edge.
* Note that the vertex indices start at zero and need to correspond to the order of * @note that the vertex indices start at zero and need to correspond to the order of
* the vertices in the vertices array in the constructor or the order of the calls * the vertices in the vertices array in the constructor or the order of the calls
* of the addVertex() methods that you use to add vertices int32_to the convex mesh. * of the addVertex() methods that you use to add vertices int32_to the convex mesh.
* @param[in] _v1 Index of the first vertex of the edge to add * @param[in] _v1 Index of the first vertex of the edge to add

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/shapes/ConvexShape.hpp> #include <ephysics/collision/shapes/ConvexShape.hpp>
ephysics::ConvexShape::ConvexShape(ephysics::CollisionShapeType _type, float _margin): ephysics::ConvexShape::ConvexShape(ephysics::CollisionShapeType _type, float _margin):

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -11,23 +14,24 @@ namespace ephysics {
* @brief It represents a convex collision shape associated with a * @brief It represents a convex collision shape associated with a
* body that is used during the narrow-phase collision detection. * body that is used during the narrow-phase collision detection.
*/ */
class ConvexShape : public CollisionShape { class ConvexShape: public CollisionShape {
protected : protected:
float m_margin; //!< Margin used for the GJK collision detection algorithm float m_margin; //!< Margin used for the GJK collision detection algorithm
/// Private copy-constructor /// Private copy-constructor
ConvexShape(const ConvexShape& shape) = delete; ConvexShape(const ConvexShape& _shape) = delete;
/// Private assignment operator /// Private assignment operator
ConvexShape& operator=(const ConvexShape& shape) = delete; ConvexShape& operator=(const ConvexShape& _shape) = delete;
// Return a local support point in a given direction with the object margin // Return a local support point in a given direction with the object margin
virtual vec3 getLocalSupportPointWithMargin(const vec3& _direction, void** _cachedCollisionData) const; virtual vec3 getLocalSupportPointWithMargin(const vec3& _direction, void** _cachedCollisionData) const;
/// Return a local support point in a given direction without the object margin /// Return a local support point in a given direction without the object margin
virtual vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const=0; virtual vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const=0;
bool testPointInside(const vec3& _worldPoint, ProxyShape* _proxyShape) const override = 0; bool testPointInside(const vec3& _worldPoint, ProxyShape* _proxyShape) const override = 0;
public : public:
/// Constructor /// Constructor
ConvexShape(CollisionShapeType type, float margin); ConvexShape(CollisionShapeType _type, float _margin);
/// Destructor /// Destructor
virtual ~ConvexShape(); virtual ~ConvexShape();
public:
/** /**
* @brief Get the current object margin * @brief Get the current object margin
* @return The margin (in meters) around the collision shape * @return The margin (in meters) around the collision shape

View File

@ -1,30 +1,27 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/collision/shapes/CylinderShape.hpp> #include <ephysics/collision/shapes/CylinderShape.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
using namespace ephysics; using namespace ephysics;
/** CylinderShape::CylinderShape(float _radius,
* @param radius Radius of the cylinder (in meters) float _height,
* @param height Height of the cylinder (in meters) float _margin):
* @param margin Collision margin (in meters) around the collision shape ConvexShape(CYLINDER, _margin), m_radius(_radius), m_halfHeight(_height/float(2.0)) {
*/ assert(_radius > 0.0f);
CylinderShape::CylinderShape(float radius, float height, float margin) assert(_height > 0.0f);
: ConvexShape(CYLINDER, margin), mRadius(radius),
m_halfHeight(height/float(2.0)) {
assert(radius > 0.0f);
assert(height > 0.0f);
} }
vec3 CylinderShape::getLocalSupportPointWithoutMargin(const vec3& _direction,
// Return a local support point in a given direction without the object margin void** _cachedCollisionData) const {
vec3 CylinderShape::getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const {
vec3 supportPoint(0.0, 0.0, 0.0); vec3 supportPoint(0.0, 0.0, 0.0);
float uDotv = _direction.y(); float uDotv = _direction.y();
vec3 w(_direction.x(), 0.0, _direction.z()); vec3 w(_direction.x(), 0.0, _direction.z());
@ -35,7 +32,7 @@ vec3 CylinderShape::getLocalSupportPointWithoutMargin(const vec3& _direction, vo
} else { } else {
supportPoint.setY(m_halfHeight); supportPoint.setY(m_halfHeight);
} }
supportPoint += (mRadius / lengthW) * w; supportPoint += (m_radius / lengthW) * w;
} else { } else {
if (uDotv < 0.0) { if (uDotv < 0.0) {
supportPoint.setY(-m_halfHeight); supportPoint.setY(-m_halfHeight);
@ -46,234 +43,196 @@ vec3 CylinderShape::getLocalSupportPointWithoutMargin(const vec3& _direction, vo
return supportPoint; return supportPoint;
} }
// Raycast method with feedback information bool CylinderShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
/// Algorithm based on the one described at page 194 in Real-ime Collision Detection by const vec3 n = _ray.point2 - _ray.point1;
/// Morgan Kaufmann.
bool CylinderShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const {
const vec3 n = ray.point2 - ray.point1;
const float epsilon = float(0.01); const float epsilon = float(0.01);
vec3 p(float(0), -m_halfHeight, float(0)); vec3 p(float(0), -m_halfHeight, float(0));
vec3 q(float(0), m_halfHeight, float(0)); vec3 q(float(0), m_halfHeight, float(0));
vec3 d = q - p; vec3 d = q - p;
vec3 m = ray.point1 - p; vec3 m = _ray.point1 - p;
float t; float t;
float mDotD = m.dot(d); float mDotD = m.dot(d);
float nDotD = n.dot(d); float nDotD = n.dot(d);
float dDotD = d.dot(d); float dDotD = d.dot(d);
// Test if the segment is outside the cylinder // Test if the segment is outside the cylinder
if (mDotD < 0.0f && mDotD + nDotD < float(0.0)) return false; if (mDotD < 0.0f && mDotD + nDotD < float(0.0)) {
if (mDotD > dDotD && mDotD + nDotD > dDotD) return false; return false;
}
if (mDotD > dDotD && mDotD + nDotD > dDotD) {
return false;
}
float nDotN = n.dot(n); float nDotN = n.dot(n);
float mDotN = m.dot(n); float mDotN = m.dot(n);
float a = dDotD * nDotN - nDotD * nDotD; float a = dDotD * nDotN - nDotD * nDotD;
float k = m.dot(m) - mRadius * mRadius; float k = m.dot(m) - m_radius * m_radius;
float c = dDotD * k - mDotD * mDotD; float c = dDotD * k - mDotD * mDotD;
// If the ray is parallel to the cylinder axis // If the ray is parallel to the cylinder axis
if (etk::abs(a) < epsilon) { if (etk::abs(a) < epsilon) {
// If the origin is outside the surface of the cylinder, we return no hit // If the origin is outside the surface of the cylinder, we return no hit
if (c > 0.0f) return false; if (c > 0.0f) {
// Here we know that the segment int32_tersect an endcap of the cylinder
// If the ray int32_tersects with the "p" endcap of the cylinder
if (mDotD < 0.0f) {
t = -mDotN / nDotN;
// If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) return false;
// Compute the hit information
vec3 localHitPoint = ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, float(-1), 0);
raycastInfo.worldNormal = normalDirection;
return true;
}
else if (mDotD > dDotD) { // If the ray int32_tersects with the "q" endcap of the cylinder
t = (nDotD - mDotN) / nDotN;
// If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) return false;
// Compute the hit information
vec3 localHitPoint = ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody();
raycastInfo.proxyShape = proxyShape;
raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, 1.0f, 0);
raycastInfo.worldNormal = normalDirection;
return true;
}
else { // If the origin is inside the cylinder, we return no hit
return false; return false;
} }
// Here we know that the segment int32_tersect an endcap of the cylinder
// If the ray int32_tersects with the "p" endcap of the cylinder
if (mDotD < 0.0f) {
t = -mDotN / nDotN;
// If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > _ray.maxFraction) {
return false;
}
// Compute the hit information
vec3 localHitPoint = _ray.point1 + t * n;
_raycastInfo.body = _proxyShape->getBody();
_raycastInfo.proxyShape = _proxyShape;
_raycastInfo.hitFraction = t;
_raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, float(-1), 0);
_raycastInfo.worldNormal = normalDirection;
return true;
}
// If the ray int32_tersects with the "q" endcap of the cylinder
if (mDotD > dDotD) {
t = (nDotD - mDotN) / nDotN;
// If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit
if (t < 0.0f || t > _ray.maxFraction) {
return false;
}
// Compute the hit information
vec3 localHitPoint = _ray.point1 + t * n;
_raycastInfo.body = _proxyShape->getBody();
_raycastInfo.proxyShape = _proxyShape;
_raycastInfo.hitFraction = t;
_raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, 1.0f, 0);
_raycastInfo.worldNormal = normalDirection;
return true;
}
// If the origin is inside the cylinder, we return no hit
return false;
} }
float b = dDotD * mDotN - nDotD * mDotD; float b = dDotD * mDotN - nDotD * mDotD;
float discriminant = b * b - a * c; float discriminant = b * b - a * c;
// If the discriminant is negative, no real roots and therfore, no hit // If the discriminant is negative, no real roots and therfore, no hit
if (discriminant < 0.0f) return false; if (discriminant < 0.0f) {
return false;
}
// Compute the smallest root (first int32_tersection along the ray) // Compute the smallest root (first int32_tersection along the ray)
float t0 = t = (-b - etk::sqrt(discriminant)) / a; float t0 = t = (-b - etk::sqrt(discriminant)) / a;
// If the int32_tersection is outside the cylinder on "p" endcap side // If the int32_tersection is outside the cylinder on "p" endcap side
float value = mDotD + t * nDotD; float value = mDotD + t * nDotD;
if (value < 0.0f) { if (value < 0.0f) {
// If the ray is pointing away from the "p" endcap, we return no hit // If the ray is pointing away from the "p" endcap, we return no hit
if (nDotD <= 0.0f) return false; if (nDotD <= 0.0f) {
return false;
}
// Compute the int32_tersection against the "p" endcap (int32_tersection agains whole plane) // Compute the int32_tersection against the "p" endcap (int32_tersection agains whole plane)
t = -mDotD / nDotD; t = -mDotD / nDotD;
// Keep the int32_tersection if the it is inside the cylinder radius // Keep the int32_tersection if the it is inside the cylinder radius
if (k + t * (float(2.0) * mDotN + t) > 0.0f) return false; if (k + t * (float(2.0) * mDotN + t) > 0.0f) {
return false;
}
// If the int32_tersection is behind the origin of the ray or beyond the maximum // If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit // raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) return false; if (t < 0.0f || t > _ray.maxFraction) {
return false;
}
// Compute the hit information // Compute the hit information
vec3 localHitPoint = ray.point1 + t * n; vec3 localHitPoint = _ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = t; _raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint; _raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, float(-1.0), 0); vec3 normalDirection(0, float(-1.0), 0);
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
else if (value > dDotD) { // If the int32_tersection is outside the cylinder on the "q" side // If the int32_tersection is outside the cylinder on the "q" side
if (value > dDotD) {
// If the ray is pointing away from the "q" endcap, we return no hit // If the ray is pointing away from the "q" endcap, we return no hit
if (nDotD >= 0.0f) return false; if (nDotD >= 0.0f) {
return false;
}
// Compute the int32_tersection against the "q" endcap (int32_tersection against whole plane) // Compute the int32_tersection against the "q" endcap (int32_tersection against whole plane)
t = (dDotD - mDotD) / nDotD; t = (dDotD - mDotD) / nDotD;
// Keep the int32_tersection if it is inside the cylinder radius // Keep the int32_tersection if it is inside the cylinder radius
if (k + dDotD - float(2.0) * mDotD + t * (float(2.0) * (mDotN - nDotD) + t) > if (k + dDotD - float(2.0) * mDotD + t * (float(2.0) * (mDotN - nDotD) + t) > 0.0f) {
0.0f) return false; return false;
}
// If the int32_tersection is behind the origin of the ray or beyond the maximum // If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit // raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) return false; if (t < 0.0f || t > _ray.maxFraction) {
return false;
}
// Compute the hit information // Compute the hit information
vec3 localHitPoint = ray.point1 + t * n; vec3 localHitPoint = _ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = t; _raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint; _raycastInfo.worldPoint = localHitPoint;
vec3 normalDirection(0, 1.0f, 0); vec3 normalDirection(0, 1.0f, 0);
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
t = t0; t = t0;
// If the int32_tersection is behind the origin of the ray or beyond the maximum // If the int32_tersection is behind the origin of the ray or beyond the maximum
// raycasting distance, we return no hit // raycasting distance, we return no hit
if (t < 0.0f || t > ray.maxFraction) return false; if (t < 0.0f || t > _ray.maxFraction) {
return false;
}
// Compute the hit information // Compute the hit information
vec3 localHitPoint = ray.point1 + t * n; vec3 localHitPoint = _ray.point1 + t * n;
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = t; _raycastInfo.hitFraction = t;
raycastInfo.worldPoint = localHitPoint; _raycastInfo.worldPoint = localHitPoint;
vec3 v = localHitPoint - p; vec3 v = localHitPoint - p;
vec3 w = (v.dot(d) / d.length2()) * d; vec3 w = (v.dot(d) / d.length2()) * d;
vec3 normalDirection = (localHitPoint - (p + w)); vec3 normalDirection = (localHitPoint - (p + w));
raycastInfo.worldNormal = normalDirection; _raycastInfo.worldNormal = normalDirection;
return true; return true;
} }
// Return the radius
/**
* @return Radius of the cylinder (in meters)
*/
float CylinderShape::getRadius() const { float CylinderShape::getRadius() const {
return mRadius; return m_radius;
} }
// Return the height
/**
* @return Height of the cylinder (in meters)
*/
float CylinderShape::getHeight() const { float CylinderShape::getHeight() const {
return m_halfHeight + m_halfHeight; return m_halfHeight + m_halfHeight;
} }
// Set the scaling vector of the collision shape void CylinderShape::setLocalScaling(const vec3& _scaling) {
void CylinderShape::setLocalScaling(const vec3& scaling) { m_halfHeight = (m_halfHeight / m_scaling.y()) * _scaling.y();
m_radius = (m_radius / m_scaling.x()) * _scaling.x();
m_halfHeight = (m_halfHeight / m_scaling.y()) * scaling.y(); CollisionShape::setLocalScaling(_scaling);
mRadius = (mRadius / m_scaling.x()) * scaling.x();
CollisionShape::setLocalScaling(scaling);
} }
// Return the number of bytes used by the collision shape
size_t CylinderShape::getSizeInBytes() const { size_t CylinderShape::getSizeInBytes() const {
return sizeof(CylinderShape); return sizeof(CylinderShape);
} }
// Return the local bounds of the shape in x, y and z directions void CylinderShape::getLocalBounds(vec3& _min, vec3& _max) const {
/**
* @param min The minimum bounds of the shape in local-space coordinates
* @param max The maximum bounds of the shape in local-space coordinates
*/
void CylinderShape::getLocalBounds(vec3& min, vec3& max) const {
// Maximum bounds // Maximum bounds
max.setX(mRadius + m_margin); _max.setX(m_radius + m_margin);
max.setY(m_halfHeight + m_margin); _max.setY(m_halfHeight + m_margin);
max.setZ(max.x()); _max.setZ(_max.x());
// Minimum bounds // Minimum bounds
min.setX(-max.x()); _min.setX(-_max.x());
min.setY(-max.y()); _min.setY(-_max.y());
min.setZ(min.x()); _min.setZ(_min.x());
} }
// Return the local inertia tensor of the cylinder void CylinderShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const {
/**
* @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
*/
void CylinderShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const {
float height = float(2.0) * m_halfHeight; float height = float(2.0) * m_halfHeight;
float diag = (1.0f / float(12.0)) * mass * (3 * mRadius * mRadius + height * height); float diag = (1.0f / float(12.0)) * _mass * (3 * m_radius * m_radius + height * height);
tensor.setValue(diag, 0.0, 0.0, 0.0, _tensor.setValue(diag, 0.0, 0.0, 0.0,
0.5f * mass * mRadius * mRadius, 0.0, 0.5f * _mass * m_radius * m_radius, 0.0,
0.0, 0.0, diag); 0.0, 0.0, diag);
} }
// Return true if a point is inside the collision shape bool CylinderShape::testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const{
bool CylinderShape::testPointInside(const vec3& localPoint, ProxyShape* proxyShape) const{ return ( (_localPoint.x() * _localPoint.x() + _localPoint.z() * _localPoint.z()) < m_radius * m_radius
return ( (localPoint.x() * localPoint.x() + localPoint.z() * localPoint.z()) < mRadius * mRadius && _localPoint.y() < m_halfHeight
&& localPoint.y() < m_halfHeight && _localPoint.y() > -m_halfHeight);
&& localPoint.y() > -m_halfHeight);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -10,42 +13,53 @@
#include <ephysics/mathematics/mathematics.hpp> #include <ephysics/mathematics/mathematics.hpp>
namespace ephysics { namespace ephysics {
/** /**
* @brief It represents a cylinder collision shape around the Y axis * @brief It represents a cylinder collision shape around the Y axis
* and centered at the origin. The cylinder is defined by its height * and centered at the origin. The cylinder is defined by its height
* and the radius of its base. The "transform" of the corresponding * and the radius of its base. The "transform" of the corresponding
* rigid body gives an orientation and a position to the cylinder. * rigid body gives an orientation and a position to the cylinder.
* This collision shape uses an extra margin distance around it for collision * This collision shape uses an extra margin distance around it for collision
* detection purpose. The default margin is 4cm (if your units are meters, * detection purpose. The default margin is 4cm (if your units are meters,
* which is recommended). In case, you want to simulate small objects * which is recommended). In case, you want to simulate small objects
* (smaller than the margin distance), you might want to reduce the margin by * (smaller than the margin distance), you might want to reduce the margin by
* specifying your own margin distance using the "margin" parameter in the * specifying your own margin distance using the "margin" parameter in the
* constructor of the cylinder shape. Otherwise, it is recommended to use the * constructor of the cylinder shape. Otherwise, it is recommended to use the
* default margin distance by not using the "margin" parameter in the constructor. * default margin distance by not using the "margin" parameter in the constructor.
*/ */
class CylinderShape : public ConvexShape { class CylinderShape: public ConvexShape {
protected : protected:
float mRadius; //!< Radius of the base float m_radius; //!< Radius of the base
float m_halfHeight; //!< Half height of the cylinder float m_halfHeight; //!< Half height of the cylinder
/// Private copy-constructor /// DELETED copy-constructor
CylinderShape(const CylinderShape&) = delete; CylinderShape(const CylinderShape&) = delete;
/// Private assignment operator /// DELETED assignment operator
CylinderShape& operator=(const CylinderShape&) = delete; CylinderShape& operator=(const CylinderShape&) = delete;
vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override; vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override;
bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override; bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override; bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
size_t getSizeInBytes() const override; size_t getSizeInBytes() const override;
public : public:
/// Constructor /**
CylinderShape(float _radius, float _height, float _margin = OBJECT_MARGIN); * @brief Contructor
/// Return the radius * @param radius Radius of the cylinder (in meters)
float getRadius() const; * @param height Height of the cylinder (in meters)
/// Return the height * @param margin Collision margin (in meters) around the collision shape
float getHeight() const; */
void setLocalScaling(const vec3& _scaling) override; CylinderShape(float _radius, float _height, float _margin = OBJECT_MARGIN);
void getLocalBounds(vec3& _min, vec3& _max) const override; /**
void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override; * @breif Get the Shape radius
}; * @return Radius of the cylinder (in meters)
*/
float getRadius() const;
/**
* @breif Get the Shape height
* @return Height of the cylinder (in meters)
*/
float getHeight() const;
void setLocalScaling(const vec3& _scaling) override;
void getLocalBounds(vec3& _min, vec3& _max) const override;
void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override;
};
} }

View File

@ -1,235 +1,190 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/shapes/HeightFieldShape.hpp> #include <ephysics/collision/shapes/HeightFieldShape.hpp>
// TODO: REMOVE this...
using namespace ephysics; using namespace ephysics;
// Constructor HeightFieldShape::HeightFieldShape(int32_t _nbGridColumns,
/** int32_t _nbGridRows,
* @param nbGridColumns Number of columns in the grid of the height field float _minHeight,
* @param nbGridRows Number of rows in the grid of the height field float _maxHeight,
* @param minHeight Minimum height value of the height field const void* _heightFieldData,
* @param maxHeight Maximum height value of the height field HeightDataType _dataType,
* @param heightFieldData Pointer to the first height value data (note that values are shared and not copied) int32_t _upAxis,
* @param dataType Data type for the height values (int32_t, float, double) float _integerHeightScale):
* @param upAxis Integer representing the up axis direction (0 for x, 1 for y and 2 for z) ConcaveShape(HEIGHTFIELD),
* @param int32_tegerHeightScale Scaling factor used to scale the height values (only when height values type is int32_teger) m_numberColumns(_nbGridColumns),
*/ m_numberRows(_nbGridRows),
HeightFieldShape::HeightFieldShape(int32_t nbGridColumns, int32_t nbGridRows, float minHeight, float maxHeight, m_width(_nbGridColumns - 1),
const void* heightFieldData, HeightDataType dataType, int32_t upAxis, m_length(_nbGridRows - 1),
float int32_tegerHeightScale) m_minHeight(_minHeight),
: ConcaveShape(HEIGHTFIELD), m_numberColumns(nbGridColumns), m_numberRows(nbGridRows), m_maxHeight(_maxHeight),
m_width(nbGridColumns - 1), m_length(nbGridRows - 1), m_minHeight(minHeight), m_upAxis(_upAxis),
m_maxHeight(maxHeight), m_upAxis(upAxis), m_integerHeightScale(int32_tegerHeightScale), m_integerHeightScale(_integerHeightScale),
m_heightDataType(dataType) { m_heightDataType(_dataType) {
assert(_nbGridColumns >= 2);
assert(nbGridColumns >= 2); assert(_nbGridRows >= 2);
assert(nbGridRows >= 2);
assert(m_width >= 1); assert(m_width >= 1);
assert(m_length >= 1); assert(m_length >= 1);
assert(minHeight <= maxHeight); assert(_minHeight <= _maxHeight);
assert(upAxis == 0 || upAxis == 1 || upAxis == 2); assert(_upAxis == 0 || _upAxis == 1 || _upAxis == 2);
m_heightFieldData = _heightFieldData;
m_heightFieldData = heightFieldData;
float halfHeight = (m_maxHeight - m_minHeight) * 0.5f; float halfHeight = (m_maxHeight - m_minHeight) * 0.5f;
assert(halfHeight >= 0); assert(halfHeight >= 0);
// Compute the local AABB of the height field // Compute the local AABB of the height field
if (m_upAxis == 0) { if (m_upAxis == 0) {
m_AABB.setMin(vec3(-halfHeight, -m_width * 0.5f, -m_length * float(0.5))); m_AABB.setMin(vec3(-halfHeight, -m_width * 0.5f, -m_length * float(0.5)));
m_AABB.setMax(vec3(halfHeight, m_width * 0.5f, m_length* float(0.5))); m_AABB.setMax(vec3(halfHeight, m_width * 0.5f, m_length* float(0.5)));
} } else if (m_upAxis == 1) {
else if (m_upAxis == 1) {
m_AABB.setMin(vec3(-m_width * 0.5f, -halfHeight, -m_length * float(0.5))); m_AABB.setMin(vec3(-m_width * 0.5f, -halfHeight, -m_length * float(0.5)));
m_AABB.setMax(vec3(m_width * 0.5f, halfHeight, m_length * float(0.5))); m_AABB.setMax(vec3(m_width * 0.5f, halfHeight, m_length * float(0.5)));
} } else if (m_upAxis == 2) {
else if (m_upAxis == 2) {
m_AABB.setMin(vec3(-m_width * 0.5f, -m_length * float(0.5), -halfHeight)); m_AABB.setMin(vec3(-m_width * 0.5f, -m_length * float(0.5), -halfHeight));
m_AABB.setMax(vec3(m_width * 0.5f, m_length * float(0.5), halfHeight)); m_AABB.setMax(vec3(m_width * 0.5f, m_length * float(0.5), halfHeight));
} }
} }
void HeightFieldShape::getLocalBounds(vec3& _min, vec3& _max) const {
// Return the local bounds of the shape in x, y and z directions. _min = m_AABB.getMin() * m_scaling;
// This method is used to compute the AABB of the box _max = m_AABB.getMax() * m_scaling;
/**
* @param min The minimum bounds of the shape in local-space coordinates
* @param max The maximum bounds of the shape in local-space coordinates
*/
void HeightFieldShape::getLocalBounds(vec3& min, vec3& max) const {
min = m_AABB.getMin() * m_scaling;
max = m_AABB.getMax() * m_scaling;
} }
// Test collision with the triangles of the height field shape. The idea is to use the AABB void HeightFieldShape::testAllTriangles(TriangleCallback& _callback, const AABB& _localAABB) const {
// of the body when need to test and see against which triangles of the height-field we need // Compute the non-scaled AABB
// to test for collision. We compute the sub-grid points that are inside the other body's AABB vec3 inverseScaling(1.0f / m_scaling.x(), 1.0f / m_scaling.y(), float(1.0) / m_scaling.z());
// and then for each rectangle in the sub-grid we generate two triangles that we use to test collision. AABB aabb(_localAABB.getMin() * inverseScaling, _localAABB.getMax() * inverseScaling);
void HeightFieldShape::testAllTriangles(TriangleCallback& callback, const AABB& localAABB) const { // Compute the int32_teger grid coordinates inside the area we need to test for collision
int32_t minGridCoords[3];
// Compute the non-scaled AABB int32_t maxGridCoords[3];
vec3 inverseScaling(1.0f / m_scaling.x(), 1.0f / m_scaling.y(), float(1.0) / m_scaling.z()); computeMinMaxGridCoordinates(minGridCoords, maxGridCoords, aabb);
AABB aabb(localAABB.getMin() * inverseScaling, localAABB.getMax() * inverseScaling); // Compute the starting and ending coords of the sub-grid according to the up axis
int32_t iMin = 0;
// Compute the int32_teger grid coordinates inside the area we need to test for collision int32_t iMax = 0;
int32_t minGridCoords[3]; int32_t jMin = 0;
int32_t maxGridCoords[3]; int32_t jMax = 0;
computeMinMaxGridCoordinates(minGridCoords, maxGridCoords, aabb); switch(m_upAxis) {
case 0 :
// Compute the starting and ending coords of the sub-grid according to the up axis iMin = clamp(minGridCoords[1], 0, m_numberColumns - 1);
int32_t iMin = 0; iMax = clamp(maxGridCoords[1], 0, m_numberColumns - 1);
int32_t iMax = 0; jMin = clamp(minGridCoords[2], 0, m_numberRows - 1);
int32_t jMin = 0; jMax = clamp(maxGridCoords[2], 0, m_numberRows - 1);
int32_t jMax = 0; break;
switch(m_upAxis) { case 1 :
case 0 : iMin = clamp(minGridCoords[1], 0, m_numberColumns - 1); iMin = clamp(minGridCoords[0], 0, m_numberColumns - 1);
iMax = clamp(maxGridCoords[1], 0, m_numberColumns - 1); iMax = clamp(maxGridCoords[0], 0, m_numberColumns - 1);
jMin = clamp(minGridCoords[2], 0, m_numberRows - 1); jMin = clamp(minGridCoords[2], 0, m_numberRows - 1);
jMax = clamp(maxGridCoords[2], 0, m_numberRows - 1); jMax = clamp(maxGridCoords[2], 0, m_numberRows - 1);
break; break;
case 1 : iMin = clamp(minGridCoords[0], 0, m_numberColumns - 1); case 2 :
iMax = clamp(maxGridCoords[0], 0, m_numberColumns - 1); iMin = clamp(minGridCoords[0], 0, m_numberColumns - 1);
jMin = clamp(minGridCoords[2], 0, m_numberRows - 1); iMax = clamp(maxGridCoords[0], 0, m_numberColumns - 1);
jMax = clamp(maxGridCoords[2], 0, m_numberRows - 1); jMin = clamp(minGridCoords[1], 0, m_numberRows - 1);
break; jMax = clamp(maxGridCoords[1], 0, m_numberRows - 1);
case 2 : iMin = clamp(minGridCoords[0], 0, m_numberColumns - 1); break;
iMax = clamp(maxGridCoords[0], 0, m_numberColumns - 1); }
jMin = clamp(minGridCoords[1], 0, m_numberRows - 1); assert(iMin >= 0 && iMin < m_numberColumns);
jMax = clamp(maxGridCoords[1], 0, m_numberRows - 1); assert(iMax >= 0 && iMax < m_numberColumns);
break; assert(jMin >= 0 && jMin < m_numberRows);
} assert(jMax >= 0 && jMax < m_numberRows);
// For each sub-grid points (except the last ones one each dimension)
assert(iMin >= 0 && iMin < m_numberColumns); for (int32_t i = iMin; i < iMax; i++) {
assert(iMax >= 0 && iMax < m_numberColumns); for (int32_t j = jMin; j < jMax; j++) {
assert(jMin >= 0 && jMin < m_numberRows); // Compute the four point of the current quad
assert(jMax >= 0 && jMax < m_numberRows); vec3 p1 = getVertexAt(i, j);
vec3 p2 = getVertexAt(i, j + 1);
// For each sub-grid points (except the last ones one each dimension) vec3 p3 = getVertexAt(i + 1, j);
for (int32_t i = iMin; i < iMax; i++) { vec3 p4 = getVertexAt(i + 1, j + 1);
for (int32_t j = jMin; j < jMax; j++) { // Generate the first triangle for the current grid rectangle
vec3 trianglePoints[3] = {p1, p2, p3};
// Compute the four point of the current quad // Test collision against the first triangle
vec3 p1 = getVertexAt(i, j); _callback.testTriangle(trianglePoints);
vec3 p2 = getVertexAt(i, j + 1); // Generate the second triangle for the current grid rectangle
vec3 p3 = getVertexAt(i + 1, j); trianglePoints[0] = p3;
vec3 p4 = getVertexAt(i + 1, j + 1); trianglePoints[1] = p2;
trianglePoints[2] = p4;
// Generate the first triangle for the current grid rectangle // Test collision against the second triangle
vec3 trianglePoints[3] = {p1, p2, p3}; _callback.testTriangle(trianglePoints);
}
// Test collision against the first triangle }
callback.testTriangle(trianglePoints);
// Generate the second triangle for the current grid rectangle
trianglePoints[0] = p3;
trianglePoints[1] = p2;
trianglePoints[2] = p4;
// Test collision against the second triangle
callback.testTriangle(trianglePoints);
}
}
} }
// Compute the min/max grid coords corresponding to the int32_tersection of the AABB of the height field and void HeightFieldShape::computeMinMaxGridCoordinates(int32_t* _minCoords, int32_t* _maxCoords, const AABB& _aabbToCollide) const {
// the AABB to collide
void HeightFieldShape::computeMinMaxGridCoordinates(int32_t* minCoords, int32_t* maxCoords, const AABB& aabbToCollide) const {
// Clamp the min/max coords of the AABB to collide inside the height field AABB // Clamp the min/max coords of the AABB to collide inside the height field AABB
vec3 minPoint = etk::max(aabbToCollide.getMin(), m_AABB.getMin()); vec3 minPoint = etk::max(_aabbToCollide.getMin(), m_AABB.getMin());
minPoint = etk::min(minPoint, m_AABB.getMax()); minPoint = etk::min(minPoint, m_AABB.getMax());
vec3 maxPoint = etk::min(_aabbToCollide.getMax(), m_AABB.getMax());
vec3 maxPoint = etk::min(aabbToCollide.getMax(), m_AABB.getMax());
maxPoint = etk::max(maxPoint, m_AABB.getMin()); maxPoint = etk::max(maxPoint, m_AABB.getMin());
// Translate the min/max points such that the we compute grid points from [0 ... mNbWidthGridPoints] // Translate the min/max points such that the we compute grid points from [0 ... mNbWidthGridPoints]
// and from [0 ... mNbLengthGridPoints] because the AABB coordinates range are [-mWdith/2 ... m_width/2] // and from [0 ... mNbLengthGridPoints] because the AABB coordinates range are [-mWdith/2 ... m_width/2]
// and [-m_length/2 ... m_length/2] // and [-m_length/2 ... m_length/2]
const vec3 translateVec = m_AABB.getExtent() * 0.5f; const vec3 translateVec = m_AABB.getExtent() * 0.5f;
minPoint += translateVec; minPoint += translateVec;
maxPoint += translateVec; maxPoint += translateVec;
// Convert the floating min/max coords of the AABB int32_to closest int32_teger // Convert the floating min/max coords of the AABB int32_to closest int32_teger
// grid values (note that we use the closest grid coordinate that is out // grid values (note that we use the closest grid coordinate that is out
// of the AABB) // of the AABB)
minCoords[0] = computeIntegerGridValue(minPoint.x()) - 1; _minCoords[0] = computeIntegerGridValue(minPoint.x()) - 1;
minCoords[1] = computeIntegerGridValue(minPoint.y()) - 1; _minCoords[1] = computeIntegerGridValue(minPoint.y()) - 1;
minCoords[2] = computeIntegerGridValue(minPoint.z()) - 1; _minCoords[2] = computeIntegerGridValue(minPoint.z()) - 1;
_maxCoords[0] = computeIntegerGridValue(maxPoint.x()) + 1;
maxCoords[0] = computeIntegerGridValue(maxPoint.x()) + 1; _maxCoords[1] = computeIntegerGridValue(maxPoint.y()) + 1;
maxCoords[1] = computeIntegerGridValue(maxPoint.y()) + 1; _maxCoords[2] = computeIntegerGridValue(maxPoint.z()) + 1;
maxCoords[2] = computeIntegerGridValue(maxPoint.z()) + 1;
} }
// Raycast method with feedback information bool HeightFieldShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
/// Note that only the first triangle hit by the ray in the mesh will be returned, even if
/// the ray hits many triangles.
bool HeightFieldShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const {
// TODO : Implement raycasting without using an AABB for the ray // TODO : Implement raycasting without using an AABB for the ray
// but using a dynamic AABB tree or octree instead // but using a dynamic AABB tree or octree instead
PROFILE("HeightFieldShape::raycast()"); PROFILE("HeightFieldShape::raycast()");
TriangleOverlapCallback triangleCallback(_ray, _proxyShape, _raycastInfo, *this);
TriangleOverlapCallback triangleCallback(ray, proxyShape, raycastInfo, *this);
// Compute the AABB for the ray // Compute the AABB for the ray
const vec3 rayEnd = ray.point1 + ray.maxFraction * (ray.point2 - ray.point1); const vec3 rayEnd = _ray.point1 + _ray.maxFraction * (_ray.point2 - _ray.point1);
const AABB rayAABB(etk::min(ray.point1, rayEnd), etk::max(ray.point1, rayEnd)); const AABB rayAABB(etk::min(_ray.point1, rayEnd), etk::max(_ray.point1, rayEnd));
testAllTriangles(triangleCallback, rayAABB); testAllTriangles(triangleCallback, rayAABB);
return triangleCallback.getIsHit(); return triangleCallback.getIsHit();
} }
// Return the vertex (local-coordinates) of the height field at a given (x,y) position vec3 HeightFieldShape::getVertexAt(int32_t _xxx, int32_t _yyy) const {
vec3 HeightFieldShape::getVertexAt(int32_t x, int32_t y) const {
// Get the height value // Get the height value
const float height = getHeightAt(x, y); const float height = getHeightAt(_xxx, _yyy);
// Height values origin // Height values origin
const float heightOrigin = -(m_maxHeight - m_minHeight) * 0.5f - m_minHeight; const float heightOrigin = -(m_maxHeight - m_minHeight) * 0.5f - m_minHeight;
vec3 vertex; vec3 vertex;
switch (m_upAxis) { switch (m_upAxis) {
case 0: vertex = vec3(heightOrigin + height, -m_width * 0.5f + x, -m_length * float(0.5) + y); case 0:
break; vertex = vec3(heightOrigin + height, -m_width * 0.5f + _xxx, -m_length * float(0.5) + _yyy);
case 1: vertex = vec3(-m_width * 0.5f + x, heightOrigin + height, -m_length * float(0.5) + y); break;
break; case 1:
case 2: vertex = vec3(-m_width * 0.5f + x, -m_length * float(0.5) + y, heightOrigin + height); vertex = vec3(-m_width * 0.5f + _xxx, heightOrigin + height, -m_length * float(0.5) + _yyy);
break; break;
default: assert(false); case 2:
vertex = vec3(-m_width * 0.5f + _xxx, -m_length * float(0.5) + _yyy, heightOrigin + height);
break;
default:
assert(false);
} }
assert(m_AABB.contains(vertex)); assert(m_AABB.contains(vertex));
return vertex * m_scaling; return vertex * m_scaling;
} }
// Raycast test between a ray and a triangle of the heightfield void TriangleOverlapCallback::testTriangle(const vec3* _trianglePoints) {
void TriangleOverlapCallback::testTriangle(const vec3* trianglePoints) {
// Create a triangle collision shape // Create a triangle collision shape
float margin = m_heightFieldShape.getTriangleMargin(); float margin = m_heightFieldShape.getTriangleMargin();
TriangleShape triangleShape(trianglePoints[0], trianglePoints[1], trianglePoints[2], margin); TriangleShape triangleShape(_trianglePoints[0], _trianglePoints[1], _trianglePoints[2], margin);
triangleShape.setRaycastTestType(m_heightFieldShape.getRaycastTestType()); triangleShape.setRaycastTestType(m_heightFieldShape.getRaycastTestType());
// Ray casting test against the collision shape // Ray casting test against the collision shape
RaycastInfo raycastInfo; RaycastInfo raycastInfo;
bool isTriangleHit = triangleShape.raycast(m_ray, raycastInfo, m_proxyShape); bool isTriangleHit = triangleShape.raycast(m_ray, raycastInfo, m_proxyShape);
// If the ray hit the collision shape // If the ray hit the collision shape
if (isTriangleHit && raycastInfo.hitFraction <= m_smallestHitFraction) { if ( isTriangleHit
&& raycastInfo.hitFraction <= m_smallestHitFraction) {
assert(raycastInfo.hitFraction >= 0.0f); assert(raycastInfo.hitFraction >= 0.0f);
m_raycastInfo.body = raycastInfo.body; m_raycastInfo.body = raycastInfo.body;
m_raycastInfo.proxyShape = raycastInfo.proxyShape; m_raycastInfo.proxyShape = raycastInfo.proxyShape;
m_raycastInfo.hitFraction = raycastInfo.hitFraction; m_raycastInfo.hitFraction = raycastInfo.hitFraction;
@ -237,66 +192,55 @@ void TriangleOverlapCallback::testTriangle(const vec3* trianglePoints) {
m_raycastInfo.worldNormal = raycastInfo.worldNormal; m_raycastInfo.worldNormal = raycastInfo.worldNormal;
m_raycastInfo.meshSubpart = -1; m_raycastInfo.meshSubpart = -1;
m_raycastInfo.triangleIndex = -1; m_raycastInfo.triangleIndex = -1;
m_smallestHitFraction = raycastInfo.hitFraction; m_smallestHitFraction = raycastInfo.hitFraction;
m_isHit = true; m_isHit = true;
} }
} }
// Return the number of rows in the height field
int32_t HeightFieldShape::getNbRows() const { int32_t HeightFieldShape::getNbRows() const {
return m_numberRows; return m_numberRows;
} }
// Return the number of columns in the height field
int32_t HeightFieldShape::getNbColumns() const { int32_t HeightFieldShape::getNbColumns() const {
return m_numberColumns; return m_numberColumns;
} }
// Return the type of height value in the height field
HeightFieldShape::HeightDataType HeightFieldShape::getHeightDataType() const { HeightFieldShape::HeightDataType HeightFieldShape::getHeightDataType() const {
return m_heightDataType; return m_heightDataType;
} }
// Return the number of bytes used by the collision shape
size_t HeightFieldShape::getSizeInBytes() const { size_t HeightFieldShape::getSizeInBytes() const {
return sizeof(HeightFieldShape); return sizeof(HeightFieldShape);
} }
// Set the local scaling vector of the collision shape void HeightFieldShape::setLocalScaling(const vec3& _scaling) {
void HeightFieldShape::setLocalScaling(const vec3& scaling) { CollisionShape::setLocalScaling(_scaling);
CollisionShape::setLocalScaling(scaling);
} }
// Return the height of a given (x,y) point in the height field float HeightFieldShape::getHeightAt(int32_t _xxx, int32_t _yyy) const {
float HeightFieldShape::getHeightAt(int32_t x, int32_t y) const {
switch(m_heightDataType) { switch(m_heightDataType) {
case HEIGHT_FLOAT_TYPE : return ((float*)m_heightFieldData)[y * m_numberColumns + x]; case HEIGHT_FLOAT_TYPE:
case HEIGHT_DOUBLE_TYPE : return ((double*)m_heightFieldData)[y * m_numberColumns + x]; return ((float*)m_heightFieldData)[_yyy * m_numberColumns + _xxx];
case HEIGHT_INT_TYPE : return ((int32_t*)m_heightFieldData)[y * m_numberColumns + x] * m_integerHeightScale; case HEIGHT_DOUBLE_TYPE:
default: assert(false); return 0; return ((double*)m_heightFieldData)[_yyy * m_numberColumns + _xxx];
case HEIGHT_INT_TYPE:
return ((int32_t*)m_heightFieldData)[_yyy * m_numberColumns + _xxx] * m_integerHeightScale;
default:
assert(false);
return 0;
} }
} }
// Return the closest inside int32_teger grid value of a given floating grid value int32_t HeightFieldShape::computeIntegerGridValue(float _value) const {
int32_t HeightFieldShape::computeIntegerGridValue(float value) const { return (_value < 0.0f) ? _value - 0.5f : _value + 0.5f;
return (value < 0.0f) ? value - 0.5f : value + float(0.5);
} }
// Return the local inertia tensor void HeightFieldShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const {
/**
* @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
*/
void HeightFieldShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const {
// Default inertia tensor // Default inertia tensor
// Note that this is not very realistic for a concave triangle mesh. // Note that this is not very realistic for a concave triangle mesh.
// However, in most cases, it will only be used static bodies and therefore, // However, in most cases, it will only be used static bodies and therefore,
// the inertia tensor is not used. // the inertia tensor is not used.
tensor.setValue(mass, 0, 0, _tensor.setValue(_mass, 0, 0,
0, mass, 0, 0, _mass, 0,
0, 0, mass); 0, 0, _mass);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -74,9 +77,9 @@ namespace ephysics {
HeightDataType m_heightDataType; //!< Data type of the height values HeightDataType m_heightDataType; //!< Data type of the height values
const void* m_heightFieldData; //!< Array of data with all the height values of the height field const void* m_heightFieldData; //!< Array of data with all the height values of the height field
AABB m_AABB; //!< Local AABB of the height field (without scaling) AABB m_AABB; //!< Local AABB of the height field (without scaling)
/// Private copy-constructor /// DELETED copy-constructor
HeightFieldShape(const HeightFieldShape&) = delete; HeightFieldShape(const HeightFieldShape&) = delete;
/// Private assignment operator /// DELETED assignment operator
HeightFieldShape& operator=(const HeightFieldShape&) = delete; HeightFieldShape& operator=(const HeightFieldShape&) = delete;
bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override; bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
size_t getSizeInBytes() const override; size_t getSizeInBytes() const override;
@ -96,7 +99,17 @@ namespace ephysics {
/// Compute the min/max grid coords corresponding to the int32_tersection of the AABB of the height field and the AABB to collide /// Compute the min/max grid coords corresponding to the int32_tersection of the AABB of the height field and the AABB to collide
void computeMinMaxGridCoordinates(int32_t* _minCoords, int32_t* _maxCoords, const AABB& _aabbToCollide) const; void computeMinMaxGridCoordinates(int32_t* _minCoords, int32_t* _maxCoords, const AABB& _aabbToCollide) const;
public: public:
/// Constructor /**
* @brief Contructor
* @param nbGridColumns Number of columns in the grid of the height field
* @param nbGridRows Number of rows in the grid of the height field
* @param minHeight Minimum height value of the height field
* @param maxHeight Maximum height value of the height field
* @param heightFieldData Pointer to the first height value data (note that values are shared and not copied)
* @param dataType Data type for the height values (int32_t, float, double)
* @param upAxis Integer representing the up axis direction (0 for x, 1 for y and 2 for z)
* @param int32_tegerHeightScale Scaling factor used to scale the height values (only when height values type is int32_teger)
*/
HeightFieldShape(int32_t _nbGridColumns, HeightFieldShape(int32_t _nbGridColumns,
int32_t _nbGridRows, int32_t _nbGridRows,
float _minHeight, float _minHeight,

View File

@ -1,25 +1,23 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/shapes/SphereShape.hpp> #include <ephysics/collision/shapes/SphereShape.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
// TODO: REMOVE this ...
using namespace ephysics; using namespace ephysics;
// Constructor SphereShape::SphereShape(float _radius):
/** ConvexShape(SPHERE, _radius) {
* @param radius Radius of the sphere (in meters) assert(_radius > 0.0f);
*/
SphereShape::SphereShape(float radius) : ConvexShape(SPHERE, radius) {
assert(radius > 0.0f);
} }
void SphereShape::setLocalScaling(const vec3& _scaling) { void SphereShape::setLocalScaling(const vec3& _scaling) {
m_margin = (m_margin / m_scaling.x()) * _scaling.x(); m_margin = (m_margin / m_scaling.x()) * _scaling.x();
CollisionShape::setLocalScaling(_scaling); CollisionShape::setLocalScaling(_scaling);
@ -51,49 +49,41 @@ void SphereShape::getLocalBounds(vec3& _min, vec3& _max) const {
_min.setZ(_min.x()); _min.setZ(_min.x());
} }
bool SphereShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
// Raycast method with feedback information const vec3 m = _ray.point1;
bool SphereShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const {
const vec3 m = ray.point1;
float c = m.dot(m) - m_margin * m_margin; float c = m.dot(m) - m_margin * m_margin;
// If the origin of the ray is inside the sphere, we return no int32_tersection // If the origin of the ray is inside the sphere, we return no int32_tersection
if (c < 0.0f) return false; if (c < 0.0f) {
return false;
const vec3 rayDirection = ray.point2 - ray.point1; }
const vec3 rayDirection = _ray.point2 - _ray.point1;
float b = m.dot(rayDirection); float b = m.dot(rayDirection);
// If the origin of the ray is outside the sphere and the ray // If the origin of the ray is outside the sphere and the ray
// is pointing away from the sphere, there is no int32_tersection // is pointing away from the sphere, there is no int32_tersection
if (b > 0.0f) return false; if (b > 0.0f) {
return false;
}
float raySquareLength = rayDirection.length2(); float raySquareLength = rayDirection.length2();
// Compute the discriminant of the quadratic equation // Compute the discriminant of the quadratic equation
float discriminant = b * b - raySquareLength * c; float discriminant = b * b - raySquareLength * c;
// If the discriminant is negative or the ray length is very small, there is no int32_tersection // If the discriminant is negative or the ray length is very small, there is no int32_tersection
if (discriminant < 0.0f || raySquareLength < FLT_EPSILON) return false; if ( discriminant < 0.0f
|| raySquareLength < FLT_EPSILON) {
return false;
}
// Compute the solution "t" closest to the origin // Compute the solution "t" closest to the origin
float t = -b - etk::sqrt(discriminant); float t = -b - etk::sqrt(discriminant);
assert(t >= 0.0f); assert(t >= 0.0f);
// If the hit point is withing the segment ray fraction // If the hit point is withing the segment ray fraction
if (t < ray.maxFraction * raySquareLength) { if (t < _ray.maxFraction * raySquareLength) {
// Compute the int32_tersection information // Compute the int32_tersection information
t /= raySquareLength; t /= raySquareLength;
raycastInfo.body = proxyShape->getBody(); _raycastInfo.body = _proxyShape->getBody();
raycastInfo.proxyShape = proxyShape; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = t; _raycastInfo.hitFraction = t;
raycastInfo.worldPoint = ray.point1 + t * rayDirection; _raycastInfo.worldPoint = _ray.point1 + t * rayDirection;
raycastInfo.worldNormal = raycastInfo.worldPoint; _raycastInfo.worldNormal = _raycastInfo.worldPoint;
return true; return true;
} }
return false; return false;
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -33,7 +36,10 @@ namespace ephysics {
return sizeof(SphereShape); return sizeof(SphereShape);
} }
public : public :
/// Constructor /**
* @brief Constructor
* @param[in] radius Radius of the sphere (in meters)
*/
SphereShape(float _radius); SphereShape(float _radius);
/** /**
* @brief Get the radius of the sphere * @brief Get the radius of the sphere

View File

@ -1,203 +1,163 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/collision/shapes/TriangleShape.hpp> #include <ephysics/collision/shapes/TriangleShape.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
#include <ephysics/engine/Profiler.hpp> #include <ephysics/engine/Profiler.hpp>
#include <ephysics/configuration.hpp> #include <ephysics/configuration.hpp>
// TODO: REMOVE this...
using namespace ephysics; using namespace ephysics;
// Constructor TriangleShape::TriangleShape(const vec3& _point1, const vec3& _point2, const vec3& _point3, float _margin)
/** : ConvexShape(TRIANGLE, _margin) {
* @param point1 First point of the triangle m_points[0] = _point1;
* @param point2 Second point of the triangle m_points[1] = _point2;
* @param point3 Third point of the triangle m_points[2] = _point3;
* @param margin The collision margin (in meters) around the collision shape
*/
TriangleShape::TriangleShape(const vec3& point1, const vec3& point2, const vec3& point3, float margin)
: ConvexShape(TRIANGLE, margin) {
m_points[0] = point1;
m_points[1] = point2;
m_points[2] = point3;
m_raycastTestType = FRONT; m_raycastTestType = FRONT;
} }
// Destructor bool TriangleShape::raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const {
TriangleShape::~TriangleShape() {
}
// Raycast method with feedback information
/// This method use the line vs triangle raycasting technique described in
/// Real-time Collision Detection by Christer Ericson.
bool TriangleShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const {
PROFILE("TriangleShape::raycast()"); PROFILE("TriangleShape::raycast()");
const vec3 pq = _ray.point2 - _ray.point1;
const vec3 pq = ray.point2 - ray.point1; const vec3 pa = m_points[0] - _ray.point1;
const vec3 pa = m_points[0] - ray.point1; const vec3 pb = m_points[1] - _ray.point1;
const vec3 pb = m_points[1] - ray.point1; const vec3 pc = m_points[2] - _ray.point1;
const vec3 pc = m_points[2] - ray.point1;
// Test if the line PQ is inside the eges BC, CA and AB. We use the triple // Test if the line PQ is inside the eges BC, CA and AB. We use the triple
// product for this test. // product for this test.
const vec3 m = pq.cross(pc); const vec3 m = pq.cross(pc);
float u = pb.dot(m); float u = pb.dot(m);
if (m_raycastTestType == FRONT) { if (m_raycastTestType == FRONT) {
if (u < 0.0f) return false; if (u < 0.0f) {
return false;
}
} else if (m_raycastTestType == BACK) {
if (u > 0.0f) {
return false;
}
} }
else if (m_raycastTestType == BACK) {
if (u > 0.0f) return false;
}
float v = -pa.dot(m); float v = -pa.dot(m);
if (m_raycastTestType == FRONT) { if (m_raycastTestType == FRONT) {
if (v < 0.0f) return false; if (v < 0.0f) {
return false;
}
} else if (m_raycastTestType == BACK) {
if (v > 0.0f) {
return false;
}
} else if (m_raycastTestType == FRONT_AND_BACK) {
if (!sameSign(u, v)) {
return false;
}
} }
else if (m_raycastTestType == BACK) {
if (v > 0.0f) return false;
}
else if (m_raycastTestType == FRONT_AND_BACK) {
if (!sameSign(u, v)) return false;
}
float w = pa.dot(pq.cross(pb)); float w = pa.dot(pq.cross(pb));
if (m_raycastTestType == FRONT) { if (m_raycastTestType == FRONT) {
if (w < 0.0f) return false; if (w < 0.0f) {
return false;
}
} else if (m_raycastTestType == BACK) {
if (w > 0.0f) {
return false;
}
} else if (m_raycastTestType == FRONT_AND_BACK) {
if (!sameSign(u, w)) {
return false;
}
} }
else if (m_raycastTestType == BACK) {
if (w > 0.0f) return false;
}
else if (m_raycastTestType == FRONT_AND_BACK) {
if (!sameSign(u, w)) return false;
}
// If the line PQ is in the triangle plane (case where u=v=w=0) // If the line PQ is in the triangle plane (case where u=v=w=0)
if (approxEqual(u, 0) && approxEqual(v, 0) && approxEqual(w, 0)) return false; if ( approxEqual(u, 0)
&& approxEqual(v, 0)
&& approxEqual(w, 0)) {
return false;
}
// Compute the barycentric coordinates (u, v, w) to determine the // Compute the barycentric coordinates (u, v, w) to determine the
// int32_tersection point R, R = u * a + v * b + w * c // int32_tersection point R, R = u * a + v * b + w * c
float denom = 1.0f / (u + v + w); float denom = 1.0f / (u + v + w);
u *= denom; u *= denom;
v *= denom; v *= denom;
w *= denom; w *= denom;
// Compute the local hit point using the barycentric coordinates // Compute the local hit point using the barycentric coordinates
const vec3 localHitPoint = u * m_points[0] + v * m_points[1] + w * m_points[2]; const vec3 localHitPoint = u * m_points[0] + v * m_points[1] + w * m_points[2];
const float hitFraction = (localHitPoint - ray.point1).length() / pq.length(); const float hitFraction = (localHitPoint - _ray.point1).length() / pq.length();
if ( hitFraction < 0.0f
if (hitFraction < 0.0f || hitFraction > ray.maxFraction) return false; || hitFraction > _ray.maxFraction) {
return false;
}
vec3 localHitNormal = (m_points[1] - m_points[0]).cross(m_points[2] - m_points[0]); vec3 localHitNormal = (m_points[1] - m_points[0]).cross(m_points[2] - m_points[0]);
if (localHitNormal.dot(pq) > 0.0f) localHitNormal = -localHitNormal; if (localHitNormal.dot(pq) > 0.0f) {
localHitNormal = -localHitNormal;
raycastInfo.body = proxyShape->getBody(); }
raycastInfo.proxyShape = proxyShape; _raycastInfo.body = _proxyShape->getBody();
raycastInfo.worldPoint = localHitPoint; _raycastInfo.proxyShape = _proxyShape;
raycastInfo.hitFraction = hitFraction; _raycastInfo.worldPoint = localHitPoint;
raycastInfo.worldNormal = localHitNormal; _raycastInfo.hitFraction = hitFraction;
_raycastInfo.worldNormal = localHitNormal;
return true; return true;
} }
// Return the number of bytes used by the collision shape
size_t TriangleShape::getSizeInBytes() const { size_t TriangleShape::getSizeInBytes() const {
return sizeof(TriangleShape); return sizeof(TriangleShape);
} }
// Return a local support point in a given direction without the object margin vec3 TriangleShape::getLocalSupportPointWithoutMargin(const vec3& _direction,
vec3 TriangleShape::getLocalSupportPointWithoutMargin(const vec3& direction, void** _cachedCollisionData) const {
void** cachedCollisionData) const { vec3 dotProducts(_direction.dot(m_points[0]), _direction.dot(m_points[1]), _direction.dot(m_points[2]));
vec3 dotProducts(direction.dot(m_points[0]), direction.dot(m_points[1]), direction.dot(m_points[2]));
return m_points[dotProducts.getMaxAxis()]; return m_points[dotProducts.getMaxAxis()];
} }
// Return the local bounds of the shape in x, y and z directions. void TriangleShape::getLocalBounds(vec3& _min, vec3& _max) const {
// This method is used to compute the AABB of the box
/**
* @param min The minimum bounds of the shape in local-space coordinates
* @param max The maximum bounds of the shape in local-space coordinates
*/
void TriangleShape::getLocalBounds(vec3& min, vec3& max) const {
const vec3 xAxis(m_points[0].x(), m_points[1].x(), m_points[2].x()); const vec3 xAxis(m_points[0].x(), m_points[1].x(), m_points[2].x());
const vec3 yAxis(m_points[0].y(), m_points[1].y(), m_points[2].y()); const vec3 yAxis(m_points[0].y(), m_points[1].y(), m_points[2].y());
const vec3 zAxis(m_points[0].z(), m_points[1].z(), m_points[2].z()); const vec3 zAxis(m_points[0].z(), m_points[1].z(), m_points[2].z());
min.setValue(xAxis.getMin(), yAxis.getMin(), zAxis.getMin()); _min.setValue(xAxis.getMin(), yAxis.getMin(), zAxis.getMin());
max.setValue(xAxis.getMax(), yAxis.getMax(), zAxis.getMax()); _max.setValue(xAxis.getMax(), yAxis.getMax(), zAxis.getMax());
_min -= vec3(m_margin, m_margin, m_margin);
min -= vec3(m_margin, m_margin, m_margin); _max += vec3(m_margin, m_margin, m_margin);
max += vec3(m_margin, m_margin, m_margin);
} }
// Set the local scaling vector of the collision shape void TriangleShape::setLocalScaling(const vec3& _scaling) {
void TriangleShape::setLocalScaling(const vec3& scaling) { m_points[0] = (m_points[0] / m_scaling) * _scaling;
m_points[1] = (m_points[1] / m_scaling) * _scaling;
m_points[0] = (m_points[0] / m_scaling) * scaling; m_points[2] = (m_points[2] / m_scaling) * _scaling;
m_points[1] = (m_points[1] / m_scaling) * scaling; CollisionShape::setLocalScaling(_scaling);
m_points[2] = (m_points[2] / m_scaling) * scaling;
CollisionShape::setLocalScaling(scaling);
} }
// Return the local inertia tensor of the triangle shape void TriangleShape::computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const {
/** _tensor.setZero();
* @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
*/
void TriangleShape::computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const {
tensor.setZero();
} }
// Update the AABB of a body using its collision shape void TriangleShape::computeAABB(AABB& _aabb, const etk::Transform3D& _transform) const {
/** const vec3 worldPoint1 = _transform * m_points[0];
* @param[out] aabb The axis-aligned bounding box (AABB) of the collision shape const vec3 worldPoint2 = _transform * m_points[1];
* computed in world-space coordinates const vec3 worldPoint3 = _transform * m_points[2];
* @param transform etk::Transform3D used to compute the AABB of the collision shape
*/
void TriangleShape::computeAABB(AABB& aabb, const etk::Transform3D& transform) const {
const vec3 worldPoint1 = transform * m_points[0];
const vec3 worldPoint2 = transform * m_points[1];
const vec3 worldPoint3 = transform * m_points[2];
const vec3 xAxis(worldPoint1.x(), worldPoint2.x(), worldPoint3.x()); const vec3 xAxis(worldPoint1.x(), worldPoint2.x(), worldPoint3.x());
const vec3 yAxis(worldPoint1.y(), worldPoint2.y(), worldPoint3.y()); const vec3 yAxis(worldPoint1.y(), worldPoint2.y(), worldPoint3.y());
const vec3 zAxis(worldPoint1.z(), worldPoint2.z(), worldPoint3.z()); const vec3 zAxis(worldPoint1.z(), worldPoint2.z(), worldPoint3.z());
aabb.setMin(vec3(xAxis.getMin(), yAxis.getMin(), zAxis.getMin())); _aabb.setMin(vec3(xAxis.getMin(), yAxis.getMin(), zAxis.getMin()));
aabb.setMax(vec3(xAxis.getMax(), yAxis.getMax(), zAxis.getMax())); _aabb.setMax(vec3(xAxis.getMax(), yAxis.getMax(), zAxis.getMax()));
} }
// Return true if a point is inside the collision shape bool TriangleShape::testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const {
bool TriangleShape::testPointInside(const vec3& localPoint, ProxyShape* proxyShape) const {
return false; return false;
} }
// Return the raycast test type (front, back, front-back)
TriangleRaycastSide TriangleShape::getRaycastTestType() const { TriangleRaycastSide TriangleShape::getRaycastTestType() const {
return m_raycastTestType; return m_raycastTestType;
} }
// Set the raycast test type (front, back, front-back)
/** void TriangleShape::setRaycastTestType(TriangleRaycastSide _testType) {
* @param testType Raycast test type for the triangle (front, back, front-back) m_raycastTestType = _testType;
*/
void TriangleShape::setRaycastTestType(TriangleRaycastSide testType) {
m_raycastTestType = testType;
} }
// Return the coordinates of a given vertex of the triangle vec3 TriangleShape::getVertex(int32_t _index) const {
/** assert( _index >= 0
* @param index Index (0 to 2) of a vertex of the triangle && _index < 3);
*/ return m_points[_index];
vec3 TriangleShape::getVertex(int32_t index) const {
assert(index >= 0 && index < 3);
return m_points[index];
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -22,34 +25,46 @@ namespace ephysics {
* This class represents a triangle collision shape that is centered * This class represents a triangle collision shape that is centered
* at the origin and defined three points. * at the origin and defined three points.
*/ */
class TriangleShape : public ConvexShape { class TriangleShape: public ConvexShape {
protected: protected:
vec3 m_points[3]; //!< Three points of the triangle vec3 m_points[3]; //!< Three points of the triangle
TriangleRaycastSide m_raycastTestType; //!< Raycast test type for the triangle (front, back, front-back) TriangleRaycastSide m_raycastTestType; //!< Raycast test type for the triangle (front, back, front-back)
/// Private copy-constructor /// Private copy-constructor
TriangleShape(const TriangleShape& shape); TriangleShape(const TriangleShape& _shape);
/// Private assignment operator /// Private assignment operator
TriangleShape& operator=(const TriangleShape& shape); TriangleShape& operator=(const TriangleShape& _shape);
vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override; vec3 getLocalSupportPointWithoutMargin(const vec3& _direction, void** _cachedCollisionData) const override;
bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override; bool testPointInside(const vec3& _localPoint, ProxyShape* _proxyShape) const override;
bool raycast(const Ray& ray, RaycastInfo& raycastInfo, ProxyShape* proxyShape) const override; bool raycast(const Ray& _ray, RaycastInfo& _raycastInfo, ProxyShape* _proxyShape) const override;
size_t getSizeInBytes() const override; size_t getSizeInBytes() const override;
public: public:
/// Constructor /**
TriangleShape(const vec3& point1, const vec3& point2, const vec3& point3, * @brief Constructor
float margin = OBJECT_MARGIN); * @param _point1 First point of the triangle
/// Destructor * @param _point2 Second point of the triangle
virtual ~TriangleShape(); * @param _point3 Third point of the triangle
void getLocalBounds(vec3& min, vec3& max) const override; * @param _margin The collision margin (in meters) around the collision shape
void setLocalScaling(const vec3& scaling) override; */
void computeLocalInertiaTensor(etk::Matrix3x3& tensor, float mass) const override; TriangleShape(const vec3& _point1,
void computeAABB(AABB& aabb, const etk::Transform3D& transform) const override; const vec3& _point2,
const vec3& _point3,
float _margin = OBJECT_MARGIN);
void getLocalBounds(vec3& _min, vec3& _max) const override;
void setLocalScaling(const vec3& _scaling) override;
void computeLocalInertiaTensor(etk::Matrix3x3& _tensor, float _mass) const override;
void computeAABB(AABB& aabb, const etk::Transform3D& _transform) const override;
/// Return the raycast test type (front, back, front-back) /// Return the raycast test type (front, back, front-back)
TriangleRaycastSide getRaycastTestType() const; TriangleRaycastSide getRaycastTestType() const;
// Set the raycast test type (front, back, front-back) /**
void setRaycastTestType(TriangleRaycastSide testType); * @brief Set the raycast test type (front, back, front-back)
/// Return the coordinates of a given vertex of the triangle * @param[in] _testType Raycast test type for the triangle (front, back, front-back)
vec3 getVertex(int32_t index) const; */
void setRaycastTestType(TriangleRaycastSide _testType);
/**
* @brief Return the coordinates of a given vertex of the triangle
* @param[in] _index Index (0 to 2) of a vertex of the triangle
*/
vec3 getVertex(int32_t _index) const;
friend class ConcaveMeshRaycastCallback; friend class ConcaveMeshRaycastCallback;
friend class TriangleOverlapCallback; friend class TriangleOverlapCallback;
}; };

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries // Libraries
#include <ephysics/constraint/BallAndSocketJoint.hpp> #include <ephysics/constraint/BallAndSocketJoint.hpp>
#include <ephysics/engine/ConstraintSolver.hpp> #include <ephysics/engine/ConstraintSolver.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/constraint/ContactPoint.hpp> #include <ephysics/constraint/ContactPoint.hpp>
#include <ephysics/collision/ProxyShape.hpp> #include <ephysics/collision/ProxyShape.hpp>
@ -12,24 +13,25 @@ using namespace ephysics;
using namespace std; using namespace std;
// Constructor // Constructor
ContactPoint::ContactPoint(const ContactPointInfo& contactInfo) ContactPoint::ContactPoint(const ContactPointInfo& _contactInfo):
: m_body1(contactInfo.shape1->getBody()), m_body2(contactInfo.shape2->getBody()), m_body1(_contactInfo.shape1->getBody()),
m_normal(contactInfo.normal), m_body2(_contactInfo.shape2->getBody()),
m_penetrationDepth(contactInfo.penetrationDepth), m_normal(_contactInfo.normal),
m_localPointOnBody1(contactInfo.localPoint1), m_penetrationDepth(_contactInfo.penetrationDepth),
m_localPointOnBody2(contactInfo.localPoint2), m_localPointOnBody1(_contactInfo.localPoint1),
m_worldPointOnBody1(contactInfo.shape1->getBody()->getTransform() * m_localPointOnBody2(_contactInfo.localPoint2),
contactInfo.shape1->getLocalToBodyTransform() * m_worldPointOnBody1(_contactInfo.shape1->getBody()->getTransform() *
contactInfo.localPoint1), _contactInfo.shape1->getLocalToBodyTransform() *
m_worldPointOnBody2(contactInfo.shape2->getBody()->getTransform() * _contactInfo.localPoint1),
contactInfo.shape2->getLocalToBodyTransform() * m_worldPointOnBody2(_contactInfo.shape2->getBody()->getTransform() *
contactInfo.localPoint2), _contactInfo.shape2->getLocalToBodyTransform() *
m_isRestingContact(false) { _contactInfo.localPoint2),
m_isRestingContact(false) {
m_frictionVectors[0] = vec3(0, 0, 0); m_frictionVectors[0] = vec3(0, 0, 0);
m_frictionVectors[1] = vec3(0, 0, 0); m_frictionVectors[1] = vec3(0, 0, 0);
assert(m_penetrationDepth > 0.0); assert(m_penetrationDepth >= 0.0);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -48,10 +51,10 @@ namespace ephysics {
} }
ContactPointInfo(): ContactPointInfo():
shape1(nullptr), shape1(null),
shape2(nullptr), shape2(null),
collisionShape1(nullptr), collisionShape1(null),
collisionShape2(nullptr) { collisionShape2(null) {
// TODO: add it for etk::Vector // TODO: add it for etk::Vector
} }
}; };

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/constraint/FixedJoint.hpp> #include <ephysics/constraint/FixedJoint.hpp>
#include <ephysics/engine/ConstraintSolver.hpp> #include <ephysics/engine/ConstraintSolver.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/constraint/HingeJoint.hpp> #include <ephysics/constraint/HingeJoint.hpp>
#include <ephysics/engine/ConstraintSolver.hpp> #include <ephysics/engine/ConstraintSolver.hpp>

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/constraint/Joint.hpp> #include <ephysics/constraint/Joint.hpp>
@ -12,8 +15,8 @@ Joint::Joint(const JointInfo& jointInfo)
m_positionCorrectionTechnique(jointInfo.positionCorrectionTechnique), m_positionCorrectionTechnique(jointInfo.positionCorrectionTechnique),
m_isCollisionEnabled(jointInfo.isCollisionEnabled), m_isAlreadyInIsland(false) { m_isCollisionEnabled(jointInfo.isCollisionEnabled), m_isAlreadyInIsland(false) {
assert(m_body1 != nullptr); assert(m_body1 != null);
assert(m_body2 != nullptr); assert(m_body2 != null);
} }
Joint::~Joint() { Joint::~Joint() {

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -47,8 +50,8 @@ namespace ephysics {
bool isCollisionEnabled; //!< True if the two bodies of the joint are allowed to collide with each other bool isCollisionEnabled; //!< True if the two bodies of the joint are allowed to collide with each other
/// Constructor /// Constructor
JointInfo(JointType _constraintType): JointInfo(JointType _constraintType):
body1(nullptr), body1(null),
body2(nullptr), body2(null),
type(_constraintType), type(_constraintType),
positionCorrectionTechnique(NON_LINEAR_GAUSS_SEIDEL), positionCorrectionTechnique(NON_LINEAR_GAUSS_SEIDEL),
isCollisionEnabled(true) { isCollisionEnabled(true) {

View File

@ -1,10 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
// Libraries
#include <ephysics/constraint/SliderJoint.hpp> #include <ephysics/constraint/SliderJoint.hpp>
using namespace ephysics; using namespace ephysics;

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/engine/CollisionWorld.hpp> #include <ephysics/engine/CollisionWorld.hpp>
#include <ephysics/debug.hpp> #include <ephysics/debug.hpp>
@ -13,220 +15,140 @@ using namespace std;
CollisionWorld::CollisionWorld() : CollisionWorld::CollisionWorld() :
m_collisionDetection(this), m_collisionDetection(this),
m_currentBodyID(0), m_currentBodyID(0),
m_eventListener(nullptr) { m_eventListener(null) {
} }
CollisionWorld::~CollisionWorld() { CollisionWorld::~CollisionWorld() {
// Destroy all the collision bodies that have not been removed while(m_bodies.size() != 0) {
etk::Set<CollisionBody*>::Iterator itBodies; destroyCollisionBody(m_bodies[0]);
for (itBodies = m_bodies.begin(); itBodies != m_bodies.end(); ) {
etk::Set<CollisionBody*>::Iterator itToRemove = itBodies;
++itBodies;
destroyCollisionBody(*itToRemove);
} }
assert(m_bodies.empty());
} }
/**
* @brief Create a collision body and add it to the world CollisionBody* CollisionWorld::createCollisionBody(const etk::Transform3D& _transform) {
* @param transform etk::Transform3Dation mapping the local-space of the body to world-space
* @return A pointer to the body that has been created in the world
*/
CollisionBody* CollisionWorld::createCollisionBody(const etk::Transform3D& transform) {
// Get the next available body ID // Get the next available body ID
bodyindex bodyID = computeNextAvailableBodyID(); bodyindex bodyID = computeNextAvailableBodyID();
// Largest index cannot be used (it is used for invalid index) // Largest index cannot be used (it is used for invalid index)
EPHY_ASSERT(bodyID < std::numeric_limits<ephysics::bodyindex>::max(), "index too big"); EPHY_ASSERT(bodyID < UINT64_MAX, "index too big");
// Create the collision body // Create the collision body
CollisionBody* collisionBody = new CollisionBody(transform, *this, bodyID); CollisionBody* collisionBody = ETK_NEW(CollisionBody, _transform, *this, bodyID);
EPHY_ASSERT(collisionBody != nullptr, "empty Body collision"); EPHY_ASSERT(collisionBody != null, "empty Body collision");
// Add the collision body to the world // Add the collision body to the world
m_bodies.add(collisionBody); m_bodies.add(collisionBody);
// Return the pointer to the rigid body // Return the pointer to the rigid body
return collisionBody; return collisionBody;
} }
/** void CollisionWorld::destroyCollisionBody(CollisionBody* _collisionBody) {
* @brief Destroy a collision body
* @param collisionBody Pointer to the body to destroy
*/
void CollisionWorld::destroyCollisionBody(CollisionBody* collisionBody) {
// Remove all the collision shapes of the body // Remove all the collision shapes of the body
collisionBody->removeAllCollisionShapes(); _collisionBody->removeAllCollisionShapes();
// Add the body ID to the list of free IDs // Add the body ID to the list of free IDs
m_freeBodiesIDs.pushBack(collisionBody->getID()); m_freeBodiesIDs.pushBack(_collisionBody->getID());
// Remove the collision body from the list of bodies // Remove the collision body from the list of bodies
m_bodies.erase(m_bodies.find(collisionBody)); m_bodies.erase(m_bodies.find(_collisionBody));
ETK_DELETE(CollisionBody, _collisionBody);
delete collisionBody; _collisionBody = null;
collisionBody = nullptr;
} }
// Return the next available body ID
bodyindex CollisionWorld::computeNextAvailableBodyID() { bodyindex CollisionWorld::computeNextAvailableBodyID() {
// Compute the body ID // Compute the body ID
bodyindex bodyID; bodyindex bodyID;
if (!m_freeBodiesIDs.empty()) { if (!m_freeBodiesIDs.empty()) {
bodyID = m_freeBodiesIDs.back(); bodyID = m_freeBodiesIDs.back();
m_freeBodiesIDs.popBack(); m_freeBodiesIDs.popBack();
} } else {
else {
bodyID = m_currentBodyID; bodyID = m_currentBodyID;
m_currentBodyID++; m_currentBodyID++;
} }
return bodyID; return bodyID;
} }
// Reset all the contact manifolds linked list of each body
void CollisionWorld::resetContactManifoldListsOfBodies() { void CollisionWorld::resetContactManifoldListsOfBodies() {
// For each rigid body of the world // For each rigid body of the world
for (etk::Set<CollisionBody*>::Iterator it = m_bodies.begin(); it != m_bodies.end(); ++it) { for (etk::Set<CollisionBody*>::Iterator it = m_bodies.begin(); it != m_bodies.end(); ++it) {
// Reset the contact manifold list of the body // Reset the contact manifold list of the body
(*it)->resetContactManifoldsList(); (*it)->resetContactManifoldsList();
} }
} }
// Test if the AABBs of two bodies overlap bool CollisionWorld::testAABBOverlap(const CollisionBody* _body1, const CollisionBody* _body2) const {
/**
* @param body1 Pointer to the first body to test
* @param body2 Pointer to the second body to test
* @return True if the AABBs of the two bodies overlap and false otherwise
*/
bool CollisionWorld::testAABBOverlap(const CollisionBody* body1,
const CollisionBody* body2) const {
// If one of the body is not active, we return no overlap // If one of the body is not active, we return no overlap
if (!body1->isActive() || !body2->isActive()) return false; if ( !_body1->isActive()
|| !_body2->isActive()) {
return false;
}
// Compute the AABBs of both bodies // Compute the AABBs of both bodies
AABB body1AABB = body1->getAABB(); AABB body1AABB = _body1->getAABB();
AABB body2AABB = body2->getAABB(); AABB body2AABB = _body2->getAABB();
// Return true if the two AABBs overlap // Return true if the two AABBs overlap
return body1AABB.testCollision(body2AABB); return body1AABB.testCollision(body2AABB);
} }
// Test and report collisions between a given shape and all the others void CollisionWorld::testCollision(const ProxyShape* _shape, CollisionCallback* _callback) {
// shapes of the world.
/**
* @param shape Pointer to the proxy shape to test
* @param callback Pointer to the object with the callback method
*/
void CollisionWorld::testCollision(const ProxyShape* shape,
CollisionCallback* callback) {
// Reset all the contact manifolds lists of each body // Reset all the contact manifolds lists of each body
resetContactManifoldListsOfBodies(); resetContactManifoldListsOfBodies();
// Create the sets of shapes // Create the sets of shapes
etk::Set<uint32_t> shapes; etk::Set<uint32_t> shapes;
shapes.add(shape->m_broadPhaseID); shapes.add(_shape->m_broadPhaseID);
etk::Set<uint32_t> emptySet; etk::Set<uint32_t> emptySet;
// Perform the collision detection and report contacts // Perform the collision detection and report contacts
m_collisionDetection.testCollisionBetweenShapes(callback, shapes, emptySet); m_collisionDetection.testCollisionBetweenShapes(_callback, shapes, emptySet);
} }
// Test and report collisions between two given shapes void CollisionWorld::testCollision(const ProxyShape* _shape1, const ProxyShape* _shape2, CollisionCallback* _callback) {
/**
* @param shape1 Pointer to the first proxy shape to test
* @param shape2 Pointer to the second proxy shape to test
* @param callback Pointer to the object with the callback method
*/
void CollisionWorld::testCollision(const ProxyShape* shape1,
const ProxyShape* shape2,
CollisionCallback* callback) {
// Reset all the contact manifolds lists of each body // Reset all the contact manifolds lists of each body
resetContactManifoldListsOfBodies(); resetContactManifoldListsOfBodies();
// Create the sets of shapes // Create the sets of shapes
etk::Set<uint32_t> shapes1; etk::Set<uint32_t> shapes1;
shapes1.add(shape1->m_broadPhaseID); shapes1.add(_shape1->m_broadPhaseID);
etk::Set<uint32_t> shapes2; etk::Set<uint32_t> shapes2;
shapes2.add(shape2->m_broadPhaseID); shapes2.add(_shape2->m_broadPhaseID);
// Perform the collision detection and report contacts // Perform the collision detection and report contacts
m_collisionDetection.testCollisionBetweenShapes(callback, shapes1, shapes2); m_collisionDetection.testCollisionBetweenShapes(_callback, shapes1, shapes2);
} }
// Test and report collisions between a body and all the others bodies of the void CollisionWorld::testCollision(const CollisionBody* _body, CollisionCallback* _callback) {
// world
/**
* @param body Pointer to the first body to test
* @param callback Pointer to the object with the callback method
*/
void CollisionWorld::testCollision(const CollisionBody* body,
CollisionCallback* callback) {
// Reset all the contact manifolds lists of each body // Reset all the contact manifolds lists of each body
resetContactManifoldListsOfBodies(); resetContactManifoldListsOfBodies();
// Create the sets of shapes // Create the sets of shapes
etk::Set<uint32_t> shapes1; etk::Set<uint32_t> shapes1;
// For each shape of the body // For each shape of the body
for (const ProxyShape* shape=body->getProxyShapesList(); shape != nullptr; for (const ProxyShape* shape = _body->getProxyShapesList();
shape = shape->getNext()) { shape != null;
shape = shape->getNext()) {
shapes1.add(shape->m_broadPhaseID); shapes1.add(shape->m_broadPhaseID);
} }
etk::Set<uint32_t> emptySet; etk::Set<uint32_t> emptySet;
// Perform the collision detection and report contacts // Perform the collision detection and report contacts
m_collisionDetection.testCollisionBetweenShapes(callback, shapes1, emptySet); m_collisionDetection.testCollisionBetweenShapes(_callback, shapes1, emptySet);
} }
// Test and report collisions between two bodies void CollisionWorld::testCollision(const CollisionBody* _body1, const CollisionBody* _body2, CollisionCallback* _callback) {
/**
* @param body1 Pointer to the first body to test
* @param body2 Pointer to the second body to test
* @param callback Pointer to the object with the callback method
*/
void CollisionWorld::testCollision(const CollisionBody* body1,
const CollisionBody* body2,
CollisionCallback* callback) {
// Reset all the contact manifolds lists of each body // Reset all the contact manifolds lists of each body
resetContactManifoldListsOfBodies(); resetContactManifoldListsOfBodies();
// Create the sets of shapes // Create the sets of shapes
etk::Set<uint32_t> shapes1; etk::Set<uint32_t> shapes1;
for (const ProxyShape* shape=body1->getProxyShapesList(); shape != nullptr; for (const ProxyShape* shape = _body1->getProxyShapesList();
shape = shape->getNext()) { shape != null;
shape = shape->getNext()) {
shapes1.add(shape->m_broadPhaseID); shapes1.add(shape->m_broadPhaseID);
} }
etk::Set<uint32_t> shapes2; etk::Set<uint32_t> shapes2;
for (const ProxyShape* shape=body2->getProxyShapesList(); shape != nullptr; for (const ProxyShape* shape = _body2->getProxyShapesList();
shape = shape->getNext()) { shape != null;
shape = shape->getNext()) {
shapes2.add(shape->m_broadPhaseID); shapes2.add(shape->m_broadPhaseID);
} }
// Perform the collision detection and report contacts // Perform the collision detection and report contacts
m_collisionDetection.testCollisionBetweenShapes(callback, shapes1, shapes2); m_collisionDetection.testCollisionBetweenShapes(_callback, shapes1, shapes2);
} }
// Test and report collisions between all shapes of the world void CollisionWorld::testCollision(CollisionCallback* _callback) {
/**
* @param callback Pointer to the object with the callback method
*/
void CollisionWorld::testCollision(CollisionCallback* callback) {
// Reset all the contact manifolds lists of each body // Reset all the contact manifolds lists of each body
resetContactManifoldListsOfBodies(); resetContactManifoldListsOfBodies();
etk::Set<uint32_t> emptySet; etk::Set<uint32_t> emptySet;
// Perform the collision detection and report contacts // Perform the collision detection and report contacts
m_collisionDetection.testCollisionBetweenShapes(callback, emptySet, emptySet); m_collisionDetection.testCollisionBetweenShapes(_callback, emptySet, emptySet);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -57,9 +60,16 @@ namespace ephysics {
etk::Set<CollisionBody*>::Iterator getBodiesEndIterator() { etk::Set<CollisionBody*>::Iterator getBodiesEndIterator() {
return m_bodies.end(); return m_bodies.end();
} }
/// Create a collision body /**
* @brief Create a collision body and add it to the world
* @param transform etk::Transform3Dation mapping the local-space of the body to world-space
* @return A pointer to the body that has been created in the world
*/
CollisionBody* createCollisionBody(const etk::Transform3D& transform); CollisionBody* createCollisionBody(const etk::Transform3D& transform);
/// Destroy a collision body /**
* @brief Destroy a collision body
* @param collisionBody Pointer to the body to destroy
*/
void destroyCollisionBody(CollisionBody* collisionBody); void destroyCollisionBody(CollisionBody* collisionBody);
/** /**
* @brief Set the collision dispatch configuration * @brief Set the collision dispatch configuration
@ -82,7 +92,12 @@ namespace ephysics {
unsigned short _raycastWithCategoryMaskBits = 0xFFFF) const { unsigned short _raycastWithCategoryMaskBits = 0xFFFF) const {
m_collisionDetection.raycast(_raycastCallback, _ray, _raycastWithCategoryMaskBits); m_collisionDetection.raycast(_raycastCallback, _ray, _raycastWithCategoryMaskBits);
} }
/// Test if the AABBs of two bodies overlap /**
* @brief Test if the AABBs of two bodies overlap
* @param _body1 Pointer to the first body to test
* @param _body2 Pointer to the second body to test
* @return True if the AABBs of the two bodies overlap and false otherwise
*/
bool testAABBOverlap(const CollisionBody* _body1, bool testAABBOverlap(const CollisionBody* _body1,
const CollisionBody* _body2) const; const CollisionBody* _body2) const;
/** /**
@ -94,23 +109,42 @@ namespace ephysics {
const ProxyShape* _shape2) const { const ProxyShape* _shape2) const {
return m_collisionDetection.testAABBOverlap(_shape1, _shape2); return m_collisionDetection.testAABBOverlap(_shape1, _shape2);
} }
/// Test and report collisions between a given shape and all the others /**
/// shapes of the world * @brief Test and report collisions between a given shape and all the others shapes of the world.
* @param _shape Pointer to the proxy shape to test
* @param _callback Pointer to the object with the callback method
*/
virtual void testCollision(const ProxyShape* _shape, virtual void testCollision(const ProxyShape* _shape,
CollisionCallback* _callback); CollisionCallback* _callback);
/// Test and report collisions between two given shapes /**
* @briefTest and report collisions between two given shapes
* @param _shape1 Pointer to the first proxy shape to test
* @param _shape2 Pointer to the second proxy shape to test
* @param _callback Pointer to the object with the callback method
*/
virtual void testCollision(const ProxyShape* _shape1, virtual void testCollision(const ProxyShape* _shape1,
const ProxyShape* _shape2, const ProxyShape* _shape2,
CollisionCallback* _callback); CollisionCallback* _callback);
/// Test and report collisions between a body and all the others bodies of the /**
/// world * @brief Test and report collisions between a body and all the others bodies of the world.
* @param _body Pointer to the first body to test
* @param _callback Pointer to the object with the callback method
*/
virtual void testCollision(const CollisionBody* _body, virtual void testCollision(const CollisionBody* _body,
CollisionCallback* _callback); CollisionCallback* _callback);
/// Test and report collisions between two bodies /**
* @brief Test and report collisions between two bodies
* @param _body1 Pointer to the first body to test
* @param _body2 Pointer to the second body to test
* @param _callback Pointer to the object with the callback method
*/
virtual void testCollision(const CollisionBody* _body1, virtual void testCollision(const CollisionBody* _body1,
const CollisionBody* _body2, const CollisionBody* _body2,
CollisionCallback* _callback); CollisionCallback* _callback);
/// Test and report collisions between all shapes of the world /**
* @brief Test and report collisions between all shapes of the world
* @param _callback Pointer to the object with the callback method
*/
virtual void testCollision(CollisionCallback* _callback); virtual void testCollision(CollisionCallback* _callback);
friend class CollisionDetection; friend class CollisionDetection;
friend class CollisionBody; friend class CollisionBody;

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/engine/ConstraintSolver.hpp> #include <ephysics/engine/ConstraintSolver.hpp>
#include <ephysics/engine/Profiler.hpp> #include <ephysics/engine/Profiler.hpp>
@ -18,7 +20,7 @@ ConstraintSolver::ConstraintSolver(const etk::Map<RigidBody*, uint32_t>& _mapBod
void ConstraintSolver::initializeForIsland(float _dt, Island* _island) { void ConstraintSolver::initializeForIsland(float _dt, Island* _island) {
PROFILE("ConstraintSolver::initializeForIsland()"); PROFILE("ConstraintSolver::initializeForIsland()");
assert(_island != nullptr); assert(_island != null);
assert(_island->getNbBodies() > 0); assert(_island->getNbBodies() > 0);
assert(_island->getNbJoints() > 0); assert(_island->getNbJoints() > 0);
// Set the current time step // Set the current time step
@ -40,7 +42,7 @@ void ConstraintSolver::initializeForIsland(float _dt, Island* _island) {
void ConstraintSolver::solveVelocityConstraints(Island* _island) { void ConstraintSolver::solveVelocityConstraints(Island* _island) {
PROFILE("ConstraintSolver::solveVelocityConstraints()"); PROFILE("ConstraintSolver::solveVelocityConstraints()");
assert(_island != nullptr); assert(_island != null);
assert(_island->getNbJoints() > 0); assert(_island->getNbJoints() > 0);
// For each joint of the island // For each joint of the island
Joint** joints = _island->getJoints(); Joint** joints = _island->getJoints();
@ -51,7 +53,7 @@ void ConstraintSolver::solveVelocityConstraints(Island* _island) {
void ConstraintSolver::solvePositionConstraints(Island* _island) { void ConstraintSolver::solvePositionConstraints(Island* _island) {
PROFILE("ConstraintSolver::solvePositionConstraints()"); PROFILE("ConstraintSolver::solvePositionConstraints()");
assert(_island != nullptr); assert(_island != null);
assert(_island->getNbJoints() > 0); assert(_island->getNbJoints() > 0);
Joint** joints = _island->getJoints(); Joint** joints = _island->getJoints();
for (uint32_t iii=0; iii < _island->getNbJoints(); ++iii) { for (uint32_t iii=0; iii < _island->getNbJoints(); ++iii) {
@ -61,16 +63,16 @@ void ConstraintSolver::solvePositionConstraints(Island* _island) {
void ConstraintSolver::setConstrainedVelocitiesArrays(vec3* _constrainedLinearVelocities, void ConstraintSolver::setConstrainedVelocitiesArrays(vec3* _constrainedLinearVelocities,
vec3* _constrainedAngularVelocities) { vec3* _constrainedAngularVelocities) {
assert(_constrainedLinearVelocities != nullptr); assert(_constrainedLinearVelocities != null);
assert(_constrainedAngularVelocities != nullptr); assert(_constrainedAngularVelocities != null);
m_constraintSolverData.linearVelocities = _constrainedLinearVelocities; m_constraintSolverData.linearVelocities = _constrainedLinearVelocities;
m_constraintSolverData.angularVelocities = _constrainedAngularVelocities; m_constraintSolverData.angularVelocities = _constrainedAngularVelocities;
} }
void ConstraintSolver::setConstrainedPositionsArrays(vec3* _constrainedPositions, void ConstraintSolver::setConstrainedPositionsArrays(vec3* _constrainedPositions,
etk::Quaternion* _constrainedOrientations) { etk::Quaternion* _constrainedOrientations) {
assert(_constrainedPositions != nullptr); assert(_constrainedPositions != null);
assert(_constrainedOrientations != nullptr); assert(_constrainedOrientations != null);
m_constraintSolverData.positions = _constrainedPositions; m_constraintSolverData.positions = _constrainedPositions;
m_constraintSolverData.orientations = _constrainedOrientations; m_constraintSolverData.orientations = _constrainedOrientations;
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -27,10 +30,10 @@ namespace ephysics {
bool isWarmStartingActive; //!< True if warm starting of the solver is active bool isWarmStartingActive; //!< True if warm starting of the solver is active
/// Constructor /// Constructor
ConstraintSolverData(const etk::Map<RigidBody*, uint32_t>& refMapBodyToConstrainedVelocityIndex): ConstraintSolverData(const etk::Map<RigidBody*, uint32_t>& refMapBodyToConstrainedVelocityIndex):
linearVelocities(nullptr), linearVelocities(null),
angularVelocities(nullptr), angularVelocities(null),
positions(nullptr), positions(null),
orientations(nullptr), orientations(null),
mapBodyToConstrainedVelocityIndex(refMapBodyToConstrainedVelocityIndex) { mapBodyToConstrainedVelocityIndex(refMapBodyToConstrainedVelocityIndex) {
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -115,7 +118,6 @@ namespace ephysics {
bool isRestingContact; //!< True if the contact was existing last time step bool isRestingContact; //!< True if the contact was existing last time step
ContactPoint* externalContact; //!< Pointer to the external contact ContactPoint* externalContact; //!< Pointer to the external contact
}; };
/** /**
* @brief Contact solver int32_ternal data structure to store all the information relative to a contact manifold. * @brief Contact solver int32_ternal data structure to store all the information relative to a contact manifold.
*/ */
@ -163,76 +165,147 @@ namespace ephysics {
vec3* m_splitLinearVelocities; //!< Split linear velocities for the position contact solver (split impulse) vec3* m_splitLinearVelocities; //!< Split linear velocities for the position contact solver (split impulse)
vec3* m_splitAngularVelocities; //!< Split angular velocities for the position contact solver (split impulse) vec3* m_splitAngularVelocities; //!< Split angular velocities for the position contact solver (split impulse)
float m_timeStep; //!< Current time step float m_timeStep; //!< Current time step
ContactManifoldSolver* m_contactConstraints; //!< Contact constraints etk::Vector<ContactManifoldSolver> m_contactConstraints; //!< Contact constraints
uint32_t m_numberContactManifolds; //!< Number of contact constraints
vec3* m_linearVelocities; //!< Array of linear velocities vec3* m_linearVelocities; //!< Array of linear velocities
vec3* m_angularVelocities; //!< Array of angular velocities vec3* m_angularVelocities; //!< Array of angular velocities
const etk::Map<RigidBody*, uint32_t>& m_mapBodyToConstrainedVelocityIndex; //!< Reference to the map of rigid body to their index in the constrained velocities array const etk::Map<RigidBody*, uint32_t>& m_mapBodyToConstrainedVelocityIndex; //!< Reference to the map of rigid body to their index in the constrained velocities array
bool m_isWarmStartingActive; //!< True if the warm starting of the solver is active bool m_isWarmStartingActive; //!< True if the warm starting of the solver is active
bool m_isSplitImpulseActive; //!< True if the split impulse position correction is active bool m_isSplitImpulseActive; //!< True if the split impulse position correction is active
bool m_isSolveFrictionAtContactManifoldCenterActive; //!< True if we solve 3 friction constraints at the contact manifold center only instead of 2 friction constraints at each contact point bool m_isSolveFrictionAtContactManifoldCenterActive; //!< True if we solve 3 friction constraints at the contact manifold center only instead of 2 friction constraints at each contact point
/// Initialize the contact constraints before solving the system /**
* @brief Initialize the contact constraints before solving the system
*/
void initializeContactConstraints(); void initializeContactConstraints();
/// Apply an impulse to the two bodies of a constraint /**
void applyImpulse(const Impulse& impulse, const ContactManifoldSolver& manifold); * @brief Apply an impulse to the two bodies of a constraint
/// Apply an impulse to the two bodies of a constraint * @param[in] _impulse Impulse to apply
void applySplitImpulse(const Impulse& impulse, * @param[in] _manifold Constraint to apply the impulse
const ContactManifoldSolver& manifold); */
/// Compute the collision restitution factor from the restitution factor of each body void applyImpulse(const Impulse& _impulse, const ContactManifoldSolver& _manifold);
float computeMixedRestitutionFactor(RigidBody *body1, /**
RigidBody *body2) const; * @brief Apply an impulse to the two bodies of a constraint
/// Compute the mixed friction coefficient from the friction coefficient of each body * @param[in] _impulse Impulse to apply
float computeMixedFrictionCoefficient(RigidBody* body1, * @param[in] _manifold Constraint to apply the impulse
RigidBody* body2) const; */
/// Compute th mixed rolling resistance factor between two bodies void applySplitImpulse(const Impulse& _impulse, const ContactManifoldSolver& _manifold);
float computeMixedRollingResistance(RigidBody* body1, RigidBody* body2) const; /**
/// Compute the two unit orthogonal vectors "t1" and "t2" that span the tangential friction * @brief Compute the collision restitution factor from the restitution factor of each body
/// plane for a contact point. The two vectors have to be * @param[in] _body1 First body to compute
/// such that : t1 x t2 = contactNormal. * @param[in] _body2 Second body to compute
void computeFrictionVectors(const vec3& deltaVelocity, * @return Collision restitution factor
ContactPointSolver &contactPoint) const; */
/// Compute the two unit orthogonal vectors "t1" and "t2" that span the tangential friction float computeMixedRestitutionFactor(RigidBody* _body1, RigidBody* _body2) const;
/// plane for a contact manifold. The two vectors have to be /**
/// such that : t1 x t2 = contactNormal. * @brief Compute the mixed friction coefficient from the friction coefficient of each body
void computeFrictionVectors(const vec3& deltaVelocity, * @param[in] _body1 First body to compute
ContactManifoldSolver& contactPoint) const; * @param[in] _body2 Second body to compute
/// Compute a penetration constraint impulse * @return Mixed friction coefficient
const Impulse computePenetrationImpulse(float deltaLambda, */
const ContactPointSolver& contactPoint) const; float computeMixedFrictionCoefficient(RigidBody* _body1, RigidBody* _body2) const;
/// Compute the first friction constraint impulse /**
const Impulse computeFriction1Impulse(float deltaLambda, * @brief Compute the mixed rolling resistance factor between two bodies
const ContactPointSolver& contactPoint) const; * @param[in] _body1 First body to compute
/// Compute the second friction constraint impulse * @param[in] _body2 Second body to compute
const Impulse computeFriction2Impulse(float deltaLambda, * @return Mixed rolling resistance
const ContactPointSolver& contactPoint) const; */
float computeMixedRollingResistance(RigidBody* _body1, RigidBody* _body2) const;
/**
* @brief Compute the two unit orthogonal vectors "t1" and "t2" that span the tangential friction
* plane for a contact point. The two vectors have to be such that : t1 x t2 = contactNormal.
* @param[in] _deltaVelocity Velocity ratio (with the delta time step)
* @param[in,out] _contactPoint Contact point property
*/
void computeFrictionVectors(const vec3& _deltaVelocity, ContactPointSolver& _contactPoint) const;
/**
* @brief Compute the two unit orthogonal vectors "t1" and "t2" that span the tangential friction
* plane for a contact manifold. The two vectors have to be such that : t1 x t2 = contactNormal.
* @param[in] _deltaVelocity Velocity ratio (with the delta time step)
* @param[in,out] _contactPoint Contact point property
*/
void computeFrictionVectors(const vec3& _deltaVelocity, ContactManifoldSolver& _contactPoint) const;
/**
* @brief Compute a penetration constraint impulse
* @param[in] _deltaLambda Ratio to apply at the calculation.
* @param[in,out] _contactPoint Contact point property
* @return Impulse of the penetration result
*/
const Impulse computePenetrationImpulse(float _deltaLambda, const ContactPointSolver& _contactPoint) const;
/**
* @brief Compute the first friction constraint impulse
* @param[in] _deltaLambda Ratio to apply at the calculation.
* @param[in] _contactPoint Contact point property
* @return Impulse of the friction result
*/
const Impulse computeFriction1Impulse(float _deltaLambda, const ContactPointSolver& _contactPoint) const;
/**
* @brief Compute the second friction constraint impulse
* @param[in] _deltaLambda Ratio to apply at the calculation.
* @param[in] _contactPoint Contact point property
* @return Impulse of the friction result
*/
const Impulse computeFriction2Impulse(float _deltaLambda, const ContactPointSolver& _contactPoint) const;
public: public:
/// Constructor /**
ContactSolver(const etk::Map<RigidBody*, uint32_t>& mapBodyToVelocityIndex); * @brief Constructor
/// Destructor * @param[in] _mapBodyToVelocityIndex
virtual ~ContactSolver(); */
/// Initialize the constraint solver for a given island ContactSolver(const etk::Map<RigidBody*, uint32_t>& _mapBodyToVelocityIndex);
void initializeForIsland(float dt, Island* island); /**
/// Set the split velocities arrays * @brief Virtualize the destructor
void setSplitVelocitiesArrays(vec3* splitLinearVelocities, */
vec3* splitAngularVelocities); virtual ~ContactSolver() = default;
/// Set the constrained velocities arrays /**
void setConstrainedVelocitiesArrays(vec3* constrainedLinearVelocities, * @brief Initialize the constraint solver for a given island
vec3* constrainedAngularVelocities); * @param[in] _dt Delta step time
/// Warm start the solver. * @param[in] _island Island list property
*/
void initializeForIsland(float _dt, Island* _island);
/**
* @brief Set the split velocities arrays
* @param[in] _splitLinearVelocities Split linear velocities Table pointer (not free)
* @param[in] _splitAngularVelocities Split angular velocities Table pointer (not free)
*/
void setSplitVelocitiesArrays(vec3* _splitLinearVelocities, vec3* _splitAngularVelocities);
/**
* @brief Set the constrained velocities arrays
* @param[in] _constrainedLinearVelocities Constrained Linear velocities Table pointer (not free)
* @param[in] _constrainedAngularVelocities Constrained angular velocities Table pointer (not free)
*/
void setConstrainedVelocitiesArrays(vec3* _constrainedLinearVelocities, vec3* _constrainedAngularVelocities);
/**
* @brief Warm start the solver.
* For each constraint, we apply the previous impulse (from the previous step)
* at the beginning. With this technique, we will converge faster towards the solution of the linear system
*/
void warmStart(); void warmStart();
/// Store the computed impulses to use them to /**
/// warm start the solver at the next iteration * @brief Store the computed impulses to use them to warm start the solver at the next iteration
*/
void storeImpulses(); void storeImpulses();
/// Solve the contacts /**
* @brief Solve the contacts
*/
void solve(); void solve();
/// Return true if the split impulses position correction technique is used for contacts /**
* @brief Get the split impulses position correction technique is used for contacts
* @return true the split status is Enable
* @return true the split status is Disable
*/
bool isSplitImpulseActive() const; bool isSplitImpulseActive() const;
/// Activate or Deactivate the split impulses for contacts /**
void setIsSplitImpulseActive(bool isActive); * @brief Activate or Deactivate the split impulses for contacts
/// Activate or deactivate the solving of friction constraints at the center of * @param[in] _isActive status to set.
*/
void setIsSplitImpulseActive(bool _isActive);
/**
* @brief Activate or deactivate the solving of friction constraints at the center of
/// the contact manifold instead of solving them at each contact point /// the contact manifold instead of solving them at each contact point
void setIsSolveFrictionAtContactManifoldCenterActive(bool isActive); * @param[in] _isActive Enable or not the center inertie
/// Clean up the constraint solver */
void setIsSolveFrictionAtContactManifoldCenterActive(bool _isActive);
/**
* @brief Clean up the constraint solver
*/
void cleanup(); void cleanup();
}; };

View File

@ -1,9 +1,11 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#include <ephysics/engine/DynamicsWorld.hpp> #include <ephysics/engine/DynamicsWorld.hpp>
#include <ephysics/constraint/BallAndSocketJoint.hpp> #include <ephysics/constraint/BallAndSocketJoint.hpp>
#include <ephysics/constraint/SliderJoint.hpp> #include <ephysics/constraint/SliderJoint.hpp>
@ -20,13 +22,6 @@ ephysics::DynamicsWorld::DynamicsWorld(const vec3& _gravity):
m_isSleepingEnabled(SPLEEPING_ENABLED), m_isSleepingEnabled(SPLEEPING_ENABLED),
m_gravity(_gravity), m_gravity(_gravity),
m_isGravityEnabled(true), m_isGravityEnabled(true),
m_constrainedLinearVelocities(nullptr),
m_constrainedAngularVelocities(nullptr),
m_splitLinearVelocities(nullptr),
m_splitAngularVelocities(nullptr),
m_constrainedPositions(nullptr),
m_constrainedOrientations(nullptr),
m_islands(),
m_numberBodiesCapacity(0), m_numberBodiesCapacity(0),
m_sleepLinearVelocity(DEFAULT_SLEEP_LINEAR_VELOCITY), m_sleepLinearVelocity(DEFAULT_SLEEP_LINEAR_VELOCITY),
m_sleepAngularVelocity(DEFAULT_SLEEP_ANGULAR_VELOCITY), m_sleepAngularVelocity(DEFAULT_SLEEP_ANGULAR_VELOCITY),
@ -52,18 +47,18 @@ ephysics::DynamicsWorld::~DynamicsWorld() {
// Release the memory allocated for the islands // Release the memory allocated for the islands
for (auto &it: m_islands) { for (auto &it: m_islands) {
// Call the island destructor // Call the island destructor
delete it; ETK_DELETE(Island, it);
it = nullptr; it = null;
} }
m_islands.clear(); m_islands.clear();
// Release the memory allocated for the bodies velocity arrays // Release the memory allocated for the bodies velocity arrays
if (m_numberBodiesCapacity > 0) { if (m_numberBodiesCapacity > 0) {
delete[] m_splitLinearVelocities; m_splitLinearVelocities.clear();
delete[] m_splitAngularVelocities; m_splitAngularVelocities.clear();
delete[] m_constrainedLinearVelocities; m_constrainedLinearVelocities.clear();
delete[] m_constrainedAngularVelocities; m_constrainedAngularVelocities.clear();
delete[] m_constrainedPositions; m_constrainedPositions.clear();
delete[] m_constrainedOrientations; m_constrainedOrientations.clear();
} }
assert(m_joints.size() == 0); assert(m_joints.size() == 0);
assert(m_rigidBodies.size() == 0); assert(m_rigidBodies.size() == 0);
@ -85,7 +80,7 @@ void ephysics::DynamicsWorld::update(float timeStep) {
PROFILE("ephysics::DynamicsWorld::update()"); PROFILE("ephysics::DynamicsWorld::update()");
m_timeStep = timeStep; m_timeStep = timeStep;
// Notify the event listener about the beginning of an int32_ternal tick // Notify the event listener about the beginning of an int32_ternal tick
if (m_eventListener != nullptr) { if (m_eventListener != null) {
m_eventListener->beginInternalTick(); m_eventListener->beginInternalTick();
} }
// Reset all the contact manifolds lists of each body // Reset all the contact manifolds lists of each body
@ -112,7 +107,7 @@ void ephysics::DynamicsWorld::update(float timeStep) {
updateSleepingBodies(); updateSleepingBodies();
} }
// Notify the event listener about the end of an int32_ternal tick // Notify the event listener about the end of an int32_ternal tick
if (m_eventListener != nullptr) { if (m_eventListener != null) {
m_eventListener->endInternalTick(); m_eventListener->endInternalTick();
} }
// Reset the external force and torque applied to the bodies // Reset the external force and torque applied to the bodies
@ -178,23 +173,22 @@ void ephysics::DynamicsWorld::initVelocityArrays() {
uint32_t nbBodies = m_rigidBodies.size(); uint32_t nbBodies = m_rigidBodies.size();
if (m_numberBodiesCapacity != nbBodies && nbBodies > 0) { if (m_numberBodiesCapacity != nbBodies && nbBodies > 0) {
if (m_numberBodiesCapacity > 0) { if (m_numberBodiesCapacity > 0) {
delete[] m_splitLinearVelocities; m_splitLinearVelocities.clear();
delete[] m_splitAngularVelocities; m_splitAngularVelocities.clear();
} }
m_numberBodiesCapacity = nbBodies; m_numberBodiesCapacity = nbBodies;
// TODO : Use better memory allocation here m_splitLinearVelocities.clear();
m_splitLinearVelocities = new vec3[m_numberBodiesCapacity]; m_splitAngularVelocities.clear();
m_splitAngularVelocities = new vec3[m_numberBodiesCapacity]; m_constrainedLinearVelocities.clear();
m_constrainedLinearVelocities = new vec3[m_numberBodiesCapacity]; m_constrainedAngularVelocities.clear();
m_constrainedAngularVelocities = new vec3[m_numberBodiesCapacity]; m_constrainedPositions.clear();
m_constrainedPositions = new vec3[m_numberBodiesCapacity]; m_constrainedOrientations.clear();
m_constrainedOrientations = new etk::Quaternion[m_numberBodiesCapacity]; m_splitLinearVelocities.resize(m_numberBodiesCapacity, vec3(0,0,0));
assert(m_splitLinearVelocities != nullptr); m_splitAngularVelocities.resize(m_numberBodiesCapacity, vec3(0,0,0));
assert(m_splitAngularVelocities != nullptr); m_constrainedLinearVelocities.resize(m_numberBodiesCapacity, vec3(0,0,0));
assert(m_constrainedLinearVelocities != nullptr); m_constrainedAngularVelocities.resize(m_numberBodiesCapacity, vec3(0,0,0));
assert(m_constrainedAngularVelocities != nullptr); m_constrainedPositions.resize(m_numberBodiesCapacity, vec3(0,0,0));
assert(m_constrainedPositions != nullptr); m_constrainedOrientations.resize(m_numberBodiesCapacity, etk::Quaternion::identity());
assert(m_constrainedOrientations != nullptr);
} }
// Reset the velocities arrays // Reset the velocities arrays
for (uint32_t i=0; i<m_numberBodiesCapacity; i++) { for (uint32_t i=0; i<m_numberBodiesCapacity; i++) {
@ -262,13 +256,13 @@ void ephysics::DynamicsWorld::integrateRigidBodiesVelocities() {
void ephysics::DynamicsWorld::solveContactsAndConstraints() { void ephysics::DynamicsWorld::solveContactsAndConstraints() {
PROFILE("ephysics::DynamicsWorld::solveContactsAndConstraints()"); PROFILE("ephysics::DynamicsWorld::solveContactsAndConstraints()");
// Set the velocities arrays // Set the velocities arrays
m_contactSolver.setSplitVelocitiesArrays(m_splitLinearVelocities, m_splitAngularVelocities); m_contactSolver.setSplitVelocitiesArrays(&m_splitLinearVelocities[0], &m_splitAngularVelocities[0]);
m_contactSolver.setConstrainedVelocitiesArrays(m_constrainedLinearVelocities, m_contactSolver.setConstrainedVelocitiesArrays(&m_constrainedLinearVelocities[0],
m_constrainedAngularVelocities); &m_constrainedAngularVelocities[0]);
m_constraintSolver.setConstrainedVelocitiesArrays(m_constrainedLinearVelocities, m_constraintSolver.setConstrainedVelocitiesArrays(&m_constrainedLinearVelocities[0],
m_constrainedAngularVelocities); &m_constrainedAngularVelocities[0]);
m_constraintSolver.setConstrainedPositionsArrays(m_constrainedPositions, m_constraintSolver.setConstrainedPositionsArrays(&m_constrainedPositions[0],
m_constrainedOrientations); &m_constrainedOrientations[0]);
// ---------- Solve velocity constraints for joints and contacts ---------- // // ---------- Solve velocity constraints for joints and contacts ---------- //
// For each island of the world // For each island of the world
for (uint32_t islandIndex = 0; islandIndex < m_islands.size(); islandIndex++) { for (uint32_t islandIndex = 0; islandIndex < m_islands.size(); islandIndex++) {
@ -329,10 +323,10 @@ ephysics::RigidBody* ephysics::DynamicsWorld::createRigidBody(const etk::Transfo
// Compute the body ID // Compute the body ID
ephysics::bodyindex bodyID = computeNextAvailableBodyID(); ephysics::bodyindex bodyID = computeNextAvailableBodyID();
// Largest index cannot be used (it is used for invalid index) // Largest index cannot be used (it is used for invalid index)
assert(bodyID < std::numeric_limits<ephysics::bodyindex>::max()); assert(bodyID < UINT64_MAX);
// Create the rigid body // Create the rigid body
ephysics::RigidBody* rigidBody = new RigidBody(_transform, *this, bodyID); ephysics::RigidBody* rigidBody = ETK_NEW(RigidBody, _transform, *this, bodyID);
assert(rigidBody != nullptr); assert(rigidBody != null);
// Add the rigid body to the physics world // Add the rigid body to the physics world
m_bodies.add(rigidBody); m_bodies.add(rigidBody);
m_rigidBodies.add(rigidBody); m_rigidBodies.add(rigidBody);
@ -347,7 +341,7 @@ void ephysics::DynamicsWorld::destroyRigidBody(RigidBody* _rigidBody) {
m_freeBodiesIDs.pushBack(_rigidBody->getID()); m_freeBodiesIDs.pushBack(_rigidBody->getID());
// Destroy all the joints in which the rigid body to be destroyed is involved // Destroy all the joints in which the rigid body to be destroyed is involved
for (ephysics::JointListElement* element = _rigidBody->m_jointsList; for (ephysics::JointListElement* element = _rigidBody->m_jointsList;
element != nullptr; element != null;
element = element->next) { element = element->next) {
destroyJoint(element->joint); destroyJoint(element->joint);
} }
@ -357,33 +351,33 @@ void ephysics::DynamicsWorld::destroyRigidBody(RigidBody* _rigidBody) {
m_bodies.erase(m_bodies.find(_rigidBody)); m_bodies.erase(m_bodies.find(_rigidBody));
m_rigidBodies.erase(m_rigidBodies.find(_rigidBody)); m_rigidBodies.erase(m_rigidBodies.find(_rigidBody));
// Call the destructor of the rigid body // Call the destructor of the rigid body
delete _rigidBody; ETK_DELETE(RigidBody, _rigidBody);
_rigidBody = nullptr; _rigidBody = null;
} }
ephysics::Joint* ephysics::DynamicsWorld::createJoint(const ephysics::JointInfo& _jointInfo) { ephysics::Joint* ephysics::DynamicsWorld::createJoint(const ephysics::JointInfo& _jointInfo) {
Joint* newJoint = nullptr; Joint* newJoint = null;
// Allocate memory to create the new joint // Allocate memory to create the new joint
switch(_jointInfo.type) { switch(_jointInfo.type) {
// Ball-and-Socket joint // Ball-and-Socket joint
case BALLSOCKETJOINT: case BALLSOCKETJOINT:
newJoint = new BallAndSocketJoint(static_cast<const ephysics::BallAndSocketJointInfo&>(_jointInfo)); newJoint = ETK_NEW(BallAndSocketJoint, static_cast<const ephysics::BallAndSocketJointInfo&>(_jointInfo));
break; break;
// Slider joint // Slider joint
case SLIDERJOINT: case SLIDERJOINT:
newJoint = new SliderJoint(static_cast<const ephysics::SliderJointInfo&>(_jointInfo)); newJoint = ETK_NEW(SliderJoint, static_cast<const ephysics::SliderJointInfo&>(_jointInfo));
break; break;
// Hinge joint // Hinge joint
case HINGEJOINT: case HINGEJOINT:
newJoint = new HingeJoint(static_cast<const ephysics::HingeJointInfo&>(_jointInfo)); newJoint = ETK_NEW(HingeJoint, static_cast<const ephysics::HingeJointInfo&>(_jointInfo));
break; break;
// Fixed joint // Fixed joint
case FIXEDJOINT: case FIXEDJOINT:
newJoint = new FixedJoint(static_cast<const ephysics::FixedJointInfo&>(_jointInfo)); newJoint = ETK_NEW(FixedJoint, static_cast<const ephysics::FixedJointInfo&>(_jointInfo));
break; break;
default: default:
assert(false); assert(false);
return nullptr; return null;
} }
// If the collision between the two bodies of the constraint is disabled // If the collision between the two bodies of the constraint is disabled
if (!_jointInfo.isCollisionEnabled) { if (!_jointInfo.isCollisionEnabled) {
@ -399,8 +393,8 @@ ephysics::Joint* ephysics::DynamicsWorld::createJoint(const ephysics::JointInfo&
} }
void ephysics::DynamicsWorld::destroyJoint(Joint* _joint) { void ephysics::DynamicsWorld::destroyJoint(Joint* _joint) {
if (_joint == nullptr) { if (_joint == null) {
EPHY_WARNING("Request destroy nullptr joint"); EPHY_WARNING("Request destroy null joint");
return; return;
} }
// If the collision between the two bodies of the constraint was disabled // If the collision between the two bodies of the constraint was disabled
@ -418,19 +412,19 @@ void ephysics::DynamicsWorld::destroyJoint(Joint* _joint) {
_joint->m_body2->removeJointFrom_jointsList(_joint); _joint->m_body2->removeJointFrom_jointsList(_joint);
size_t nbBytes = _joint->getSizeInBytes(); size_t nbBytes = _joint->getSizeInBytes();
// Call the destructor of the joint // Call the destructor of the joint
delete _joint; ETK_DELETE(Joint, _joint);
_joint = nullptr; _joint = null;
} }
void ephysics::DynamicsWorld::addJointToBody(ephysics::Joint* _joint) { void ephysics::DynamicsWorld::addJointToBody(ephysics::Joint* _joint) {
if (_joint == nullptr) { if (_joint == null) {
EPHY_WARNING("Request add nullptr joint"); EPHY_WARNING("Request add null joint");
return; return;
} }
// Add the joint at the beginning of the linked list of joints of the first body // Add the joint at the beginning of the linked list of joints of the first body
_joint->m_body1->m_jointsList = new JointListElement(_joint, _joint->m_body1->m_jointsList); _joint->m_body1->m_jointsList = ETK_NEW(JointListElement, _joint, _joint->m_body1->m_jointsList);
// Add the joint at the beginning of the linked list of joints of the second body // Add the joint at the beginning of the linked list of joints of the second body
_joint->m_body2->m_jointsList = new JointListElement(_joint, _joint->m_body2->m_jointsList); _joint->m_body2->m_jointsList = ETK_NEW(JointListElement, _joint, _joint->m_body2->m_jointsList);
} }
void ephysics::DynamicsWorld::computeIslands() { void ephysics::DynamicsWorld::computeIslands() {
@ -438,8 +432,8 @@ void ephysics::DynamicsWorld::computeIslands() {
uint32_t nbBodies = m_rigidBodies.size(); uint32_t nbBodies = m_rigidBodies.size();
// Clear all the islands // Clear all the islands
for (auto &it: m_islands) { for (auto &it: m_islands) {
delete it; ETK_DELETE(Island, it);
it = nullptr; it = null;
} }
// Call the island destructor // Call the island destructor
m_islands.clear(); m_islands.clear();
@ -454,7 +448,7 @@ void ephysics::DynamicsWorld::computeIslands() {
} }
// Create a stack (using an array) for the rigid bodies to visit during the Depth First Search // Create a stack (using an array) for the rigid bodies to visit during the Depth First Search
etk::Vector<ephysics::RigidBody*> stackBodiesToVisit; etk::Vector<ephysics::RigidBody*> stackBodiesToVisit;
stackBodiesToVisit.resize(nbBodies, nullptr); stackBodiesToVisit.resize(nbBodies, null);
// For each rigid body of the world // For each rigid body of the world
for (etk::Set<ephysics::RigidBody*>::Iterator it = m_rigidBodies.begin(); it != m_rigidBodies.end(); ++it) { for (etk::Set<ephysics::RigidBody*>::Iterator it = m_rigidBodies.begin(); it != m_rigidBodies.end(); ++it) {
ephysics::RigidBody* body = *it; ephysics::RigidBody* body = *it;
@ -476,7 +470,7 @@ void ephysics::DynamicsWorld::computeIslands() {
stackIndex++; stackIndex++;
body->m_isAlreadyInIsland = true; body->m_isAlreadyInIsland = true;
// Create the new island // Create the new island
m_islands.pushBack(new Island(nbBodies, nbContactManifolds, m_joints.size())); m_islands.pushBack(ETK_NEW(Island, nbBodies, nbContactManifolds, m_joints.size()));
// While there are still some bodies to visit in the stack // While there are still some bodies to visit in the stack
while (stackIndex > 0) { while (stackIndex > 0) {
// Get the next body to visit from the stack // Get the next body to visit from the stack
@ -495,7 +489,7 @@ void ephysics::DynamicsWorld::computeIslands() {
// For each contact manifold in which the current body is involded // For each contact manifold in which the current body is involded
ephysics::ContactManifoldListElement* contactElement; ephysics::ContactManifoldListElement* contactElement;
for (contactElement = bodyToVisit->m_contactManifoldsList; for (contactElement = bodyToVisit->m_contactManifoldsList;
contactElement != nullptr; contactElement != null;
contactElement = contactElement->next) { contactElement = contactElement->next) {
ephysics::ContactManifold* contactManifold = contactElement->contactManifold; ephysics::ContactManifold* contactManifold = contactElement->contactManifold;
assert(contactManifold->getNbContactPoints() > 0); assert(contactManifold->getNbContactPoints() > 0);
@ -522,7 +516,7 @@ void ephysics::DynamicsWorld::computeIslands() {
// For each joint in which the current body is involved // For each joint in which the current body is involved
ephysics::JointListElement* jointElement; ephysics::JointListElement* jointElement;
for (jointElement = bodyToVisit->m_jointsList; for (jointElement = bodyToVisit->m_jointsList;
jointElement != nullptr; jointElement != null;
jointElement = jointElement->next) { jointElement = jointElement->next) {
ephysics::Joint* joint = jointElement->joint; ephysics::Joint* joint = jointElement->joint;
// Check if the current joint has already been added int32_to an island // Check if the current joint has already been added int32_to an island
@ -542,13 +536,7 @@ void ephysics::DynamicsWorld::computeIslands() {
otherBody->m_isAlreadyInIsland = true; otherBody->m_isAlreadyInIsland = true;
} }
} }
// Reset the isAlreadyIsland variable of the static bodies so that they m_islands.back()->resetStaticBobyNotInIsland();
// can also be included in the other islands
for (uint32_t i=0; i < m_islands.back()->m_numberBodies; i++) {
if (m_islands.back()->m_bodies[i]->getType() == STATIC) {
m_islands.back()->m_bodies[i]->m_isAlreadyInIsland = false;
}
}
} }
} }
@ -627,7 +615,7 @@ void ephysics::DynamicsWorld::testCollision(const ephysics::CollisionBody* _body
etk::Set<uint32_t> shapes1; etk::Set<uint32_t> shapes1;
// For each shape of the body // For each shape of the body
for (const ProxyShape* shape = _body->getProxyShapesList(); for (const ProxyShape* shape = _body->getProxyShapesList();
shape != nullptr; shape != null;
shape = shape->getNext()) { shape = shape->getNext()) {
shapes1.add(shape->m_broadPhaseID); shapes1.add(shape->m_broadPhaseID);
} }
@ -639,12 +627,12 @@ void ephysics::DynamicsWorld::testCollision(const ephysics::CollisionBody* _body
void ephysics::DynamicsWorld::testCollision(const ephysics::CollisionBody* _body1, const ephysics::CollisionBody* _body2, ephysics::CollisionCallback* _callback) { void ephysics::DynamicsWorld::testCollision(const ephysics::CollisionBody* _body1, const ephysics::CollisionBody* _body2, ephysics::CollisionCallback* _callback) {
// Create the sets of shapes // Create the sets of shapes
etk::Set<uint32_t> shapes1; etk::Set<uint32_t> shapes1;
for (const ProxyShape* shape=_body1->getProxyShapesList(); shape != nullptr; for (const ProxyShape* shape=_body1->getProxyShapesList(); shape != null;
shape = shape->getNext()) { shape = shape->getNext()) {
shapes1.add(shape->m_broadPhaseID); shapes1.add(shape->m_broadPhaseID);
} }
etk::Set<uint32_t> shapes2; etk::Set<uint32_t> shapes2;
for (const ProxyShape* shape=_body2->getProxyShapesList(); shape != nullptr; for (const ProxyShape* shape=_body2->getProxyShapesList(); shape != null;
shape = shape->getNext()) { shape = shape->getNext()) {
shapes2.add(shape->m_broadPhaseID); shapes2.add(shape->m_broadPhaseID);
} }

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once
@ -31,12 +34,12 @@ namespace ephysics {
vec3 m_gravity; //!< Gravity vector of the world vec3 m_gravity; //!< Gravity vector of the world
float m_timeStep; //!< Current frame time step (in seconds) float m_timeStep; //!< Current frame time step (in seconds)
bool m_isGravityEnabled; //!< True if the gravity force is on bool m_isGravityEnabled; //!< True if the gravity force is on
vec3* m_constrainedLinearVelocities; //!< Array of constrained linear velocities (state of the linear velocities after solving the constraints) etk::Vector<vec3> m_constrainedLinearVelocities; //!< Array of constrained linear velocities (state of the linear velocities after solving the constraints)
vec3* m_constrainedAngularVelocities; //!< Array of constrained angular velocities (state of the angular velocities after solving the constraints) etk::Vector<vec3> m_constrainedAngularVelocities; //!< Array of constrained angular velocities (state of the angular velocities after solving the constraints)
vec3* m_splitLinearVelocities; //!< Split linear velocities for the position contact solver (split impulse) etk::Vector<vec3> m_splitLinearVelocities; //!< Split linear velocities for the position contact solver (split impulse)
vec3* m_splitAngularVelocities; //!< Split angular velocities for the position contact solver (split impulse) etk::Vector<vec3> m_splitAngularVelocities; //!< Split angular velocities for the position contact solver (split impulse)
vec3* m_constrainedPositions; //!< Array of constrained rigid bodies position (for position error correction) etk::Vector<vec3> m_constrainedPositions; //!< Array of constrained rigid bodies position (for position error correction)
etk::Quaternion* m_constrainedOrientations; //!< Array of constrained rigid bodies orientation (for position error correction) etk::Vector<etk::Quaternion> m_constrainedOrientations; //!< Array of constrained rigid bodies orientation (for position error correction)
etk::Map<RigidBody*, uint32_t> m_mapBodyToConstrainedVelocityIndex; //!< Map body to their index in the constrained velocities array etk::Map<RigidBody*, uint32_t> m_mapBodyToConstrainedVelocityIndex; //!< Map body to their index in the constrained velocities array
etk::Vector<Island*> m_islands; //!< Array with all the islands of awaken bodies etk::Vector<Island*> m_islands; //!< Array with all the islands of awaken bodies
uint32_t m_numberBodiesCapacity; //!< Current allocated capacity for the bodies uint32_t m_numberBodiesCapacity; //!< Current allocated capacity for the bodies
@ -263,7 +266,7 @@ namespace ephysics {
void setTimeBeforeSleep(float timeBeforeSleep); void setTimeBeforeSleep(float timeBeforeSleep);
/** /**
* @brief Set an event listener object to receive events callbacks. * @brief Set an event listener object to receive events callbacks.
* @note If you use nullptr as an argument, the events callbacks will be disabled. * @note If you use null as an argument, the events callbacks will be disabled.
* @param[in] _eventListener Pointer to the event listener object that will receive * @param[in] _eventListener Pointer to the event listener object that will receive
* event callbacks during the simulation * event callbacks during the simulation
*/ */

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

View File

@ -1,7 +1,10 @@
/** @file /** @file
* @author Daniel Chappuis * Original ReactPhysics3D C++ library by Daniel Chappuis <http://www.reactphysics3d.com/> This code is re-licensed with permission from ReactPhysics3D author.
* @copyright 2010-2016 Daniel Chappuis * @author Daniel CHAPPUIS
* @license BSD 3 clauses (see license file) * @author Edouard DUPIN
* @copyright 2010-2016, Daniel Chappuis
* @copyright 2017, Edouard DUPIN
* @license MPL v2.0 (see license file)
*/ */
#pragma once #pragma once

Some files were not shown because too many files have changed in this diff Show More